Changeset 6


Ignore:
Timestamp:
Jan 7, 2017, 11:32:14 AM (7 years ago)
Author:
chronos
Message:
  • Modified: Formated all project source files using Delphi formatter as original indentation and other formatting was really bad.
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       if Border then inc(TSpriteSize[i].Top)
    313     until not Border or (TSpriteSize[i].Top=yyt*3-1);
    314     TSpriteSize[i].Right:=xxt*2;
    315     repeat
    316       Border:=true;
    317       for y:=0 to yyt*3-1 do
    318         if MaskLine[y]^[xSrc*(xxt*2+1)+TSpriteSize[i].Right,0]=0 then
    319           Border:=false;
    320       if Border then dec(TSpriteSize[i].Right)
    321     until not Border or (TSpriteSize[i].Right=TSpriteSize[i].Left);
    322     TSpriteSize[i].Bottom:=yyt*3;
    323     repeat
    324       Border:=true;
    325       for x:=0 to xxt*2-1 do
    326         if MaskLine[TSpriteSize[i].Bottom-1]^[1+xSrc*(xxt*2+1)+x,0]=0 then
    327           Border:=false;
    328       if Border then dec(TSpriteSize[i].Bottom)
    329     until not Border or (TSpriteSize[i].Bottom=TSpriteSize[i].Top);
    330     end
    331   end;
    332 Mask24.Free;
    333 
    334 if Borders<>nil then
    335   Borders.Free;
    336 Borders:=TBitmap.Create;
    337 Borders.PixelFormat:=pf24bit;
    338 Borders.Width:=xxt*2; Borders.Height:=(yyt*2)*nPl;
    339 BordersOK:=0;
    340 end;
    341 
    342 procedure Done;
    343 begin
    344 NoMap.Free;
    345 NoMap:=nil;
    346 LandPatch.Free;
    347 LandPatch:=nil;
    348 OceanPatch.Free;
    349 OceanPatch:=nil;
    350 Borders.Free;
    351 Borders:=nil;
    352 end;
    353 
    354 procedure Reset;
    355 begin
    356 BordersOK:=0;
    357 end;
    358 
    359 constructor TIsoMap.Create;
    360 begin
    361 inherited;
    362 FLeft:=0;
    363 FTop:=0;
    364 FRight:=0;
    365 FBottom:=0;
    366 AttLoc:=-1;
    367 DefLoc:=-1;
    368 FAdviceLoc:=-1;
    369 end;
    370 
    371 procedure TIsoMap.SetOutput(Output: TBitmap);
    372 begin
    373 FOutput:=Output;
    374 FLeft:=0;
    375 FTop:=0;
    376 FRight:=FOutput.Width;
    377 FBottom:=FOutput.Height;
    378 end;
    379 
    380 procedure TIsoMap.SetPaintBounds(Left, Top, Right, Bottom: integer);
    381 begin
    382 FLeft:=Left; FTop:=Top; FRight:=Right; FBottom:=Bottom;
    383 end;
    384 
    385 procedure TIsoMap.FillRect(x,y,Width,Height,Color: integer);
    386 begin
    387 if x<FLeft then
    388   begin Width:=Width-(FLeft-x); x:=FLeft end;
    389 if y<FTop then
    390   begin Height:=Height-(FTop-y); y:=FTop end;
    391 if x+Width>=FRight then Width:=FRight-x;
    392 if y+Height>=FBottom then Height:=FBottom-y;
    393 if (Width<=0) or (Height<=0) then
    394   exit;
    395  
    396 with FOutput.Canvas do
    397   begin
    398   Brush.Color:=Color;
    399   FillRect(Rect(x,y,x+Width,y+Height));
    400   Brush.Style:=bsClear;
    401   end
    402 end;
    403 
    404 procedure TIsoMap.Textout(x,y,Color: integer; const s: string);
    405 begin
    406 FOutput.Canvas.Font.Color:=Color;
    407 FOutput.Canvas.TextRect(Rect(FLeft,FTop,FRight,FBottom), x, y, s)
    408 end;
    409 
    410 procedure TIsoMap.BitBlt(Src: TBitmap; x,y,Width,Height,xSrc,ySrc,Rop: integer);
    411 begin
    412 if x<FLeft then
    413   begin Width:=Width-(FLeft-x); xSrc:=xSrc+(FLeft-x); x:=FLeft end;
    414 if y<FTop then
    415   begin Height:=Height-(FTop-y); ySrc:=ySrc+(FTop-y); y:=FTop end;
    416 if x+Width>=FRight then Width:=FRight-x;
    417 if y+Height>=FBottom then Height:=FBottom-y;
    418 if (Width<=0) or (Height<=0) then
    419   exit;
    420 
    421 Windows.BitBlt(FOutput.Canvas.Handle,x,y,Width,Height,Src.Canvas.Handle,xSrc,
    422   ySrc,Rop);
    423 end;
    424 
    425 procedure TIsoMap.Sprite(HGr,xDst,yDst,Width,Height,xGr,yGr: integer);
    426 begin
    427 BitBlt(GrExt[HGr].Mask,xDst,yDst,Width,Height,xGr,yGr,SRCAND);
    428 BitBlt(GrExt[HGr].Data,xDst,yDst,Width,Height,xGr,yGr,SRCPAINT);
    429 end;
    430 
    431 procedure TIsoMap.TSprite(xDst,yDst,grix: integer; PureBlack: boolean = false);
    432 var
    433 Width, Height, xSrc, ySrc: integer;
    434 begin
    435 Width:=TSpriteSize[grix].Right-TSpriteSize[grix].Left;
    436 Height:=TSpriteSize[grix].Bottom-TSpriteSize[grix].Top;
    437 xSrc:=1+grix mod 9 *(xxt*2+1)+TSpriteSize[grix].Left;
    438 ySrc:=1+grix div 9 *(yyt*3+1)+TSpriteSize[grix].Top;
    439 xDst:=xDst+TSpriteSize[grix].Left;
    440 yDst:=yDst-yyt+TSpriteSize[grix].Top;
    441 if xDst<FLeft then
    442   begin Width:=Width-(FLeft-xDst); xSrc:=xSrc+(FLeft-xDst); xDst:=FLeft end;
    443 if yDst<FTop then
    444   begin Height:=Height-(FTop-yDst); ySrc:=ySrc+(FTop-yDst); yDst:=FTop end;
    445 if xDst+Width>=FRight then Width:=FRight-xDst;
    446 if yDst+Height>=FBottom then Height:=FBottom-yDst;
    447 if (Width<=0) or (Height<=0) then
    448   exit;
    449  
    450 Windows.BitBlt(OutDC,xDst,yDst,Width,Height,MaskDC,xSrc,ySrc,SRCAND);
    451 if not PureBlack then
    452   Windows.BitBlt(OutDC,xDst,yDst,Width,Height,DataDC,xSrc,ySrc,SRCPAINT);
    453 end;
    454 
    455 procedure TIsoMap.PaintUnit(x,y:integer;const UnitInfo:TUnitInfo;Status:integer);
    456 var
    457 xsh,ysh,xGr,yGr,j,mixShow: integer;
    458 begin
    459 with UnitInfo do if (Owner=me) or (emix<>$FFFF) then
    460   begin
    461   if Job=jCity then mixShow:=-1 // building site
    462   else mixShow:=mix;
    463   if (Tribe[Owner].ModelPicture[mixShow].HGr=0) and (@OnInitEnemyModel<>nil) then
    464     if not OnInitEnemyModel(emix) then
    465       exit;
    466   xsh:=Tribe[Owner].ModelPicture[mixShow].xShield;
    467   ysh:=Tribe[Owner].ModelPicture[mixShow].yShield;
    468   {$IFNDEF SCR}if Status and usStay<>0 then j:=19
    469   else if Status and usRecover<>0 then j:=16
    470   else if Status and (usGoto or usEnhance)=usGoto or usEnhance then j:=18
    471   else if Status and usEnhance<>0 then j:=17
    472   else if Status and usGoto<>0 then j:=20
    473   else{$ENDIF} if Job=jCity then j:=jNone
    474   else j:=Job;
    475   if Flags and unMulti<>0 then
    476     Sprite(Tribe[Owner].symHGr,x+xsh-1+4,y+ysh-2,14,12,
    477       33+Tribe[Owner].sympix mod 10 *65,1+Tribe[Owner].sympix div 10 *49);
    478   Sprite(Tribe[Owner].symHGr,x+xsh-1,y+ysh-2,14,12,
    479     18+Tribe[Owner].sympix mod 10 *65,1+Tribe[Owner].sympix div 10 *49);
    480   FillRect(x+xsh,y+ysh+5,1+Health*11 div 100,3,ColorOfHealth(Health));
    481   if j>0 then
    482     begin
    483     xGr:=121+j mod 7 *9; yGr:=1+j div 7 *9;
    484     BitBlt(GrExt[HGrSystem].Mask,x+xsh+3,y+ysh+9,8,8,xGr,yGr,SRCAND);
    485     Sprite(HGrSystem,x+xsh+2,y+ysh+8,8,8,xGr,yGr);
    486     end;
    487   with Tribe[Owner].ModelPicture[mixShow] do
    488     Sprite(HGr,x,y,64,48,pix mod 10 *65+1,pix div 10 *49+1);
    489   if Flags and unFortified<>0 then
    490     begin
    491 {    OutDC:=FOutput.Canvas.Handle;
    492     DataDC:=GrExt[HGrTerrain].Data.Canvas.Handle;
    493     MaskDC:=GrExt[HGrTerrain].Mask.Canvas.Handle;
    494     TSprite(x,y+16,12*9+7);}
    495     Sprite(HGrStdUnits,x,y,xxu*2,yyu*2,1+6*(xxu*2+1),1);
    496     end
    497   end
    498 end;{PaintUnit}
    499 
    500 procedure TIsoMap.PaintCity(x,y:integer; const CityInfo:TCityInfo;
    501   accessory: boolean);
    502 var
    503 age,cHGr,cpix,xGr,xShield,yShield,LabelTextColor,LabelLength: integer;
    504 cpic:TCityPicture;
    505 s:string;
    506 begin
    507 age:=GetAge(CityInfo.Owner);
    508 if CityInfo.Size<5 then xGr:=0
    509 else if CityInfo.Size<9 then xGr:=1
    510 else if CityInfo.Size<13 then xGr:=2
    511 else xGr:=3;
    512 Tribe[CityInfo.Owner].InitAge(age);
    513 if age<2 then
    514   begin
    515   cHGr:=Tribe[CityInfo.Owner].cHGr;
    516   cpix:=Tribe[CityInfo.Owner].cpix;
    517   if (ciWalled and CityInfo.Flags=0)
    518     or (GrExt[cHGr].Data.Canvas.Pixels[(xGr+4)*65,cpix*49+48]=$00FFFF) then
    519     Sprite(cHGr,x-xxc,y-2*yyc,xxc*2,yyc*3,xGr*(xxc*2+1)+1,1+cpix*(yyc*3+1));
    520   if ciWalled and CityInfo.Flags<>0 then
    521     Sprite(cHGr,x-xxc,y-2*yyc,xxc*2,yyc*3,(xGr+4)*(xxc*2+1)+1,1+cpix*(yyc*3+1));
    522   end
    523 else
    524   begin
    525   if ciWalled and CityInfo.Flags<>0 then
    526     Sprite(HGrCities,x-xxt,y-2*yyt,2*xxt,3*yyt,(xGr+4)*(2*xxt+1)+1,1+(age-2)*(3*yyt+1))
    527   else Sprite(HGrCities,x-xxt,y-2*yyt,2*xxt,3*yyt,xGr*(2*xxt+1)+1,1+(age-2)*(3*yyt+1));
    528   end;
    529 
    530 if not Accessory then exit;
    531 
    532 {if ciCapital and CityInfo.Flags<>0 then
    533   Sprite(Tribe[CityInfo.Owner].symHGr,x+cpic.xf,y-13+cpic.yf,13,14,
    534     1+Tribe[CityInfo.Owner].sympix mod 10 *65,
    535     1+Tribe[CityInfo.Owner].sympix div 10 *49); {capital -- paint flag}
    536 
    537 if MyMap[CityInfo.Loc] and fObserved<>0 then
    538   begin
    539   if age<2 then
    540     begin
    541     cpic:=Tribe[CityInfo.Owner].CityPicture[xGr];
    542     xShield:=x-xxc+cpic.xShield;
    543     yShield:=y-2*yyc+cpic.yShield;
    544     end
    545   else
    546     begin
    547     cpic:=CitiesPictures[age,xGr];
    548     xShield:=x-xxt+cpic.xShield;
    549     yShield:=y-2*yyt+cpic.yShield;
    550     end;
    551   s:=IntToStr(CityInfo.Size);
    552   LabelLength:=FOutput.Canvas.TextWidth(s);
    553   FillRect(xShield,yShield,LabelLength+4,16,$000000);
    554   if MyMap[CityInfo.Loc] and (fUnit or fObserved)=fObserved then
    555     // empty city
    556     LabelTextColor:=Tribe[CityInfo.Owner].Color
    557   else
    558     begin
    559     FillRect(xShield+1,yShield+1,LabelLength+2,14,Tribe[CityInfo.Owner].Color);
    560     LabelTextColor:=$000000;
    561     end;
    562   Textout(xShield+2,yShield-1,LabelTextColor,s);
    563   end
    564 end;{PaintCity}
    565 
    566 function PoleTile(Loc: integer): integer;
    567 begin {virtual pole tile}
    568 result:=fUNKNOWN;
    569 if Loc<-2*G.lx then
    570 else if Loc<-G.lx then
    571   begin
    572   if (MyMap[dLoc(Loc,0,2)] and fTerrain<>fUNKNOWN)
    573     and (MyMap[dLoc(Loc,-2,2)] and fTerrain<>fUNKNOWN)
    574     and (MyMap[dLoc(Loc,2,2)] and fTerrain<>fUNKNOWN) then result:=fArctic;
    575   if (MyMap[dLoc(Loc,0,2)] and fObserved<>0)
    576     and (MyMap[dLoc(Loc,-2,2)] and fObserved<>0)
    577     and (MyMap[dLoc(Loc,2,2)] and fObserved<>0) then
    578     result:=result or fObserved
    579   end
    580 else if Loc<0 then
    581   begin
    582   if (MyMap[dLoc(Loc,-1,1)] and fTerrain<>fUNKNOWN)
    583     and (MyMap[dLoc(Loc,1,1)] and fTerrain<>fUNKNOWN) then result:=fArctic;
    584   if (MyMap[dLoc(Loc,-1,1)] and fObserved<>0)
    585     and (MyMap[dLoc(Loc,1,1)] and fObserved<>0) then
    586     result:=result or fObserved
    587   end
    588 else if Loc<G.lx*(G.ly+1) then
    589   begin
    590   if (MyMap[dLoc(Loc,-1,-1)] and fTerrain<>fUNKNOWN)
    591     and (MyMap[dLoc(Loc,1,-1)] and fTerrain<>fUNKNOWN) then result:=fArctic;
    592   if (MyMap[dLoc(Loc,-1,-1)] and fObserved<>0)
    593     and (MyMap[dLoc(Loc,1,-1)] and fObserved<>0) then
    594     result:=result or fObserved
    595   end
    596 else if Loc<G.lx*(G.ly+2) then
    597   begin
    598   if (MyMap[dLoc(Loc,0,-2)] and fTerrain<>fUNKNOWN)
    599     and (MyMap[dLoc(Loc,-2,-2)] and fTerrain<>fUNKNOWN)
    600     and (MyMap[dLoc(Loc,2,-2)] and fTerrain<>fUNKNOWN) then result:=fArctic;
    601   if (MyMap[dLoc(Loc,0,-2)] and fObserved<>0)
    602     and (MyMap[dLoc(Loc,-2,-2)] and fObserved<>0)
    603     and (MyMap[dLoc(Loc,2,-2)] and fObserved<>0) then
    604     result:=result or fObserved
    605   end
    606 end;
    607 
    608 const
    609 Dirx: array[0..7] of integer=(1,2,1,0,-1,-2,-1,0);
    610 Diry: array[0..7] of integer=(-1,0,1,2,1,0,-1,-2);
    611 
    612 function TIsoMap.Connection4(Loc,Mask,Value:integer):integer;
    613 begin
    614 result:=0;
    615 if dLoc(Loc,1,-1)>=0 then
    616   begin
    617   if MyMap[dLoc(Loc,1,-1)] and Mask=Cardinal(Value) then inc(result,1);
    618   if MyMap[dLoc(Loc,-1,-1)] and Mask=Cardinal(Value) then inc(result,8);
    619   end;
    620 if dLoc(Loc,1,1)<G.lx*G.ly then
    621   begin
    622   if MyMap[dLoc(Loc,1,1)] and Mask=Cardinal(Value) then inc(result,2);
    623   if MyMap[dLoc(Loc,-1,1)] and Mask=Cardinal(Value) then inc(result,4);
    624   end
    625 end;
    626 
    627 function TIsoMap.Connection8(Loc,Mask:integer):integer;
    628 var
    629 Dir, ConnLoc: integer;
    630 begin
    631 result:=0;
    632 for Dir:=0 to 7 do
    633   begin
    634   ConnLoc:=dLoc(Loc,Dirx[Dir],Diry[Dir]);
    635   if (ConnLoc>=0) and (ConnLoc<G.lx*G.ly) and (MyMap[ConnLoc] and Mask<>0) then
    636     inc(result,1 shl Dir);
    637   end
    638 end;
    639 
    640 function TIsoMap.OceanConnection(Loc: integer): integer;
    641 var
    642 Dir,ConnLoc: integer;
    643 begin
    644 result:=0;
    645 for Dir:=0 to 7 do
    646   begin
    647   ConnLoc:=dLoc(Loc,Dirx[Dir],Diry[Dir]);
    648   if (ConnLoc<0) or (ConnLoc>=G.lx*G.ly)
    649     or ((MyMap[ConnLoc]-2) and fTerrain<13) then
    650     inc(result,1 shl Dir);
    651   end
    652 end;
    653 
    654 procedure TIsoMap.PaintShore(x,y,Loc:integer);
    655 var
    656 Conn,Tile:integer;
    657 begin
    658 if (y<=FTop-yyt*2) or (y>FBottom) or (x<=FLeft-xxt*2) or (x>FRight) then exit;
    659 if (Loc<0) or (Loc>=G.lx*G.ly) then exit;
    660 Tile:=MyMap[Loc];
    661 if Tile and fTerrain>=fGrass then exit;
    662 Conn:=OceanConnection(Loc);
    663 if Conn=0 then exit;
    664 
    665 BitBlt(GrExt[HGrTerrain].Data,x+xxt div 2,y,xxt,yyt,
    666   1+(Conn shr 6 +Conn and 1 shl 2)*(xxt*2+1),
    667   1+yyt+(16+Tile and fTerrain)*(yyt*3+1),SRCPAINT);
    668 BitBlt(GrExt[HGrTerrain].Data,x+xxt,y+yyt div 2,xxt,yyt,
    669   1+(Conn and 7)*(xxt*2+1)+xxt,
    670   1+yyt*2+(16+Tile and fTerrain)*(yyt*3+1),SRCPAINT);
    671 BitBlt(GrExt[HGrTerrain].Data,x+xxt div 2,y+yyt,xxt,yyt,
    672   1+(Conn shr 2 and 7)*(xxt*2+1)+xxt,
    673   1+yyt+(16+Tile and fTerrain)*(yyt*3+1),SRCPAINT);
    674 BitBlt(GrExt[HGrTerrain].Data,x,y+yyt div 2,xxt,yyt,
    675   1+(Conn shr 4 and 7)*(xxt*2+1),
    676   1+yyt*2+(16+Tile and fTerrain)*(yyt*3+1),SRCPAINT);
    677 Conn:=Connection4(Loc,fTerrain,fUNKNOWN); {dither to black}
    678 if Conn and 1<>0 then
    679   BitBlt(GrExt[HGrTerrain].Mask,x+xxt,y,xxt,yyt,1+7*(xxt*2+1)+xxt,
    680     1+yyt+15*(yyt*3+1),SRCAND);
    681 if Conn and 2<>0 then
    682   BitBlt(GrExt[HGrTerrain].Mask,x+xxt,y+yyt,xxt,yyt,1+7*(xxt*2+1)+xxt,
    683     1+yyt*2+15*(yyt*3+1),SRCAND);
    684 if Conn and 4<>0 then
    685   BitBlt(GrExt[HGrTerrain].Mask,x,y+yyt,xxt,yyt,1+7*(xxt*2+1),
    686     1+yyt*2+15*(yyt*3+1),SRCAND);
    687 if Conn and 8<>0 then
    688   BitBlt(GrExt[HGrTerrain].Mask,x,y,xxt,yyt,1+7*(xxt*2+1),
    689     1+yyt+15*(yyt*3+1),SRCAND);
    690 end;
    691 
    692 procedure TIsoMap.PaintTileExtraTerrain(x,y,Loc: integer);
    693 var
    694 Dir,Conn,RRConn,yGr,Tile,yLoc:integer;
    695 begin
    696 if (Loc<0) or (Loc>=G.lx*G.ly) or (y<=-yyt*2) or (y>FOutput.Height)
    697   or (x<=-xxt*2) or (x>FOutput.Width) then exit;
    698 Tile:=MyMap[Loc];
    699 if Tile and fTerrain=fForest then
    700   begin
    701   yLoc:=Loc div G.lx;
    702   if IsJungle(yLoc) then yGr:=18
    703   else yGr:=3;
    704   Conn:=Connection4(Loc,fTerrain,Tile and fTerrain);
    705   if (yLoc=(G.ly-2) div 4) or (G.ly-1-yLoc=(G.ly+2) div 4) then
    706     Conn:=Conn and not 6 // no connection to south
    707   else if (yLoc=(G.ly+2) div 4) or (G.ly-1-yLoc=(G.ly-2) div 4) then
    708     Conn:=Conn and not 9; // no connection to north
    709   TSprite(x,y,Conn mod 8+(yGr+Conn div 8)*9);
    710   end
    711 else if Tile and fTerrain in [fHills,fMountains,fForest] then
    712   begin
    713   yGr:=3+2*(Tile and fTerrain-fForest);
    714   Conn:=Connection4(Loc,fTerrain,Tile and fTerrain);
    715   TSprite(x,y,Conn mod 8+(yGr+Conn div 8)*9);
    716   end
    717 else if Tile and fDeadLands<>0 then
    718   TSprite(x,y,2*9+6);
    719 
    720 if ShowObjects then
    721   begin
    722   if Tile and fTerImp=tiFarm then TSprite(x,y,109) {farmland}
    723   else if Tile and fTerImp=tiIrrigation then TSprite(x,y,108); // irrigation
    724   end;
    725 if Tile and fRiver<>0 then
    726   begin
    727   Conn:=Connection4(Loc,fRiver,fRiver) or Connection4(Loc,fTerrain,fShore)
    728     or Connection4(Loc,fTerrain,fUNKNOWN);
    729   TSprite(x,y,Conn mod 8+(13+Conn div 8)*9);
    730   end;
    731 
    732 if Tile and fTerrain<fGrass then
    733   begin
    734   Conn:=Connection4(Loc,fRiver,fRiver);
    735   for Dir:=0 to 3 do if Conn and (1 shl Dir)<>0 then {river mouths}
    736     TSprite(x,y,15*9+Dir);
    737   if ShowObjects then
    738     begin
    739     Conn:=Connection8(Loc,fCanal);
    740     for Dir:=0 to 7 do if Conn and (1 shl Dir)<>0 then {canal mouths}
    741       TSprite(x,y,20*9+1+Dir);
    742     end
    743   end;
    744 
    745 if ShowObjects then
    746   begin
    747   if (Tile and fCanal<>0) or (Tile and fCity<>0) then
    748     begin // paint canal connections
    749     Conn:=Connection8(Loc,fCanal or fCity);
    750     if Tile and fCanal<>0 then Conn:=Conn or ($FF-OceanConnection(Loc));
    751     if Conn=0 then
    752       begin
    753       if Tile and fCanal<>0 then TSprite(x,y,99)
    754       end
    755     else for Dir:=0 to 7 do if (1 shl Dir) and Conn<>0 then
    756       TSprite(x,y,100+Dir);
    757     end;
    758   if Tile and (fRR or fCity)<>0 then RRConn:=Connection8(Loc,fRR or fCity)
    759   else RRConn:=0;
    760   if Tile and (fRoad or fRR or fCity)<>0 then
    761     begin // paint road connections
    762     Conn:=Connection8(Loc,fRoad or fRR or fCity) and not RRConn;
    763     if (Conn=0) and (Tile and (fRR or fCity)=0) then TSprite(x,y,81)
    764     else if Conn>0 then
    765       for Dir:=0 to 7 do if (1 shl Dir) and Conn<>0 then TSprite(x,y,82+Dir);
    766     end;
    767   // paint railroad connections
    768   if (Tile and fRR<>0) and (RRConn=0) then TSprite(x,y,90)
    769   else if RRConn>0 then
    770     for Dir:=0 to 7 do if (1 shl Dir) and RRConn<>0 then TSprite(x,y,91+Dir);
    771   end;
    772 end;
    773 
    774 // (x,y) is top left pixel of (2*xxt,3*yyt) rectangle
    775 procedure TIsoMap.PaintTileObjects(x,y,Loc,CityLoc,CityOwner:integer;
    776   UseBlink: boolean);
    777 type
    778 TLine=array[0..9*65,0..2] of Byte;
    779 var
    780 p1,p2,uix,cix,dy,Loc1,Tile,Multi,Destination: integer;
    781 CityInfo:TCityInfo;
    782 UnitInfo:TUnitInfo;
    783 fog: boolean;
    784 
    785   procedure NameCity;
    786   var
    787   cix,xs,w: integer;
    788   BehindCityInfo:TCityInfo;
    789   s: string;
    790   IsCapital: boolean;
    791   begin
    792   BehindCityInfo.Loc:=Loc-2*G.lx;
    793   if ShowCityNames and (Options and (1 shl moEditMode)=0)
    794     and (BehindCityInfo.Loc>=0) and (BehindCityInfo.Loc<G.lx*G.ly)
    795     and (MyMap[BehindCityInfo.Loc] and fCity<>0) then
    796     begin
    797     GetCityInfo(BehindCityInfo.Loc,cix,BehindCityInfo);
    798     IsCapital:= BehindCityInfo.Flags and ciCapital<>0;
    799     {if Showuix and (cix>=0) then s:=IntToStr(cix)
    800     else} s:=CityName(BehindCityInfo.ID);
    801     w:=FOutput.Canvas.TextWidth(s);
    802     xs:=x+xxt-(w+1) div 2;
    803     if IsCapital then
    804       FOutput.Canvas.Font.Style:=FOutput.Canvas.Font.Style+[fsUnderline];
    805     Textout(xs+1,y-9,$000000,s);
    806     Textout(xs,y-10,$FFFFFF,s);
    807     if IsCapital then
    808       FOutput.Canvas.Font.Style:=FOutput.Canvas.Font.Style-[fsUnderline];
    809     end;
    810   end;
    811 
    812   procedure ShowSpacePort;
    813   begin
    814   if ShowObjects and (Options and (1 shl moEditMode)=0) and (Tile and fCity<>0)
    815     and (CityInfo.Flags and ciSpacePort<>0) then
    816     TSprite(x+xxt,y-6,12*9+5);
    817   end;
    818 
    819   procedure PaintBorder;
    820   var
    821   dx,dy: integer;
    822   Line: ^TLine;
    823   begin
    824   if ShowBorder and (Loc>=0) and (Loc<G.lx*G.ly)
    825     and (Tile and fTerrain<>fUNKNOWN) then
    826     begin
    827     p1:=MyRO.Territory[Loc];
    828     if (p1>=0) and (ShowMyBorder or (p1<>me)) then
    829       begin
    830       if BordersOK and (1 shl p1)=0 then
    831         begin
    832         Windows.BitBlt(Borders.Canvas.Handle,0,p1*(yyt*2),xxt*2,yyt*2,
    833           GrExt[HGrTerrain].Data.Canvas.Handle,1+8*(xxt*2+1),1+yyt+16*(yyt*3+1),SRCCOPY);
    834         for dy:=0 to yyt*2-1 do
    835           begin
    836           Line:=Borders.ScanLine[p1*(yyt*2)+dy];
    837           for dx:=0 to xxt*2-1 do if Line[dx,0]=99 then
     351      i := ySrc * 9 + xSrc;
     352      TSpriteSize[i].Left := 0;
     353      repeat
     354        Border := true;
     355        for y := 0 to yyt * 3 - 1 do
     356          if MaskLine[y]^[1 + xSrc * (xxt * 2 + 1) + TSpriteSize[i].Left, 0] = 0
     357          then
     358            Border := false;
     359        if Border then
     360          inc(TSpriteSize[i].Left) until not Border or
     361            (TSpriteSize[i].Left = xxt * 2 - 1);
     362        TSpriteSize[i].Top := 0;
     363        repeat
     364          Border := true;
     365          for x := 0 to xxt * 2 - 1 do
     366            if MaskLine[TSpriteSize[i].Top]^[1 + xSrc * (xxt * 2 + 1) + x, 0] = 0
     367            then
     368              Border := false;
     369          if Border then
     370            inc(TSpriteSize[i].Top) until not Border or
     371              (TSpriteSize[i].Top = yyt * 3 - 1);
     372          TSpriteSize[i].Right := xxt * 2;
     373          repeat
     374            Border := true;
     375            for y := 0 to yyt * 3 - 1 do
     376              if MaskLine[y]^[xSrc * (xxt * 2 + 1) + TSpriteSize[i].Right, 0] = 0
     377              then
     378                Border := false;
     379            if Border then
     380              dec(TSpriteSize[i].Right) until not Border or
     381                (TSpriteSize[i].Right = TSpriteSize[i].Left);
     382            TSpriteSize[i].Bottom := yyt * 3;
     383            repeat
     384              Border := true;
     385              for x := 0 to xxt * 2 - 1 do
     386                if MaskLine[TSpriteSize[i].Bottom - 1]^
     387                  [1 + xSrc * (xxt * 2 + 1) + x, 0] = 0 then
     388                  Border := false;
     389              if Border then
     390                dec(TSpriteSize[i].Bottom) until not Border or
     391                  (TSpriteSize[i].Bottom = TSpriteSize[i].Top);
     392            end
     393          end;
     394          Mask24.Free;
     395
     396          if Borders <> nil then
     397            Borders.Free;
     398          Borders := TBitmap.Create;
     399          Borders.PixelFormat := pf24bit;
     400          Borders.Width := xxt * 2;
     401          Borders.Height := (yyt * 2) * nPl;
     402          BordersOK := 0;
     403        end;
     404
     405        procedure Done;
     406        begin
     407          NoMap.Free;
     408          NoMap := nil;
     409          LandPatch.Free;
     410          LandPatch := nil;
     411          OceanPatch.Free;
     412          OceanPatch := nil;
     413          Borders.Free;
     414          Borders := nil;
     415        end;
     416
     417        procedure Reset;
     418        begin
     419          BordersOK := 0;
     420        end;
     421
     422        constructor TIsoMap.Create;
     423        begin
     424          inherited;
     425          FLeft := 0;
     426          FTop := 0;
     427          FRight := 0;
     428          FBottom := 0;
     429          AttLoc := -1;
     430          DefLoc := -1;
     431          FAdviceLoc := -1;
     432        end;
     433
     434        procedure TIsoMap.SetOutput(Output: TBitmap);
     435        begin
     436          FOutput := Output;
     437          FLeft := 0;
     438          FTop := 0;
     439          FRight := FOutput.Width;
     440          FBottom := FOutput.Height;
     441        end;
     442
     443        procedure TIsoMap.SetPaintBounds(Left, Top, Right, Bottom: integer);
     444        begin
     445          FLeft := Left;
     446          FTop := Top;
     447          FRight := Right;
     448          FBottom := Bottom;
     449        end;
     450
     451        procedure TIsoMap.FillRect(x, y, Width, Height, Color: integer);
     452        begin
     453          if x < FLeft then
     454          begin
     455            Width := Width - (FLeft - x);
     456            x := FLeft
     457          end;
     458          if y < FTop then
     459          begin
     460            Height := Height - (FTop - y);
     461            y := FTop
     462          end;
     463          if x + Width >= FRight then
     464            Width := FRight - x;
     465          if y + Height >= FBottom then
     466            Height := FBottom - y;
     467          if (Width <= 0) or (Height <= 0) then
     468            exit;
     469
     470          with FOutput.Canvas do
     471          begin
     472            Brush.Color := Color;
     473            FillRect(Rect(x, y, x + Width, y + Height));
     474            Brush.Style := bsClear;
     475          end
     476        end;
     477
     478        procedure TIsoMap.Textout(x, y, Color: integer; const s: string);
     479        begin
     480          FOutput.Canvas.Font.Color := Color;
     481          FOutput.Canvas.TextRect(Rect(FLeft, FTop, FRight, FBottom), x, y, s)
     482        end;
     483
     484        procedure TIsoMap.BitBlt(Src: TBitmap; x, y, Width, Height, xSrc, ySrc,
     485          Rop: integer);
     486        begin
     487          if x < FLeft then
     488          begin
     489            Width := Width - (FLeft - x);
     490            xSrc := xSrc + (FLeft - x);
     491            x := FLeft
     492          end;
     493          if y < FTop then
     494          begin
     495            Height := Height - (FTop - y);
     496            ySrc := ySrc + (FTop - y);
     497            y := FTop
     498          end;
     499          if x + Width >= FRight then
     500            Width := FRight - x;
     501          if y + Height >= FBottom then
     502            Height := FBottom - y;
     503          if (Width <= 0) or (Height <= 0) then
     504            exit;
     505
     506          Windows.BitBlt(FOutput.Canvas.Handle, x, y, Width, Height,
     507            Src.Canvas.Handle, xSrc, ySrc, Rop);
     508        end;
     509
     510        procedure TIsoMap.Sprite(HGr, xDst, yDst, Width, Height, xGr,
     511          yGr: integer);
     512        begin
     513          BitBlt(GrExt[HGr].Mask, xDst, yDst, Width, Height, xGr, yGr, SRCAND);
     514          BitBlt(GrExt[HGr].Data, xDst, yDst, Width, Height, xGr, yGr,
     515            SRCPAINT);
     516        end;
     517
     518        procedure TIsoMap.TSprite(xDst, yDst, grix: integer;
     519          PureBlack: boolean = false);
     520        var
     521          Width, Height, xSrc, ySrc: integer;
     522        begin
     523          Width := TSpriteSize[grix].Right - TSpriteSize[grix].Left;
     524          Height := TSpriteSize[grix].Bottom - TSpriteSize[grix].Top;
     525          xSrc := 1 + grix mod 9 * (xxt * 2 + 1) + TSpriteSize[grix].Left;
     526          ySrc := 1 + grix div 9 * (yyt * 3 + 1) + TSpriteSize[grix].Top;
     527          xDst := xDst + TSpriteSize[grix].Left;
     528          yDst := yDst - yyt + TSpriteSize[grix].Top;
     529          if xDst < FLeft then
     530          begin
     531            Width := Width - (FLeft - xDst);
     532            xSrc := xSrc + (FLeft - xDst);
     533            xDst := FLeft
     534          end;
     535          if yDst < FTop then
     536          begin
     537            Height := Height - (FTop - yDst);
     538            ySrc := ySrc + (FTop - yDst);
     539            yDst := FTop
     540          end;
     541          if xDst + Width >= FRight then
     542            Width := FRight - xDst;
     543          if yDst + Height >= FBottom then
     544            Height := FBottom - yDst;
     545          if (Width <= 0) or (Height <= 0) then
     546            exit;
     547
     548          Windows.BitBlt(OutDC, xDst, yDst, Width, Height, MaskDC, xSrc,
     549            ySrc, SRCAND);
     550          if not PureBlack then
     551            Windows.BitBlt(OutDC, xDst, yDst, Width, Height, DataDC, xSrc, ySrc,
     552              SRCPAINT);
     553        end;
     554
     555        procedure TIsoMap.PaintUnit(x, y: integer; const UnitInfo: TUnitInfo;
     556          Status: integer);
     557        var
     558          xsh, ysh, xGr, yGr, j, mixShow: integer;
     559        begin
     560          with UnitInfo do
     561            if (Owner = me) or (emix <> $FFFF) then
    838562            begin
    839             Line[dx,0]:=Tribe[p1].Color shr 16 and $FF;
    840             Line[dx,1]:=Tribe[p1].Color shr 8 and $FF;
    841             Line[dx,2]:=Tribe[p1].Color and $FF;
     563              if Job = jCity then
     564                mixShow := -1 // building site
     565              else
     566                mixShow := mix;
     567              if (Tribe[Owner].ModelPicture[mixShow].HGr = 0) and
     568                (@OnInitEnemyModel <> nil) then
     569                if not OnInitEnemyModel(emix) then
     570                  exit;
     571              xsh := Tribe[Owner].ModelPicture[mixShow].xShield;
     572              ysh := Tribe[Owner].ModelPicture[mixShow].yShield;
     573{$IFNDEF SCR} if Status and usStay <> 0 then
     574                j := 19
     575              else if Status and usRecover <> 0 then
     576                j := 16
     577              else if Status and (usGoto or usEnhance) = usGoto or usEnhance
     578              then
     579                j := 18
     580              else if Status and usEnhance <> 0 then
     581                j := 17
     582              else if Status and usGoto <> 0 then
     583                j := 20
     584              else {$ENDIF} if Job = jCity then
     585                  j := jNone
     586                else
     587                  j := Job;
     588              if Flags and unMulti <> 0 then
     589                Sprite(Tribe[Owner].symHGr, x + xsh - 1 + 4, y + ysh - 2, 14,
     590                  12, 33 + Tribe[Owner].sympix mod 10 * 65,
     591                  1 + Tribe[Owner].sympix div 10 * 49);
     592              Sprite(Tribe[Owner].symHGr, x + xsh - 1, y + ysh - 2, 14, 12,
     593                18 + Tribe[Owner].sympix mod 10 * 65,
     594                1 + Tribe[Owner].sympix div 10 * 49);
     595              FillRect(x + xsh, y + ysh + 5, 1 + Health * 11 div 100, 3,
     596                ColorOfHealth(Health));
     597              if j > 0 then
     598              begin
     599                xGr := 121 + j mod 7 * 9;
     600                yGr := 1 + j div 7 * 9;
     601                BitBlt(GrExt[HGrSystem].Mask, x + xsh + 3, y + ysh + 9, 8, 8,
     602                  xGr, yGr, SRCAND);
     603                Sprite(HGrSystem, x + xsh + 2, y + ysh + 8, 8, 8, xGr, yGr);
     604              end;
     605              with Tribe[Owner].ModelPicture[mixShow] do
     606                Sprite(HGr, x, y, 64, 48, pix mod 10 * 65 + 1,
     607                  pix div 10 * 49 + 1);
     608              if Flags and unFortified <> 0 then
     609              begin
     610                { OutDC:=FOutput.Canvas.Handle;
     611                  DataDC:=GrExt[HGrTerrain].Data.Canvas.Handle;
     612                  MaskDC:=GrExt[HGrTerrain].Mask.Canvas.Handle;
     613                  TSprite(x,y+16,12*9+7); }
     614                Sprite(HGrStdUnits, x, y, xxu * 2, yyu * 2,
     615                  1 + 6 * (xxu * 2 + 1), 1);
     616              end
    842617            end
    843           end;
    844         BordersOK:=BordersOK or 1 shl p1;
    845         end;
    846       for dy:=0 to 1 do for dx:=0 to 1 do
    847         begin
    848         Loc1:=dLoc(Loc,dx*2-1,dy*2-1);
    849           begin
    850           if (Loc1<0) or (Loc1>=G.lx*G.ly) then p2:=-1
    851           else if MyMap[Loc1] and fTerrain=fUNKNOWN then
    852             p2:=p1
    853           else p2:=MyRO.Territory[Loc1];
    854           if p2<>p1 then
     618        end; { PaintUnit }
     619
     620        procedure TIsoMap.PaintCity(x, y: integer; const CityInfo: TCityInfo;
     621          accessory: boolean);
     622        var
     623          age, cHGr, cpix, xGr, xShield, yShield, LabelTextColor,
     624            LabelLength: integer;
     625          cpic: TCityPicture;
     626          s: string;
     627        begin
     628          age := GetAge(CityInfo.Owner);
     629          if CityInfo.size < 5 then
     630            xGr := 0
     631          else if CityInfo.size < 9 then
     632            xGr := 1
     633          else if CityInfo.size < 13 then
     634            xGr := 2
     635          else
     636            xGr := 3;
     637          Tribe[CityInfo.Owner].InitAge(age);
     638          if age < 2 then
     639          begin
     640            cHGr := Tribe[CityInfo.Owner].cHGr;
     641            cpix := Tribe[CityInfo.Owner].cpix;
     642            if (ciWalled and CityInfo.Flags = 0) or
     643              (GrExt[cHGr].Data.Canvas.Pixels[(xGr + 4) * 65, cpix * 49 + 48]
     644              = $00FFFF) then
     645              Sprite(cHGr, x - xxc, y - 2 * yyc, xxc * 2, yyc * 3,
     646                xGr * (xxc * 2 + 1) + 1, 1 + cpix * (yyc * 3 + 1));
     647            if ciWalled and CityInfo.Flags <> 0 then
     648              Sprite(cHGr, x - xxc, y - 2 * yyc, xxc * 2, yyc * 3,
     649                (xGr + 4) * (xxc * 2 + 1) + 1, 1 + cpix * (yyc * 3 + 1));
     650          end
     651          else
     652          begin
     653            if ciWalled and CityInfo.Flags <> 0 then
     654              Sprite(HGrCities, x - xxt, y - 2 * yyt, 2 * xxt, 3 * yyt,
     655                (xGr + 4) * (2 * xxt + 1) + 1, 1 + (age - 2) * (3 * yyt + 1))
     656            else
     657              Sprite(HGrCities, x - xxt, y - 2 * yyt, 2 * xxt, 3 * yyt,
     658                xGr * (2 * xxt + 1) + 1, 1 + (age - 2) * (3 * yyt + 1));
     659          end;
     660
     661          if not accessory then
     662            exit;
     663
     664          { if ciCapital and CityInfo.Flags<>0 then
     665            Sprite(Tribe[CityInfo.Owner].symHGr,x+cpic.xf,y-13+cpic.yf,13,14,
     666            1+Tribe[CityInfo.Owner].sympix mod 10 *65,
     667            1+Tribe[CityInfo.Owner].sympix div 10 *49); {capital -- paint flag }
     668
     669          if MyMap[CityInfo.Loc] and fObserved <> 0 then
     670          begin
     671            if age < 2 then
    855672            begin
    856             BitBlt(GrExt[HGrTerrain].Mask,x+dx*xxt,y+dy*yyt,xxt,yyt,
    857               1+8*(xxt*2+1)+dx*xxt,1+yyt+16*(yyt*3+1)+dy*yyt,SRCAND);
    858             BitBlt(Borders,x+dx*xxt,y+dy*yyt,xxt,yyt,dx*xxt,p1*(yyt*2)+dy*yyt,SRCPAINT);
     673              cpic := Tribe[CityInfo.Owner].CityPicture[xGr];
     674              xShield := x - xxc + cpic.xShield;
     675              yShield := y - 2 * yyc + cpic.yShield;
    859676            end
    860           end;
    861         end
    862       end
    863     end;
    864   end;
    865 
    866 begin
    867 if (Loc<0) or (Loc>=G.lx*G.ly) then Tile:=PoleTile(Loc)
    868 else Tile:=MyMap[Loc];
    869 if ShowObjects and (Options and (1 shl moEditMode)=0) and (Tile and fCity<>0) then
    870   GetCityInfo(Loc,cix,CityInfo);
    871 if (y<=FTop-yyt*2) or (y>FBottom) or (x<=FLeft-xxt*2) or (x>FRight) then
    872   begin NameCity; ShowSpacePort; exit; end;
    873 if Tile and fTerrain=fUNKNOWN then
    874   begin NameCity; ShowSpacePort; exit end;{square not discovered}
    875 
    876 if not (FoW and (Tile and fObserved=0)) then
    877   PaintBorder;
    878 
    879 if (Loc>=0) and (Loc<G.lx*G.ly) and (Loc=FAdviceLoc) then
    880   TSprite(x,y,7+9*2);
    881 
    882 if (Loc>=0) and (Loc<G.lx*G.ly) and (Tile and fSpecial<>0) then {special ressources}
    883   begin
    884   dy:=Loc div G.lx;
    885   if Tile and fTerrain<fForest then
    886     TSprite(x,y,Tile and fTerrain+(Tile and fSpecial shr 5)*9)
    887   else if (Tile and fTerrain=fForest) and IsJungle(dy) then
    888     TSprite(x,y,8+17*9+(Tile and fSpecial shr 5)*9)
    889   else TSprite(x,y,8+2*9+((Tile and fTerrain-fForest)*2+Tile and fSpecial shr 5)*9);
    890   end;
    891 
    892 if ShowObjects then
    893   begin
    894   if Tile and fTerImp=tiMine then
    895     TSprite(x,y,2+9*12);
    896   if Tile and fTerImp=tiBase then
    897     TSprite(x,y,4+9*12);
    898   if Tile and fPoll<>0 then
    899     TSprite(x,y,6+9*12);
    900   if Tile and fTerImp=tiFort then
    901     begin
    902     TSprite(x,y,7+9*12);
    903     if Tile and fObserved=0 then
    904       TSprite(x,y,3+9*12);
    905     end;
    906   end;
    907 if Tile and fDeadLands<>0 then TSprite(x,y,(12+Tile shr 25 and 3)*9+8);
    908 
    909 if Options and (1 shl moEditMode)<>0 then
    910   fog:= (Loc<0) or (Loc>=G.lx*G.ly)
    911 //else if CityLoc>=0 then
    912 //  fog:= (Loc<0) or (Loc>=G.lx*G.ly) or (Distance(Loc,CityLoc)>5)
    913 else if ShowGrWall then fog:= Tile and fGrWall=0
    914 else fog:=FoW and (Tile and fObserved=0);
    915 if fog and ShowObjects then
    916   if Loc<-G.lx then
    917     Sprite(HGrTerrain,x,y+yyt,xxt*2,yyt,1+6*(xxt*2+1),1+yyt*2+15*(yyt*3+1))
    918   else if Loc>=G.lx*(G.ly+1) then
    919     Sprite(HGrTerrain,x,y,xxt*2,yyt,1+6*(xxt*2+1),1+yyt+15*(yyt*3+1))
    920   else TSprite(x,y,6+9*15,xxt<>33);
    921 
    922 if FoW and (Tile and fObserved=0) then
    923   PaintBorder;
     677            else
     678            begin
     679              cpic := CitiesPictures[age, xGr];
     680              xShield := x - xxt + cpic.xShield;
     681              yShield := y - 2 * yyt + cpic.yShield;
     682            end;
     683            s := IntToStr(CityInfo.size);
     684            LabelLength := FOutput.Canvas.TextWidth(s);
     685            FillRect(xShield, yShield, LabelLength + 4, 16, $000000);
     686            if MyMap[CityInfo.Loc] and (fUnit or fObserved) = fObserved then
     687              // empty city
     688              LabelTextColor := Tribe[CityInfo.Owner].Color
     689            else
     690            begin
     691              FillRect(xShield + 1, yShield + 1, LabelLength + 2, 14,
     692                Tribe[CityInfo.Owner].Color);
     693              LabelTextColor := $000000;
     694            end;
     695            Textout(xShield + 2, yShield - 1, LabelTextColor, s);
     696          end
     697        end; { PaintCity }
     698
     699        function PoleTile(Loc: integer): integer;
     700        begin { virtual pole tile }
     701          result := fUNKNOWN;
     702          if Loc < -2 * G.lx then
     703          else if Loc < -G.lx then
     704          begin
     705            if (MyMap[dLoc(Loc, 0, 2)] and fTerrain <> fUNKNOWN) and
     706              (MyMap[dLoc(Loc, -2, 2)] and fTerrain <> fUNKNOWN) and
     707              (MyMap[dLoc(Loc, 2, 2)] and fTerrain <> fUNKNOWN) then
     708              result := fArctic;
     709            if (MyMap[dLoc(Loc, 0, 2)] and fObserved <> 0) and
     710              (MyMap[dLoc(Loc, -2, 2)] and fObserved <> 0) and
     711              (MyMap[dLoc(Loc, 2, 2)] and fObserved <> 0) then
     712              result := result or fObserved
     713          end
     714          else if Loc < 0 then
     715          begin
     716            if (MyMap[dLoc(Loc, -1, 1)] and fTerrain <> fUNKNOWN) and
     717              (MyMap[dLoc(Loc, 1, 1)] and fTerrain <> fUNKNOWN) then
     718              result := fArctic;
     719            if (MyMap[dLoc(Loc, -1, 1)] and fObserved <> 0) and
     720              (MyMap[dLoc(Loc, 1, 1)] and fObserved <> 0) then
     721              result := result or fObserved
     722          end
     723          else if Loc < G.lx * (G.ly + 1) then
     724          begin
     725            if (MyMap[dLoc(Loc, -1, -1)] and fTerrain <> fUNKNOWN) and
     726              (MyMap[dLoc(Loc, 1, -1)] and fTerrain <> fUNKNOWN) then
     727              result := fArctic;
     728            if (MyMap[dLoc(Loc, -1, -1)] and fObserved <> 0) and
     729              (MyMap[dLoc(Loc, 1, -1)] and fObserved <> 0) then
     730              result := result or fObserved
     731          end
     732          else if Loc < G.lx * (G.ly + 2) then
     733          begin
     734            if (MyMap[dLoc(Loc, 0, -2)] and fTerrain <> fUNKNOWN) and
     735              (MyMap[dLoc(Loc, -2, -2)] and fTerrain <> fUNKNOWN) and
     736              (MyMap[dLoc(Loc, 2, -2)] and fTerrain <> fUNKNOWN) then
     737              result := fArctic;
     738            if (MyMap[dLoc(Loc, 0, -2)] and fObserved <> 0) and
     739              (MyMap[dLoc(Loc, -2, -2)] and fObserved <> 0) and
     740              (MyMap[dLoc(Loc, 2, -2)] and fObserved <> 0) then
     741              result := result or fObserved
     742          end
     743        end;
     744
     745        const
     746          Dirx: array [0 .. 7] of integer = (1, 2, 1, 0, -1, -2, -1, 0);
     747          Diry: array [0 .. 7] of integer = (-1, 0, 1, 2, 1, 0, -1, -2);
     748
     749        function TIsoMap.Connection4(Loc, Mask, Value: integer): integer;
     750        begin
     751          result := 0;
     752          if dLoc(Loc, 1, -1) >= 0 then
     753          begin
     754            if MyMap[dLoc(Loc, 1, -1)] and Mask = Cardinal(Value) then
     755              inc(result, 1);
     756            if MyMap[dLoc(Loc, -1, -1)] and Mask = Cardinal(Value) then
     757              inc(result, 8);
     758          end;
     759          if dLoc(Loc, 1, 1) < G.lx * G.ly then
     760          begin
     761            if MyMap[dLoc(Loc, 1, 1)] and Mask = Cardinal(Value) then
     762              inc(result, 2);
     763            if MyMap[dLoc(Loc, -1, 1)] and Mask = Cardinal(Value) then
     764              inc(result, 4);
     765          end
     766        end;
     767
     768        function TIsoMap.Connection8(Loc, Mask: integer): integer;
     769        var
     770          Dir, ConnLoc: integer;
     771        begin
     772          result := 0;
     773          for Dir := 0 to 7 do
     774          begin
     775            ConnLoc := dLoc(Loc, Dirx[Dir], Diry[Dir]);
     776            if (ConnLoc >= 0) and (ConnLoc < G.lx * G.ly) and
     777              (MyMap[ConnLoc] and Mask <> 0) then
     778              inc(result, 1 shl Dir);
     779          end
     780        end;
     781
     782        function TIsoMap.OceanConnection(Loc: integer): integer;
     783        var
     784          Dir, ConnLoc: integer;
     785        begin
     786          result := 0;
     787          for Dir := 0 to 7 do
     788          begin
     789            ConnLoc := dLoc(Loc, Dirx[Dir], Diry[Dir]);
     790            if (ConnLoc < 0) or (ConnLoc >= G.lx * G.ly) or
     791              ((MyMap[ConnLoc] - 2) and fTerrain < 13) then
     792              inc(result, 1 shl Dir);
     793          end
     794        end;
     795
     796        procedure TIsoMap.PaintShore(x, y, Loc: integer);
     797        var
     798          Conn, Tile: integer;
     799        begin
     800          if (y <= FTop - yyt * 2) or (y > FBottom) or (x <= FLeft - xxt * 2) or
     801            (x > FRight) then
     802            exit;
     803          if (Loc < 0) or (Loc >= G.lx * G.ly) then
     804            exit;
     805          Tile := MyMap[Loc];
     806          if Tile and fTerrain >= fGrass then
     807            exit;
     808          Conn := OceanConnection(Loc);
     809          if Conn = 0 then
     810            exit;
     811
     812          BitBlt(GrExt[HGrTerrain].Data, x + xxt div 2, y, xxt, yyt,
     813            1 + (Conn shr 6 + Conn and 1 shl 2) * (xxt * 2 + 1),
     814            1 + yyt + (16 + Tile and fTerrain) * (yyt * 3 + 1), SRCPAINT);
     815          BitBlt(GrExt[HGrTerrain].Data, x + xxt, y + yyt div 2, xxt, yyt,
     816            1 + (Conn and 7) * (xxt * 2 + 1) + xxt,
     817            1 + yyt * 2 + (16 + Tile and fTerrain) * (yyt * 3 + 1), SRCPAINT);
     818          BitBlt(GrExt[HGrTerrain].Data, x + xxt div 2, y + yyt, xxt, yyt,
     819            1 + (Conn shr 2 and 7) * (xxt * 2 + 1) + xxt,
     820            1 + yyt + (16 + Tile and fTerrain) * (yyt * 3 + 1), SRCPAINT);
     821          BitBlt(GrExt[HGrTerrain].Data, x, y + yyt div 2, xxt, yyt,
     822            1 + (Conn shr 4 and 7) * (xxt * 2 + 1),
     823            1 + yyt * 2 + (16 + Tile and fTerrain) * (yyt * 3 + 1), SRCPAINT);
     824          Conn := Connection4(Loc, fTerrain, fUNKNOWN); { dither to black }
     825          if Conn and 1 <> 0 then
     826            BitBlt(GrExt[HGrTerrain].Mask, x + xxt, y, xxt, yyt,
     827              1 + 7 * (xxt * 2 + 1) + xxt,
     828              1 + yyt + 15 * (yyt * 3 + 1), SRCAND);
     829          if Conn and 2 <> 0 then
     830            BitBlt(GrExt[HGrTerrain].Mask, x + xxt, y + yyt, xxt, yyt,
     831              1 + 7 * (xxt * 2 + 1) + xxt, 1 + yyt * 2 + 15 *
     832              (yyt * 3 + 1), SRCAND);
     833          if Conn and 4 <> 0 then
     834            BitBlt(GrExt[HGrTerrain].Mask, x, y + yyt, xxt, yyt,
     835              1 + 7 * (xxt * 2 + 1), 1 + yyt * 2 + 15 * (yyt * 3 + 1), SRCAND);
     836          if Conn and 8 <> 0 then
     837            BitBlt(GrExt[HGrTerrain].Mask, x, y, xxt, yyt,
     838              1 + 7 * (xxt * 2 + 1), 1 + yyt + 15 * (yyt * 3 + 1), SRCAND);
     839        end;
     840
     841        procedure TIsoMap.PaintTileExtraTerrain(x, y, Loc: integer);
     842        var
     843          Dir, Conn, RRConn, yGr, Tile, yLoc: integer;
     844        begin
     845          if (Loc < 0) or (Loc >= G.lx * G.ly) or (y <= -yyt * 2) or
     846            (y > FOutput.Height) or (x <= -xxt * 2) or (x > FOutput.Width) then
     847            exit;
     848          Tile := MyMap[Loc];
     849          if Tile and fTerrain = fForest then
     850          begin
     851            yLoc := Loc div G.lx;
     852            if IsJungle(yLoc) then
     853              yGr := 18
     854            else
     855              yGr := 3;
     856            Conn := Connection4(Loc, fTerrain, Tile and fTerrain);
     857            if (yLoc = (G.ly - 2) div 4) or (G.ly - 1 - yLoc = (G.ly + 2) div 4)
     858            then
     859              Conn := Conn and not 6 // no connection to south
     860            else if (yLoc = (G.ly + 2) div 4) or
     861              (G.ly - 1 - yLoc = (G.ly - 2) div 4) then
     862              Conn := Conn and not 9; // no connection to north
     863            TSprite(x, y, Conn mod 8 + (yGr + Conn div 8) * 9);
     864          end
     865          else if Tile and fTerrain in [fHills, fMountains, fForest] then
     866          begin
     867            yGr := 3 + 2 * (Tile and fTerrain - fForest);
     868            Conn := Connection4(Loc, fTerrain, Tile and fTerrain);
     869            TSprite(x, y, Conn mod 8 + (yGr + Conn div 8) * 9);
     870          end
     871          else if Tile and fDeadLands <> 0 then
     872            TSprite(x, y, 2 * 9 + 6);
     873
     874          if ShowObjects then
     875          begin
     876            if Tile and fTerImp = tiFarm then
     877              TSprite(x, y, 109) { farmland }
     878            else if Tile and fTerImp = tiIrrigation then
     879              TSprite(x, y, 108); // irrigation
     880          end;
     881          if Tile and fRiver <> 0 then
     882          begin
     883            Conn := Connection4(Loc, fRiver, fRiver) or
     884              Connection4(Loc, fTerrain, fShore) or
     885              Connection4(Loc, fTerrain, fUNKNOWN);
     886            TSprite(x, y, Conn mod 8 + (13 + Conn div 8) * 9);
     887          end;
     888
     889          if Tile and fTerrain < fGrass then
     890          begin
     891            Conn := Connection4(Loc, fRiver, fRiver);
     892            for Dir := 0 to 3 do
     893              if Conn and (1 shl Dir) <> 0 then { river mouths }
     894                TSprite(x, y, 15 * 9 + Dir);
     895            if ShowObjects then
     896            begin
     897              Conn := Connection8(Loc, fCanal);
     898              for Dir := 0 to 7 do
     899                if Conn and (1 shl Dir) <> 0 then { canal mouths }
     900                  TSprite(x, y, 20 * 9 + 1 + Dir);
     901            end
     902          end;
     903
     904          if ShowObjects then
     905          begin
     906            if (Tile and fCanal <> 0) or (Tile and fCity <> 0) then
     907            begin // paint canal connections
     908              Conn := Connection8(Loc, fCanal or fCity);
     909              if Tile and fCanal <> 0 then
     910                Conn := Conn or ($FF - OceanConnection(Loc));
     911              if Conn = 0 then
     912              begin
     913                if Tile and fCanal <> 0 then
     914                  TSprite(x, y, 99)
     915              end
     916              else
     917                for Dir := 0 to 7 do
     918                  if (1 shl Dir) and Conn <> 0 then
     919                    TSprite(x, y, 100 + Dir);
     920            end;
     921            if Tile and (fRR or fCity) <> 0 then
     922              RRConn := Connection8(Loc, fRR or fCity)
     923            else
     924              RRConn := 0;
     925            if Tile and (fRoad or fRR or fCity) <> 0 then
     926            begin // paint road connections
     927              Conn := Connection8(Loc, fRoad or fRR or fCity) and not RRConn;
     928              if (Conn = 0) and (Tile and (fRR or fCity) = 0) then
     929                TSprite(x, y, 81)
     930              else if Conn > 0 then
     931                for Dir := 0 to 7 do
     932                  if (1 shl Dir) and Conn <> 0 then
     933                    TSprite(x, y, 82 + Dir);
     934            end;
     935            // paint railroad connections
     936            if (Tile and fRR <> 0) and (RRConn = 0) then
     937              TSprite(x, y, 90)
     938            else if RRConn > 0 then
     939              for Dir := 0 to 7 do
     940                if (1 shl Dir) and RRConn <> 0 then
     941                  TSprite(x, y, 91 + Dir);
     942          end;
     943        end;
     944
     945        // (x,y) is top left pixel of (2*xxt,3*yyt) rectangle
     946        procedure TIsoMap.PaintTileObjects(x, y, Loc, CityLoc,
     947          CityOwner: integer; UseBlink: boolean);
     948        type
     949          TLine = array [0 .. 9 * 65, 0 .. 2] of Byte;
     950        var
     951          p1, p2, uix, cix, dy, Loc1, Tile, Multi, Destination: integer;
     952          CityInfo: TCityInfo;
     953          UnitInfo: TUnitInfo;
     954          fog: boolean;
     955
     956          procedure NameCity;
     957          var
     958            cix, xs, w: integer;
     959            BehindCityInfo: TCityInfo;
     960            s: string;
     961            IsCapital: boolean;
     962          begin
     963            BehindCityInfo.Loc := Loc - 2 * G.lx;
     964            if ShowCityNames and (Options and (1 shl moEditMode) = 0) and
     965              (BehindCityInfo.Loc >= 0) and (BehindCityInfo.Loc < G.lx * G.ly)
     966              and (MyMap[BehindCityInfo.Loc] and fCity <> 0) then
     967            begin
     968              GetCityInfo(BehindCityInfo.Loc, cix, BehindCityInfo);
     969              IsCapital := BehindCityInfo.Flags and ciCapital <> 0;
     970              { if Showuix and (cix>=0) then s:=IntToStr(cix)
     971                else } s := CityName(BehindCityInfo.ID);
     972              w := FOutput.Canvas.TextWidth(s);
     973              xs := x + xxt - (w + 1) div 2;
     974              if IsCapital then
     975                FOutput.Canvas.Font.Style := FOutput.Canvas.Font.Style +
     976                  [fsUnderline];
     977              Textout(xs + 1, y - 9, $000000, s);
     978              Textout(xs, y - 10, $FFFFFF, s);
     979              if IsCapital then
     980                FOutput.Canvas.Font.Style := FOutput.Canvas.Font.Style -
     981                  [fsUnderline];
     982            end;
     983          end;
     984
     985          procedure ShowSpacePort;
     986          begin
     987            if ShowObjects and (Options and (1 shl moEditMode) = 0) and
     988              (Tile and fCity <> 0) and (CityInfo.Flags and ciSpacePort <> 0)
     989            then
     990              TSprite(x + xxt, y - 6, 12 * 9 + 5);
     991          end;
     992
     993          procedure PaintBorder;
     994          var
     995            dx, dy: integer;
     996            Line: ^TLine;
     997          begin
     998            if ShowBorder and (Loc >= 0) and (Loc < G.lx * G.ly) and
     999              (Tile and fTerrain <> fUNKNOWN) then
     1000            begin
     1001              p1 := MyRO.Territory[Loc];
     1002              if (p1 >= 0) and (ShowMyBorder or (p1 <> me)) then
     1003              begin
     1004                if BordersOK and (1 shl p1) = 0 then
     1005                begin
     1006                  Windows.BitBlt(Borders.Canvas.Handle, 0, p1 * (yyt * 2),
     1007                    xxt * 2, yyt * 2, GrExt[HGrTerrain].Data.Canvas.Handle,
     1008                    1 + 8 * (xxt * 2 + 1),
     1009                    1 + yyt + 16 * (yyt * 3 + 1), SRCCOPY);
     1010                  for dy := 0 to yyt * 2 - 1 do
     1011                  begin
     1012                    Line := Borders.ScanLine[p1 * (yyt * 2) + dy];
     1013                    for dx := 0 to xxt * 2 - 1 do
     1014                      if Line[dx, 0] = 99 then
     1015                      begin
     1016                        Line[dx, 0] := Tribe[p1].Color shr 16 and $FF;
     1017                        Line[dx, 1] := Tribe[p1].Color shr 8 and $FF;
     1018                        Line[dx, 2] := Tribe[p1].Color and $FF;
     1019                      end
     1020                  end;
     1021                  BordersOK := BordersOK or 1 shl p1;
     1022                end;
     1023                for dy := 0 to 1 do
     1024                  for dx := 0 to 1 do
     1025                  begin
     1026                    Loc1 := dLoc(Loc, dx * 2 - 1, dy * 2 - 1);
     1027                    begin
     1028                      if (Loc1 < 0) or (Loc1 >= G.lx * G.ly) then
     1029                        p2 := -1
     1030                      else if MyMap[Loc1] and fTerrain = fUNKNOWN then
     1031                        p2 := p1
     1032                      else
     1033                        p2 := MyRO.Territory[Loc1];
     1034                      if p2 <> p1 then
     1035                      begin
     1036                        BitBlt(GrExt[HGrTerrain].Mask, x + dx * xxt,
     1037                          y + dy * yyt, xxt, yyt, 1 + 8 * (xxt * 2 + 1) + dx *
     1038                          xxt, 1 + yyt + 16 * (yyt * 3 + 1) + dy * yyt, SRCAND);
     1039                        BitBlt(Borders, x + dx * xxt, y + dy * yyt, xxt, yyt,
     1040                          dx * xxt, p1 * (yyt * 2) + dy * yyt, SRCPAINT);
     1041                      end
     1042                    end;
     1043                  end
     1044              end
     1045            end;
     1046          end;
     1047
     1048        begin
     1049          if (Loc < 0) or (Loc >= G.lx * G.ly) then
     1050            Tile := PoleTile(Loc)
     1051          else
     1052            Tile := MyMap[Loc];
     1053          if ShowObjects and (Options and (1 shl moEditMode) = 0) and
     1054            (Tile and fCity <> 0) then
     1055            GetCityInfo(Loc, cix, CityInfo);
     1056          if (y <= FTop - yyt * 2) or (y > FBottom) or (x <= FLeft - xxt * 2) or
     1057            (x > FRight) then
     1058          begin
     1059            NameCity;
     1060            ShowSpacePort;
     1061            exit;
     1062          end;
     1063          if Tile and fTerrain = fUNKNOWN then
     1064          begin
     1065            NameCity;
     1066            ShowSpacePort;
     1067            exit
     1068          end; { square not discovered }
     1069
     1070          if not(FoW and (Tile and fObserved = 0)) then
     1071            PaintBorder;
     1072
     1073          if (Loc >= 0) and (Loc < G.lx * G.ly) and (Loc = FAdviceLoc) then
     1074            TSprite(x, y, 7 + 9 * 2);
     1075
     1076          if (Loc >= 0) and (Loc < G.lx * G.ly) and (Tile and fSpecial <> 0)
     1077          then { special ressources }
     1078          begin
     1079            dy := Loc div G.lx;
     1080            if Tile and fTerrain < fForest then
     1081              TSprite(x, y, Tile and fTerrain + (Tile and fSpecial shr 5) * 9)
     1082            else if (Tile and fTerrain = fForest) and IsJungle(dy) then
     1083              TSprite(x, y, 8 + 17 * 9 + (Tile and fSpecial shr 5) * 9)
     1084            else
     1085              TSprite(x, y, 8 + 2 * 9 + ((Tile and fTerrain - fForest) * 2 +
     1086                Tile and fSpecial shr 5) * 9);
     1087          end;
     1088
     1089          if ShowObjects then
     1090          begin
     1091            if Tile and fTerImp = tiMine then
     1092              TSprite(x, y, 2 + 9 * 12);
     1093            if Tile and fTerImp = tiBase then
     1094              TSprite(x, y, 4 + 9 * 12);
     1095            if Tile and fPoll <> 0 then
     1096              TSprite(x, y, 6 + 9 * 12);
     1097            if Tile and fTerImp = tiFort then
     1098            begin
     1099              TSprite(x, y, 7 + 9 * 12);
     1100              if Tile and fObserved = 0 then
     1101                TSprite(x, y, 3 + 9 * 12);
     1102            end;
     1103          end;
     1104          if Tile and fDeadLands <> 0 then
     1105            TSprite(x, y, (12 + Tile shr 25 and 3) * 9 + 8);
     1106
     1107          if Options and (1 shl moEditMode) <> 0 then
     1108            fog := (Loc < 0) or (Loc >= G.lx * G.ly)
     1109            // else if CityLoc>=0 then
     1110            // fog:= (Loc<0) or (Loc>=G.lx*G.ly) or (Distance(Loc,CityLoc)>5)
     1111          else if ShowGrWall then
     1112            fog := Tile and fGrWall = 0
     1113          else
     1114            fog := FoW and (Tile and fObserved = 0);
     1115          if fog and ShowObjects then
     1116            if Loc < -G.lx then
     1117              Sprite(HGrTerrain, x, y + yyt, xxt * 2, yyt,
     1118                1 + 6 * (xxt * 2 + 1), 1 + yyt * 2 + 15 * (yyt * 3 + 1))
     1119            else if Loc >= G.lx * (G.ly + 1) then
     1120              Sprite(HGrTerrain, x, y, xxt * 2, yyt, 1 + 6 * (xxt * 2 + 1),
     1121                1 + yyt + 15 * (yyt * 3 + 1))
     1122            else
     1123              TSprite(x, y, 6 + 9 * 15, xxt <> 33);
     1124
     1125          if FoW and (Tile and fObserved = 0) then
     1126            PaintBorder;
    9241127
    9251128{$IFNDEF SCR}
    926 // paint goto destination mark
    927 if DestinationMarkON and (CityOwner<0) and (UnFocus>=0)
    928   and (MyUn[UnFocus].Status and usGoto<>0) then
    929   begin
    930   Destination:=MyUn[UnFocus].Status shr 16;
    931   if (Destination=Loc) and (Destination<>MyUn[UnFocus].Loc) then
    932     if not UseBlink or BlinkOn then TSprite(x,y,8+9*1)
    933     else TSprite(x,y,8+9*2)
    934   end; 
     1129          // paint goto destination mark
     1130          if DestinationMarkON and (CityOwner < 0) and (UnFocus >= 0) and
     1131            (MyUn[UnFocus].Status and usGoto <> 0) then
     1132          begin
     1133            Destination := MyUn[UnFocus].Status shr 16;
     1134            if (Destination = Loc) and (Destination <> MyUn[UnFocus].Loc) then
     1135              if not UseBlink or BlinkOn then
     1136                TSprite(x, y, 8 + 9 * 1)
     1137              else
     1138                TSprite(x, y, 8 + 9 * 2)
     1139          end;
    9351140{$ENDIF}
    936 
    937 if Options and (1 shl moEditMode)<>0 then
    938   begin
    939   if Tile and fPrefStartPos<>0 then TSprite(x,y,0+9*1)
    940   else if Tile and fStartPos<>0 then TSprite(x,y,0+9*2);
    941   end
    942 else if ShowObjects then
    943   begin
    944 {  if (CityLoc<0) and (UnFocus>=0) and (Loc=MyUn[UnFocus].Loc) then
    945     if BlinkOn then TSprite(x,y,8+9*0)
    946     else TSprite(x,y,8+9*1);}
    947 
    948   NameCity;
    949   ShowSpacePort;
    950   if Tile and fCity<>0 then
    951     PaintCity(x+xxt,y+yyt,CityInfo,CityOwner<0);
    952 
    953   if (Tile and fUnit<>0) and (Loc<>AttLoc)
    954     and ((Loc<>DefLoc) or (DefHealth<>0))
    955     {$IFNDEF SCR}and ((CityOwner>=0) or (UnFocus<0) or not UseBlink or BlinkON
    956         or (Loc<>MyUn[UnFocus].Loc)){$ENDIF}
    957     and ((Tile and fCity<>fCity) or (Loc=DefLoc)
    958       {$IFNDEF SCR}or (not UseBlink or BlinkON) and (UnFocus>=0)
    959       and (Loc=MyUn[UnFocus].Loc){$ENDIF}) then
    960     begin {unit}
    961     GetUnitInfo(Loc,uix,UnitInfo);
    962     if (Loc=DefLoc) and (DefHealth>=0) then
    963       UnitInfo.Health:=DefHealth;
    964     if (UnitInfo.Owner<>CityOwner)
    965       and not ((CityOwner=me) and (MyRO.Treaty[UnitInfo.Owner]=trAlliance)) then
    966       {$IFNDEF SCR}if (UnFocus>=0) and (Loc=MyUn[UnFocus].Loc) then {active unit}
    967         begin
    968         Multi:=UnitInfo.Flags and unMulti;
    969         MakeUnitInfo(me,MyUn[UnFocus],UnitInfo);
    970         UnitInfo.Flags:=UnitInfo.Flags or Multi;
    971         PaintUnit(x+(xxt-xxu),y+(yyt-yyu_anchor),UnitInfo,MyUn[UnFocus].Status);
    972         end
    973       else if UnitInfo.Owner=me then
    974         begin
    975         if ClientMode=cMovieTurn then
    976           PaintUnit(x+(xxt-xxu),y+(yyt-yyu_anchor),UnitInfo,0)
    977           // status is not set with precise timing during loading
    978         else PaintUnit(x+(xxt-xxu),y+(yyt-yyu_anchor),UnitInfo,MyUn[uix].Status);
    979   //      if Showuix then Textout(x+16,y+5,$80FF00,IntToStr(uix));
    980         end
    981       else{$ENDIF} PaintUnit(x+(xxt-xxu),y+(yyt-yyu_anchor),UnitInfo,0);
    982     end
    983   else if Tile and fHiddenUnit<>0 then
    984     Sprite(HGrStdUnits,x+(xxt-xxu),y+(yyt-yyu_anchor),xxu*2,yyu*2,
    985       1+5*(xxu*2+1),1)
    986   else if Tile and fStealthUnit<>0 then
    987     Sprite(HGrStdUnits,x+(xxt-xxu),y+(yyt-yyu_anchor),xxu*2,yyu*2,
    988       1+5*(xxu*2+1),1+1*(yyu*2+1))
    989   end;
    990 
    991 if ShowObjects and (Tile and fTerImp=tiFort) and (Tile and fObserved<>0) then
    992   TSprite(x,y,3+9*12);
    993 
    994 if (Loc>=0) and (Loc<G.lx*G.ly) then
    995   if ShowLoc then Textout(x+xxt-16,y+yyt-9,$FFFF00,IntToStr(Loc))
    996   else if ShowDebug and (DebugMap<>nil)
    997     and (Loc>=0) and (Loc<G.lx*G.ly) and (DebugMap[Loc]<>0) then
    998     Textout(x+xxt-16,y+yyt-9,$00E0FF,IntToStr(integer(DebugMap[Loc])))
    999 end;{PaintTileObjects}
    1000 
    1001 procedure TIsoMap.PaintGrid(x,y,nx,ny: integer);
    1002 
    1003   procedure ClippedLine(dx0,dy0: integer; mirror: boolean);
    1004   var
    1005   x0,x1,dxmin,dymin,dxmax,dymax,n: integer;
    1006   begin
    1007   with FOutput.Canvas do
    1008     begin
    1009     dxmin:=(FLeft-x) div xxt;
    1010     dymin:=(RealTop-y) div yyt;
    1011     dxmax:=(FRight-x-1) div xxt+1;
    1012     dymax:=(RealBottom-y-1) div yyt+1;
    1013     n:=dymax-dy0;
    1014     if mirror then
    1015       begin
    1016       if dx0-dxmin<n then n:=dx0-dxmin;
    1017       if dx0>dxmax then
    1018         begin n:=n-(dx0-dxmax); dy0:=dy0+(dx0-dxmax); dx0:=dxmax end;
    1019       if dy0<dymin then
    1020         begin n:=n-(dymin-dy0); dx0:=dx0-(dymin-dy0); dy0:=dymin end;
    1021       end
    1022     else
    1023       begin
    1024       if dxmax-dx0<n then n:=dxmax-dx0;
    1025       if dx0<dxmin then
    1026         begin n:=n-(dxmin-dx0); dy0:=dy0+(dxmin-dx0); dx0:=dxmin end;
    1027       if dy0<dymin then
    1028         begin n:=n-(dymin-dy0); dx0:=dx0+(dymin-dy0); dy0:=dymin end;
    1029       end;
    1030     if n<=0 then exit;
    1031     if mirror then begin x0:=x+dx0*xxt-1; x1:=x+(dx0-n)*xxt-1; end
    1032     else begin x0:=x+dx0*xxt; x1:=x+(dx0+n)*xxt; end;
    1033     moveto(x0,y+dy0*yyt);
    1034     lineto(x1,y+(dy0+n)*yyt);
    1035     end
    1036   end;
    1037 
    1038 var
    1039 i: integer;
    1040 begin
    1041 FOutput.Canvas.pen.color:=$000000; //$FF shl (8*random(3));
    1042 for i:=0 to nx div 2 do ClippedLine(i*2,0,false);
    1043 for i:=1 to (nx+1) div 2 do ClippedLine(i*2,0,true);
    1044 for i:=0 to ny div 2 do
    1045   begin
    1046   ClippedLine(0,2*i+2,false);
    1047   ClippedLine(nx+1,2*i+1+nx and 1,true);
    1048   end;
    1049 end;
    1050 
    1051 procedure TIsoMap.Paint(x,y,Loc,nx,ny,CityLoc,CityOwner:integer;
    1052   UseBlink: boolean; CityAllowClick: boolean);
    1053 
    1054   function IsShoreTile(Loc: integer):boolean;
    1055   const
    1056   Dirx: array[0..7] of integer=(1,2,1,0,-1,-2,-1,0);
    1057   Diry: array[0..7] of integer=(-1,0,1,2,1,0,-1,-2);
    1058   var
    1059   Dir,ConnLoc: integer;
    1060   begin
    1061   result:=false;
    1062   for Dir:=0 to 7 do
    1063     begin
    1064     ConnLoc:=dLoc(Loc,Dirx[Dir],Diry[Dir]);
    1065     if (ConnLoc<0) or (ConnLoc>=G.lx*G.ly)
    1066       or ((MyMap[ConnLoc]-2) and fTerrain<13) then
    1067       result:=true
    1068     end
    1069   end;
    1070 
    1071   procedure ShadeOutside(x0,y0,x1,y1,xm,ym: integer);
    1072   const
    1073   rShade=3.75;
    1074 
    1075     procedure MakeDark(line: pointer; length: integer);
    1076     type
    1077     TCardArray=array[0..9999] of cardinal;
    1078     PCardArray=^TCardArray;
    1079     TByteArray=array[0..9999] of byte;
    1080     PByteArray=^TByteArray;
    1081     var
    1082     i,rest: integer;
    1083     begin
    1084     for i:=length*3 div 4-1 downto 0 do
    1085       PCardArray(line)[i]:=PCardArray(line)[i] shr 1 and $7F7F7F7F;
    1086     rest:=(length*3 div 4)*4;
    1087     for i:=length*3 mod 4-1 downto 0 do
    1088       PByteArray(line)[rest+i]:=PByteArray(line)[rest+i] shr 1 and $7F;
    1089     end;
    1090 
    1091   type
    1092   TLine=array[0..99999,0..2] of Byte;
    1093   var
    1094   y,wBright: integer;
    1095   y_n,w_n: single;
    1096   line: ^TLine;
    1097   begin
    1098   for y:=y0 to y1-1 do
    1099     begin
    1100     line:=FOutput.ScanLine[y];
    1101     y_n:=(y-ym)/yyt;
    1102     if abs(y_n)<rShade then
    1103       begin
    1104       w_n:=sqrt(sqr(rShade)-sqr(y_n));
    1105       wBright:=trunc(w_n*xxt+0.5);
    1106       MakeDark(@line[x0],xm-x0-wBright);
    1107       MakeDark(@line[xm+wBright],x1-xm-wBright);
    1108       end
    1109     else MakeDark(@line[x0],x1-x0);
    1110     end
    1111   end;
    1112 
    1113   procedure CityGrid(xm,ym: integer);
    1114   var
    1115   i: integer;
    1116   begin
    1117   with FOutput.Canvas do
    1118     begin
    1119     if CityAllowClick then pen.Color:=$FFFFFF
    1120     else pen.color:=$000000;
    1121     pen.width:=1;
    1122     for i:=0 to 3 do
    1123       begin
    1124       moveto(xm-xxt*(4-i),ym+yyt*(1+i)); lineto(xm+xxt*(1+i),ym-yyt*(4-i));
    1125       moveto(xm-xxt*(4-i),ym-yyt*(1+i)); lineto(xm+xxt*(1+i),ym+yyt*(4-i));
    1126       end;
    1127     moveto(xm-xxt*4,ym+yyt*1); lineto(xm-xxt*1,ym+yyt*4);
    1128     moveto(xm+xxt*1,ym+yyt*4); lineto(xm+xxt*4,ym+yyt*1);
    1129     moveto(xm-xxt*4,ym-yyt*1); lineto(xm-xxt*1,ym-yyt*4);
    1130     moveto(xm+xxt*1,ym-yyt*4); lineto(xm+xxt*4,ym-yyt*1);
    1131     pen.width:=1;
    1132     end
    1133   end;
    1134 
    1135 var
    1136 dx,dy,xm,ym,ALoc,BLoc,ATer,BTer,Aix,bix:integer;
    1137 begin
    1138 FoW:=true;
    1139 ShowLoc:=Options and (1 shl moLocCodes)<>0;
    1140 ShowDebug:= pDebugMap>=0;
    1141 ShowObjects:= (CityOwner>=0) or (Options and (1 shl moBareTerrain)=0);
    1142 ShowCityNames:= ShowObjects and (CityOwner<0) and (Options and (1 shl moCityNames)<>0);
    1143 ShowBorder:=true;
    1144 ShowMyBorder:= CityOwner<0;
    1145 ShowGrWall:= (CityOwner<0) and (Options and (1 shl moGreatWall)<>0);
    1146 if ShowDebug then
    1147   Server(sGetDebugMap,me,pDebugMap,DebugMap)
    1148 else DebugMap:=nil;
    1149 with FOutput.Canvas do
    1150   begin
    1151   RealTop:=y-((Loc+12345*G.lx) div G.lx-12345)*yyt;
    1152   RealBottom:=y+(G.ly-((Loc+12345*G.lx) div G.lx-12345)+3)*yyt;
    1153   Brush.Color:=EmptySpaceColor;
    1154   if RealTop>FTop then
    1155     FillRect(Rect(FLeft,FTop,FRight,RealTop))
    1156   else RealTop:=FTop;
    1157   if RealBottom<FBottom then
    1158     FillRect(Rect(FLeft,RealBottom,FRight,FBottom))
    1159   else RealBottom:=FBottom;
    1160   Brush.Color:=$000000;
    1161   FillRect(Rect(FLeft,RealTop,FRight,RealBottom));
    1162   Brush.Style:=bsClear;
    1163   end;
    1164 
    1165 for dy:=0 to ny+1 do if (Loc+dy*G.lx>=0) and (Loc+(dy-3)*G.lx<G.lx*G.ly) then
    1166   for dx:=0 to nx do
    1167     begin
    1168     ALoc:=dLoc(Loc,dx-(dy+dx) and 1,dy-2);
    1169     BLoc:=dLoc(Loc,dx-(dy+dx+1) and 1,dy-1);
    1170     if (ALoc<0) or (ALoc>=G.lx*G.ly) then ATer:=PoleTile(ALoc) and fTerrain
    1171     else ATer:=MyMap[ALoc] and fTerrain;
    1172     if (BLoc<0) or (BLoc>=G.lx*G.ly) then BTer:=PoleTile(BLoc) and fTerrain
    1173     else BTer:=MyMap[BLoc] and fTerrain;
    1174 
    1175     if (ATer<>fUNKNOWN) or (BTer<>fUNKNOWN) then
    1176       if ((ATer<fGrass) or (ATer=fUNKNOWN)) and ((BTer<fGrass) or (BTer=fUNKNOWN)) then
    1177         begin
    1178         if ATer=fUNKNOWN then Aix:=0
    1179         else if IsShoreTile(ALoc) then
    1180           if ATer=fOcean then Aix:=-1
    1181           else Aix:=1
    1182         else Aix:=ATer+2;
    1183         if BTer=fUNKNOWN then bix:=0
    1184         else if IsShoreTile(BLoc) then
    1185           if BTer=fOcean then bix:=-1
    1186           else bix:=1
    1187         else bix:=BTer+2;
    1188         if (Aix>1) or (bix>1) then
    1189           begin
    1190           if aix=-1 then
    1191             if bix=fOcean+2 then begin aix:=0; bix:=0 end
    1192             else begin aix:=0; bix:=1 end
    1193           else if bix=-1 then
    1194             if aix=fOcean+2 then begin aix:=1; bix:=1 end
    1195             else begin aix:=1; bix:=0 end;
    1196           BitBlt(OceanPatch,x+dx*xxt,y+dy*yyt,xxt,yyt,
    1197             Aix*(xxt*2)+(dx+dy+1) and 1 *xxt,bix*yyt,SRCCOPY)
     1141          if Options and (1 shl moEditMode) <> 0 then
     1142          begin
     1143            if Tile and fPrefStartPos <> 0 then
     1144              TSprite(x, y, 0 + 9 * 1)
     1145            else if Tile and fStartPos <> 0 then
     1146              TSprite(x, y, 0 + 9 * 2);
    11981147          end
    1199         end
    1200       else
    1201         begin
    1202         if ATer=fUNKNOWN then Aix:=0
    1203         else if (ALoc>=0) and (ALoc<G.lx*G.ly) and (MyMap[ALoc] and fDeadLands<>0) then
    1204           Aix:=-2
    1205         else if ATer=fOcean then Aix:=-1
    1206         else if ATer=fShore then Aix:=1
    1207         else if ATer>=fForest then Aix:=8
    1208         else Aix:=ATer;
    1209         if BTer=fUNKNOWN then bix:=0
    1210         else if (BLoc>=0) and (BLoc<G.lx*G.ly) and (MyMap[BLoc] and fDeadLands<>0) then
    1211           Bix:=-2
    1212         else if BTer=fOcean then bix:=-1
    1213         else if BTer=fShore then bix:=1
    1214         else if BTer>=fForest then bix:=8
    1215         else bix:=BTer;
    1216         if (Aix=-2) and (Bix=-2) then
    1217           begin Aix:=fDesert; Bix:=fDesert end
    1218         else if Aix=-2 then
    1219           if Bix<2 then Aix:=8 else Aix:=Bix
    1220         else if Bix=-2 then
    1221           if Aix<2 then Bix:=8 else Bix:=Aix;
    1222         if Aix=-1 then BitBlt(GrExt[HGrTerrain].Data,x+dx*xxt,y+dy*yyt,xxt,yyt,
    1223           1+6*(xxt*2+1)+(dx+dy+1) and 1 *xxt,1+yyt,SRCCOPY) // arctic <-> ocean
    1224         else if bix=-1 then BitBlt(GrExt[HGrTerrain].Data,x+dx*xxt,y+dy*yyt,xxt,
    1225           yyt,1+6*(xxt*2+1)+xxt-(dx+dy+1) and 1 *xxt,1+yyt*2,SRCCOPY) // arctic <-> ocean
    1226         else BitBlt(LandPatch,x+dx*xxt,y+dy*yyt,xxt,yyt,
    1227           Aix*(xxt*2)+(dx+dy+1) and 1 *xxt,bix*yyt,SRCCOPY)
    1228         end
    1229     end;
    1230 
    1231 OutDC:=FOutput.Canvas.Handle;
    1232 DataDC:=GrExt[HGrTerrain].Data.Canvas.Handle;
    1233 MaskDC:=GrExt[HGrTerrain].Mask.Canvas.Handle;
    1234 for dy:=-2 to ny+1 do for dx:=-1 to nx do if (dx+dy) and 1=0 then
    1235   PaintShore(x+xxt*dx,y+yyt+yyt*dy,dLoc(Loc,dx,dy));
    1236 for dy:=-2 to ny+1 do for dx:=-1 to nx do if (dx+dy) and 1=0 then
    1237   PaintTileExtraTerrain(x+xxt*dx,y+yyt+yyt*dy,dLoc(Loc,dx,dy));
    1238 if CityOwner>=0 then
    1239   begin
    1240   for dy:=-2 to ny+1 do for dx:=-2 to nx+1 do if (dx+dy) and 1=0 then
    1241     begin
    1242     ALoc:=dLoc(Loc,dx,dy);
    1243     if Distance(ALoc,CityLoc)>5 then
    1244       PaintTileObjects(x+xxt*dx,y+yyt+yyt*dy,ALoc,CityLoc,CityOwner,UseBlink);
    1245     end;
    1246   dx:=((CityLoc mod G.lx *2 +CityLoc div G.lx and 1)
    1247     -((Loc+666*G.lx) mod G.lx *2
    1248     +(Loc+666*G.lx) div G.lx and 1)+3*G.lx) mod (2*G.lx) -G.lx;
    1249   dy:=CityLoc div G.lx-(Loc+666*G.lx) div G.lx+666;
    1250   xm:=x+(dx+1)*xxt;
    1251   ym:=y+(dy+1)*yyt+yyt;
    1252   ShadeOutside(FLeft,FTop,FRight,FBottom,xm,ym);
    1253   CityGrid(xm,ym);
    1254   for dy:=-2 to ny+1 do for dx:=-2 to nx+1 do if (dx+dy) and 1=0 then
    1255     begin
    1256     ALoc:=dLoc(Loc,dx,dy);
    1257     if Distance(ALoc,CityLoc)<=5 then
    1258       PaintTileObjects(x+xxt*dx,y+yyt+yyt*dy,ALoc,CityLoc,CityOwner,UseBlink);
    1259     end;
    1260   end
    1261 else
    1262   begin
    1263   if ShowLoc or (Options and (1 shl moEditMode)<>0)
    1264     or (Options and (1 shl moGrid)<>0) then
    1265     PaintGrid(x,y,nx,ny);
    1266   for dy:=-2 to ny+1 do for dx:=-2 to nx+1 do if (dx+dy) and 1=0 then
    1267     PaintTileObjects(x+xxt*dx,y+yyt+yyt*dy,dLoc(Loc,dx,dy),CityLoc,CityOwner,UseBlink);
    1268   end;
    1269 
    1270 //frame(FOutput.Canvas,x+1,y+1,x+nx*33+33-2,y+ny*16+32-2,$FFFF,$FFFF);
    1271 end; {Paint}
    1272 
    1273 procedure TIsoMap.AttackBegin(const ShowMove: TShowMove);
    1274 begin
    1275 AttLoc:=ShowMove.FromLoc;
    1276 DefLoc:=dLoc(AttLoc,ShowMove.dx,ShowMove.dy);
    1277 DefHealth:=-1;
    1278 end;
    1279 
    1280 procedure TIsoMap.AttackEffect(const ShowMove: TShowMove);
    1281 begin
    1282 DefHealth:=ShowMove.EndHealthDef;
    1283 end;
    1284 
    1285 procedure TIsoMap.AttackEnd;
    1286 begin
    1287 AttLoc:=-1;
    1288 DefLoc:=-1;
    1289 end;
    1290 
     1148          else if ShowObjects then
     1149          begin
     1150            { if (CityLoc<0) and (UnFocus>=0) and (Loc=MyUn[UnFocus].Loc) then
     1151              if BlinkOn then TSprite(x,y,8+9*0)
     1152              else TSprite(x,y,8+9*1); }
     1153
     1154            NameCity;
     1155            ShowSpacePort;
     1156            if Tile and fCity <> 0 then
     1157              PaintCity(x + xxt, y + yyt, CityInfo, CityOwner < 0);
     1158
     1159            if (Tile and fUnit <> 0) and (Loc <> AttLoc) and
     1160              ((Loc <> DefLoc) or (DefHealth <> 0))
     1161{$IFNDEF SCR} and ((CityOwner >= 0) or (UnFocus < 0) or not UseBlink or
     1162              BlinkOn or (Loc <> MyUn[UnFocus].Loc)){$ENDIF}
     1163              and ((Tile and fCity <> fCity) or (Loc = DefLoc)
     1164{$IFNDEF SCR} or (not UseBlink or BlinkOn) and (UnFocus >= 0) and
     1165              (Loc = MyUn[UnFocus].Loc){$ENDIF}) then
     1166            begin { unit }
     1167              GetUnitInfo(Loc, uix, UnitInfo);
     1168              if (Loc = DefLoc) and (DefHealth >= 0) then
     1169                UnitInfo.Health := DefHealth;
     1170              if (UnitInfo.Owner <> CityOwner) and
     1171                not((CityOwner = me) and
     1172                (MyRO.Treaty[UnitInfo.Owner] = trAlliance)) then
     1173{$IFNDEF SCR} if (UnFocus >= 0) and (Loc = MyUn[UnFocus].Loc) then { active unit }
     1174                begin
     1175                  Multi := UnitInfo.Flags and unMulti;
     1176                  MakeUnitInfo(me, MyUn[UnFocus], UnitInfo);
     1177                  UnitInfo.Flags := UnitInfo.Flags or Multi;
     1178                  PaintUnit(x + (xxt - xxu), y + (yyt - yyu_anchor), UnitInfo,
     1179                    MyUn[UnFocus].Status);
     1180                end
     1181                else if UnitInfo.Owner = me then
     1182                begin
     1183                  if ClientMode = cMovieTurn then
     1184                    PaintUnit(x + (xxt - xxu), y + (yyt - yyu_anchor),
     1185                      UnitInfo, 0)
     1186                    // status is not set with precise timing during loading
     1187                  else
     1188                    PaintUnit(x + (xxt - xxu), y + (yyt - yyu_anchor), UnitInfo,
     1189                      MyUn[uix].Status);
     1190                  // if Showuix then Textout(x+16,y+5,$80FF00,IntToStr(uix));
     1191                end
     1192                else {$ENDIF} PaintUnit(x + (xxt - xxu), y + (yyt - yyu_anchor), UnitInfo, 0);
     1193            end
     1194            else if Tile and fHiddenUnit <> 0 then
     1195              Sprite(HGrStdUnits, x + (xxt - xxu), y + (yyt - yyu_anchor),
     1196                xxu * 2, yyu * 2, 1 + 5 * (xxu * 2 + 1), 1)
     1197            else if Tile and fStealthUnit <> 0 then
     1198              Sprite(HGrStdUnits, x + (xxt - xxu), y + (yyt - yyu_anchor),
     1199                xxu * 2, yyu * 2, 1 + 5 * (xxu * 2 + 1), 1 + 1 * (yyu * 2 + 1))
     1200          end;
     1201
     1202          if ShowObjects and (Tile and fTerImp = tiFort) and
     1203            (Tile and fObserved <> 0) then
     1204            TSprite(x, y, 3 + 9 * 12);
     1205
     1206          if (Loc >= 0) and (Loc < G.lx * G.ly) then
     1207            if ShowLoc then
     1208              Textout(x + xxt - 16, y + yyt - 9, $FFFF00, IntToStr(Loc))
     1209            else if ShowDebug and (DebugMap <> nil) and (Loc >= 0) and
     1210              (Loc < G.lx * G.ly) and (DebugMap[Loc] <> 0) then
     1211              Textout(x + xxt - 16, y + yyt - 9, $00E0FF,
     1212                IntToStr(integer(DebugMap[Loc])))
     1213        end; { PaintTileObjects }
     1214
     1215        procedure TIsoMap.PaintGrid(x, y, nx, ny: integer);
     1216
     1217          procedure ClippedLine(dx0, dy0: integer; mirror: boolean);
     1218          var
     1219            x0, x1, dxmin, dymin, dxmax, dymax, n: integer;
     1220          begin
     1221            with FOutput.Canvas do
     1222            begin
     1223              dxmin := (FLeft - x) div xxt;
     1224              dymin := (RealTop - y) div yyt;
     1225              dxmax := (FRight - x - 1) div xxt + 1;
     1226              dymax := (RealBottom - y - 1) div yyt + 1;
     1227              n := dymax - dy0;
     1228              if mirror then
     1229              begin
     1230                if dx0 - dxmin < n then
     1231                  n := dx0 - dxmin;
     1232                if dx0 > dxmax then
     1233                begin
     1234                  n := n - (dx0 - dxmax);
     1235                  dy0 := dy0 + (dx0 - dxmax);
     1236                  dx0 := dxmax
     1237                end;
     1238                if dy0 < dymin then
     1239                begin
     1240                  n := n - (dymin - dy0);
     1241                  dx0 := dx0 - (dymin - dy0);
     1242                  dy0 := dymin
     1243                end;
     1244              end
     1245              else
     1246              begin
     1247                if dxmax - dx0 < n then
     1248                  n := dxmax - dx0;
     1249                if dx0 < dxmin then
     1250                begin
     1251                  n := n - (dxmin - dx0);
     1252                  dy0 := dy0 + (dxmin - dx0);
     1253                  dx0 := dxmin
     1254                end;
     1255                if dy0 < dymin then
     1256                begin
     1257                  n := n - (dymin - dy0);
     1258                  dx0 := dx0 + (dymin - dy0);
     1259                  dy0 := dymin
     1260                end;
     1261              end;
     1262              if n <= 0 then
     1263                exit;
     1264              if mirror then
     1265              begin
     1266                x0 := x + dx0 * xxt - 1;
     1267                x1 := x + (dx0 - n) * xxt - 1;
     1268              end
     1269              else
     1270              begin
     1271                x0 := x + dx0 * xxt;
     1272                x1 := x + (dx0 + n) * xxt;
     1273              end;
     1274              moveto(x0, y + dy0 * yyt);
     1275              lineto(x1, y + (dy0 + n) * yyt);
     1276            end
     1277          end;
     1278
     1279        var
     1280          i: integer;
     1281        begin
     1282          FOutput.Canvas.pen.Color := $000000; // $FF shl (8*random(3));
     1283          for i := 0 to nx div 2 do
     1284            ClippedLine(i * 2, 0, false);
     1285          for i := 1 to (nx + 1) div 2 do
     1286            ClippedLine(i * 2, 0, true);
     1287          for i := 0 to ny div 2 do
     1288          begin
     1289            ClippedLine(0, 2 * i + 2, false);
     1290            ClippedLine(nx + 1, 2 * i + 1 + nx and 1, true);
     1291          end;
     1292        end;
     1293
     1294        procedure TIsoMap.Paint(x, y, Loc, nx, ny, CityLoc, CityOwner: integer;
     1295          UseBlink: boolean; CityAllowClick: boolean);
     1296
     1297          function IsShoreTile(Loc: integer): boolean;
     1298          const
     1299            Dirx: array [0 .. 7] of integer = (1, 2, 1, 0, -1, -2, -1, 0);
     1300            Diry: array [0 .. 7] of integer = (-1, 0, 1, 2, 1, 0, -1, -2);
     1301          var
     1302            Dir, ConnLoc: integer;
     1303          begin
     1304            result := false;
     1305            for Dir := 0 to 7 do
     1306            begin
     1307              ConnLoc := dLoc(Loc, Dirx[Dir], Diry[Dir]);
     1308              if (ConnLoc < 0) or (ConnLoc >= G.lx * G.ly) or
     1309                ((MyMap[ConnLoc] - 2) and fTerrain < 13) then
     1310                result := true
     1311            end
     1312          end;
     1313
     1314          procedure ShadeOutside(x0, y0, x1, y1, xm, ym: integer);
     1315          const
     1316            rShade = 3.75;
     1317
     1318            procedure MakeDark(Line: pointer; length: integer);
     1319            type
     1320              TCardArray = array [0 .. 9999] of Cardinal;
     1321              PCardArray = ^TCardArray;
     1322              TByteArray = array [0 .. 9999] of Byte;
     1323              PByteArray = ^TByteArray;
     1324            var
     1325              i, rest: integer;
     1326            begin
     1327              for i := length * 3 div 4 - 1 downto 0 do
     1328                PCardArray(Line)[i] := PCardArray(Line)[i] shr 1 and $7F7F7F7F;
     1329              rest := (length * 3 div 4) * 4;
     1330              for i := length * 3 mod 4 - 1 downto 0 do
     1331                PByteArray(Line)[rest + i] := PByteArray(Line)
     1332                  [rest + i] shr 1 and $7F;
     1333            end;
     1334
     1335          type
     1336            TLine = array [0 .. 99999, 0 .. 2] of Byte;
     1337          var
     1338            y, wBright: integer;
     1339            y_n, w_n: single;
     1340            Line: ^TLine;
     1341          begin
     1342            for y := y0 to y1 - 1 do
     1343            begin
     1344              Line := FOutput.ScanLine[y];
     1345              y_n := (y - ym) / yyt;
     1346              if abs(y_n) < rShade then
     1347              begin
     1348                w_n := sqrt(sqr(rShade) - sqr(y_n));
     1349                wBright := trunc(w_n * xxt + 0.5);
     1350                MakeDark(@Line[x0], xm - x0 - wBright);
     1351                MakeDark(@Line[xm + wBright], x1 - xm - wBright);
     1352              end
     1353              else
     1354                MakeDark(@Line[x0], x1 - x0);
     1355            end
     1356          end;
     1357
     1358          procedure CityGrid(xm, ym: integer);
     1359          var
     1360            i: integer;
     1361          begin
     1362            with FOutput.Canvas do
     1363            begin
     1364              if CityAllowClick then
     1365                pen.Color := $FFFFFF
     1366              else
     1367                pen.Color := $000000;
     1368              pen.Width := 1;
     1369              for i := 0 to 3 do
     1370              begin
     1371                moveto(xm - xxt * (4 - i), ym + yyt * (1 + i));
     1372                lineto(xm + xxt * (1 + i), ym - yyt * (4 - i));
     1373                moveto(xm - xxt * (4 - i), ym - yyt * (1 + i));
     1374                lineto(xm + xxt * (1 + i), ym + yyt * (4 - i));
     1375              end;
     1376              moveto(xm - xxt * 4, ym + yyt * 1);
     1377              lineto(xm - xxt * 1, ym + yyt * 4);
     1378              moveto(xm + xxt * 1, ym + yyt * 4);
     1379              lineto(xm + xxt * 4, ym + yyt * 1);
     1380              moveto(xm - xxt * 4, ym - yyt * 1);
     1381              lineto(xm - xxt * 1, ym - yyt * 4);
     1382              moveto(xm + xxt * 1, ym - yyt * 4);
     1383              lineto(xm + xxt * 4, ym - yyt * 1);
     1384              pen.Width := 1;
     1385            end
     1386          end;
     1387
     1388        var
     1389          dx, dy, xm, ym, ALoc, BLoc, ATer, BTer, Aix, bix: integer;
     1390        begin
     1391          FoW := true;
     1392          ShowLoc := Options and (1 shl moLocCodes) <> 0;
     1393          ShowDebug := pDebugMap >= 0;
     1394          ShowObjects := (CityOwner >= 0) or
     1395            (Options and (1 shl moBareTerrain) = 0);
     1396          ShowCityNames := ShowObjects and (CityOwner < 0) and
     1397            (Options and (1 shl moCityNames) <> 0);
     1398          ShowBorder := true;
     1399          ShowMyBorder := CityOwner < 0;
     1400          ShowGrWall := (CityOwner < 0) and
     1401            (Options and (1 shl moGreatWall) <> 0);
     1402          if ShowDebug then
     1403            Server(sGetDebugMap, me, pDebugMap, DebugMap)
     1404          else
     1405            DebugMap := nil;
     1406          with FOutput.Canvas do
     1407          begin
     1408            RealTop := y - ((Loc + 12345 * G.lx) div G.lx - 12345) * yyt;
     1409            RealBottom := y +
     1410              (G.ly - ((Loc + 12345 * G.lx) div G.lx - 12345) + 3) * yyt;
     1411            Brush.Color := EmptySpaceColor;
     1412            if RealTop > FTop then
     1413              FillRect(Rect(FLeft, FTop, FRight, RealTop))
     1414            else
     1415              RealTop := FTop;
     1416            if RealBottom < FBottom then
     1417              FillRect(Rect(FLeft, RealBottom, FRight, FBottom))
     1418            else
     1419              RealBottom := FBottom;
     1420            Brush.Color := $000000;
     1421            FillRect(Rect(FLeft, RealTop, FRight, RealBottom));
     1422            Brush.Style := bsClear;
     1423          end;
     1424
     1425          for dy := 0 to ny + 1 do
     1426            if (Loc + dy * G.lx >= 0) and (Loc + (dy - 3) * G.lx < G.lx * G.ly)
     1427            then
     1428              for dx := 0 to nx do
     1429              begin
     1430                ALoc := dLoc(Loc, dx - (dy + dx) and 1, dy - 2);
     1431                BLoc := dLoc(Loc, dx - (dy + dx + 1) and 1, dy - 1);
     1432                if (ALoc < 0) or (ALoc >= G.lx * G.ly) then
     1433                  ATer := PoleTile(ALoc) and fTerrain
     1434                else
     1435                  ATer := MyMap[ALoc] and fTerrain;
     1436                if (BLoc < 0) or (BLoc >= G.lx * G.ly) then
     1437                  BTer := PoleTile(BLoc) and fTerrain
     1438                else
     1439                  BTer := MyMap[BLoc] and fTerrain;
     1440
     1441                if (ATer <> fUNKNOWN) or (BTer <> fUNKNOWN) then
     1442                  if ((ATer < fGrass) or (ATer = fUNKNOWN)) and
     1443                    ((BTer < fGrass) or (BTer = fUNKNOWN)) then
     1444                  begin
     1445                    if ATer = fUNKNOWN then
     1446                      Aix := 0
     1447                    else if IsShoreTile(ALoc) then
     1448                      if ATer = fOcean then
     1449                        Aix := -1
     1450                      else
     1451                        Aix := 1
     1452                    else
     1453                      Aix := ATer + 2;
     1454                    if BTer = fUNKNOWN then
     1455                      bix := 0
     1456                    else if IsShoreTile(BLoc) then
     1457                      if BTer = fOcean then
     1458                        bix := -1
     1459                      else
     1460                        bix := 1
     1461                    else
     1462                      bix := BTer + 2;
     1463                    if (Aix > 1) or (bix > 1) then
     1464                    begin
     1465                      if Aix = -1 then
     1466                        if bix = fOcean + 2 then
     1467                        begin
     1468                          Aix := 0;
     1469                          bix := 0
     1470                        end
     1471                        else
     1472                        begin
     1473                          Aix := 0;
     1474                          bix := 1
     1475                        end
     1476                      else if bix = -1 then
     1477                        if Aix = fOcean + 2 then
     1478                        begin
     1479                          Aix := 1;
     1480                          bix := 1
     1481                        end
     1482                        else
     1483                        begin
     1484                          Aix := 1;
     1485                          bix := 0
     1486                        end;
     1487                      BitBlt(OceanPatch, x + dx * xxt, y + dy * yyt, xxt, yyt,
     1488                        Aix * (xxt * 2) + (dx + dy + 1) and 1 * xxt,
     1489                        bix * yyt, SRCCOPY)
     1490                    end
     1491                  end
     1492                  else
     1493                  begin
     1494                    if ATer = fUNKNOWN then
     1495                      Aix := 0
     1496                    else if (ALoc >= 0) and (ALoc < G.lx * G.ly) and
     1497                      (MyMap[ALoc] and fDeadLands <> 0) then
     1498                      Aix := -2
     1499                    else if ATer = fOcean then
     1500                      Aix := -1
     1501                    else if ATer = fShore then
     1502                      Aix := 1
     1503                    else if ATer >= fForest then
     1504                      Aix := 8
     1505                    else
     1506                      Aix := ATer;
     1507                    if BTer = fUNKNOWN then
     1508                      bix := 0
     1509                    else if (BLoc >= 0) and (BLoc < G.lx * G.ly) and
     1510                      (MyMap[BLoc] and fDeadLands <> 0) then
     1511                      bix := -2
     1512                    else if BTer = fOcean then
     1513                      bix := -1
     1514                    else if BTer = fShore then
     1515                      bix := 1
     1516                    else if BTer >= fForest then
     1517                      bix := 8
     1518                    else
     1519                      bix := BTer;
     1520                    if (Aix = -2) and (bix = -2) then
     1521                    begin
     1522                      Aix := fDesert;
     1523                      bix := fDesert
     1524                    end
     1525                    else if Aix = -2 then
     1526                      if bix < 2 then
     1527                        Aix := 8
     1528                      else
     1529                        Aix := bix
     1530                    else if bix = -2 then
     1531                      if Aix < 2 then
     1532                        bix := 8
     1533                      else
     1534                        bix := Aix;
     1535                    if Aix = -1 then
     1536                      BitBlt(GrExt[HGrTerrain].Data, x + dx * xxt, y + dy * yyt,
     1537                        xxt, yyt, 1 + 6 * (xxt * 2 + 1) + (dx + dy + 1) and
     1538                        1 * xxt, 1 + yyt, SRCCOPY) // arctic <-> ocean
     1539                    else if bix = -1 then
     1540                      BitBlt(GrExt[HGrTerrain].Data, x + dx * xxt, y + dy * yyt,
     1541                        xxt, yyt, 1 + 6 * (xxt * 2 + 1) + xxt - (dx + dy + 1)
     1542                        and 1 * xxt, 1 + yyt * 2, SRCCOPY) // arctic <-> ocean
     1543                    else
     1544                      BitBlt(LandPatch, x + dx * xxt, y + dy * yyt, xxt, yyt,
     1545                        Aix * (xxt * 2) + (dx + dy + 1) and 1 * xxt,
     1546                        bix * yyt, SRCCOPY)
     1547                  end
     1548              end;
     1549
     1550          OutDC := FOutput.Canvas.Handle;
     1551          DataDC := GrExt[HGrTerrain].Data.Canvas.Handle;
     1552          MaskDC := GrExt[HGrTerrain].Mask.Canvas.Handle;
     1553          for dy := -2 to ny + 1 do
     1554            for dx := -1 to nx do
     1555              if (dx + dy) and 1 = 0 then
     1556                PaintShore(x + xxt * dx, y + yyt + yyt * dy, dLoc(Loc, dx, dy));
     1557          for dy := -2 to ny + 1 do
     1558            for dx := -1 to nx do
     1559              if (dx + dy) and 1 = 0 then
     1560                PaintTileExtraTerrain(x + xxt * dx, y + yyt + yyt * dy,
     1561                  dLoc(Loc, dx, dy));
     1562          if CityOwner >= 0 then
     1563          begin
     1564            for dy := -2 to ny + 1 do
     1565              for dx := -2 to nx + 1 do
     1566                if (dx + dy) and 1 = 0 then
     1567                begin
     1568                  ALoc := dLoc(Loc, dx, dy);
     1569                  if Distance(ALoc, CityLoc) > 5 then
     1570                    PaintTileObjects(x + xxt * dx, y + yyt + yyt * dy, ALoc,
     1571                      CityLoc, CityOwner, UseBlink);
     1572                end;
     1573            dx := ((CityLoc mod G.lx * 2 + CityLoc div G.lx and 1) -
     1574              ((Loc + 666 * G.lx) mod G.lx * 2 + (Loc + 666 * G.lx) div G.lx and
     1575              1) + 3 * G.lx) mod (2 * G.lx) - G.lx;
     1576            dy := CityLoc div G.lx - (Loc + 666 * G.lx) div G.lx + 666;
     1577            xm := x + (dx + 1) * xxt;
     1578            ym := y + (dy + 1) * yyt + yyt;
     1579            ShadeOutside(FLeft, FTop, FRight, FBottom, xm, ym);
     1580            CityGrid(xm, ym);
     1581            for dy := -2 to ny + 1 do
     1582              for dx := -2 to nx + 1 do
     1583                if (dx + dy) and 1 = 0 then
     1584                begin
     1585                  ALoc := dLoc(Loc, dx, dy);
     1586                  if Distance(ALoc, CityLoc) <= 5 then
     1587                    PaintTileObjects(x + xxt * dx, y + yyt + yyt * dy, ALoc,
     1588                      CityLoc, CityOwner, UseBlink);
     1589                end;
     1590          end
     1591          else
     1592          begin
     1593            if ShowLoc or (Options and (1 shl moEditMode) <> 0) or
     1594              (Options and (1 shl moGrid) <> 0) then
     1595              PaintGrid(x, y, nx, ny);
     1596            for dy := -2 to ny + 1 do
     1597              for dx := -2 to nx + 1 do
     1598                if (dx + dy) and 1 = 0 then
     1599                  PaintTileObjects(x + xxt * dx, y + yyt + yyt * dy,
     1600                    dLoc(Loc, dx, dy), CityLoc, CityOwner, UseBlink);
     1601          end;
     1602
     1603          // frame(FOutput.Canvas,x+1,y+1,x+nx*33+33-2,y+ny*16+32-2,$FFFF,$FFFF);
     1604        end; { Paint }
     1605
     1606        procedure TIsoMap.AttackBegin(const ShowMove: TShowMove);
     1607        begin
     1608          AttLoc := ShowMove.FromLoc;
     1609          DefLoc := dLoc(AttLoc, ShowMove.dx, ShowMove.dy);
     1610          DefHealth := -1;
     1611        end;
     1612
     1613        procedure TIsoMap.AttackEffect(const ShowMove: TShowMove);
     1614        begin
     1615          DefHealth := ShowMove.EndHealthDef;
     1616        end;
     1617
     1618        procedure TIsoMap.AttackEnd;
     1619        begin
     1620          AttLoc := -1;
     1621          DefLoc := -1;
     1622        end;
    12911623
    12921624initialization
    12931625
    1294 NoMap:=nil;
    1295 LandPatch:=nil;
    1296 OceanPatch:=nil;
    1297 Borders:=nil;
     1626NoMap := nil;
     1627LandPatch := nil;
     1628OceanPatch := nil;
     1629Borders := nil;
     1630
    12981631end.
    1299 
  • trunk/LocalPlayer/LocalPlayer.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit LocalPlayer;
    43
    54interface
    65
    7 procedure Client(Command,Player:integer;var Data); stdcall;
     6procedure Client(Command, Player: integer; var Data); stdcall;
    87
    98procedure SetAIName(p: integer; Name: string);
    10 
    119
    1210implementation
    1311
    1412uses
    15 Term,CityScreen,Draft,MessgEx,Select,CityType,Help,UnitStat,Diagram,
    16 NatStat,Wonders,Nego,Enhance,BaseWin,Battle,Rates,TechTree,
     13  Term, CityScreen, Draft, MessgEx, Select, CityType, Help, UnitStat, Diagram,
     14  NatStat, Wonders, Nego, Enhance, BaseWin, Battle, Rates, TechTree,
    1715
    18 Forms;
     16  Forms;
    1917
    2018var
    21 FormsCreated: boolean;
     19  FormsCreated: boolean;
    2220
    2321procedure Client;
    2422begin
    25 if not FormsCreated then
     23  if not FormsCreated then
    2624  begin
    27   FormsCreated:=true;
    28   BaseWin.CreateOffscreen;
    29   Application.CreateForm(TMainScreen, MainScreen);
    30   Application.CreateForm(TCityDlg, CityDlg);
    31   Application.CreateForm(TModalSelectDlg, ModalSelectDlg);
    32   Application.CreateForm(TListDlg, ListDlg);
    33   Application.CreateForm(TMessgExDlg, MessgExDlg);
    34   Application.CreateForm(TDraftDlg, DraftDlg);
    35   Application.CreateForm(TCityTypeDlg, CityTypeDlg);
    36   Application.CreateForm(THelpDlg, HelpDlg);
    37   Application.CreateForm(TUnitStatDlg, UnitStatDlg);
    38   Application.CreateForm(TDiaDlg, DiaDlg);
    39   Application.CreateForm(TNatStatDlg, NatStatDlg);
    40   Application.CreateForm(TWondersDlg, WondersDlg);
    41   Application.CreateForm(TNegoDlg, NegoDlg);
    42   Application.CreateForm(TEnhanceDlg, EnhanceDlg);
    43   Application.CreateForm(TBattleDlg, BattleDlg);
    44   //Application.CreateForm(TAdvisorDlg, AdvisorDlg);
    45   Application.CreateForm(TRatesDlg, RatesDlg);
    46   Application.CreateForm(TTechTreeDlg, TechTreeDlg);
     25    FormsCreated := true;
     26    BaseWin.CreateOffscreen;
     27    Application.CreateForm(TMainScreen, MainScreen);
     28    Application.CreateForm(TCityDlg, CityDlg);
     29    Application.CreateForm(TModalSelectDlg, ModalSelectDlg);
     30    Application.CreateForm(TListDlg, ListDlg);
     31    Application.CreateForm(TMessgExDlg, MessgExDlg);
     32    Application.CreateForm(TDraftDlg, DraftDlg);
     33    Application.CreateForm(TCityTypeDlg, CityTypeDlg);
     34    Application.CreateForm(THelpDlg, HelpDlg);
     35    Application.CreateForm(TUnitStatDlg, UnitStatDlg);
     36    Application.CreateForm(TDiaDlg, DiaDlg);
     37    Application.CreateForm(TNatStatDlg, NatStatDlg);
     38    Application.CreateForm(TWondersDlg, WondersDlg);
     39    Application.CreateForm(TNegoDlg, NegoDlg);
     40    Application.CreateForm(TEnhanceDlg, EnhanceDlg);
     41    Application.CreateForm(TBattleDlg, BattleDlg);
     42    // Application.CreateForm(TAdvisorDlg, AdvisorDlg);
     43    Application.CreateForm(TRatesDlg, RatesDlg);
     44    Application.CreateForm(TTechTreeDlg, TechTreeDlg);
    4745  end;
    48 MainScreen.Client(Command,Player,Data);
     46  MainScreen.Client(Command, Player, Data);
    4947end;
    5048
    5149procedure SetAIName(p: integer; Name: string);
    5250begin
    53 MainScreen.SetAIName(p, Name);
     51  MainScreen.SetAIName(p, Name);
    5452end;
    5553
    5654initialization
    57 FormsCreated:=false;
     55
     56FormsCreated := false;
    5857
    5958end.
    60 
  • trunk/LocalPlayer/MessgEx.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit MessgEx;
    43
     
    65
    76uses
    8   Messg,Protocol,ScreenTools,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,ButtonA,
     7  Messg, Protocol, ScreenTools,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ButtonA,
    1110  ButtonB, ButtonBase, StdCtrls;
    1211
     
    1817    RemoveBtn: TButtonB;
    1918    EInput: TEdit;
    20     procedure FormCreate(Sender:TObject);
    21     procedure FormPaint(Sender:TObject);
     19    procedure FormCreate(Sender: TObject);
     20    procedure FormPaint(Sender: TObject);
    2221    procedure FormShow(Sender: TObject);
    2322    procedure Button1Click(Sender: TObject);
     
    3029    Kind, IconKind, IconIndex, HelpKind, HelpNo, CenterTo: integer;
    3130    OpenSound: string;
    32     function ShowModal: Integer; override;
     31    function ShowModal: integer; override;
    3332    procedure CancelMovie;
    3433  private
    3534    MovieCancelled: boolean;
    36     procedure PaintBook(ca: TCanvas; x,y,clPage,clCover: integer);
     35    procedure PaintBook(ca: TCanvas; x, y, clPage, clCover: integer);
    3736    procedure PaintMyArmy;
    3837    procedure PaintEnemyArmy;
    39     procedure OnPlaySound(var Msg:TMessage); message WM_PLAYSOUND;
     38    procedure OnPlaySound(var Msg: TMessage); message WM_PLAYSOUND;
    4039  end;
    4140
    4241const
    43 // extra message kinds
    44 mkYesNoCancel=4; mkOkCancelRemove=5; mkOkHelp=6; mkModel=7;
    45 
    46 
    47 //message icon kinds
    48 mikNone=-1; mikImp=0; mikModel=1; mikTribe=2; mikBook=3; mikAge=4;
    49 mikPureIcon=5; mikMyArmy=6; mikEnemyArmy=7; mikFullControl=8; mikShip=9;
    50 mikBigIcon=10; mikEnemyShipComplete=11;
    51 
    52 
    53 var
    54   MessgExDlg:TMessgExDlg;
     42  // extra message kinds
     43  mkYesNoCancel = 4;
     44  mkOkCancelRemove = 5;
     45  mkOkHelp = 6;
     46  mkModel = 7;
     47
     48  // message icon kinds
     49  mikNone = -1;
     50  mikImp = 0;
     51  mikModel = 1;
     52  mikTribe = 2;
     53  mikBook = 3;
     54  mikAge = 4;
     55  mikPureIcon = 5;
     56  mikMyArmy = 6;
     57  mikEnemyArmy = 7;
     58  mikFullControl = 8;
     59  mikShip = 9;
     60  mikBigIcon = 10;
     61  mikEnemyShipComplete = 11;
     62
     63var
     64  MessgExDlg: TMessgExDlg;
    5565
    5666procedure SoundMessageEx(SimpleText, SoundItem: string);
    5767procedure TribeMessage(p: integer; SimpleText, SoundItem: string);
    58 function SimpleQuery(QueryKind: integer; SimpleText, SoundItem: string):
    59   integer;
    60 procedure ContextMessage(SimpleText, SoundItem: string; ContextKind,
    61   ContextNo: integer);
    62 
     68function SimpleQuery(QueryKind: integer; SimpleText, SoundItem: string)
     69  : integer;
     70procedure ContextMessage(SimpleText, SoundItem: string;
     71  ContextKind, ContextNo: integer);
    6372
    6473implementation
    6574
    6675uses
    67 ClientTools,BaseWin,Term,Help, Select, Diplomacy, Inp, UnitStat, Tribes,
    68 IsoEngine,Diagram;
     76  ClientTools, BaseWin, Term, Help, Select, Diplomacy, Inp, UnitStat, Tribes,
     77  IsoEngine, Diagram;
    6978
    7079{$R *.DFM}
    7180
    7281const
    73 LostUnitsPerLine=6;
    74 
    75 var
    76 PerfFreq: int64;
    77 
    78 
    79 procedure TMessgExDlg.FormCreate(Sender:TObject);
    80 begin
    81 inherited;
    82 IconKind:=mikNone;
    83 CenterTo:=0;
    84 OpenSound:='';
     82  LostUnitsPerLine = 6;
     83
     84var
     85  PerfFreq: int64;
     86
     87procedure TMessgExDlg.FormCreate(Sender: TObject);
     88begin
     89  inherited;
     90  IconKind := mikNone;
     91  CenterTo := 0;
     92  OpenSound := '';
    8593end;
    8694
    8795procedure TMessgExDlg.FormShow(Sender: TObject);
    8896var
    89 i: integer;
    90 begin
    91 if IconKind=mikEnemyArmy then
    92   InitAllEnemyModels;
    93 
    94 Button1.Visible:= GameMode<>cMovie;
    95 Button2.Visible:= (GameMode<>cMovie) and (Kind<>mkOk);
    96 Button3.Visible:= (GameMode<>cMovie) and (Kind=mkYesNoCancel);
    97 RemoveBtn.Visible:= (GameMode<>cMovie) and (Kind=mkOkCancelRemove);
    98 EInput.Visible:= (GameMode<>cMovie) and (Kind=mkModel);
    99 if Button3.Visible then
    100   begin Button1.Left:=43; Button2.Left:=159; end
    101 else if Button2.Visible then
    102   begin Button1.Left:=101; Button2.Left:=217; end
    103 else Button1.Left:=159;
    104 RemoveBtn.Left:=ClientWidth-38;
    105 case Kind of
    106   mkYesNo, mkYesNoCancel:
     97  i: integer;
     98begin
     99  if IconKind = mikEnemyArmy then
     100    InitAllEnemyModels;
     101
     102  Button1.Visible := GameMode <> cMovie;
     103  Button2.Visible := (GameMode <> cMovie) and (Kind <> mkOk);
     104  Button3.Visible := (GameMode <> cMovie) and (Kind = mkYesNoCancel);
     105  RemoveBtn.Visible := (GameMode <> cMovie) and (Kind = mkOkCancelRemove);
     106  EInput.Visible := (GameMode <> cMovie) and (Kind = mkModel);
     107  if Button3.Visible then
     108  begin
     109    Button1.Left := 43;
     110    Button2.Left := 159;
     111  end
     112  else if Button2.Visible then
     113  begin
     114    Button1.Left := 101;
     115    Button2.Left := 217;
     116  end
     117  else
     118    Button1.Left := 159;
     119  RemoveBtn.Left := ClientWidth - 38;
     120  case Kind of
     121    mkYesNo, mkYesNoCancel:
     122      begin
     123        Button1.Caption := Phrases.Lookup('BTN_YES');
     124        Button2.Caption := Phrases.Lookup('BTN_NO')
     125      end;
     126    mkOKCancel, mkOkCancelRemove:
     127      begin
     128        Button1.Caption := Phrases.Lookup('BTN_OK');
     129        Button2.Caption := Phrases.Lookup('BTN_CANCEL');
     130      end;
     131  else
    107132    begin
    108     Button1.Caption:=Phrases.Lookup('BTN_YES');
    109     Button2.Caption:=Phrases.Lookup('BTN_NO')
     133      Button1.Caption := Phrases.Lookup('BTN_OK');
     134      Button2.Caption := Phrases.Lookup('BTN_INFO');
    110135    end;
    111   mkOKCancel, mkOkCancelRemove:
     136  end;
     137  Button3.Caption := Phrases.Lookup('BTN_CANCEL');
     138  RemoveBtn.Hint := Phrases.Lookup('BTN_DELGAME');
     139
     140  case IconKind of
     141    mikImp, mikModel, mikAge, mikPureIcon:
     142      TopSpace := 56;
     143    mikBigIcon:
     144      TopSpace := 152;
     145    mikEnemyShipComplete:
     146      TopSpace := 136;
     147    mikBook:
     148      if IconIndex >= 0 then
     149        TopSpace := 84
     150      else
     151        TopSpace := 47;
     152    mikTribe:
     153      begin
     154        Tribe[IconIndex].InitAge(GetAge(IconIndex));
     155        if Tribe[IconIndex].faceHGr >= 0 then
     156          TopSpace := 64
     157      end;
     158    mikFullControl:
     159      TopSpace := 80;
     160    mikShip:
     161      TopSpace := 240;
     162  else
     163    TopSpace := 0;
     164  end;
     165
     166  SplitText(true);
     167  ClientHeight := 72 + Border + TopSpace + Lines * MessageLineSpacing;
     168  if GameMode = cMovie then
     169    ClientHeight := ClientHeight - 32;
     170  if Kind = mkModel then
     171    ClientHeight := ClientHeight + 36;
     172  if IconKind in [mikMyArmy, mikEnemyArmy] then
     173  begin
     174    if nLostArmy > LostUnitsPerLine * 6 then
     175      ClientHeight := ClientHeight + 6 * 48
     176    else
     177      ClientHeight := ClientHeight + ((nLostArmy - 1) div LostUnitsPerLine
     178        + 1) * 48;
     179  end;
     180  case CenterTo of
     181    0:
     182      begin
     183        Left := (Screen.Width - ClientWidth) div 2;
     184        Top := (Screen.Height - ClientHeight) div 2 - MapCenterUp;
     185      end;
     186    1:
     187      begin
     188        Left := (Screen.Width - ClientWidth) div 4;
     189        Top := (Screen.Height - ClientHeight) * 2 div 3 - MapCenterUp;
     190      end;
     191    -1:
     192      begin
     193        Left := (Screen.Width - ClientWidth) div 4;
     194        Top := (Screen.Height - ClientHeight) div 3 - MapCenterUp;
     195      end;
     196  end;
     197  for i := 0 to ControlCount - 1 do
     198    Controls[i].Top := ClientHeight - (34 + Border);
     199  if Kind = mkModel then
     200    EInput.Top := ClientHeight - (76 + Border);
     201end;
     202
     203function TMessgExDlg.ShowModal: integer;
     204var
     205  Ticks0, Ticks: int64;
     206begin
     207  if GameMode = cMovie then
     208  begin
     209    if not((GameMode = cMovie) and (MovieSpeed = 4)) then
    112210    begin
    113     Button1.Caption:=Phrases.Lookup('BTN_OK');
    114     Button2.Caption:=Phrases.Lookup('BTN_CANCEL');
     211      MovieCancelled := false;
     212      Show;
     213      QueryPerformanceCounter(Ticks0);
     214      repeat
     215        Application.ProcessMessages;
     216        Sleep(1);
     217        QueryPerformanceCounter(Ticks);
     218      until MovieCancelled or ((Ticks - Ticks0) * 1000 >= 1500 * PerfFreq);
     219      Hide;
    115220    end;
    116   else
     221    result := mrOk;
     222  end
     223  else
     224    result := inherited ShowModal;
     225end;
     226
     227procedure TMessgExDlg.CancelMovie;
     228begin
     229  MovieCancelled := true;
     230end;
     231
     232procedure TMessgExDlg.PaintBook(ca: TCanvas; x, y, clPage, clCover: integer);
     233const
     234  xScrewed = 77;
     235  yScrewed = 10;
     236  wScrewed = 43;
     237  hScrewed = 27;
     238type
     239  TLine = array [0 .. 9999, 0 .. 2] of Byte;
     240var
     241  ix, iy, xDst, yDst, dx, dy, xIcon, yIcon, xb, yb, wb, hb: integer;
     242  x1, xR, yR, share: single;
     243  Screwed: array [0 .. wScrewed - 1, 0 .. hScrewed - 1, 0 .. 3] of single;
     244  SrcLine: ^TLine;
     245
     246begin
     247  if IconIndex >= 0 then
     248  begin
     249    xIcon := IconIndex mod 7 * xSizeBig;
     250    yIcon := (IconIndex + SystemIconLines * 7) div 7 * ySizeBig;
     251    // prepare screwed icon
     252    fillchar(Screwed, sizeof(Screwed), 0);
     253    for iy := 0 to 39 do
    117254    begin
    118     Button1.Caption:=Phrases.Lookup('BTN_OK');
    119     Button2.Caption:=Phrases.Lookup('BTN_INFO');
     255      SrcLine := BigImp.ScanLine[iy + yIcon];
     256      for ix := 0 to 55 do
     257      begin
     258        xR := ix * (37 + iy * 5 / 40) / 56;
     259        xDst := Trunc(xR);
     260        xR := Frac(xR);
     261        x1 := (120 - ix) * (120 - ix) - 10000;
     262        yR := iy * 18 / 40 + x1 * x1 / 4000000;
     263        yDst := Trunc(yR);
     264        yR := Frac(yR);
     265        for dx := 0 to 1 do
     266          for dy := 0 to 1 do
     267          begin
     268            if dx = 0 then
     269              share := 1 - xR
     270            else
     271              share := xR;
     272            if dy = 0 then
     273              share := share * (1 - yR)
     274            else
     275              share := share * yR;
     276            Screwed[xDst + dx, yDst + dy, 0] := Screwed[xDst + dx, yDst + dy, 0]
     277              + share * SrcLine[ix + xIcon, 0];
     278            Screwed[xDst + dx, yDst + dy, 1] := Screwed[xDst + dx, yDst + dy, 1]
     279              + share * SrcLine[ix + xIcon, 1];
     280            Screwed[xDst + dx, yDst + dy, 2] := Screwed[xDst + dx, yDst + dy, 2]
     281              + share * SrcLine[ix + xIcon, 2];
     282            Screwed[xDst + dx, yDst + dy, 3] := Screwed[xDst + dx, yDst + dy,
     283              3] + share;
     284          end
     285      end;
    120286    end;
    121   end;
    122 Button3.Caption:=Phrases.Lookup('BTN_CANCEL');
    123 RemoveBtn.Hint:=Phrases.Lookup('BTN_DELGAME');
    124 
    125 case IconKind of
    126   mikImp,mikModel,mikAge,mikPureIcon:
    127     TopSpace:=56;
    128   mikBigIcon:
    129     TopSpace:=152;
    130   mikEnemyShipComplete:
    131     TopSpace:=136;
    132   mikBook:
    133     if IconIndex>=0 then TopSpace:=84
    134     else TopSpace:=47;
    135   mikTribe:
     287    xb := xBBook;
     288    yb := yBBook;
     289    wb := wBBook;
     290    hb := hBBook;
     291  end
     292  else
     293  begin
     294    xb := xSBook;
     295    yb := ySBook;
     296    wb := wSBook;
     297    hb := hSBook;
     298  end;
     299  x := x - wb div 2;
     300
     301  // paint
     302  BitBlt(LogoBuffer.Canvas.Handle, 0, 0, wb, hb, ca.Handle, x, y, SRCCOPY);
     303
     304  if IconIndex >= 0 then
     305    for iy := 0 to hScrewed - 1 do
     306      for ix := 0 to wScrewed - 1 do
     307        if Screwed[ix, iy, 3] > 0.01 then
     308          LogoBuffer.Canvas.Pixels[xScrewed + ix, yScrewed + iy] :=
     309            Trunc(Screwed[ix, iy, 2] / Screwed[ix, iy, 3]) +
     310            Trunc(Screwed[ix, iy, 1] / Screwed[ix, iy, 3]) shl 8 +
     311            Trunc(Screwed[ix, iy, 0] / Screwed[ix, iy, 3]) shl 16;
     312
     313  ImageOp_BCC(LogoBuffer, Templates, 0, 0, xb, yb, wb, hb, clCover, clPage);
     314
     315  BitBlt(ca.Handle, x, y, wb, hb, LogoBuffer.Canvas.Handle, 0, 0, SRCCOPY);
     316end;
     317
     318procedure TMessgExDlg.PaintMyArmy;
     319begin
     320end;
     321
     322procedure TMessgExDlg.PaintEnemyArmy;
     323var
     324  emix, ix, iy, x, y, count, UnitsInLine: integer;
     325begin
     326  ix := 0;
     327  iy := 0;
     328  if nLostArmy > LostUnitsPerLine then
     329    UnitsInLine := LostUnitsPerLine
     330  else
     331    UnitsInLine := nLostArmy;
     332  for emix := 0 to MyRO.nEnemyModel - 1 do
     333    for count := 0 to LostArmy[emix] - 1 do
    136334    begin
    137     Tribe[IconIndex].InitAge(GetAge(IconIndex));
    138     if Tribe[IconIndex].faceHGr>=0 then
    139       TopSpace:=64
    140     end;
    141   mikFullControl:
    142     TopSpace:=80; 
    143   mikShip:
    144     TopSpace:=240;
    145   else TopSpace:=0;
    146   end;
    147 
    148 SplitText(true);
    149 ClientHeight:=72+Border+TopSpace+Lines*MessageLineSpacing;
    150 if GameMode=cMovie then ClientHeight:=ClientHeight-32;
    151 if Kind=mkModel then
    152   ClientHeight:=ClientHeight+36;
    153 if IconKind in [mikMyArmy,mikEnemyArmy] then
    154   begin
    155   if nLostArmy>LostUnitsPerLine*6 then ClientHeight:=ClientHeight+6*48
    156   else ClientHeight:=ClientHeight+((nLostArmy-1) div LostUnitsPerLine +1)*48;
    157   end;
    158 case CenterTo of
    159   0:
    160     begin
    161     Left:=(Screen.Width-ClientWidth) div 2;
    162     Top:=(Screen.Height-ClientHeight) div 2-MapCenterUp;
    163     end;
    164   1:
    165     begin
    166     Left:=(Screen.Width-ClientWidth) div 4;
    167     Top:=(Screen.Height-ClientHeight)*2 div 3-MapCenterUp;
    168     end;
    169   -1:
    170     begin
    171     Left:=(Screen.Width-ClientWidth) div 4;
    172     Top:=(Screen.Height-ClientHeight) div 3-MapCenterUp;
    173     end;
    174   end;
    175 for i:=0 to ControlCount-1 do
    176   Controls[i].Top:=ClientHeight-(34+Border);
    177 if Kind=mkModel then
    178   EInput.Top:=ClientHeight-(76+Border);
    179 end;
    180 
    181 function TMessgExDlg.ShowModal: Integer;
    182 var
    183 Ticks0,Ticks: int64;
    184 begin
    185 if GameMode=cMovie then
    186   begin
    187   if not ((GameMode=cMovie) and (MovieSpeed=4)) then
    188     begin
    189     MovieCancelled:=false;
    190     Show;
    191     QueryPerformanceCounter(Ticks0);
    192     repeat
    193       Application.ProcessMessages;
    194       Sleep(1);
    195       QueryPerformanceCounter(Ticks);
    196     until MovieCancelled or ((Ticks-Ticks0)*1000>=1500*PerfFreq);
    197     Hide;
    198     end;
    199   result:=mrOk;
    200   end
    201 else
    202   result:=inherited ShowModal;
    203 end;
    204 
    205 procedure TMessgExDlg.CancelMovie;
    206 begin
    207 MovieCancelled:=true;
    208 end;
    209 
    210 procedure TMessgExDlg.PaintBook(ca: TCanvas; x,y,clPage,clCover: integer);
    211 const
    212 xScrewed=77; yScrewed=10; wScrewed=43; hScrewed=27;
    213 type
    214 TLine=array[0..9999,0..2] of Byte;
    215 var
    216 ix,iy,xDst,yDst,dx,dy,xIcon,yIcon,xb,yb,wb,hb: integer;
    217 x1,xR,yR,share: single;
    218 Screwed: array[0..wScrewed-1,0..hScrewed-1,0..3] of single;
    219 SrcLine: ^TLine;
    220 
    221 begin
    222 if IconIndex>=0 then
    223   begin
    224   xIcon:=IconIndex mod 7*xSizeBig;
    225   yIcon:=(IconIndex+SystemIconLines*7) div 7*ySizeBig;
    226   // prepare screwed icon
    227   fillchar(Screwed,sizeof(Screwed),0);
    228   for iy:=0 to 39 do
    229     begin
    230     SrcLine:=BigImp.ScanLine[iy+yIcon];
    231     for ix:=0 to 55 do
    232       begin
    233       xR:=ix*(37+iy*5/40)/56;
    234       xDst:=Trunc(xR);
    235       xR:=Frac(xR);
    236       x1:=(120-ix)*(120-ix)-10000;
    237       yR:=iy*18/40 +x1*x1/4000000;
    238       yDst:=Trunc(yR);
    239       yR:=Frac(yR);
    240       for dx:=0 to 1 do for dy:=0 to 1 do
    241         begin
    242         if dx=0 then share:=1-xR else share:=xR;
    243         if dy=0 then share:=share*(1-yR) else share:=share*yR;
    244         Screwed[xDst+dx,yDst+dy,0]:=
    245           Screwed[xDst+dx,yDst+dy,0]+share*SrcLine[ix+xIcon,0];
    246         Screwed[xDst+dx,yDst+dy,1]:=
    247           Screwed[xDst+dx,yDst+dy,1]+share*SrcLine[ix+xIcon,1];
    248         Screwed[xDst+dx,yDst+dy,2]:=
    249           Screwed[xDst+dx,yDst+dy,2]+share*SrcLine[ix+xIcon,2];
    250         Screwed[xDst+dx,yDst+dy,3]:=
    251           Screwed[xDst+dx,yDst+dy,3]+share;
    252         end
    253       end;
    254     end;
    255   xb:=xBBook; yb:=yBBook; wb:=wBBook; hb:=hBBook;
    256   end
    257 else begin xb:=xSBook; yb:=ySBook; wb:=wSBook; hb:=hSBook; end;
    258 x:=x-wb div 2;
    259 
    260 // paint
    261 BitBlt(LogoBuffer.Canvas.Handle,0,0,wb,hb,ca.handle,x,y,SRCCOPY);
    262 
    263 if IconIndex>=0 then
    264   for iy:=0 to hScrewed-1 do for ix:=0 to wScrewed-1 do
    265     if Screwed[ix,iy,3]>0.01 then
    266     LogoBuffer.Canvas.Pixels[xScrewed+ix,yScrewed+iy]:=
    267       trunc(Screwed[ix,iy,2]/Screwed[ix,iy,3])
    268       +trunc(Screwed[ix,iy,1]/Screwed[ix,iy,3]) shl 8
    269       +trunc(Screwed[ix,iy,0]/Screwed[ix,iy,3]) shl 16;
    270 
    271 ImageOp_BCC(LogoBuffer,Templates,0,0,xb,yb,wb,hb,clCover,clPage);
    272 
    273 BitBlt(ca.handle,x,y,wb,hb,LogoBuffer.Canvas.Handle,0,0,SRCCOPY);
    274 end;
    275 
    276 procedure TMessgExDlg.PaintMyArmy;
    277 begin
    278 end;
    279 
    280 procedure TMessgExDlg.PaintEnemyArmy;
    281 var
    282 emix,ix,iy,x,y,count,UnitsInLine: integer;
    283 begin
    284 ix:=0;
    285 iy:=0;
    286 if nLostArmy>LostUnitsPerLine then
    287   UnitsInLine:=LostUnitsPerLine
    288 else UnitsInLine:=nLostArmy;
    289 for emix:=0 to MyRO.nEnemyModel-1 do
    290   for count:=0 to LostArmy[emix]-1 do
    291     begin
    292     x:=ClientWidth div 2+ix*64-UnitsInLine*32;
    293     y:=26+Border+TopSpace+Lines*MessageLineSpacing+iy*48;
    294     with MyRO.EnemyModel[emix],Tribe[Owner].ModelPicture[mix] do
    295       begin
    296       BitBlt(Canvas.Handle,x,y,64,48,GrExt[HGr].Mask.Canvas.Handle,
    297         pix mod 10 *65+1,pix div 10 *49+1,SRCAND);
    298       BitBlt(Canvas.Handle,x,y,64,48,GrExt[HGr].Data.Canvas.Handle,
    299         pix mod 10 *65+1,pix div 10 *49+1,SRCPAINT);
    300       end; 
    301 
    302     // next position
    303     inc(ix);
    304     if ix=LostUnitsPerLine then
     335      x := ClientWidth div 2 + ix * 64 - UnitsInLine * 32;
     336      y := 26 + Border + TopSpace + Lines * MessageLineSpacing + iy * 48;
     337      with MyRO.EnemyModel[emix], Tribe[Owner].ModelPicture[mix] do
     338      begin
     339        BitBlt(Canvas.Handle, x, y, 64, 48, GrExt[HGr].Mask.Canvas.Handle,
     340          pix mod 10 * 65 + 1, pix div 10 * 49 + 1, SRCAND);
     341        BitBlt(Canvas.Handle, x, y, 64, 48, GrExt[HGr].Data.Canvas.Handle,
     342          pix mod 10 * 65 + 1, pix div 10 * 49 + 1, SRCPAINT);
     343      end;
     344
     345      // next position
     346      inc(ix);
     347      if ix = LostUnitsPerLine then
    305348      begin // next line
    306       ix:=0;
    307       inc(iy);
    308       if iy=6 then
    309         exit;
    310       UnitsInLine:=nLostArmy-LostUnitsPerLine*iy;
    311       if UnitsInLine>LostUnitsPerLine then
    312         UnitsInLine:=LostUnitsPerLine;
     349        ix := 0;
     350        inc(iy);
     351        if iy = 6 then
     352          exit;
     353        UnitsInLine := nLostArmy - LostUnitsPerLine * iy;
     354        if UnitsInLine > LostUnitsPerLine then
     355          UnitsInLine := LostUnitsPerLine;
    313356      end
    314357    end;
    315358end;
    316359
    317 procedure TMessgExDlg.FormPaint(Sender:TObject);
    318 var
    319 p1,clSaveTextLight,clSaveTextShade: integer;
    320 begin
    321 if (IconKind=mikImp) and (IconIndex=27) then
     360procedure TMessgExDlg.FormPaint(Sender: TObject);
     361var
     362  p1, clSaveTextLight, clSaveTextShade: integer;
     363begin
     364  if (IconKind = mikImp) and (IconIndex = 27) then
    322365  begin // "YOU WIN" message
    323   clSaveTextLight:=MainTexture.clTextLight;
    324   clSaveTextShade:=MainTexture.clTextShade;
    325   MainTexture.clTextLight:=$000000; // gold
    326   MainTexture.clTextShade:=$0FDBFF;
    327   inherited;
    328   MainTexture.clTextLight:=clSaveTextLight;
    329   MainTexture.clTextShade:=clSaveTextShade;
    330   end
    331 else
    332   inherited;
    333 
    334 case IconKind of
    335   mikImp:
    336     if Imp[IconIndex].Kind=ikWonder then
    337       begin
    338       p1:=MyRO.Wonder[IconIndex].EffectiveOwner;
    339       BitBlt(Buffer.Canvas.Handle,0,0,xSizeBig+2*GlowRange,ySizeBig+2*GlowRange,
    340         Canvas.Handle,ClientWidth div 2-(28+GlowRange),24-GlowRange,SRCCOPY);
    341       BitBlt(Buffer.Canvas.Handle,GlowRange,GlowRange,xSizeBig,ySizeBig,
    342         BigImp.Canvas.Handle,IconIndex mod 7*xSizeBig,
    343         (IconIndex+SystemIconLines*7) div 7*ySizeBig,SRCCOPY);
    344       if p1<0 then
    345         GlowFrame(Buffer, GlowRange, GlowRange, xSizeBig, ySizeBig, $000000)
    346       else GlowFrame(Buffer, GlowRange, GlowRange, xSizeBig, ySizeBig,
    347         Tribe[p1].Color);
    348       BitBlt(Canvas.Handle,ClientWidth div 2-(28+GlowRange),24-GlowRange,
    349         xSizeBig+2*GlowRange,ySizeBig+2*GlowRange,Buffer.Canvas.Handle,0,0,
    350         SRCCOPY);
     366    clSaveTextLight := MainTexture.clTextLight;
     367    clSaveTextShade := MainTexture.clTextShade;
     368    MainTexture.clTextLight := $000000; // gold
     369    MainTexture.clTextShade := $0FDBFF;
     370    inherited;
     371    MainTexture.clTextLight := clSaveTextLight;
     372    MainTexture.clTextShade := clSaveTextShade;
     373  end
     374  else
     375    inherited;
     376
     377  case IconKind of
     378    mikImp:
     379      if Imp[IconIndex].Kind = ikWonder then
     380      begin
     381        p1 := MyRO.Wonder[IconIndex].EffectiveOwner;
     382        BitBlt(Buffer.Canvas.Handle, 0, 0, xSizeBig + 2 * GlowRange,
     383          ySizeBig + 2 * GlowRange, Canvas.Handle,
     384          ClientWidth div 2 - (28 + GlowRange), 24 - GlowRange, SRCCOPY);
     385        BitBlt(Buffer.Canvas.Handle, GlowRange, GlowRange, xSizeBig, ySizeBig,
     386          BigImp.Canvas.Handle, IconIndex mod 7 * xSizeBig,
     387          (IconIndex + SystemIconLines * 7) div 7 * ySizeBig, SRCCOPY);
     388        if p1 < 0 then
     389          GlowFrame(Buffer, GlowRange, GlowRange, xSizeBig, ySizeBig, $000000)
     390        else
     391          GlowFrame(Buffer, GlowRange, GlowRange, xSizeBig, ySizeBig,
     392            Tribe[p1].Color);
     393        BitBlt(Canvas.Handle, ClientWidth div 2 - (28 + GlowRange),
     394          24 - GlowRange, xSizeBig + 2 * GlowRange, ySizeBig + 2 * GlowRange,
     395          Buffer.Canvas.Handle, 0, 0, SRCCOPY);
    351396      end
    352     else ImpImage(Canvas,ClientWidth div 2-28,24,IconIndex);
    353   mikAge:
    354     begin
    355     if IconIndex=0 then
    356       ImpImage(Canvas,ClientWidth div 2-28,24,-7)
    357     else ImpImage(Canvas,ClientWidth div 2-28,24,24+IconIndex)
    358     end;
    359   mikModel:
    360     with Tribe[me].ModelPicture[IconIndex] do
    361       begin
    362       FrameImage(Canvas,BigImp,ClientWidth div 2-28,24,xSizeBig,ySizeBig,0,0);
    363       BitBlt(Canvas.Handle,ClientWidth div 2-32,20,64,44,
    364         GrExt[HGr].Mask.Canvas.Handle,pix mod 10 *65+1,pix div 10*49+1,SRCAND);
    365       BitBlt(Canvas.Handle,ClientWidth div 2-32,20,64,44,
    366         GrExt[HGr].Data.Canvas.Handle,pix mod 10 *65+1,pix div 10*49+1,SRCPAINT);
    367       end;
    368   mikBook:
    369     PaintBook(Canvas,ClientWidth div 2,24,MainTexture.clPage,MainTexture.clCover);
    370   mikTribe:
    371     if Tribe[IconIndex].faceHGr>=0 then
    372       begin
    373       Frame(Canvas,ClientWidth div 2-32-1,24-1,ClientWidth div 2+32,
    374         24+48,$000000,$000000);
    375       BitBlt(Canvas.Handle,ClientWidth div 2-32,24,64,48,
    376         GrExt[Tribe[IconIndex].faceHGr].Data.Canvas.Handle,
    377         1+Tribe[IconIndex].facepix mod 10 *65,
    378         1+Tribe[IconIndex].facepix div 10 *49, SRCCOPY)
    379       end;
    380   mikPureIcon:
    381     FrameImage(Canvas, BigImp, ClientWidth div 2-28,24,xSizeBig, ySizeBig,
    382       IconIndex mod 7*xSizeBig,
    383       IconIndex div 7*ySizeBig);
    384   mikBigIcon:
    385     FrameImage(Canvas, BigImp, ClientWidth div 2-3*28,32,xSizeBig*3, ySizeBig*3,
    386       IconIndex mod 2*3*xSizeBig,
    387       IconIndex div 2*3*ySizeBig);
    388   mikEnemyShipComplete:
    389     begin
    390     BitBlt(Buffer.Canvas.Handle,0,0,140,120,Canvas.Handle,
    391       (ClientWidth-140) div 2,24,SRCCOPY);
    392     ImageOp_BCC(Buffer,Templates,0,0,1,279,140,120,0,$FFFFFF);
    393     BitBlt(Canvas.Handle,(ClientWidth-140) div 2,24,140,
    394       120,Buffer.Canvas.Handle,0,0,SRCCOPY);
    395     end;
    396   mikMyArmy:
    397     PaintMyArmy;
    398   mikEnemyArmy:
    399     PaintEnemyArmy;
    400   mikFullControl:
    401     Sprite(Canvas,HGrSystem2,ClientWidth div 2-31,24,63,63,1,281);
    402   mikShip:
    403     PaintColonyShip(Canvas,IconIndex,17,ClientWidth-34,38);
    404   end;
    405 
    406 if EInput.Visible then EditFrame(Canvas,EInput.BoundsRect,MainTexture);
    407 
    408 if OpenSound<>'' then PostMessage(Handle, WM_PLAYSOUND, 0, 0);
    409 end; {FormPaint}
     397      else
     398        ImpImage(Canvas, ClientWidth div 2 - 28, 24, IconIndex);
     399    mikAge:
     400      begin
     401        if IconIndex = 0 then
     402          ImpImage(Canvas, ClientWidth div 2 - 28, 24, -7)
     403        else
     404          ImpImage(Canvas, ClientWidth div 2 - 28, 24, 24 + IconIndex)
     405      end;
     406    mikModel:
     407      with Tribe[me].ModelPicture[IconIndex] do
     408      begin
     409        FrameImage(Canvas, BigImp, ClientWidth div 2 - 28, 24, xSizeBig,
     410          ySizeBig, 0, 0);
     411        BitBlt(Canvas.Handle, ClientWidth div 2 - 32, 20, 64, 44,
     412          GrExt[HGr].Mask.Canvas.Handle, pix mod 10 * 65 + 1,
     413          pix div 10 * 49 + 1, SRCAND);
     414        BitBlt(Canvas.Handle, ClientWidth div 2 - 32, 20, 64, 44,
     415          GrExt[HGr].Data.Canvas.Handle, pix mod 10 * 65 + 1,
     416          pix div 10 * 49 + 1, SRCPAINT);
     417      end;
     418    mikBook:
     419      PaintBook(Canvas, ClientWidth div 2, 24, MainTexture.clPage,
     420        MainTexture.clCover);
     421    mikTribe:
     422      if Tribe[IconIndex].faceHGr >= 0 then
     423      begin
     424        Frame(Canvas, ClientWidth div 2 - 32 - 1, 24 - 1,
     425          ClientWidth div 2 + 32, 24 + 48, $000000, $000000);
     426        BitBlt(Canvas.Handle, ClientWidth div 2 - 32, 24, 64, 48,
     427          GrExt[Tribe[IconIndex].faceHGr].Data.Canvas.Handle,
     428          1 + Tribe[IconIndex].facepix mod 10 * 65,
     429          1 + Tribe[IconIndex].facepix div 10 * 49, SRCCOPY)
     430      end;
     431    mikPureIcon:
     432      FrameImage(Canvas, BigImp, ClientWidth div 2 - 28, 24, xSizeBig, ySizeBig,
     433        IconIndex mod 7 * xSizeBig, IconIndex div 7 * ySizeBig);
     434    mikBigIcon:
     435      FrameImage(Canvas, BigImp, ClientWidth div 2 - 3 * 28, 32, xSizeBig * 3,
     436        ySizeBig * 3, IconIndex mod 2 * 3 * xSizeBig,
     437        IconIndex div 2 * 3 * ySizeBig);
     438    mikEnemyShipComplete:
     439      begin
     440        BitBlt(Buffer.Canvas.Handle, 0, 0, 140, 120, Canvas.Handle,
     441          (ClientWidth - 140) div 2, 24, SRCCOPY);
     442        ImageOp_BCC(Buffer, Templates, 0, 0, 1, 279, 140, 120, 0, $FFFFFF);
     443        BitBlt(Canvas.Handle, (ClientWidth - 140) div 2, 24, 140, 120,
     444          Buffer.Canvas.Handle, 0, 0, SRCCOPY);
     445      end;
     446    mikMyArmy:
     447      PaintMyArmy;
     448    mikEnemyArmy:
     449      PaintEnemyArmy;
     450    mikFullControl:
     451      Sprite(Canvas, HGrSystem2, ClientWidth div 2 - 31, 24, 63, 63, 1, 281);
     452    mikShip:
     453      PaintColonyShip(Canvas, IconIndex, 17, ClientWidth - 34, 38);
     454  end;
     455
     456  if EInput.Visible then
     457    EditFrame(Canvas, EInput.BoundsRect, MainTexture);
     458
     459  if OpenSound <> '' then
     460    PostMessage(Handle, WM_PLAYSOUND, 0, 0);
     461end; { FormPaint }
    410462
    411463procedure TMessgExDlg.Button1Click(Sender: TObject);
    412464begin
    413 ModalResult:=mrOK;
     465  ModalResult := mrOk;
    414466end;
    415467
    416468procedure TMessgExDlg.Button2Click(Sender: TObject);
    417469begin
    418 if Kind=mkOkHelp then
    419   HelpDlg.ShowNewContent(wmSubmodal, HelpKind, HelpNo)
    420 else if Kind=mkModel then
    421   UnitStatDlg.ShowNewContent_OwnModel(wmSubmodal, IconIndex)
    422 else ModalResult:=mrIgnore;
     470  if Kind = mkOkHelp then
     471    HelpDlg.ShowNewContent(wmSubmodal, HelpKind, HelpNo)
     472  else if Kind = mkModel then
     473    UnitStatDlg.ShowNewContent_OwnModel(wmSubmodal, IconIndex)
     474  else
     475    ModalResult := mrIgnore;
    423476end;
    424477
    425478procedure TMessgExDlg.Button3Click(Sender: TObject);
    426479begin
    427 ModalResult:=mrCancel
     480  ModalResult := mrCancel
    428481end;
    429482
    430483procedure TMessgExDlg.RemoveBtnClick(Sender: TObject);
    431484begin
    432 ModalResult:=mrNo
     485  ModalResult := mrNo
    433486end;
    434487
    435488procedure TMessgExDlg.FormKeyPress(Sender: TObject; var Key: char);
    436489begin
    437 if Key=#13 then ModalResult:=mrOK
    438 else if (Key=#27) then
    439   if Button3.Visible then ModalResult:=mrCancel
    440   else if Button2.Visible then ModalResult:=mrIgnore
     490  if Key = #13 then
     491    ModalResult := mrOk
     492  else if (Key = #27) then
     493    if Button3.Visible then
     494      ModalResult := mrCancel
     495    else if Button2.Visible then
     496      ModalResult := mrIgnore
    441497end;
    442498
     
    444500// because Messg.SoundMessage not capable of movie mode
    445501begin
    446 with MessgExDlg do
    447   begin
    448   MessgText:=SimpleText;
    449   OpenSound:=SoundItem;
    450   Kind:=mkOK;
    451   ShowModal;
     502  with MessgExDlg do
     503  begin
     504    MessgText := SimpleText;
     505    OpenSound := SoundItem;
     506    Kind := mkOk;
     507    ShowModal;
    452508  end
    453509end;
     
    455511procedure TribeMessage(p: integer; SimpleText, SoundItem: string);
    456512begin
    457 with MessgExDlg do
    458   begin
    459   OpenSound:=SoundItem;
    460   MessgText:=SimpleText;
    461   Kind:=mkOK;
    462   IconKind:=mikTribe;
    463   IconIndex:=p;
    464   ShowModal;
    465   end;
    466 end;
    467 
    468 function SimpleQuery(QueryKind: integer; SimpleText, SoundItem: string):
    469   integer;
    470 begin
    471 with MessgExDlg do
    472   begin
    473   MessgText:=SimpleText;
    474   OpenSound:=SoundItem;
    475   Kind:=QueryKind;
    476   ShowModal;
    477   result:=ModalResult
    478   end
    479 end;
    480 
    481 procedure ContextMessage(SimpleText, SoundItem: string; ContextKind,
    482   ContextNo: integer);
    483 begin
    484 with MessgExDlg do
    485   begin
    486   MessgText:=SimpleText;
    487   OpenSound:=SoundItem;
    488   Kind:=mkOkHelp;
    489   HelpKind:=ContextKind;
    490   HelpNo:=ContextNo;
    491   ShowModal;
     513  with MessgExDlg do
     514  begin
     515    OpenSound := SoundItem;
     516    MessgText := SimpleText;
     517    Kind := mkOk;
     518    IconKind := mikTribe;
     519    IconIndex := p;
     520    ShowModal;
     521  end;
     522end;
     523
     524function SimpleQuery(QueryKind: integer; SimpleText, SoundItem: string)
     525  : integer;
     526begin
     527  with MessgExDlg do
     528  begin
     529    MessgText := SimpleText;
     530    OpenSound := SoundItem;
     531    Kind := QueryKind;
     532    ShowModal;
     533    result := ModalResult
     534  end
     535end;
     536
     537procedure ContextMessage(SimpleText, SoundItem: string;
     538  ContextKind, ContextNo: integer);
     539begin
     540  with MessgExDlg do
     541  begin
     542    MessgText := SimpleText;
     543    OpenSound := SoundItem;
     544    Kind := mkOkHelp;
     545    HelpKind := ContextKind;
     546    HelpNo := ContextNo;
     547    ShowModal;
    492548  end
    493549end;
     
    495551procedure TMessgExDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    496552begin
    497 IconKind:=mikNone;
    498 CenterTo:=0;
    499 end;
    500 
    501 procedure TMessgExDlg.OnPlaySound(var Msg:TMessage);
    502 begin
    503 Play(OpenSound);
    504 OpenSound:='';
    505 end;
    506 
     553  IconKind := mikNone;
     554  CenterTo := 0;
     555end;
     556
     557procedure TMessgExDlg.OnPlaySound(var Msg: TMessage);
     558begin
     559  Play(OpenSound);
     560  OpenSound := '';
     561end;
    507562
    508563initialization
     564
    509565QueryPerformanceFrequency(PerfFreq);
    510566
    511567end.
    512 
  • trunk/LocalPlayer/NatStat.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit NatStat;
    43
     
    65
    76uses
    8   Protocol,ClientTools,Term,ScreenTools,BaseWin,
     7  Protocol, ClientTools, Term, ScreenTools, BaseWin,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    1211
    1312type
    14   PEnemyReport=^TEnemyReport;
     13  PEnemyReport = ^TEnemyReport;
    1514
    1615  TNatStatDlg = class(TBufferedDrawDlg)
     
    2827    procedure ToggleBtnClick(Sender: TObject);
    2928    procedure PlayerClick(Sender: TObject);
    30     procedure FormKeyDown(Sender: TObject; var Key: word;
    31       Shift: TShiftState);
     29    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
    3230    procedure FormDestroy(Sender: TObject);
    3331    procedure ScrollUpBtnClick(Sender: TObject);
     
    4543  private
    4644    pView, AgePrepared, LinesDown: integer;
    47     SelfReport,CurrentReport: PEnemyReport;
    48     ShowContact,ContactEnabled: boolean;
     45    SelfReport, CurrentReport: PEnemyReport;
     46    ShowContact, ContactEnabled: boolean;
    4947    Back, Template: TBitmap;
    5048    ReportText: TStringList;
     
    5553  NatStatDlg: TNatStatDlg;
    5654
    57 
    5855implementation
    5956
     
    6158
    6259uses
    63   Diagram,Select,Messg,MessgEx, Help,Tribes,Directories;
     60  Diagram, Select, Messg, MessgEx, Help, Tribes, Directories;
    6461
    6562const
    66 xIcon=326; yIcon=49;
    67 xAttrib=96; yAttrib=40;
    68 xRelation=16; yRelation=110;
    69 PaperShade=3;
    70 ReportLines=12;
    71 LineSpacing=22;
    72 xReport=24; yReport=165; wReport=352; hReport=ReportLines*LineSpacing;
    73 
     63  xIcon = 326;
     64  yIcon = 49;
     65  xAttrib = 96;
     66  yAttrib = 40;
     67  xRelation = 16;
     68  yRelation = 110;
     69  PaperShade = 3;
     70  ReportLines = 12;
     71  LineSpacing = 22;
     72  xReport = 24;
     73  yReport = 165;
     74  wReport = 352;
     75  hReport = ReportLines * LineSpacing;
    7476
    7577procedure TNatStatDlg.FormCreate(Sender: TObject);
    7678begin
    77 inherited;
    78 AgePrepared:=-2;
    79 GetMem(SelfReport,SizeOf(TEnemyReport)-2*(INFIN+1));
    80 ReportText:=TStringList.Create;
    81 InitButtons();
    82 ContactBtn.Template:=Templates;
    83 HelpContext:='DIPLOMACY';
    84 ToggleBtn.Hint:=Phrases.Lookup('BTN_SELECT');
    85 ContactBtn.Hint:=Phrases.Lookup('BTN_DIALOG');
    86 
    87 Back:=TBitmap.Create;
    88 Back.PixelFormat:=pf24bit;
    89 Back.Width:=ClientWidth; Back.Height:=ClientHeight;
    90 Template:=TBitmap.Create;
    91 LoadGraphicFile(Template, HomeDir+'Graphics\Nation', gfNoGamma);
    92 Template.PixelFormat:=pf8bit;
     79  inherited;
     80  AgePrepared := -2;
     81  GetMem(SelfReport, SizeOf(TEnemyReport) - 2 * (INFIN + 1));
     82  ReportText := TStringList.Create;
     83  InitButtons();
     84  ContactBtn.Template := Templates;
     85  HelpContext := 'DIPLOMACY';
     86  ToggleBtn.Hint := Phrases.Lookup('BTN_SELECT');
     87  ContactBtn.Hint := Phrases.Lookup('BTN_DIALOG');
     88
     89  Back := TBitmap.Create;
     90  Back.PixelFormat := pf24bit;
     91  Back.Width := ClientWidth;
     92  Back.Height := ClientHeight;
     93  Template := TBitmap.Create;
     94  LoadGraphicFile(Template, HomeDir + 'Graphics\Nation', gfNoGamma);
     95  Template.PixelFormat := pf8bit;
    9396end;
    9497
    9598procedure TNatStatDlg.FormDestroy(Sender: TObject);
    9699begin
    97 ReportText.Free;
    98 FreeMem(SelfReport);
    99 Template.Free;
    100 Back.Free;
     100  ReportText.Free;
     101  FreeMem(SelfReport);
     102  Template.Free;
     103  Back.Free;
    101104end;
    102105
    103106procedure TNatStatDlg.CheckAge;
    104107begin
    105 if MainTextureAge<>AgePrepared then
    106   begin
    107   AgePrepared:=MainTextureAge;
    108   bitblt(Back.Canvas.Handle,0,0,ClientWidth,ClientHeight,
    109     MainTexture.Image.Canvas.Handle,(wMainTexture-ClientWidth) div 2,
    110     (hMainTexture-ClientHeight) div 2,SRCCOPY);
    111   ImageOp_B(Back,Template,0,0,0,0,ClientWidth,ClientHeight);
     108  if MainTextureAge <> AgePrepared then
     109  begin
     110    AgePrepared := MainTextureAge;
     111    bitblt(Back.Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
     112      MainTexture.Image.Canvas.Handle, (wMainTexture - ClientWidth) div 2,
     113      (hMainTexture - ClientHeight) div 2, SRCCOPY);
     114    ImageOp_B(Back, Template, 0, 0, 0, 0, ClientWidth, ClientHeight);
    112115  end
    113116end;
     
    115118procedure TNatStatDlg.FormShow(Sender: TObject);
    116119begin
    117 if pView=me then
    118   begin
    119   SelfReport.TurnOfCivilReport:=MyRO.Turn;
    120   SelfReport.TurnOfMilReport:=MyRO.Turn;
    121   move(MyRO.Treaty, SelfReport.Treaty, sizeof(SelfReport.Treaty));
    122   SelfReport.Government:=MyRO.Government;
    123   SelfReport.Money:=MyRO.Money;
    124   CurrentReport:=pointer(SelfReport);
    125   end
    126 else CurrentReport:=pointer(MyRO.EnemyReport[pView]);
    127 if CurrentReport.TurnOfCivilReport>=0 then
    128   GenerateReportText;
    129 ShowContact:= (pView<>me) and (not supervising or (me<>0));
    130 ContactEnabled:= ShowContact and not supervising
    131   and (1 shl pView and MyRO.Alive<>0);
    132 ContactBtn.Visible:=ContactEnabled and (MyRO.Happened and phGameEnd=0)
    133   and (ClientMode<scContact);
    134 ScrollUpBtn.Visible:=(CurrentReport.TurnOfCivilReport>=0)
    135   and (ReportText.Count>ReportLines);
    136 ScrollDownBtn.Visible:=(CurrentReport.TurnOfCivilReport>=0)
    137   and (ReportText.Count>ReportLines);
    138 if OptionChecked and (1 shl soTellAI)<>0 then
    139   TellAIBtn.ButtonIndex:=3
    140 else TellAIBtn.ButtonIndex:=2;
    141 Caption:=Tribe[pView].TPhrase('TITLE_NATION');
    142 LinesDown:=0;
    143 
    144 OffscreenPaint;
    145 end;
    146 
    147 procedure TNatStatDlg.ShowNewContent(NewMode,p: integer);
    148 begin
    149 if p<0 then
    150   if ClientMode>=scContact then
    151     pView:=DipMem[me].pContact
     120  if pView = me then
     121  begin
     122    SelfReport.TurnOfCivilReport := MyRO.Turn;
     123    SelfReport.TurnOfMilReport := MyRO.Turn;
     124    move(MyRO.Treaty, SelfReport.Treaty, SizeOf(SelfReport.Treaty));
     125    SelfReport.Government := MyRO.Government;
     126    SelfReport.Money := MyRO.Money;
     127    CurrentReport := pointer(SelfReport);
     128  end
    152129  else
    153     begin
    154     pView:=0;
    155     while (pView<nPl) and ((MyRO.Treaty[pView]<trNone)
    156       or (1 shl pView and MyRO.Alive=0)) do
    157       inc(pView);
    158     if pView>=nPl then pView:=me;
     130    CurrentReport := pointer(MyRO.EnemyReport[pView]);
     131  if CurrentReport.TurnOfCivilReport >= 0 then
     132    GenerateReportText;
     133  ShowContact := (pView <> me) and (not supervising or (me <> 0));
     134  ContactEnabled := ShowContact and not supervising and
     135    (1 shl pView and MyRO.Alive <> 0);
     136  ContactBtn.Visible := ContactEnabled and (MyRO.Happened and phGameEnd = 0) and
     137    (ClientMode < scContact);
     138  ScrollUpBtn.Visible := (CurrentReport.TurnOfCivilReport >= 0) and
     139    (ReportText.Count > ReportLines);
     140  ScrollDownBtn.Visible := (CurrentReport.TurnOfCivilReport >= 0) and
     141    (ReportText.Count > ReportLines);
     142  if OptionChecked and (1 shl soTellAI) <> 0 then
     143    TellAIBtn.ButtonIndex := 3
     144  else
     145    TellAIBtn.ButtonIndex := 2;
     146  Caption := Tribe[pView].TPhrase('TITLE_NATION');
     147  LinesDown := 0;
     148
     149  OffscreenPaint;
     150end;
     151
     152procedure TNatStatDlg.ShowNewContent(NewMode, p: integer);
     153begin
     154  if p < 0 then
     155    if ClientMode >= scContact then
     156      pView := DipMem[me].pContact
     157    else
     158    begin
     159      pView := 0;
     160      while (pView < nPl) and ((MyRO.Treaty[pView] < trNone) or
     161        (1 shl pView and MyRO.Alive = 0)) do
     162        inc(pView);
     163      if pView >= nPl then
     164        pView := me;
    159165    end
    160 else pView:=p;
    161 inherited ShowNewContent(NewMode);
     166  else
     167    pView := p;
     168  inherited ShowNewContent(NewMode);
    162169end;
    163170
    164171procedure TNatStatDlg.PlayerClick(Sender: TObject);
    165172begin
    166 ShowNewContent(FWindowMode, TComponent(Sender).Tag);
     173  ShowNewContent(FWindowMode, TComponent(Sender).Tag);
    167174end;
    168175
    169176procedure TNatStatDlg.GenerateReportText;
    170177var
    171 List: ^TChart;
     178  List: ^TChart;
    172179
    173180  function StatText(no: integer): string;
    174181  var
    175   i: integer;
    176   begin
    177   if (CurrentReport.TurnOfCivilReport>=0) and (Server(sGetChart+no shl 4,me,pView,List^)>=rExecuted) then
    178     begin
    179     i:=List[CurrentReport.TurnOfCivilReport];
    180     case no of
    181       stPop: result:=Format(Phrases.Lookup('FRSTATPOP'),[i]);
    182       stTerritory: result:=Format(Phrases.Lookup('FRSTATTER'),[i]);
    183       stScience: result:=Format(Phrases.Lookup('FRSTATTECH'),[i div nAdv]);
    184       stExplore: result:=Format(Phrases.Lookup('FRSTATEXP'),[i*100 div (G.lx*G.ly)]);
     182    i: integer;
     183  begin
     184    if (CurrentReport.TurnOfCivilReport >= 0) and
     185      (Server(sGetChart + no shl 4, me, pView, List^) >= rExecuted) then
     186    begin
     187      i := List[CurrentReport.TurnOfCivilReport];
     188      case no of
     189        stPop:
     190          result := Format(Phrases.Lookup('FRSTATPOP'), [i]);
     191        stTerritory:
     192          result := Format(Phrases.Lookup('FRSTATTER'), [i]);
     193        stScience:
     194          result := Format(Phrases.Lookup('FRSTATTECH'), [i div nAdv]);
     195        stExplore:
     196          result := Format(Phrases.Lookup('FRSTATEXP'),
     197            [i * 100 div (G.lx * G.ly)]);
    185198      end;
    186199    end
     
    188201
    189202var
    190 p1,Treaty: integer;
    191 s: string;
    192 HasContact,ExtinctPart: boolean;
    193 begin
    194 GetMem(List,4*(MyRO.Turn+2));
    195 
    196 ReportText.Clear;
    197 ReportText.Add('');
    198 if (MyRO.Turn-CurrentReport.TurnOfCivilReport>1)
    199   and (1 shl pView and MyRO.Alive<>0) then
    200   begin
    201   s:=Format(Phrases.Lookup('FROLDCIVILREP'),
    202     [TurnToString(CurrentReport.TurnOfCivilReport)]);
    203   ReportText.Add('C'+s);
     203  p1, Treaty: integer;
     204  s: string;
     205  HasContact, ExtinctPart: boolean;
     206begin
     207  GetMem(List, 4 * (MyRO.Turn + 2));
     208
     209  ReportText.Clear;
    204210  ReportText.Add('');
    205   end;
    206 
    207 if (1 shl pView and MyRO.Alive<>0) then
    208   begin
    209   ReportText.Add('M'+Format(Phrases.Lookup('FRTREASURY'),[CurrentReport.Money]));
    210   ReportText.Add('P'+StatText(stPop));
    211   ReportText.Add('T'+StatText(stTerritory));
    212   end;
    213 ReportText.Add('S'+StatText(stScience));
    214 ReportText.Add('E'+StatText(stExplore));
    215 HasContact:=false;
    216 for p1:=0 to nPl-1 do
    217   if (p1<>me) and (CurrentReport.Treaty[p1]>trNoContact) then
    218     HasContact:=true;
    219 if HasContact then
    220   begin
     211  if (MyRO.Turn - CurrentReport.TurnOfCivilReport > 1) and
     212    (1 shl pView and MyRO.Alive <> 0) then
     213  begin
     214    s := Format(Phrases.Lookup('FROLDCIVILREP'),
     215      [TurnToString(CurrentReport.TurnOfCivilReport)]);
     216    ReportText.Add('C' + s);
     217    ReportText.Add('');
     218  end;
     219
     220  if (1 shl pView and MyRO.Alive <> 0) then
     221  begin
     222    ReportText.Add('M' + Format(Phrases.Lookup('FRTREASURY'),
     223      [CurrentReport.Money]));
     224    ReportText.Add('P' + StatText(stPop));
     225    ReportText.Add('T' + StatText(stTerritory));
     226  end;
     227  ReportText.Add('S' + StatText(stScience));
     228  ReportText.Add('E' + StatText(stExplore));
     229  HasContact := false;
     230  for p1 := 0 to nPl - 1 do
     231    if (p1 <> me) and (CurrentReport.Treaty[p1] > trNoContact) then
     232      HasContact := true;
     233  if HasContact then
     234  begin
     235    ReportText.Add('');
     236    ReportText.Add(' ' + Phrases.Lookup('FRRELATIONS'));
     237    for ExtinctPart := false to true do
     238      for Treaty := trAlliance downto trNone do
     239        for p1 := 0 to nPl - 1 do
     240          if (p1 <> me) and (CurrentReport.Treaty[p1] = Treaty) and
     241            ((1 shl p1 and MyRO.Alive = 0) = ExtinctPart) then
     242          begin
     243            s := Tribe[p1].TString(Phrases.Lookup('HAVETREATY', Treaty));
     244            if ExtinctPart then
     245              s := '(' + s + ')';
     246            ReportText.Add(char(48 + Treaty) + s);
     247          end;
     248  end;
    221249  ReportText.Add('');
    222   ReportText.Add(' '+Phrases.Lookup('FRRELATIONS'));
    223   for ExtinctPart:=false to true do
    224     for Treaty:=trAlliance downto trNone do
    225       for p1:=0 to nPl-1 do
    226         if (p1<>me) and (CurrentReport.Treaty[p1]=Treaty)
    227           and ((1 shl p1 and MyRO.Alive=0)=ExtinctPart) then
    228           begin
    229           s:=Tribe[p1].TString(Phrases.Lookup('HAVETREATY', Treaty));
    230           if ExtinctPart then s:='('+s+')';
    231           ReportText.Add(char(48+Treaty)+s);
    232           end;
    233   end;
    234 ReportText.Add('');
    235 
    236 FreeMem(List);
     250
     251  FreeMem(List);
    237252end;
    238253
    239254procedure TNatStatDlg.OffscreenPaint;
    240255var
    241 i, y: integer;
    242 s: string;
    243 ps: pchar;
    244 Extinct: boolean;
    245 
    246 begin
    247 inherited;
    248 
    249 Extinct:= 1 shl pView and MyRO.Alive=0;
    250 
    251 bitblt(offscreen.canvas.handle,0,0,ClientWidth,ClientHeight,Back.Canvas.handle,0,0,SRCCOPY);
    252 
    253 offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
    254 RisedTextout(offscreen.Canvas,40{(ClientWidth-BiColorTextWidth(offscreen.canvas,caption)) div 2},7,Caption);
    255 
    256 offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    257 
    258 with offscreen do
    259   begin
    260   // show leader picture
    261   Tribe[pView].InitAge(GetAge(pView));
    262   if Tribe[pView].faceHGr>=0 then
    263     begin
    264     Dump(offscreen,Tribe[pView].faceHGr,18,yIcon-4,64,48,
    265       1+Tribe[pView].facepix mod 10 *65,1+Tribe[pView].facepix div 10 *49);
    266     frame(offscreen.Canvas,18-1,yIcon-4-1,18+64,yIcon-4+48,$000000,$000000);
     256  i, y: integer;
     257  s: string;
     258  ps: pchar;
     259  Extinct: boolean;
     260
     261begin
     262  inherited;
     263
     264  Extinct := 1 shl pView and MyRO.Alive = 0;
     265
     266  bitblt(offscreen.Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
     267    Back.Canvas.Handle, 0, 0, SRCCOPY);
     268
     269  offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
     270  RisedTextout(offscreen.Canvas,
     271    40 { (ClientWidth-BiColorTextWidth(offscreen.canvas,caption)) div 2 } ,
     272    7, Caption);
     273
     274  offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
     275
     276  with offscreen do
     277  begin
     278    // show leader picture
     279    Tribe[pView].InitAge(GetAge(pView));
     280    if Tribe[pView].faceHGr >= 0 then
     281    begin
     282      Dump(offscreen, Tribe[pView].faceHGr, 18, yIcon - 4, 64, 48,
     283        1 + Tribe[pView].facepix mod 10 * 65,
     284        1 + Tribe[pView].facepix div 10 * 49);
     285      frame(offscreen.Canvas, 18 - 1, yIcon - 4 - 1, 18 + 64, yIcon - 4 + 48,
     286        $000000, $000000);
    267287    end;
    268288
    269   if (pView=me) or not Extinct then
    270     LoweredTextOut(Canvas,-1,MainTexture,xAttrib,yAttrib,
    271       Phrases.Lookup('GOVERNMENT',CurrentReport.Government)+Phrases.Lookup('FRAND'));
    272   if pView=me then
    273     begin
    274     LoweredTextOut(Canvas,-1,MainTexture,xAttrib,yAttrib+19,
    275       Phrases.Lookup('CREDIBILITY',RoughCredibility(MyRO.Credibility)));
    276     LoweredTextOut(Canvas,-1,MainTexture,xAttrib,yAttrib+38,
    277       Format(Phrases.Lookup('FRCREDIBILITY'),[MyRO.Credibility]));
     289    if (pView = me) or not Extinct then
     290      LoweredTextOut(Canvas, -1, MainTexture, xAttrib, yAttrib,
     291        Phrases.Lookup('GOVERNMENT', CurrentReport.Government) +
     292        Phrases.Lookup('FRAND'));
     293    if pView = me then
     294    begin
     295      LoweredTextOut(Canvas, -1, MainTexture, xAttrib, yAttrib + 19,
     296        Phrases.Lookup('CREDIBILITY', RoughCredibility(MyRO.Credibility)));
     297      LoweredTextOut(Canvas, -1, MainTexture, xAttrib, yAttrib + 38,
     298        Format(Phrases.Lookup('FRCREDIBILITY'), [MyRO.Credibility]));
    278299    end
    279   else
    280     begin
    281     if Extinct then
    282       begin
    283       LoweredTextOut(Canvas,-1,MainTexture,xAttrib,yAttrib+9,
    284         Phrases.Lookup('FREXTINCT'));
    285       LoweredTextOut(Canvas,-1,MainTexture,xAttrib,yAttrib+28,
    286         TurnToString(CurrentReport.TurnOfCivilReport))
     300    else
     301    begin
     302      if Extinct then
     303      begin
     304        LoweredTextOut(Canvas, -1, MainTexture, xAttrib, yAttrib + 9,
     305          Phrases.Lookup('FREXTINCT'));
     306        LoweredTextOut(Canvas, -1, MainTexture, xAttrib, yAttrib + 28,
     307          TurnToString(CurrentReport.TurnOfCivilReport))
    287308      end
    288     else
    289       begin
    290       LoweredTextOut(Canvas,-1,MainTexture,xAttrib,yAttrib+19,
    291         Phrases.Lookup('CREDIBILITY',RoughCredibility(CurrentReport.Credibility)));
    292       LoweredTextOut(Canvas,-1,MainTexture,xAttrib,yAttrib+38,
    293         Format(Phrases.Lookup('FRCREDIBILITY'),[CurrentReport.Credibility]));
     309      else
     310      begin
     311        LoweredTextOut(Canvas, -1, MainTexture, xAttrib, yAttrib + 19,
     312          Phrases.Lookup('CREDIBILITY',
     313          RoughCredibility(CurrentReport.Credibility)));
     314        LoweredTextOut(Canvas, -1, MainTexture, xAttrib, yAttrib + 38,
     315          Format(Phrases.Lookup('FRCREDIBILITY'), [CurrentReport.Credibility]));
    294316      end;
    295317
    296     if MyRO.Treaty[pView]=trNoContact then
    297       begin
    298       s:=Phrases.Lookup('FRNOCONTACT');
    299       LoweredTextOut(Canvas,-1,MainTexture,
    300         (ClientWidth-BiColorTextWidth(canvas,s)) div 2,yRelation+9,s)
     318      if MyRO.Treaty[pView] = trNoContact then
     319      begin
     320        s := Phrases.Lookup('FRNOCONTACT');
     321        LoweredTextOut(Canvas, -1, MainTexture,
     322          (ClientWidth - BiColorTextWidth(Canvas, s)) div 2, yRelation + 9, s)
    301323      end
    302     else if ShowContact then
    303       begin
    304       LoweredTextOut(Canvas,-1,MainTexture,xRelation,yRelation,
    305         Phrases.Lookup('FRTREATY'));
    306       LoweredTextOut(Canvas,-1,MainTexture,ClientWidth div 2,yRelation,
    307         Phrases.Lookup('TREATY',MyRO.Treaty[pView]));
    308       if CurrentReport.TurnOfContact<0 then
    309         LoweredTextOut(Canvas,-1,MainTexture,ClientWidth div 2,yRelation+19,
    310           Phrases.Lookup('FRNOVISIT'))
    311       else
     324      else if ShowContact then
     325      begin
     326        LoweredTextOut(Canvas, -1, MainTexture, xRelation, yRelation,
     327          Phrases.Lookup('FRTREATY'));
     328        LoweredTextOut(Canvas, -1, MainTexture, ClientWidth div 2, yRelation,
     329          Phrases.Lookup('TREATY', MyRO.Treaty[pView]));
     330        if CurrentReport.TurnOfContact < 0 then
     331          LoweredTextOut(Canvas, -1, MainTexture, ClientWidth div 2,
     332            yRelation + 19, Phrases.Lookup('FRNOVISIT'))
     333        else
    312334        begin
    313         LoweredTextOut(Canvas,-1,MainTexture,xRelation,yRelation+19,
    314           Phrases.Lookup('FRLASTCONTACT'));
    315         if CurrentReport.TurnOfContact>=0 then
    316           LoweredTextOut(Canvas,-1,MainTexture,ClientWidth div 2,yRelation+19,
    317             TurnToString(CurrentReport.TurnOfContact));
     335          LoweredTextOut(Canvas, -1, MainTexture, xRelation, yRelation + 19,
     336            Phrases.Lookup('FRLASTCONTACT'));
     337          if CurrentReport.TurnOfContact >= 0 then
     338            LoweredTextOut(Canvas, -1, MainTexture, ClientWidth div 2,
     339              yRelation + 19, TurnToString(CurrentReport.TurnOfContact));
    318340        end;
    319341      end;
    320342
    321     if Extinct then
    322       FrameImage(canvas,BigImp,xIcon,yIcon,xSizeBig,ySizeBig,0,200)
    323     {else if CurrentReport.Government=gAnarchy then
    324       FrameImage(canvas,BigImp,xIcon,yIcon,xSizeBig,ySizeBig,112,400,
    325         ContactEnabled and (MyRO.Happened and phGameEnd=0) and (ClientMode<scContact))
    326     else
    327       FrameImage(canvas,BigImp,xIcon,yIcon,xSizeBig,ySizeBig,
    328         56*(CurrentReport.Government-1),40,
    329         ContactEnabled and (MyRO.Happened and phGameEnd=0) and (ClientMode<scContact))};
     343      if Extinct then
     344        FrameImage(Canvas, BigImp, xIcon, yIcon, xSizeBig, ySizeBig, 0, 200)
     345        { else if CurrentReport.Government=gAnarchy then
     346          FrameImage(canvas,BigImp,xIcon,yIcon,xSizeBig,ySizeBig,112,400,
     347          ContactEnabled and (MyRO.Happened and phGameEnd=0) and (ClientMode<scContact))
     348          else
     349          FrameImage(canvas,BigImp,xIcon,yIcon,xSizeBig,ySizeBig,
     350          56*(CurrentReport.Government-1),40,
     351          ContactEnabled and (MyRO.Happened and phGameEnd=0) and (ClientMode<scContact)) };
    330352    end;
    331353
    332   if CurrentReport.TurnOfCivilReport>=0 then
     354    if CurrentReport.TurnOfCivilReport >= 0 then
    333355    begin // print state report
    334     FillSeamless(Canvas, xReport, yReport, wReport, hReport, 0, 0, Paper);
    335     with canvas do
    336       begin
    337       Brush.Color:=MainTexture.clBevelShade;
    338       FillRect(Rect(xReport+wReport, yReport+PaperShade,
    339         xReport+wReport+PaperShade, yReport+hReport+PaperShade));
    340       FillRect(Rect(xReport+PaperShade, yReport+hReport,
    341         xReport+wReport+PaperShade, yReport+hReport+PaperShade));
    342       Brush.Style:=bsClear;
     356      FillSeamless(Canvas, xReport, yReport, wReport, hReport, 0, 0, Paper);
     357      with Canvas do
     358      begin
     359        Brush.Color := MainTexture.clBevelShade;
     360        FillRect(Rect(xReport + wReport, yReport + PaperShade,
     361          xReport + wReport + PaperShade, yReport + hReport + PaperShade));
     362        FillRect(Rect(xReport + PaperShade, yReport + hReport,
     363          xReport + wReport + PaperShade, yReport + hReport + PaperShade));
     364        Brush.Style := bsClear;
    343365      end;
    344366
    345     y:=0;
    346     for i:=0 to ReportText.Count-1 do
    347       begin
    348       if (i>=LinesDown) and (i<LinesDown+ReportLines) then
     367      y := 0;
     368      for i := 0 to ReportText.Count - 1 do
     369      begin
     370        if (i >= LinesDown) and (i < LinesDown + ReportLines) then
    349371        begin
    350         s:=ReportText[i];
    351         if s<>'' then
     372          s := ReportText[i];
     373          if s <> '' then
    352374          begin
    353 //          LineType:=s[1];
    354           delete(s,1,1);
    355           BiColorTextOut(canvas,Colors.Canvas.Pixels[clkMisc,cliPaperText],
    356             $7F007F,xReport+8,yReport+LineSpacing*y,s);
     375            // LineType:=s[1];
     376            delete(s, 1, 1);
     377            BiColorTextOut(Canvas, Colors.Canvas.Pixels[clkMisc, cliPaperText],
     378              $7F007F, xReport + 8, yReport + LineSpacing * y, s);
    357379          end;
    358         inc(y);
     380          inc(y);
    359381        end
    360382      end;
    361383    end
     384    else
     385    begin
     386      s := Phrases.Lookup('FRNOCIVILREP');
     387      RisedTextout(Canvas, (ClientWidth - BiColorTextWidth(Canvas, s)) div 2,
     388        yReport + hReport div 2 - 10, s);
     389    end;
     390
     391    if OptionChecked and (1 shl soTellAI) <> 0 then
     392    begin
     393      Server(sGetAIInfo, me, pView, ps);
     394      LoweredTextOut(Canvas, -1, MainTexture, 42, 445, ps);
     395    end
     396    else
     397      LoweredTextOut(Canvas, -2, MainTexture, 42, 445,
     398        Phrases2.Lookup('MENU_TELLAI'));
     399  end;
     400  ContactBtn.SetBack(offscreen.Canvas, ContactBtn.Left, ContactBtn.Top);
     401
     402  MarkUsedOffscreen(ClientWidth, ClientHeight);
     403end; { OffscreenPaint }
     404
     405procedure TNatStatDlg.CloseBtnClick(Sender: TObject);
     406begin
     407  Close
     408end;
     409
     410procedure TNatStatDlg.DialogBtnClick(Sender: TObject);
     411var
     412  ContactResult: integer;
     413begin
     414  ContactResult := MainScreen.DipCall(scContact + pView shl 4);
     415  if ContactResult < rExecuted then
     416  begin
     417    if ContactResult = eColdWar then
     418      SoundMessage(Phrases.Lookup('FRCOLDWAR'), 'MSG_DEFAULT')
     419    else if MyRO.Government = gAnarchy then
     420      SoundMessage(Tribe[me].TPhrase('FRMYANARCHY'), 'MSG_DEFAULT')
     421    else if ContactResult = eAnarchy then
     422      if MyRO.Treaty[pView] >= trPeace then
     423      begin
     424        if MainScreen.ContactRefused(pView, 'FRANARCHY') then
     425          SmartUpdateContent
     426      end
     427      else
     428        SoundMessage(Tribe[pView].TPhrase('FRANARCHY'), 'MSG_DEFAULT');
     429  end
    362430  else
    363     begin
    364     s:=Phrases.Lookup('FRNOCIVILREP');
    365     RisedTextOut(Canvas,(ClientWidth-BiColorTextWidth(Canvas,s)) div 2,
    366       yReport+hReport div 2-10,s);
     431    Close
     432end;
     433
     434procedure TNatStatDlg.ToggleBtnClick(Sender: TObject);
     435var
     436  p1, StartCount: integer;
     437  m: TMenuItem;
     438  ExtinctPart: boolean;
     439begin
     440  EmptyMenu(Popup.Items);
     441
     442  // own nation
     443  if G.Difficulty[me] <> 0 then
     444  begin
     445    m := TMenuItem.Create(Popup);
     446    m.RadioItem := true;
     447    m.Caption := Tribe[me].TPhrase('TITLE_NATION');
     448    m.Tag := me;
     449    m.OnClick := PlayerClick;
     450    if me = pView then
     451      m.Checked := true;
     452    Popup.Items.Add(m);
     453  end;
     454
     455  // foreign nations
     456  for ExtinctPart := false to true do
     457  begin
     458    StartCount := Popup.Items.Count;
     459    for p1 := 0 to nPl - 1 do
     460      if ExtinctPart and (G.Difficulty[p1] > 0) and
     461        (1 shl p1 and MyRO.Alive = 0) or not ExtinctPart and
     462        (1 shl p1 and MyRO.Alive <> 0) and (MyRO.Treaty[p1] >= trNone) then
     463      begin
     464        m := TMenuItem.Create(Popup);
     465        m.RadioItem := true;
     466        m.Caption := Tribe[p1].TPhrase('TITLE_NATION');
     467        if ExtinctPart then
     468          m.Caption := '(' + m.Caption + ')';
     469        m.Tag := p1;
     470        m.OnClick := PlayerClick;
     471        if p1 = pView then
     472          m.Checked := true;
     473        Popup.Items.Add(m);
     474      end;
     475    if (StartCount > 0) and (Popup.Items.Count > StartCount) then
     476    begin // seperator
     477      m := TMenuItem.Create(Popup);
     478      m.Caption := '-';
     479      Popup.Items.Insert(StartCount, m);
    367480    end;
    368 
    369   if OptionChecked and (1 shl soTellAI)<>0 then
    370     begin
    371     Server(sGetAIInfo,me,pView,ps);
    372     LoweredTextOut(Canvas,-1,MainTexture,42,445,ps);
    373     end
    374   else LoweredTextOut(Canvas,-2,MainTexture,42,445,Phrases2.Lookup('MENU_TELLAI'));
    375   end;
    376 ContactBtn.SetBack(Offscreen.Canvas,ContactBtn.Left,ContactBtn.Top);
    377 
    378 MarkUsedOffscreen(ClientWidth,ClientHeight);
    379 end; {OffscreenPaint}
    380 
    381 procedure TNatStatDlg.CloseBtnClick(Sender: TObject);
    382 begin
    383 Close
    384 end;
    385 
    386 procedure TNatStatDlg.DialogBtnClick(Sender: TObject);
    387 var
    388 ContactResult: integer;
    389 begin
    390 ContactResult:=MainScreen.DipCall(scContact+pView shl 4);
    391 if ContactResult<rExecuted then
    392   begin
    393   if ContactResult=eColdWar then
    394     SoundMessage(Phrases.Lookup('FRCOLDWAR'),'MSG_DEFAULT')
    395   else if MyRO.Government=gAnarchy then
    396     SoundMessage(Tribe[me].TPhrase('FRMYANARCHY'),'MSG_DEFAULT')
    397   else if ContactResult=eAnarchy then
    398     if MyRO.Treaty[pView]>=trPeace then
    399       begin
    400       if MainScreen.ContactRefused(pView, 'FRANARCHY') then
    401         SmartUpdateContent
    402       end
    403     else SoundMessage(Tribe[pView].TPhrase('FRANARCHY'),'MSG_DEFAULT');
    404   end
    405 else Close
    406 end;
    407 
    408 procedure TNatStatDlg.ToggleBtnClick(Sender: TObject);
    409 var
    410 p1,StartCount: integer;
    411 m: TMenuItem;
    412 ExtinctPart: boolean;
    413 begin
    414 EmptyMenu(Popup.Items);
    415 
    416 // own nation
    417 if G.Difficulty[me]<>0 then
    418   begin
    419   m:=TMenuItem.Create(Popup);
    420   m.RadioItem:=true;
    421   m.Caption:=Tribe[me].TPhrase('TITLE_NATION');
    422   m.Tag:=me;
    423   m.OnClick:=PlayerClick;
    424   if me=pView then m.Checked:=true;
    425   Popup.Items.Add(m);
    426   end;
    427 
    428 // foreign nations
    429 for ExtinctPart:=false to true do
    430   begin
    431   StartCount:=Popup.Items.Count;
    432   for p1:=0 to nPl-1 do
    433     if ExtinctPart and (G.Difficulty[p1]>0) and (1 shl p1 and MyRO.Alive=0)
    434       or not ExtinctPart and (1 shl p1 and MyRO.Alive<>0)
    435       and (MyRO.Treaty[p1]>=trNone) then
    436       begin
    437       m:=TMenuItem.Create(Popup);
    438       m.RadioItem:=true;
    439       m.Caption:=Tribe[p1].TPhrase('TITLE_NATION');
    440       if ExtinctPart then
    441         m.Caption:='('+m.Caption+')';
    442       m.Tag:=p1;
    443       m.OnClick:=PlayerClick;
    444       if p1=pView then m.Checked:=true;
    445       Popup.Items.Add(m);
    446       end;
    447   if (StartCount>0) and (Popup.Items.Count>StartCount) then
    448     begin //seperator
    449     m:=TMenuItem.Create(Popup);
    450     m.Caption:='-';
    451     Popup.Items.Insert(StartCount,m);
    452     end;
    453   end;
    454 
    455 Popup.Popup(Left+ToggleBtn.Left, Top+ToggleBtn.Top+ToggleBtn.Height);
     481  end;
     482
     483  Popup.Popup(Left + ToggleBtn.Left, Top + ToggleBtn.Top + ToggleBtn.Height);
    456484end;
    457485
     
    459487  Shift: TShiftState);
    460488var
    461 i: integer;
    462 begin
    463 if Key=VK_F9 then // my key
     489  i: integer;
     490begin
     491  if Key = VK_F9 then // my key
    464492  begin // toggle nation
    465   i:=0;
    466   repeat
    467     pView:=(pView+1) mod nPl;
    468     inc(i);
    469   until (i>=nPl)
    470     or (1 shl pView and MyRO.Alive<>0) and (MyRO.Treaty[pView]>=trNone);
    471   if i>=nPl then pView:=me;
    472   Tag:=pView;
    473   PlayerClick(self); // no, this is not nice
    474   end
    475 else inherited 
     493    i := 0;
     494    repeat
     495      pView := (pView + 1) mod nPl;
     496      inc(i);
     497    until (i >= nPl) or (1 shl pView and MyRO.Alive <> 0) and
     498      (MyRO.Treaty[pView] >= trNone);
     499    if i >= nPl then
     500      pView := me;
     501    Tag := pView;
     502    PlayerClick(self); // no, this is not nice
     503  end
     504  else
     505    inherited
    476506end;
    477507
    478508procedure TNatStatDlg.EcoChange;
    479509begin
    480 if Visible and (pView=me) then
    481   begin
    482   SelfReport.Government:=MyRO.Government;
    483   SelfReport.Money:=MyRO.Money;
     510  if Visible and (pView = me) then
     511  begin
     512    SelfReport.Government := MyRO.Government;
     513    SelfReport.Money := MyRO.Money;
     514    SmartUpdateContent
     515  end
     516end;
     517
     518procedure TNatStatDlg.ScrollUpBtnClick(Sender: TObject);
     519begin
     520  if LinesDown > 0 then
     521  begin
     522    dec(LinesDown);
     523    SmartUpdateContent;
     524  end
     525end;
     526
     527procedure TNatStatDlg.ScrollDownBtnClick(Sender: TObject);
     528begin
     529  if LinesDown + ReportLines < ReportText.Count then
     530  begin
     531    inc(LinesDown);
     532    SmartUpdateContent;
     533  end
     534end;
     535
     536procedure TNatStatDlg.TellAIBtnClick(Sender: TObject);
     537begin
     538  OptionChecked := OptionChecked xor (1 shl soTellAI);
     539  if OptionChecked and (1 shl soTellAI) <> 0 then
     540    TellAIBtn.ButtonIndex := 3
     541  else
     542    TellAIBtn.ButtonIndex := 2;
    484543  SmartUpdateContent
    485   end
    486 end;
    487 
    488 procedure TNatStatDlg.ScrollUpBtnClick(Sender: TObject);
    489 begin
    490 if LinesDown>0 then
    491   begin
    492   dec(LinesDown);
    493   SmartUpdateContent;
    494   end
    495 end;
    496 
    497 procedure TNatStatDlg.ScrollDownBtnClick(Sender: TObject);
    498 begin
    499 if LinesDown+ReportLines<ReportText.Count then
    500   begin
    501   inc(LinesDown);
    502   SmartUpdateContent;
    503   end
    504 end;
    505 
    506 procedure TNatStatDlg.TellAIBtnClick(Sender: TObject);
    507 begin
    508 OptionChecked:=OptionChecked xor (1 shl soTellAI);
    509 if OptionChecked and (1 shl soTellAI)<>0 then
    510   TellAIBtn.ButtonIndex:=3
    511 else TellAIBtn.ButtonIndex:=2;
    512 SmartUpdateContent
    513544end;
    514545
    515546end.
    516 
  • trunk/LocalPlayer/Nego.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Nego;
    43
     
    65
    76uses
    8   ScreenTools,BaseWin,Protocol,Term,
     7  ScreenTools, BaseWin, Protocol, Term,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ButtonA,
    1110  ButtonBase, ButtonB, ButtonC, ButtonN;
    1211
    13 
    1412const
    15 MaxHistory=62;
     13  MaxHistory = 62;
    1614
    1715type
    18   THistory=record
     16  THistory = record
    1917    n: integer;
    20     Text: array[0..MaxHistory-1] of ansistring;
    21     end;
     18    Text: array [0 .. MaxHistory - 1] of ansistring;
     19  end;
    2220
    2321  TNegoDlg = class(TBufferedDrawDlg)
     
    5654    procedure FormCreate(Sender: TObject);
    5755    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
    58       Shift: TShiftState; X, Y: Integer);
     56      Shift: TShiftState; X, Y: integer);
    5957    procedure OkBtnClick(Sender: TObject);
    6058    procedure BwdBtnClick(Sender: TObject);
    6159    procedure FwdBtnClick(Sender: TObject);
    6260    procedure CloseBtnClick(Sender: TObject);
    63     procedure FormKeyDown(Sender: TObject; var Key: Word;
    64       Shift: TShiftState);
     61    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    6562    procedure FormShow(Sender: TObject);
    6663    procedure WantClick(Sender: TObject);
     
    7976    CurrentOffer: TOffer;
    8077    MyAllowed, OppoAllowed: TPriceSet;
    81     CommandAllowed: set of scDipNotice-scDipStart..scDipBreak-scDipStart;
    82     History: array[0..nPl-1] of THistory;
     78    CommandAllowed: set of scDipNotice - scDipStart .. scDipBreak - scDipStart;
     79    History: array [0 .. nPl - 1] of THistory;
    8380    RomanFont: TFont;
    84     Costs,Delivers: array[0..11] of cardinal;
     81    Costs, Delivers: array [0 .. 11] of cardinal;
    8582    procedure ResetCurrentOffer;
    8683    procedure BuildCurrentOffer;
    8784    procedure FindAllowed;
    8885    procedure SplitText(Text: string; Bounds: TRect);
    89     procedure PaintNationPicture(x,y,p: integer);
     86    procedure PaintNationPicture(X, Y, p: integer);
    9087    procedure SetButtonStates;
    9188  end;
     
    9794
    9895uses
    99 Messg,ClientTools,Diplomacy, Inp, Select, NatStat, Help,Tribes, MessgEx;
     96  Messg, ClientTools, Diplomacy, Inp, Select, NatStat, Help, Tribes, MessgEx;
    10097
    10198{$R *.DFM}
    10299
    103100const
    104 xPadC=140; yPadC=427;
    105 xPad0=140; yPad0=13;
    106 xPad1=334; yPad1=13;
    107 wIcon=40; hIcon=40;
    108 wText=300; hText=256;
    109 xText0=14; yText0=154;
    110 xText1=326; yText1=154;
    111 xNationPicture0=20; xNationPicture1=556;
    112 yNationPicture=40;
    113 yAttitude=148;
    114 xCred0=42; yCred0=92;
    115 xCred1=578; yCred1=92;
    116 PaperShade=3;
    117 PaperBorder_Left=12; PaperBorder_Right=8;
    118 ListIndent=24;
    119 
    120 opLowTreaty=$FE000000;
    121 
    122 RomanNo: array[0..15] of string=
    123 ('I','II','III','IV','V','VI','VII','VIII','IX','X','XI','XII','XIII','XIV','XV','XVI');
    124 
    125 ButtonPrice: array[0..11] of cardinal=
    126 (opChoose,opCivilReport,opMilReport,opMap,opAllTech,opAllTech,opAllModel,opMoney,
    127   opTreaty,opLowTreaty,opShipParts,opShipParts);
    128 
     101  xPadC = 140;
     102  yPadC = 427;
     103  xPad0 = 140;
     104  yPad0 = 13;
     105  xPad1 = 334;
     106  yPad1 = 13;
     107  wIcon = 40;
     108  hIcon = 40;
     109  wText = 300;
     110  hText = 256;
     111  xText0 = 14;
     112  yText0 = 154;
     113  xText1 = 326;
     114  yText1 = 154;
     115  xNationPicture0 = 20;
     116  xNationPicture1 = 556;
     117  yNationPicture = 40;
     118  yAttitude = 148;
     119  xCred0 = 42;
     120  yCred0 = 92;
     121  xCred1 = 578;
     122  yCred1 = 92;
     123  PaperShade = 3;
     124  PaperBorder_Left = 12;
     125  PaperBorder_Right = 8;
     126  ListIndent = 24;
     127
     128  opLowTreaty = $FE000000;
     129
     130  RomanNo: array [0 .. 15] of string = ('I', 'II', 'III', 'IV', 'V', 'VI',
     131    'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI');
     132
     133  ButtonPrice: array [0 .. 11] of cardinal = (opChoose, opCivilReport,
     134    opMilReport, opMap, opAllTech, opAllTech, opAllModel, opMoney, opTreaty,
     135    opLowTreaty, opShipParts, opShipParts);
    129136
    130137procedure TNegoDlg.FormCreate(Sender: TObject);
    131138var
    132 cix: integer;
    133 begin
    134 InitButtons();
    135 for cix:=0 to ComponentCount-1 do
    136   if Components[cix] is TButtonN then with TButtonN(Components[cix]) do
    137     begin
    138     Graphic:=GrExt[HGrSystem].Data;
    139     Mask:=GrExt[HGrSystem].Mask;
    140     BackGraphic:=GrExt[HGrSystem2].Data;
    141     case Tag shr 8 of
    142       1: SmartHint:=Phrases.Lookup('WANT', ButtonIndex-6);
    143       2: SmartHint:=Phrases.Lookup('OFFER', ButtonIndex-6);
     139  cix: integer;
     140begin
     141  InitButtons();
     142  for cix := 0 to ComponentCount - 1 do
     143    if Components[cix] is TButtonN then
     144      with TButtonN(Components[cix]) do
     145      begin
     146        Graphic := GrExt[HGrSystem].Data;
     147        Mask := GrExt[HGrSystem].Mask;
     148        BackGraphic := GrExt[HGrSystem2].Data;
     149        case Tag shr 8 of
     150          1:
     151            SmartHint := Phrases.Lookup('WANT', ButtonIndex - 6);
     152          2:
     153            SmartHint := Phrases.Lookup('OFFER', ButtonIndex - 6);
     154        end;
    144155      end;
     156
     157  fillchar(History, sizeof(History), 0);
     158  RomanFont := TFont.Create;
     159  RomanFont.Name := 'Times New Roman';
     160  RomanFont.Size := Round(144 * 72 / RomanFont.PixelsPerInch);
     161  RomanFont.Color := Colors.Canvas.Pixels[clkMisc, cliPaper];
     162  HelpContext := 'DIPLOMACY';
     163  OkBtn.Caption := Phrases.Lookup('BTN_OK');
     164  AcceptBtn.SmartHint := Phrases.Lookup('BTN_ACCEPT');
     165  ExitBtn.SmartHint := Phrases.Lookup('BTN_BREAK');
     166  CancelTreatyBtn.SmartHint := Phrases.Lookup('BTN_CNTREATY');
     167end;
     168
     169procedure TNegoDlg.FormShow(Sender: TObject);
     170begin
     171  OffscreenPaint;
     172end;
     173
     174procedure TNegoDlg.ResetCurrentOffer;
     175var
     176  i: integer;
     177begin
     178  CurrentOffer.nDeliver := 0;
     179  CurrentOffer.nCost := 0;
     180  for i := 0 to 11 do
     181    Costs[i] := $FFFFFFFF;
     182  for i := 0 to 11 do
     183    Delivers[i] := $FFFFFFFF;
     184end;
     185
     186procedure TNegoDlg.ShowNewContent(NewMode: integer);
     187begin
     188  inherited ShowNewContent(NewMode);
     189  SetButtonStates;
     190  if (ClientMode = scDipCancelTreaty) or (ClientMode = scDipBreak) then
     191    PassBtn.SmartHint := Phrases.Lookup('BTN_NOTICE')
     192  else
     193    PassBtn.SmartHint := Phrases.Lookup('BTN_PASS');
     194  case MyRO.Treaty[DipMem[me].pContact] of
     195    trNone:
     196      begin
     197        WantHiTreatyBtn.SmartHint := Phrases.Lookup('BTN_WANTPEACE');
     198        OfferHiTreatyBtn.SmartHint := Phrases.Lookup('BTN_OFFERPEACE');
     199        // WantLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTCEASEFIRE');
     200        // OfferLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERCEASEFIRE');
     201      end;
     202    { trCeasefire:
     203      begin
     204      WantHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTPEACE');
     205      OfferHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERPEACE');
     206      end; }
     207    trPeace:
     208      begin
     209        WantHiTreatyBtn.SmartHint := Phrases.Lookup('BTN_WANTFRIENDLY');
     210        OfferHiTreatyBtn.SmartHint := Phrases.Lookup('BTN_OFFERFRIENDLY');
     211        // WantLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTENDPEACE');
     212        // OfferLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERENDPEACE');
     213      end;
     214    trFriendlyContact:
     215      begin
     216        WantHiTreatyBtn.SmartHint := Phrases.Lookup('BTN_WANTALLIANCE');
     217        OfferHiTreatyBtn.SmartHint := Phrases.Lookup('BTN_OFFERALLIANCE');
     218      end;
     219    { trAlliance:
     220      begin
     221      WantLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTENDALLIANCE');
     222      OfferLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERENDALLIANCE');
     223      end; }
     224  end;
     225end;
     226
     227procedure TNegoDlg.Start;
     228begin
     229  if ClientMode <> scDipStart then
     230    with History[me] do
     231    begin
     232      if n = MaxHistory then
     233      begin
     234        move(Text[2], Text[0], (MaxHistory - 2) * sizeof(integer));
     235        dec(n, 2);
     236      end;
     237      Text[n] := copy(DipCommandToString(DipMem[me].pContact, me,
     238        DipMem[me].FormerTreaty, DipMem[me].SentCommand, ClientMode,
     239        DipMem[me].SentOffer, ReceivedOffer), 1, 255);
     240      inc(n);
    145241    end;
    146 
    147 fillchar(History, sizeof(History), 0);
    148 RomanFont:=TFont.Create;
    149 RomanFont.Name:='Times New Roman';
    150 RomanFont.Size:=Round(144 * 72/RomanFont.PixelsPerInch);
    151 RomanFont.Color:=Colors.Canvas.Pixels[clkMisc,cliPaper];
    152 HelpContext:='DIPLOMACY';
    153 OkBtn.Caption:=Phrases.Lookup('BTN_OK');
    154 AcceptBtn.SmartHint:=Phrases.Lookup('BTN_ACCEPT');
    155 ExitBtn.SmartHint:=Phrases.Lookup('BTN_BREAK');
    156 CancelTreatyBtn.SmartHint:=Phrases.Lookup('BTN_CNTREATY');
    157 end;
    158 
    159 procedure TNegoDlg.FormShow(Sender: TObject);
    160 begin
    161 OffscreenPaint;
    162 end;
    163 
    164 procedure TNegoDlg.ResetCurrentOffer;
    165 var
    166 i: integer;
    167 begin
    168 CurrentOffer.nDeliver:=0;
    169 CurrentOffer.nCost:=0;
    170 for i:=0 to 11 do
    171   Costs[i]:=$FFFFFFFF;
    172 for i:=0 to 11 do
    173   Delivers[i]:=$FFFFFFFF;
    174 end;
    175 
    176 procedure TNegoDlg.ShowNewContent(NewMode: integer);
    177 begin
    178 inherited ShowNewContent(NewMode);
    179 SetButtonStates;
    180 if (ClientMode=scDipCancelTreaty) or (ClientMode=scDipBreak) then
    181   PassBtn.SmartHint:=Phrases.Lookup('BTN_NOTICE')
    182 else PassBtn.SmartHint:=Phrases.Lookup('BTN_PASS');
    183 case MyRO.Treaty[DipMem[me].pContact] of
    184   trNone:
    185     begin
    186     WantHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTPEACE');
    187     OfferHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERPEACE');
    188     //WantLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTCEASEFIRE');
    189     //OfferLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERCEASEFIRE');
    190     end;
    191   {trCeasefire:
    192     begin
    193     WantHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTPEACE');
    194     OfferHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERPEACE');
    195     end;}
    196   trPeace:
    197     begin
    198     WantHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTFRIENDLY');
    199     OfferHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERFRIENDLY');
    200     //WantLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTENDPEACE');
    201     //OfferLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERENDPEACE');
    202     end;
    203   trFriendlyContact:
    204     begin
    205     WantHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTALLIANCE');
    206     OfferHiTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERALLIANCE');
    207     end;
    208   {trAlliance:
    209     begin
    210     WantLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_WANTENDALLIANCE');
    211     OfferLoTreatyBtn.SmartHint:=Phrases.Lookup('BTN_OFFERENDALLIANCE');
    212     end;}
    213   end;
    214 end;
    215 
    216 procedure TNegoDlg.Start;
    217 begin
    218 if ClientMode<>scDipStart then with History[me] do
    219   begin
    220   if n=MaxHistory then
    221     begin
    222     move(Text[2], Text[0], (MaxHistory-2)*sizeof(integer));
    223     dec(n,2);
    224     end;
    225   Text[n]:=copy(DipCommandToString(DipMem[me].pContact,me,
    226     DipMem[me].FormerTreaty, DipMem[me].SentCommand, ClientMode,
    227     DipMem[me].SentOffer, ReceivedOffer),1,255);
    228   inc(n);
    229   end;
    230 assert(History[me].n mod 2=1);
    231 
    232 Page:=History[me].n;
    233 FindAllowed;
    234 ResetCurrentOffer;
    235 
    236 (*if (ClientMode=scDipOffer) and (ReceivedOffer.nDeliver=1)
    237   and (ReceivedOffer.nCost=0) and (ReceivedOffer.Price[0] and opMask=opTreaty) then
    238   begin // prepare to demand price for treaty
    239   CurrentOffer.nDeliver:=1;
    240   CurrentOffer.Price[0]:=ReceivedOffer.Price[0];
    241   CurrentOffer.nCost:=0;
    242   end
    243 else
    244   begin
    245   if (ClientMode=scDipOffer) and (ReceivedOffer.nCost>0) then
     242  assert(History[me].n mod 2 = 1);
     243
     244  Page := History[me].n;
     245  FindAllowed;
     246  ResetCurrentOffer;
     247
     248  (* if (ClientMode=scDipOffer) and (ReceivedOffer.nDeliver=1)
     249    and (ReceivedOffer.nCost=0) and (ReceivedOffer.Price[0] and opMask=opTreaty) then
     250    begin // prepare to demand price for treaty
     251    CurrentOffer.nDeliver:=1;
     252    CurrentOffer.Price[0]:=ReceivedOffer.Price[0];
     253    CurrentOffer.nCost:=0;
     254    end
     255    else
     256    begin
     257    if (ClientMode=scDipOffer) and (ReceivedOffer.nCost>0) then
    246258    begin
    247259    CurrentOffer.nDeliver:=1;
    248260    CurrentOffer.Price[0]:=ReceivedOffer.Price[ReceivedOffer.nDeliver]
    249261    end
    250   else CurrentOffer.nDeliver:=0;
    251   if (ClientMode=scDipOffer) and (ReceivedOffer.nDeliver>0) then
     262    else CurrentOffer.nDeliver:=0;
     263    if (ClientMode=scDipOffer) and (ReceivedOffer.nDeliver>0) then
    252264    begin
    253265    CurrentOffer.nCost:=1;
    254266    CurrentOffer.Price[CurrentOffer.nDeliver]:=ReceivedOffer.Price[0]
    255267    end
    256   else CurrentOffer.nCost:=0
    257   end;*)
    258 DipCommand:=-1;
    259 ShowNewContent(wmPersistent);
     268    else CurrentOffer.nCost:=0
     269    end; *)
     270  DipCommand := -1;
     271  ShowNewContent(wmPersistent);
    260272end;
    261273
    262274procedure TNegoDlg.SplitText(Text: string; Bounds: TRect);
    263275var
    264 nLines,Line,Start,Stop,OrdinaryStop,Indent,y: integer;
    265 s: string;
    266 preview, Dot: boolean;
    267 begin
    268 for preview:=true downto false do
    269   begin
    270   Start:=1;
    271   Line:=0;
    272   Indent:=0;
    273   while Start<Length(Text) do
    274     begin
    275     Dot:=false;
    276     if (Start=1) or (Text[Start-1]='\') then
    277       if Text[Start]='-' then
     276  nLines, Line, Start, Stop, OrdinaryStop, Indent, Y: integer;
     277  s: string;
     278  preview, Dot: boolean;
     279begin
     280  for preview := true downto false do
     281  begin
     282    Start := 1;
     283    Line := 0;
     284    Indent := 0;
     285    while Start < Length(Text) do
     286    begin
     287      Dot := false;
     288      if (Start = 1) or (Text[Start - 1] = '\') then
     289        if Text[Start] = '-' then
    278290        begin
    279         Indent:=ListIndent;
    280         inc(Start);
    281         if Start=Length(Text) then break;
    282         Dot:=true;
     291          Indent := ListIndent;
     292          inc(Start);
     293          if Start = Length(Text) then
     294            break;
     295          Dot := true;
    283296        end
    284       else Indent:=0;
    285     Stop:=Start;
    286     while (Stop<Length(Text)) and (Text[Stop]<>'\') do
    287       begin
    288       inc(Stop);
    289       if BiColorTextWidth(Offscreen.Canvas,Copy(Text,Start,Stop-Start+1))
    290         >Bounds.Right-Bounds.Left-PaperBorder_Left-PaperBorder_Right-Indent then
    291         begin dec(Stop); break end;
     297        else
     298          Indent := 0;
     299      Stop := Start;
     300      while (Stop < Length(Text)) and (Text[Stop] <> '\') do
     301      begin
     302        inc(Stop);
     303        if BiColorTextWidth(Offscreen.Canvas,
     304          copy(Text, Start, Stop - Start + 1)) > Bounds.Right - Bounds.Left -
     305          PaperBorder_Left - PaperBorder_Right - Indent then
     306        begin
     307          dec(Stop);
     308          break
     309        end;
    292310      end;
    293     if Stop<>Length(Text) then
    294       begin
    295       OrdinaryStop:=Stop;
    296       while (Text[OrdinaryStop+1]<>' ') and (Text[OrdinaryStop+1]<>'\') do
    297         dec(OrdinaryStop);
    298       if (OrdinaryStop+1-Start)*2>=Stop-Start then
    299         Stop:=OrdinaryStop
     311      if Stop <> Length(Text) then
     312      begin
     313        OrdinaryStop := Stop;
     314        while (Text[OrdinaryStop + 1] <> ' ') and
     315          (Text[OrdinaryStop + 1] <> '\') do
     316          dec(OrdinaryStop);
     317        if (OrdinaryStop + 1 - Start) * 2 >= Stop - Start then
     318          Stop := OrdinaryStop
    300319      end;
    301     if not preview then
    302       begin
    303       y:=(Bounds.Top+Bounds.Bottom) div 2-10*nLines+20*Line-1;
    304       if Dot then
    305         Sprite(offscreen,HGrSystem,Bounds.Left+PaperBorder_Left+(ListIndent-14),
    306           y+7,8,8,90,16);
    307       s:=Copy(Text,Start,Stop-Start+1);
    308       BiColorTextOut(Offscreen.Canvas,Colors.Canvas.Pixels[clkMisc,cliPaperText],
    309         $7F007F,Bounds.Left+PaperBorder_Left+Indent,y,s);
     320      if not preview then
     321      begin
     322        Y := (Bounds.Top + Bounds.Bottom) div 2 - 10 * nLines + 20 * Line - 1;
     323        if Dot then
     324          Sprite(Offscreen, HGrSystem, Bounds.Left + PaperBorder_Left +
     325            (ListIndent - 14), Y + 7, 8, 8, 90, 16);
     326        s := copy(Text, Start, Stop - Start + 1);
     327        BiColorTextOut(Offscreen.Canvas, Colors.Canvas.Pixels[clkMisc,
     328          cliPaperText], $7F007F, Bounds.Left + PaperBorder_Left +
     329          Indent, Y, s);
    310330      end;
    311     inc(Line);
    312     Start:=Stop+2;
     331      inc(Line);
     332      Start := Stop + 2;
    313333    end;
    314   nLines:=Line;
     334    nLines := Line;
    315335  end
    316336end;
     
    318338procedure TNegoDlg.FindAllowed;
    319339var
    320 i: integer;
    321 begin
    322 CommandAllowed:=[scDipOffer-scDipStart];
    323 if ClientMode<>scDipBreak then include(CommandAllowed,scDipBreak-scDipStart);
    324 if MyRO.Treaty[DipMem[me].pContact]>=trPeace then
    325   include(CommandAllowed,scDipCancelTreaty-scDipStart);
    326 if (ClientMode=scDipOffer)
    327   and (Server(scDipAccept-sExecute,me,0,nil^)>=rExecuted) then
    328   include(CommandAllowed,scDipAccept-scDipStart);
    329 
    330 MyAllowed:=[opChoose shr 24, opMoney shr 24];
    331 OppoAllowed:=[opChoose shr 24, opMoney shr 24];
    332 if not IsCivilReportNew(DipMem[me].pContact) then
     340  i: integer;
     341begin
     342  CommandAllowed := [scDipOffer - scDipStart];
     343  if ClientMode <> scDipBreak then
     344    include(CommandAllowed, scDipBreak - scDipStart);
     345  if MyRO.Treaty[DipMem[me].pContact] >= trPeace then
     346    include(CommandAllowed, scDipCancelTreaty - scDipStart);
     347  if (ClientMode = scDipOffer) and (Server(scDipAccept - sExecute, me, 0, nil^)
     348    >= rExecuted) then
     349    include(CommandAllowed, scDipAccept - scDipStart);
     350
     351  MyAllowed := [opChoose shr 24, opMoney shr 24];
     352  OppoAllowed := [opChoose shr 24, opMoney shr 24];
     353  if not IsCivilReportNew(DipMem[me].pContact) then
    333354  begin // no up-to-date civil report
    334   MyAllowed:=MyAllowed+[opCivilReport shr 24];
    335   for i:=0 to nAdv-1 do if MyRO.Tech[i]>=tsApplicable then
    336     begin MyAllowed:=MyAllowed+[opAllTech shr 24]; break end;
    337   OppoAllowed:=OppoAllowed+[opCivilReport shr 24,opAllTech shr 24];
     355    MyAllowed := MyAllowed + [opCivilReport shr 24];
     356    for i := 0 to nAdv - 1 do
     357      if MyRO.Tech[i] >= tsApplicable then
     358      begin
     359        MyAllowed := MyAllowed + [opAllTech shr 24];
     360        break
     361      end;
     362    OppoAllowed := OppoAllowed + [opCivilReport shr 24, opAllTech shr 24];
    338363  end
    339 else
     364  else
    340365  begin // check techs
    341   for i:=0 to nAdv-1 do if not (i in FutureTech) then
    342     if (MyRO.Tech[i]<tsSeen)
    343       and (MyRO.EnemyReport[DipMem[me].pContact].Tech[i]>=tsApplicable) then
    344       OppoAllowed:=OppoAllowed+[opAllTech shr 24]
    345     else if (MyRO.EnemyReport[DipMem[me].pContact].Tech[i]<tsSeen)
    346       and (MyRO.Tech[i]>=tsApplicable) then
    347       MyAllowed:=MyAllowed+[opAllTech shr 24];
    348   end;
    349 if not IsMilReportNew(DipMem[me].pContact) then
     366    for i := 0 to nAdv - 1 do
     367      if not(i in FutureTech) then
     368        if (MyRO.Tech[i] < tsSeen) and
     369          (MyRO.EnemyReport[DipMem[me].pContact].Tech[i] >= tsApplicable) then
     370          OppoAllowed := OppoAllowed + [opAllTech shr 24]
     371        else if (MyRO.EnemyReport[DipMem[me].pContact].Tech[i] < tsSeen) and
     372          (MyRO.Tech[i] >= tsApplicable) then
     373          MyAllowed := MyAllowed + [opAllTech shr 24];
     374  end;
     375  if not IsMilReportNew(DipMem[me].pContact) then
    350376  begin // no up-to-date military report
    351   MyAllowed:=MyAllowed+[opMilReport shr 24];
    352   if MyRO.nModel>3 then
    353     MyAllowed:=MyAllowed+[opAllModel shr 24];
    354   OppoAllowed:=OppoAllowed+[opMilReport shr 24,opAllModel shr 24];
     377    MyAllowed := MyAllowed + [opMilReport shr 24];
     378    if MyRO.nModel > 3 then
     379      MyAllowed := MyAllowed + [opAllModel shr 24];
     380    OppoAllowed := OppoAllowed + [opMilReport shr 24, opAllModel shr 24];
    355381  end
    356 else
    357   begin
    358   if ModalSelectDlg.OnlyChoice(kChooseModel)<>mixAll then
    359     MyAllowed:=MyAllowed+[opAllModel shr 24];
    360   if ModalSelectDlg.OnlyChoice(kChooseEModel)<>mixAll then
    361     OppoAllowed:=OppoAllowed+[opAllModel shr 24];
    362   end;
    363 if MyRO.Treaty[DipMem[me].pContact]<trAlliance then
    364   begin
    365   MyAllowed:=MyAllowed+[opTreaty shr 24,opMap shr 24];
    366   OppoAllowed:=OppoAllowed+[opTreaty shr 24,opMap shr 24];
    367   end;
    368 {if MyRO.Treaty[DipMem[me].pContact] in [trNone,trPeace,trAlliance] then
    369   begin
    370   MyAllowed:=MyAllowed+[opLowTreaty shr 24];
    371   OppoAllowed:=OppoAllowed+[opLowTreaty shr 24];
    372   end;}
    373 for i:=0 to nShipPart-1 do
    374   begin
    375   if MyRO.Ship[me].Parts[i]>0 then
    376     include(MyAllowed, opShipParts shr 24);
    377   if MyRO.Ship[DipMem[me].pContact].Parts[i]>0 then
    378     include(OppoAllowed, opShipParts shr 24);
    379   end;
    380 MyAllowed:=MyAllowed-DipMem[me].DeliveredPrices*[opAllTech shr 24,opAllModel shr 24,opCivilReport shr 24,opMilReport shr 24,opMap shr 24];
    381 OppoAllowed:=OppoAllowed-DipMem[me].ReceivedPrices*[opAllTech shr 24,opAllModel shr 24,opCivilReport shr 24,opMilReport shr 24,opMap shr 24];
    382 end;
    383 
    384 procedure TNegoDlg.PaintNationPicture(x,y,p: integer);
    385 begin
    386 with Offscreen.Canvas do
    387   begin
    388   Pen.Color:=$000000;
    389   Brush.Color:=Tribe[p].Color;
    390   Rectangle(x-6,y-1,x+70,y+49);
    391   Brush.Color:=$000000;
    392   Tribe[p].InitAge(GetAge(p));
    393   if Tribe[p].faceHGr>=0 then
    394     Dump(offscreen,Tribe[p].faceHGr,x,y,64,48,
    395       1+Tribe[p].facepix mod 10 *65,1+Tribe[p].facepix div 10 *49)
    396   else FillRect(Rect(x,y,x+64,y+48));
    397   Brush.Style:=bsClear;
    398   Frame(Offscreen.Canvas,x-1,y-1,x+64,y+48,$000000,$000000);
     382  else
     383  begin
     384    if ModalSelectDlg.OnlyChoice(kChooseModel) <> mixAll then
     385      MyAllowed := MyAllowed + [opAllModel shr 24];
     386    if ModalSelectDlg.OnlyChoice(kChooseEModel) <> mixAll then
     387      OppoAllowed := OppoAllowed + [opAllModel shr 24];
     388  end;
     389  if MyRO.Treaty[DipMem[me].pContact] < trAlliance then
     390  begin
     391    MyAllowed := MyAllowed + [opTreaty shr 24, opMap shr 24];
     392    OppoAllowed := OppoAllowed + [opTreaty shr 24, opMap shr 24];
     393  end;
     394  { if MyRO.Treaty[DipMem[me].pContact] in [trNone,trPeace,trAlliance] then
     395    begin
     396    MyAllowed:=MyAllowed+[opLowTreaty shr 24];
     397    OppoAllowed:=OppoAllowed+[opLowTreaty shr 24];
     398    end; }
     399  for i := 0 to nShipPart - 1 do
     400  begin
     401    if MyRO.Ship[me].Parts[i] > 0 then
     402      include(MyAllowed, opShipParts shr 24);
     403    if MyRO.Ship[DipMem[me].pContact].Parts[i] > 0 then
     404      include(OppoAllowed, opShipParts shr 24);
     405  end;
     406  MyAllowed := MyAllowed - DipMem[me].DeliveredPrices *
     407    [opAllTech shr 24, opAllModel shr 24, opCivilReport shr 24,
     408    opMilReport shr 24, opMap shr 24];
     409  OppoAllowed := OppoAllowed - DipMem[me].ReceivedPrices *
     410    [opAllTech shr 24, opAllModel shr 24, opCivilReport shr 24,
     411    opMilReport shr 24, opMap shr 24];
     412end;
     413
     414procedure TNegoDlg.PaintNationPicture(X, Y, p: integer);
     415begin
     416  with Offscreen.Canvas do
     417  begin
     418    Pen.Color := $000000;
     419    Brush.Color := Tribe[p].Color;
     420    Rectangle(X - 6, Y - 1, X + 70, Y + 49);
     421    Brush.Color := $000000;
     422    Tribe[p].InitAge(GetAge(p));
     423    if Tribe[p].faceHGr >= 0 then
     424      Dump(Offscreen, Tribe[p].faceHGr, X, Y, 64, 48,
     425        1 + Tribe[p].facepix mod 10 * 65, 1 + Tribe[p].facepix div 10 * 49)
     426    else
     427      FillRect(Rect(X, Y, X + 64, Y + 48));
     428    Brush.Style := bsClear;
     429    Frame(Offscreen.Canvas, X - 1, Y - 1, X + 64, Y + 48, $000000, $000000);
    399430  end
    400431end;
     
    402433procedure TNegoDlg.SetButtonStates;
    403434var
    404 cix: integer;
    405 IsActionPage: boolean;
    406 begin
    407 IsActionPage:= Page=History[me].n;
    408 
    409 AcceptBtn.Possible:= IsActionPage and (scDipAccept-scDipStart in CommandAllowed);
    410 AcceptBtn.Lit:= DipCommand=scDipAccept;
    411 PassBtn.Possible:= IsActionPage and (scDipOffer-scDipStart in CommandAllowed);
    412 PassBtn.Lit:= (DipCommand=scDipNotice)
    413   or (DipCommand=scDipOffer) and (CurrentOffer.nDeliver=0) and (CurrentOffer.nCost=0);
    414 ExitBtn.Possible:= IsActionPage and (scDipBreak-scDipStart in CommandAllowed);
    415 ExitBtn.Lit:= DipCommand=scDipBreak;
    416 CancelTreatyBtn.Possible:= IsActionPage and (scDipCancelTreaty-scDipStart in CommandAllowed);
    417 CancelTreatyBtn.Lit:= DipCommand=scDipCancelTreaty;
    418 
    419 for cix:=0 to ComponentCount-1 do
    420   if Components[cix] is TButtonN then
    421     with TButtonN(Components[cix]) do
    422       case Tag shr 8 of
    423         1: // Costs
    424           begin
    425           Possible:= IsActionPage and (ButtonPrice[Tag and $FF] shr 24 in OppoAllowed);
    426           Lit:=Costs[Tag and $FF]<>$FFFFFFFF;
    427           end;
    428         2: // Delivers
    429           begin
    430           Possible:= IsActionPage and (ButtonPrice[Tag and $FF] shr 24 in MyAllowed);
    431           Lit:=Delivers[Tag and $FF]<>$FFFFFFFF;
    432           end
     435  cix: integer;
     436  IsActionPage: boolean;
     437begin
     438  IsActionPage := Page = History[me].n;
     439
     440  AcceptBtn.Possible := IsActionPage and
     441    (scDipAccept - scDipStart in CommandAllowed);
     442  AcceptBtn.Lit := DipCommand = scDipAccept;
     443  PassBtn.Possible := IsActionPage and
     444    (scDipOffer - scDipStart in CommandAllowed);
     445  PassBtn.Lit := (DipCommand = scDipNotice) or (DipCommand = scDipOffer) and
     446    (CurrentOffer.nDeliver = 0) and (CurrentOffer.nCost = 0);
     447  ExitBtn.Possible := IsActionPage and
     448    (scDipBreak - scDipStart in CommandAllowed);
     449  ExitBtn.Lit := DipCommand = scDipBreak;
     450  CancelTreatyBtn.Possible := IsActionPage and
     451    (scDipCancelTreaty - scDipStart in CommandAllowed);
     452  CancelTreatyBtn.Lit := DipCommand = scDipCancelTreaty;
     453
     454  for cix := 0 to ComponentCount - 1 do
     455    if Components[cix] is TButtonN then
     456      with TButtonN(Components[cix]) do
     457        case Tag shr 8 of
     458          1: // Costs
     459            begin
     460              Possible := IsActionPage and
     461                (ButtonPrice[Tag and $FF] shr 24 in OppoAllowed);
     462              Lit := Costs[Tag and $FF] <> $FFFFFFFF;
     463            end;
     464          2: // Delivers
     465            begin
     466              Possible := IsActionPage and
     467                (ButtonPrice[Tag and $FF] shr 24 in MyAllowed);
     468              Lit := Delivers[Tag and $FF] <> $FFFFFFFF;
     469            end
    433470        end;
    434471end;
     
    436473procedure TNegoDlg.OffscreenPaint;
    437474var
    438 i,cred: integer;
    439 s: string;
    440 OkEnabled: boolean;
    441 begin
    442 if (OffscreenUser<>nil) and (OffscreenUser<>self) then OffscreenUser.Update;
     475  i, cred: integer;
     476  s: string;
     477  OkEnabled: boolean;
     478begin
     479  if (OffscreenUser <> nil) and (OffscreenUser <> self) then
     480    OffscreenUser.Update;
    443481  // complete working with old owner to prevent rebound
    444 OffscreenUser:=self;
    445 
    446 if (DipCommand>=0) and (Page=History[me].n) then
    447   History[me].Text[History[me].n]:=copy(DipCommandToString(me,DipMem[me].pContact,
    448     MyRO.Treaty[DipMem[me].pContact],ClientMode, DipCommand, ReceivedOffer, CurrentOffer),1,255);
    449 
    450 FwdBtn.Visible:= Page<History[me].n;
    451 BwdBtn.Visible:= Page>=2;
    452 if Page<History[me].n then OkEnabled:=false
    453 else if DipCommand=scDipOffer then
    454   OkEnabled:= Server(scDipOffer-sExecute,me,0,CurrentOffer)>=rExecuted
    455 else OkEnabled:= DipCommand>=0;
    456 OkBtn.Visible:=OkEnabled;
    457 
    458 Fill(Offscreen.Canvas,3,3,ClientWidth-6,ClientHeight-6,
    459   (wMaintexture-ClientWidth) div 2,(hMaintexture-ClientHeight) div 2);
    460 Frame(Offscreen.Canvas,0,0,ClientWidth-1,ClientHeight-1,0,0);
    461 Frame(Offscreen.Canvas,1,1,ClientWidth-2,ClientHeight-2,MainTexture.clBevelLight,MainTexture.clBevelShade);
    462 Frame(Offscreen.Canvas,2,2,ClientWidth-3,ClientHeight-3,MainTexture.clBevelLight,MainTexture.clBevelShade);
    463 Corner(Offscreen.Canvas,1,1,0,MainTexture);
    464 Corner(Offscreen.Canvas,ClientWidth-9,1,1,MainTexture);
    465 Corner(Offscreen.Canvas,1,ClientHeight-9,2,MainTexture);
    466 Corner(Offscreen.Canvas,ClientWidth-9,ClientHeight-9,3,MainTexture);
    467 
    468 BtnFrame(Offscreen.Canvas,OkBtn.BoundsRect,MainTexture);
    469 BtnFrame(Offscreen.Canvas,BwdBtn.BoundsRect,MainTexture);
    470 BtnFrame(Offscreen.Canvas,FwdBtn.BoundsRect,MainTexture);
    471 BtnFrame(Offscreen.Canvas,CloseBtn.BoundsRect,MainTexture);
    472 
    473 RFrame(Offscreen.Canvas,xPadC-2, yPadC-2, xPadC+41+42*3,yPadC+41,
    474   $FFFFFF,$B0B0B0);
    475 RFrame(Offscreen.Canvas,xPad0-2, yPad0-2,xPad0+41+42*3,
    476   yPad0+41+42*2,$FFFFFF,$B0B0B0);
    477 RFrame(Offscreen.Canvas,xPad1-2, yPad1-2,xPad1+41+42*3,
    478   yPad1+41+42*2,$FFFFFF,$B0B0B0);
    479 
    480 PaintNationPicture(xNationPicture0,yNationPicture,DipMem[me].pContact);
    481 PaintNationPicture(xNationPicture1,yNationPicture,me);
    482 
    483 if History[me].Text[Page-1]<>'' then
    484   begin
    485   FillSeamless(Offscreen.Canvas, xText0, yText0, wText, hText, 0, 0, Paper);
    486   i:=Page-1;
    487   if History[me].Text[0]='' then dec(i);
    488   if i<16 then
    489     begin
     482  OffscreenUser := self;
     483
     484  if (DipCommand >= 0) and (Page = History[me].n) then
     485    History[me].Text[History[me].n] :=
     486      copy(DipCommandToString(me, DipMem[me].pContact,
     487      MyRO.Treaty[DipMem[me].pContact], ClientMode, DipCommand, ReceivedOffer,
     488      CurrentOffer), 1, 255);
     489
     490  FwdBtn.Visible := Page < History[me].n;
     491  BwdBtn.Visible := Page >= 2;
     492  if Page < History[me].n then
     493    OkEnabled := false
     494  else if DipCommand = scDipOffer then
     495    OkEnabled := Server(scDipOffer - sExecute, me, 0, CurrentOffer) >= rExecuted
     496  else
     497    OkEnabled := DipCommand >= 0;
     498  OkBtn.Visible := OkEnabled;
     499
     500  Fill(Offscreen.Canvas, 3, 3, ClientWidth - 6, ClientHeight - 6,
     501    (wMaintexture - ClientWidth) div 2, (hMaintexture - ClientHeight) div 2);
     502  Frame(Offscreen.Canvas, 0, 0, ClientWidth - 1, ClientHeight - 1, 0, 0);
     503  Frame(Offscreen.Canvas, 1, 1, ClientWidth - 2, ClientHeight - 2,
     504    MainTexture.clBevelLight, MainTexture.clBevelShade);
     505  Frame(Offscreen.Canvas, 2, 2, ClientWidth - 3, ClientHeight - 3,
     506    MainTexture.clBevelLight, MainTexture.clBevelShade);
     507  Corner(Offscreen.Canvas, 1, 1, 0, MainTexture);
     508  Corner(Offscreen.Canvas, ClientWidth - 9, 1, 1, MainTexture);
     509  Corner(Offscreen.Canvas, 1, ClientHeight - 9, 2, MainTexture);
     510  Corner(Offscreen.Canvas, ClientWidth - 9, ClientHeight - 9, 3, MainTexture);
     511
     512  BtnFrame(Offscreen.Canvas, OkBtn.BoundsRect, MainTexture);
     513  BtnFrame(Offscreen.Canvas, BwdBtn.BoundsRect, MainTexture);
     514  BtnFrame(Offscreen.Canvas, FwdBtn.BoundsRect, MainTexture);
     515  BtnFrame(Offscreen.Canvas, CloseBtn.BoundsRect, MainTexture);
     516
     517  RFrame(Offscreen.Canvas, xPadC - 2, yPadC - 2, xPadC + 41 + 42 * 3,
     518    yPadC + 41, $FFFFFF, $B0B0B0);
     519  RFrame(Offscreen.Canvas, xPad0 - 2, yPad0 - 2, xPad0 + 41 + 42 * 3,
     520    yPad0 + 41 + 42 * 2, $FFFFFF, $B0B0B0);
     521  RFrame(Offscreen.Canvas, xPad1 - 2, yPad1 - 2, xPad1 + 41 + 42 * 3,
     522    yPad1 + 41 + 42 * 2, $FFFFFF, $B0B0B0);
     523
     524  PaintNationPicture(xNationPicture0, yNationPicture, DipMem[me].pContact);
     525  PaintNationPicture(xNationPicture1, yNationPicture, me);
     526
     527  if History[me].Text[Page - 1] <> '' then
     528  begin
     529    FillSeamless(Offscreen.Canvas, xText0, yText0, wText, hText, 0, 0, Paper);
     530    i := Page - 1;
     531    if History[me].Text[0] = '' then
     532      dec(i);
     533    if i < 16 then
     534    begin
     535      Offscreen.Canvas.Font.Assign(RomanFont);
     536      Offscreen.Canvas.TextOut
     537        (xText0 + (wText - Offscreen.Canvas.TextWidth(RomanNo[i])) div 2,
     538        yText0 + (hText - Offscreen.Canvas.TextHeight(RomanNo[i])) div 2,
     539        RomanNo[i]);
     540    end
     541  end;
     542  FillSeamless(Offscreen.Canvas, xText1, yText1, wText, hText, 0, 0, Paper);
     543  i := Page;
     544  if History[me].Text[0] = '' then
     545    dec(i);
     546  if i < 16 then
     547  begin
    490548    Offscreen.Canvas.Font.Assign(RomanFont);
    491     Offscreen.Canvas.TextOut(xText0+(wText-Offscreen.Canvas.TextWidth(RomanNo[i])) div 2,
    492       yText0+(hText-Offscreen.Canvas.TextHeight(RomanNo[i])) div 2,RomanNo[i]);
    493     end
    494   end;
    495 FillSeamless(Offscreen.Canvas, xText1, yText1, wText, hText, 0, 0, Paper);
    496 i:=Page;
    497 if History[me].Text[0]='' then dec(i);
    498 if i<16 then
    499   begin
    500   Offscreen.Canvas.Font.Assign(RomanFont);
    501   Offscreen.Canvas.TextOut(xText1+(wText-Offscreen.Canvas.TextWidth(RomanNo[i])) div 2,
    502     yText1+(hText-Offscreen.Canvas.TextHeight(RomanNo[i])) div 2,RomanNo[i]);
    503   end;
    504 with Offscreen.Canvas do
    505   begin
    506   Brush.Color:=MainTexture.clBevelShade;
    507   if History[me].Text[Page-1]<>'' then
    508     begin
    509     FillRect(Rect(xText0+wText, yText0+PaperShade, xText0+wText+PaperShade,
    510       yText0+hText+PaperShade));
    511     FillRect(Rect(xText0+PaperShade, yText0+hText, xText0+wText+PaperShade,
    512       yText0+hText+PaperShade));
     549    Offscreen.Canvas.TextOut
     550      (xText1 + (wText - Offscreen.Canvas.TextWidth(RomanNo[i])) div 2,
     551      yText1 + (hText - Offscreen.Canvas.TextHeight(RomanNo[i])) div 2,
     552      RomanNo[i]);
     553  end;
     554  with Offscreen.Canvas do
     555  begin
     556    Brush.Color := MainTexture.clBevelShade;
     557    if History[me].Text[Page - 1] <> '' then
     558    begin
     559      FillRect(Rect(xText0 + wText, yText0 + PaperShade,
     560        xText0 + wText + PaperShade, yText0 + hText + PaperShade));
     561      FillRect(Rect(xText0 + PaperShade, yText0 + hText,
     562        xText0 + wText + PaperShade, yText0 + hText + PaperShade));
    513563    end;
    514   FillRect(Rect(xText1+wText, yText1+PaperShade, xText1+wText+PaperShade,
    515     yText1+hText+PaperShade));
    516   FillRect(Rect(xText1+PaperShade, yText1+hText, xText1+wText+PaperShade,
    517     yText1+hText+PaperShade));
    518   Brush.Style:=bsClear;
    519   end;
    520 
    521 Offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    522 
    523 {if Page=History[me].n then
    524   begin // show attitude
    525   s:=Phrases.Lookup('ATTITUDE',MyRO.EnemyReport[DipMem[me].pContact].Attitude);
    526   //LoweredTextOut(Offscreen.Canvas,-1,MainTexture,
    527   RisedTextOut(Offscreen.Canvas,xText0+wText div 2-
     564    FillRect(Rect(xText1 + wText, yText1 + PaperShade,
     565      xText1 + wText + PaperShade, yText1 + hText + PaperShade));
     566    FillRect(Rect(xText1 + PaperShade, yText1 + hText,
     567      xText1 + wText + PaperShade, yText1 + hText + PaperShade));
     568    Brush.Style := bsClear;
     569  end;
     570
     571  Offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
     572
     573  { if Page=History[me].n then
     574    begin // show attitude
     575    s:=Phrases.Lookup('ATTITUDE',MyRO.EnemyReport[DipMem[me].pContact].Attitude);
     576    //LoweredTextOut(Offscreen.Canvas,-1,MainTexture,
     577    RisedTextOut(Offscreen.Canvas,xText0+wText div 2-
    528578    BiColorTextWidth(Offscreen.Canvas,s) div 2,yAttitude,s);
    529   s:=Phrases.Lookup('ATTITUDE',MyRO.Attitude[DipMem[me].pContact]);
    530   //LoweredTextOut(Offscreen.Canvas,-1,MainTexture,
    531   RisedTextOut(Offscreen.Canvas,xText1+wText div 2-
     579    s:=Phrases.Lookup('ATTITUDE',MyRO.Attitude[DipMem[me].pContact]);
     580    //LoweredTextOut(Offscreen.Canvas,-1,MainTexture,
     581    RisedTextOut(Offscreen.Canvas,xText1+wText div 2-
    532582    BiColorTextWidth(Offscreen.Canvas,s) div 2,yAttitude,s);
    533   end;}
    534 
    535 if History[me].Text[Page-1]<>'' then
    536   SplitText(History[me].Text[Page-1],
    537     Rect(xText0, yText0, xText0+wText, yText0+hText));
    538 if (Page<History[me].n) or OkEnabled then
    539   SplitText(History[me].Text[Page], Rect(xText1, yText1, xText1+wText, yText1+hText));
    540 
    541 // show credibility
    542 Offscreen.Canvas.Font.Assign(UniFont[ftTiny]);
    543 cred:=MyRO.EnemyReport[DipMem[me].pContact].Credibility;
    544 case cred of
    545   0..49: i:= 3; 50..90: i:=0; 91..100: i:=1; end;
    546 PaintProgressBar(Offscreen.Canvas,i,xCred0,yCred0+17,(cred+2) div 5,0,20,MainTexture);
    547 s:=IntToStr(cred);
    548 RisedTextOut(Offscreen.Canvas,xCred0+10-(BiColorTextWidth(Offscreen.Canvas,s)+1) div 2,yCred0,s);
    549 case MyRO.Credibility of
    550   0..49: i:= 3; 50..90: i:=0; 91..100: i:=1; end;
    551 PaintProgressBar(Offscreen.Canvas,i,xCred1,yCred1+17,(MyRO.Credibility+2) div 5,0,20,MainTexture);
    552 s:=IntToStr(MyRO.Credibility);
    553 RisedTextOut(Offscreen.Canvas,xCred1+10-(BiColorTextWidth(Offscreen.Canvas,s)+1) div 2,yCred1,s);
    554 
    555 MarkUsedOffscreen(ClientWidth,ClientHeight);
    556 end; {OffscreenPaint}
     583    end; }
     584
     585  if History[me].Text[Page - 1] <> '' then
     586    SplitText(History[me].Text[Page - 1], Rect(xText0, yText0, xText0 + wText,
     587      yText0 + hText));
     588  if (Page < History[me].n) or OkEnabled then
     589    SplitText(History[me].Text[Page], Rect(xText1, yText1, xText1 + wText,
     590      yText1 + hText));
     591
     592  // show credibility
     593  Offscreen.Canvas.Font.Assign(UniFont[ftTiny]);
     594  cred := MyRO.EnemyReport[DipMem[me].pContact].Credibility;
     595  case cred of
     596    0 .. 49:
     597      i := 3;
     598    50 .. 90:
     599      i := 0;
     600    91 .. 100:
     601      i := 1;
     602  end;
     603  PaintProgressBar(Offscreen.Canvas, i, xCred0, yCred0 + 17, (cred + 2) div 5,
     604    0, 20, MainTexture);
     605  s := IntToStr(cred);
     606  RisedTextOut(Offscreen.Canvas, xCred0 + 10 -
     607    (BiColorTextWidth(Offscreen.Canvas, s) + 1) div 2, yCred0, s);
     608  case MyRO.Credibility of
     609    0 .. 49:
     610      i := 3;
     611    50 .. 90:
     612      i := 0;
     613    91 .. 100:
     614      i := 1;
     615  end;
     616  PaintProgressBar(Offscreen.Canvas, i, xCred1, yCred1 + 17,
     617    (MyRO.Credibility + 2) div 5, 0, 20, MainTexture);
     618  s := IntToStr(MyRO.Credibility);
     619  RisedTextOut(Offscreen.Canvas, xCred1 + 10 -
     620    (BiColorTextWidth(Offscreen.Canvas, s) + 1) div 2, yCred1, s);
     621
     622  MarkUsedOffscreen(ClientWidth, ClientHeight);
     623end; { OffscreenPaint }
    557624
    558625procedure TNegoDlg.Initiate;
    559626begin
    560 History[me].n:=1;
    561 History[me].Text[0]:='';
     627  History[me].n := 1;
     628  History[me].Text[0] := '';
    562629end;
    563630
    564631procedure TNegoDlg.Respond;
    565632begin
    566 History[me].n:=0;
     633  History[me].n := 0;
    567634end;
    568635
    569636procedure TNegoDlg.FormMouseDown(Sender: TObject; Button: TMouseButton;
    570   Shift: TShiftState; X, Y: Integer);
    571 begin
    572 if (x>=xNationPicture0) and (x<xNationPicture0+64)
    573   and (y>=yNationPicture) and (y<yNationPicture+48) then
    574   NatStatDlg.ShowNewContent(FWindowMode or wmPersistent, DipMem[me].pContact)
    575 else if (x>=xNationPicture1) and (x<xNationPicture1+64)
    576   and (y>=yNationPicture) and (y<yNationPicture+48) then
    577   NatStatDlg.ShowNewContent(FWindowMode or wmPersistent,me)
     637  Shift: TShiftState; X, Y: integer);
     638begin
     639  if (X >= xNationPicture0) and (X < xNationPicture0 + 64) and
     640    (Y >= yNationPicture) and (Y < yNationPicture + 48) then
     641    NatStatDlg.ShowNewContent(FWindowMode or wmPersistent, DipMem[me].pContact)
     642  else if (X >= xNationPicture1) and (X < xNationPicture1 + 64) and
     643    (Y >= yNationPicture) and (Y < yNationPicture + 48) then
     644    NatStatDlg.ShowNewContent(FWindowMode or wmPersistent, me)
    578645end;
    579646
    580647procedure TNegoDlg.BwdBtnClick(Sender: TObject);
    581648begin
    582 dec(Page,2);
    583 SetButtonStates;
    584 SmartUpdateContent;
     649  dec(Page, 2);
     650  SetButtonStates;
     651  SmartUpdateContent;
    585652end;
    586653
    587654procedure TNegoDlg.FwdBtnClick(Sender: TObject);
    588655begin
    589 inc(Page,2);
    590 SetButtonStates;
    591 SmartUpdateContent;
     656  inc(Page, 2);
     657  SetButtonStates;
     658  SmartUpdateContent;
    592659end;
    593660
    594661procedure TNegoDlg.OkBtnClick(Sender: TObject);
    595662begin
    596 inc(History[me].n);
    597 if DipCommand=scDipOffer then
    598   MainScreen.OfferCall(CurrentOffer)
    599 else MainScreen.DipCall(DipCommand);
     663  inc(History[me].n);
     664  if DipCommand = scDipOffer then
     665    MainScreen.OfferCall(CurrentOffer)
     666  else
     667    MainScreen.DipCall(DipCommand);
    600668end;
    601669
    602670procedure TNegoDlg.CloseBtnClick(Sender: TObject);
    603671begin
    604 Close
     672  Close
    605673end;
    606674
     
    608676  Shift: TShiftState);
    609677begin
    610 if Key=VK_RETURN then
    611   begin
    612   if OkBtn.Visible then OkBtnClick(nil)
     678  if Key = VK_RETURN then
     679  begin
     680    if OkBtn.Visible then
     681      OkBtnClick(nil)
    613682  end
    614 else inherited
     683  else
     684    inherited
    615685end;
    616686
    617687procedure TNegoDlg.BuildCurrentOffer;
    618688var
    619 i: integer;
    620 begin
    621 CurrentOffer.nDeliver:=0;
    622 CurrentOffer.nCost:=0;
    623 for i:=0 to 11 do if Delivers[i]<>$FFFFFFFF then
    624   begin
    625   CurrentOffer.Price[CurrentOffer.nDeliver]:=Delivers[i];
    626   inc(CurrentOffer.nDeliver);
    627   end;
    628 for i:=0 to 11 do if Costs[i]<>$FFFFFFFF then
    629   begin
    630   CurrentOffer.Price[CurrentOffer.nDeliver+CurrentOffer.nCost]:=Costs[i];
    631   inc(CurrentOffer.nCost);
    632   end;
     689  i: integer;
     690begin
     691  CurrentOffer.nDeliver := 0;
     692  CurrentOffer.nCost := 0;
     693  for i := 0 to 11 do
     694    if Delivers[i] <> $FFFFFFFF then
     695    begin
     696      CurrentOffer.Price[CurrentOffer.nDeliver] := Delivers[i];
     697      inc(CurrentOffer.nDeliver);
     698    end;
     699  for i := 0 to 11 do
     700    if Costs[i] <> $FFFFFFFF then
     701    begin
     702      CurrentOffer.Price[CurrentOffer.nDeliver + CurrentOffer.nCost] :=
     703        Costs[i];
     704      inc(CurrentOffer.nCost);
     705    end;
    633706end;
    634707
    635708procedure TNegoDlg.WantClick(Sender: TObject);
    636709var
    637 a,i,max: integer;
    638 Price: cardinal;
    639 begin
    640 if (Page<>History[me].n)
    641   or (ClientMode=scDipCancelTreaty) or (ClientMode=scDipBreak) then
    642   exit;
    643 if Costs[TButtonN(Sender).Tag and $FF]<>$FFFFFFFF then
    644   Price:=$FFFFFFFF // toggle off
    645 else
    646   begin
    647   if CurrentOffer.nCost>=2 then
    648     begin
    649     SimpleMessage(Phrases.Lookup('MAX2WANTS'));
    650     exit
     710  a, i, max: integer;
     711  Price: cardinal;
     712begin
     713  if (Page <> History[me].n) or (ClientMode = scDipCancelTreaty) or
     714    (ClientMode = scDipBreak) then
     715    exit;
     716  if Costs[TButtonN(Sender).Tag and $FF] <> $FFFFFFFF then
     717    Price := $FFFFFFFF // toggle off
     718  else
     719  begin
     720    if CurrentOffer.nCost >= 2 then
     721    begin
     722      SimpleMessage(Phrases.Lookup('MAX2WANTS'));
     723      exit
    651724    end;
    652   Price:=ButtonPrice[TButtonN(Sender).Tag and $FF];
    653   if not (Price shr 24 in OppoAllowed) then exit;
    654   case Price of
    655     opCivilReport, opMilReport:
    656       inc(Price,DipMem[me].pContact shl 16+MyRO.Turn); // !!! choose player and year!
    657     opMoney:
    658       begin // choose amount
    659       InputDlg.Caption:=Phrases.Lookup('TITLE_AMOUNT');
    660       InputDlg.EInput.Text:='';
    661       InputDlg.CenterToRect(BoundsRect);
    662       InputDlg.ShowModal;
    663       if InputDlg.ModalResult<>mrOK then exit;
    664       val(InputDlg.EInput.Text,a,i);
    665       if (i<>0) or (a<=0) or (a>=MaxMoneyPrice) then exit;
    666       inc(Price,a);
    667       end;
    668     opShipParts:
    669       begin // choose type and number
    670       if MyRO.NatBuilt[imSpacePort]=0 then with MessgExDlg do
     725    Price := ButtonPrice[TButtonN(Sender).Tag and $FF];
     726    if not(Price shr 24 in OppoAllowed) then
     727      exit;
     728    case Price of
     729      opCivilReport, opMilReport:
     730        inc(Price, DipMem[me].pContact shl 16 + MyRO.Turn);
     731        // !!! choose player and year!
     732      opMoney:
     733        begin // choose amount
     734          InputDlg.Caption := Phrases.Lookup('TITLE_AMOUNT');
     735          InputDlg.EInput.Text := '';
     736          InputDlg.CenterToRect(BoundsRect);
     737          InputDlg.ShowModal;
     738          if InputDlg.ModalResult <> mrOK then
     739            exit;
     740          val(InputDlg.EInput.Text, a, i);
     741          if (i <> 0) or (a <= 0) or (a >= MaxMoneyPrice) then
     742            exit;
     743          inc(Price, a);
     744        end;
     745      opShipParts:
     746        begin // choose type and number
     747          if MyRO.NatBuilt[imSpacePort] = 0 then
     748            with MessgExDlg do
     749            begin
     750              OpenSound := 'WARNING_LOWSUPPORT';
     751              MessgText := Phrases.Lookup('NOSPACEPORT');
     752              Kind := mkYesNo;
     753              IconKind := mikImp;
     754              IconIndex := imSpacePort;
     755              ShowModal;
     756              if ModalResult <> mrOK then
     757                exit
     758            end;
     759          ModalSelectDlg.ShowNewContent(wmModal, kEShipPart);
     760          if ModalSelectDlg.result < 0 then
     761            exit;
     762          inc(Price, ModalSelectDlg.result shl 16);
     763          max := MyRO.Ship[DipMem[me].pContact].Parts[ModalSelectDlg.result];
     764          InputDlg.Caption := Phrases.Lookup('TITLE_NUMBER');
     765          InputDlg.EInput.Text := '';
     766          InputDlg.CenterToRect(BoundsRect);
     767          InputDlg.ShowModal;
     768          if InputDlg.ModalResult <> mrOK then
     769            exit;
     770          val(InputDlg.EInput.Text, a, i);
     771          if (i <> 0) or (a <= 0) then
     772            exit;
     773          if a > max then
     774            a := max;
     775          if a > MaxShipPartPrice then
     776            a := MaxShipPartPrice;
     777          inc(Price, a)
     778        end;
     779      opAllTech:
     780        begin // choose technology
     781          ModalSelectDlg.ShowNewContent(wmModal, kChooseETech);
     782          if ModalSelectDlg.result < 0 then
     783            exit;
     784          if ModalSelectDlg.result = adAll then
     785            Price := opAllTech
     786          else
     787            Price := OpTech + ModalSelectDlg.result;
     788        end;
     789      opAllModel:
     790        begin // choose model
     791          ModalSelectDlg.ShowNewContent(wmModal, kChooseEModel);
     792          if ModalSelectDlg.result < 0 then
     793            exit;
     794          if ModalSelectDlg.result = mixAll then
     795            Price := opAllModel
     796          else
     797            Price := OpModel + MyRO.EnemyModel[ModalSelectDlg.result].mix;
     798        end;
     799      opTreaty:
    671800        begin
    672         OpenSound:='WARNING_LOWSUPPORT';
    673         MessgText:=Phrases.Lookup('NOSPACEPORT');
    674         Kind:=mkYesNo;
    675         IconKind:=mikImp;
    676         IconIndex:=imSpacePort;
    677         ShowModal;
    678         if ModalResult<>mrOK then exit
    679         end;
    680       ModalSelectDlg.ShowNewContent(wmModal,kEShipPart);
    681       if ModalSelectDlg.result<0 then exit;
    682       inc(Price, ModalSelectDlg.result shl 16);
    683       max:=MyRO.Ship[DipMem[me].pContact].Parts[ModalSelectDlg.result];
    684       InputDlg.Caption:=Phrases.Lookup('TITLE_NUMBER');
    685       InputDlg.EInput.Text:='';
    686       InputDlg.CenterToRect(BoundsRect);
    687       InputDlg.ShowModal;
    688       if InputDlg.ModalResult<>mrOK then exit;
    689       val(InputDlg.EInput.Text,a,i);
    690       if (i<>0) or (a<=0) then exit;
    691       if a>max then a:=max;
    692       if a>MaxShipPartPrice then a:=MaxShipPartPrice;
    693       inc(Price,a)
    694       end;
    695     opAllTech:
    696       begin // choose technology
    697       ModalSelectDlg.ShowNewContent(wmModal,kChooseETech);
    698       if ModalSelectDlg.result<0 then exit;
    699       if ModalSelectDlg.result=adAll then Price:=opAllTech
    700       else Price:=OpTech+ModalSelectDlg.result;
    701       end;
    702     opAllModel:
    703       begin // choose model
    704       ModalSelectDlg.ShowNewContent(wmModal,kChooseEModel);
    705       if ModalSelectDlg.result<0 then exit;
    706       if ModalSelectDlg.result=mixAll then Price:=opAllModel
    707       else Price:=OpModel+MyRO.EnemyModel[ModalSelectDlg.result].mix;
    708       end;
    709     opTreaty:
    710       begin
    711       if MyRO.Treaty[DipMem[me].pContact]<trPeace then Price:=opTreaty+trPeace
    712       else Price:=opTreaty+MyRO.Treaty[DipMem[me].pContact]+1;
    713       end;
    714 {    opLowTreaty:
    715       begin
    716       if MyRO.Treaty[DipMem[me].pContact]=trNone then Price:=opTreaty+trCeaseFire
    717       else Price:=opTreaty+MyRO.Treaty[DipMem[me].pContact]-1;
    718       end}
     801          if MyRO.Treaty[DipMem[me].pContact] < trPeace then
     802            Price := opTreaty + trPeace
     803          else
     804            Price := opTreaty + MyRO.Treaty[DipMem[me].pContact] + 1;
     805        end;
     806      { opLowTreaty:
     807        begin
     808        if MyRO.Treaty[DipMem[me].pContact]=trNone then Price:=opTreaty+trCeaseFire
     809        else Price:=opTreaty+MyRO.Treaty[DipMem[me].pContact]-1;
     810        end }
    719811    end;
    720812  end;
    721813
    722 Costs[TButtonN(Sender).Tag and $FF]:=Price;
    723 BuildCurrentOffer;
    724 DipCommand:=scDipOffer;
    725 SetButtonStates;
    726 SmartUpdateContent;
     814  Costs[TButtonN(Sender).Tag and $FF] := Price;
     815  BuildCurrentOffer;
     816  DipCommand := scDipOffer;
     817  SetButtonStates;
     818  SmartUpdateContent;
    727819end;
    728820
    729821procedure TNegoDlg.OfferClick(Sender: TObject);
    730822var
    731 a,i,max: integer;
    732 Price: cardinal;
    733 begin
    734 if (Page<>History[me].n)
    735   or (ClientMode=scDipCancelTreaty) or (ClientMode=scDipBreak) then
    736   exit;
    737 if Delivers[TButtonN(Sender).Tag and $FF]<>$FFFFFFFF then
    738   Price:=$FFFFFFFF // toggle off
    739 else
    740   begin
    741   if CurrentOffer.nDeliver>=2 then
    742     begin
    743     SimpleMessage(Phrases.Lookup('MAX2OFFERS'));
    744     exit
     823  a, i, max: integer;
     824  Price: cardinal;
     825begin
     826  if (Page <> History[me].n) or (ClientMode = scDipCancelTreaty) or
     827    (ClientMode = scDipBreak) then
     828    exit;
     829  if Delivers[TButtonN(Sender).Tag and $FF] <> $FFFFFFFF then
     830    Price := $FFFFFFFF // toggle off
     831  else
     832  begin
     833    if CurrentOffer.nDeliver >= 2 then
     834    begin
     835      SimpleMessage(Phrases.Lookup('MAX2OFFERS'));
     836      exit
    745837    end;
    746   Price:=ButtonPrice[TButtonN(Sender).Tag and $FF];
    747   if not (Price shr 24 in MyAllowed) then exit;
    748   case Price of
    749     opCivilReport, opMilReport:
    750       inc(Price,me shl 16+MyRO.Turn); // !!! choose player and year!
    751     opMoney:
    752       begin // choose amount
    753       InputDlg.Caption:=Phrases.Lookup('TITLE_AMOUNT');
    754       InputDlg.EInput.Text:='';
    755       InputDlg.CenterToRect(BoundsRect);
    756       InputDlg.ShowModal;
    757       if InputDlg.ModalResult<>mrOK then exit;
    758       val(InputDlg.EInput.Text,a,i);
    759       if (i<>0) or (a<=0) or (a>=MaxMoneyPrice) then exit;
    760       if (Price=opMoney) and (a>MyRO.Money) then
    761         a:=MyRO.Money;
    762       inc(Price,a);
    763       end;
    764     opShipParts:
    765       begin // choose type and number
    766       ModalSelectDlg.ShowNewContent(wmModal,kShipPart);
    767       if ModalSelectDlg.result<0 then exit;
    768       inc(Price, ModalSelectDlg.result shl 16);
    769       max:=MyRO.Ship[me].Parts[ModalSelectDlg.result];
    770       InputDlg.Caption:=Phrases.Lookup('TITLE_NUMBER');
    771       InputDlg.EInput.Text:='';
    772       InputDlg.CenterToRect(BoundsRect);
    773       InputDlg.ShowModal;
    774       if InputDlg.ModalResult<>mrOK then exit;
    775       val(InputDlg.EInput.Text,a,i);
    776       if (i<>0) or (a<=0) then exit;
    777       if a>max then a:=max;
    778       if a>MaxShipPartPrice then a:=MaxShipPartPrice;
    779       inc(Price,a)
    780       end;
    781     opAllTech:
    782       begin // choose technology
    783       ModalSelectDlg.ShowNewContent(wmModal,kChooseTech);
    784       if ModalSelectDlg.result<0 then exit;
    785       if ModalSelectDlg.result=adAll then Price:=opAllTech
    786       else Price:=OpTech+ModalSelectDlg.result;
    787       end;
    788     opAllModel:
    789       begin // choose model
    790       ModalSelectDlg.ShowNewContent(wmModal,kChooseModel);
    791       if ModalSelectDlg.result<0 then exit;
    792       if ModalSelectDlg.result=mixAll then Price:=opAllModel
    793       else Price:=opModel+ModalSelectDlg.result
    794       end;
    795     opTreaty:
    796       begin
    797       if MyRO.Treaty[DipMem[me].pContact]<trPeace then Price:=opTreaty+trPeace
    798       else Price:=opTreaty+MyRO.Treaty[DipMem[me].pContact]+1;
    799       end;
    800 {    opLowTreaty:
    801       begin
    802       if MyRO.Treaty[DipMem[me].pContact]=trNone then Price:=opTreaty+trCeaseFire
    803       else Price:=opTreaty+MyRO.Treaty[DipMem[me].pContact]-1;
    804       end}
     838    Price := ButtonPrice[TButtonN(Sender).Tag and $FF];
     839    if not(Price shr 24 in MyAllowed) then
     840      exit;
     841    case Price of
     842      opCivilReport, opMilReport:
     843        inc(Price, me shl 16 + MyRO.Turn); // !!! choose player and year!
     844      opMoney:
     845        begin // choose amount
     846          InputDlg.Caption := Phrases.Lookup('TITLE_AMOUNT');
     847          InputDlg.EInput.Text := '';
     848          InputDlg.CenterToRect(BoundsRect);
     849          InputDlg.ShowModal;
     850          if InputDlg.ModalResult <> mrOK then
     851            exit;
     852          val(InputDlg.EInput.Text, a, i);
     853          if (i <> 0) or (a <= 0) or (a >= MaxMoneyPrice) then
     854            exit;
     855          if (Price = opMoney) and (a > MyRO.Money) then
     856            a := MyRO.Money;
     857          inc(Price, a);
     858        end;
     859      opShipParts:
     860        begin // choose type and number
     861          ModalSelectDlg.ShowNewContent(wmModal, kShipPart);
     862          if ModalSelectDlg.result < 0 then
     863            exit;
     864          inc(Price, ModalSelectDlg.result shl 16);
     865          max := MyRO.Ship[me].Parts[ModalSelectDlg.result];
     866          InputDlg.Caption := Phrases.Lookup('TITLE_NUMBER');
     867          InputDlg.EInput.Text := '';
     868          InputDlg.CenterToRect(BoundsRect);
     869          InputDlg.ShowModal;
     870          if InputDlg.ModalResult <> mrOK then
     871            exit;
     872          val(InputDlg.EInput.Text, a, i);
     873          if (i <> 0) or (a <= 0) then
     874            exit;
     875          if a > max then
     876            a := max;
     877          if a > MaxShipPartPrice then
     878            a := MaxShipPartPrice;
     879          inc(Price, a)
     880        end;
     881      opAllTech:
     882        begin // choose technology
     883          ModalSelectDlg.ShowNewContent(wmModal, kChooseTech);
     884          if ModalSelectDlg.result < 0 then
     885            exit;
     886          if ModalSelectDlg.result = adAll then
     887            Price := opAllTech
     888          else
     889            Price := OpTech + ModalSelectDlg.result;
     890        end;
     891      opAllModel:
     892        begin // choose model
     893          ModalSelectDlg.ShowNewContent(wmModal, kChooseModel);
     894          if ModalSelectDlg.result < 0 then
     895            exit;
     896          if ModalSelectDlg.result = mixAll then
     897            Price := opAllModel
     898          else
     899            Price := OpModel + ModalSelectDlg.result
     900        end;
     901      opTreaty:
     902        begin
     903          if MyRO.Treaty[DipMem[me].pContact] < trPeace then
     904            Price := opTreaty + trPeace
     905          else
     906            Price := opTreaty + MyRO.Treaty[DipMem[me].pContact] + 1;
     907        end;
     908      { opLowTreaty:
     909        begin
     910        if MyRO.Treaty[DipMem[me].pContact]=trNone then Price:=opTreaty+trCeaseFire
     911        else Price:=opTreaty+MyRO.Treaty[DipMem[me].pContact]-1;
     912        end }
    805913    end;
    806914  end;
    807915
    808 Delivers[TButtonN(Sender).Tag and $FF]:=Price;
    809 BuildCurrentOffer;
    810 DipCommand:=scDipOffer;
    811 SetButtonStates;
    812 SmartUpdateContent;
     916  Delivers[TButtonN(Sender).Tag and $FF] := Price;
     917  BuildCurrentOffer;
     918  DipCommand := scDipOffer;
     919  SetButtonStates;
     920  SmartUpdateContent;
    813921end;
    814922
    815923procedure TNegoDlg.FastBtnClick(Sender: TObject);
    816924var
    817 NewCommand: cardinal;
    818 begin
    819 if Page<>History[me].n then exit;
    820 NewCommand:=TButtonN(Sender).Tag and $FF+scDipStart;
    821 if not (NewCommand-scDipStart in CommandAllowed) then exit;
    822 if (NewCommand=scDipCancelTreaty)
    823   and (MyRO.Turn<MyRO.LastCancelTreaty[DipMem[me].pContact]+CancelTreatyTurns) then
    824   begin
    825   SimpleMessage(Phrases.Lookup('CANCELTREATYRUSH'));
    826   exit;
    827   end;
    828 if (NewCommand=scDipOffer)
    829   and ((ClientMode=scDipCancelTreaty) or (ClientMode=scDipBreak)) then
    830   DipCommand:=scDipNotice
    831 else DipCommand:=NewCommand;
    832 ResetCurrentOffer;
    833 SetButtonStates;
    834 SmartUpdateContent;
     925  NewCommand: cardinal;
     926begin
     927  if Page <> History[me].n then
     928    exit;
     929  NewCommand := TButtonN(Sender).Tag and $FF + scDipStart;
     930  if not(NewCommand - scDipStart in CommandAllowed) then
     931    exit;
     932  if (NewCommand = scDipCancelTreaty) and
     933    (MyRO.Turn < MyRO.LastCancelTreaty[DipMem[me].pContact] + CancelTreatyTurns)
     934  then
     935  begin
     936    SimpleMessage(Phrases.Lookup('CANCELTREATYRUSH'));
     937    exit;
     938  end;
     939  if (NewCommand = scDipOffer) and ((ClientMode = scDipCancelTreaty) or
     940    (ClientMode = scDipBreak)) then
     941    DipCommand := scDipNotice
     942  else
     943    DipCommand := NewCommand;
     944  ResetCurrentOffer;
     945  SetButtonStates;
     946  SmartUpdateContent;
    835947end;
    836948
    837949end.
    838 
  • trunk/LocalPlayer/PVSB.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit PVSB;
    43
     
    65
    76uses
    8   Windows,Messages,SysUtils;
     7  Windows, Messages, SysUtils;
    98
    109type
    11 TPVScrollbar=record h:integer;si:TScrollInfo end;
     10  TPVScrollbar = record
     11    h: integer;
     12    si: TScrollInfo end;
    1213
    13 procedure CreatePVSB(var sb:TPVScrollbar;Handle,y0,x1,y1:integer);
    14 procedure InitPVSB(var sb:TPVScrollbar;max,Page:integer);
    15 function ProcessPVSB(var sb:TPVScrollbar;const m:TMessage):boolean;
    16 function ProcessMouseWheel(var sb:TPVScrollbar;const m:TMessage):boolean;
    17 procedure ShowPVSB(var sb:TPVScrollbar; Visible: boolean);
    18 procedure EndPVSB(var sb:TPVScrollbar);
     14    procedure CreatePVSB(var sb: TPVScrollbar; Handle, y0, x1, y1: integer);
     15    procedure InitPVSB(var sb: TPVScrollbar; max, Page: integer);
     16    function ProcessPVSB(var sb: TPVScrollbar; const m: TMessage): boolean;
     17    function ProcessMouseWheel(var sb: TPVScrollbar; const m: TMessage)
     18      : boolean;
     19    procedure ShowPVSB(var sb: TPVScrollbar; Visible: boolean);
     20    procedure EndPVSB(var sb: TPVScrollbar);
    1921
    2022implementation
    2123
    2224const
    23 Count:integer= 0;
     25  Count: integer = 0;
    2426
    2527procedure CreatePVSB;
    2628begin
    27 inc(Count);
    28 sb.h:=CreateWindowEx(0,'SCROLLBAR',pchar('PVSB'+IntToStr(Count)),
    29   SBS_VERT or WS_CHILD or SBS_RIGHTALIGN,x1-100,y0,100,y1-y0,Handle,0,0,nil);
    30 sb.si.cbSize:=28;
     29  inc(Count);
     30  sb.h := CreateWindowEx(0, 'SCROLLBAR', pchar('PVSB' + IntToStr(Count)),
     31    SBS_VERT or WS_CHILD or SBS_RIGHTALIGN, x1 - 100, y0, 100, y1 - y0,
     32    Handle, 0, 0, nil);
     33  sb.si.cbSize := 28;
    3134end;
    3235
    3336procedure InitPVSB;
    3437begin
    35 with sb.si do
     38  with sb.si do
    3639  begin
    37   nMin:=0;nMax:=max;npos:=0;nPage:=Page;
    38   FMask:=SIF_PAGE or SIF_POS or SIF_RANGE;
     40    nMin := 0;
     41    nMax := max;
     42    npos := 0;
     43    nPage := Page;
     44    FMask := SIF_PAGE or SIF_POS or SIF_RANGE;
    3945  end;
    40 SetScrollInfo(sb.h,SB_CTL,sb.si,true);
    41 if max<Page then ShowWindow(sb.h,SW_HIDE) else ShowWindow(sb.h,SW_SHOW)
     46  SetScrollInfo(sb.h, SB_CTL, sb.si, true);
     47  if max < Page then
     48    ShowWindow(sb.h, SW_HIDE)
     49  else
     50    ShowWindow(sb.h, SW_SHOW)
    4251end;
    4352
    4453function ProcessPVSB;
    4554var
    46 NewPos:integer;
     55  NewPos: integer;
    4756begin
    48 with sb.si do
    49   if nMax<integer(nPage) then result:=false
    50   else
     57  with sb.si do
     58    if nMax < integer(nPage) then
     59      result := false
     60    else
    5161    begin
    52     if m.wParamLo in[SB_THUMBPOSITION,SB_THUMBTRACK] then
    53       begin result:=m.wParamHi<>npos;npos:=m.wParamHi;end
    54     else
     62      if m.wParamLo in [SB_THUMBPOSITION, SB_THUMBTRACK] then
    5563      begin
    56       case m.wParamLo of
    57         SB_LINEUP:NewPos:=npos-1;SB_LINEDOWN:NewPos:=npos+1;
    58         SB_PAGEUP:NewPos:=npos-integer(nPage);SB_PAGEDOWN:NewPos:=npos+integer(nPage);
    59         else NewPos:=npos
     64        result := m.wParamHi <> npos;
     65        npos := m.wParamHi;
     66      end
     67      else
     68      begin
     69        case m.wParamLo of
     70          SB_LINEUP:
     71            NewPos := npos - 1;
     72          SB_LINEDOWN:
     73            NewPos := npos + 1;
     74          SB_PAGEUP:
     75            NewPos := npos - integer(nPage);
     76          SB_PAGEDOWN:
     77            NewPos := npos + integer(nPage);
     78        else
     79          NewPos := npos
    6080        end;
    61       if NewPos<0 then NewPos:=0;
    62       if NewPos>nMax-integer(nPage)+1 then NewPos:=nMax-integer(nPage)+1;
    63       result:=NewPos<>npos;
    64       if (NewPos<>npos) or (m.wParamLo=SB_ENDSCROLL) then
     81        if NewPos < 0 then
     82          NewPos := 0;
     83        if NewPos > nMax - integer(nPage) + 1 then
     84          NewPos := nMax - integer(nPage) + 1;
     85        result := NewPos <> npos;
     86        if (NewPos <> npos) or (m.wParamLo = SB_ENDSCROLL) then
    6587        begin
    66         npos:=NewPos;FMask:=SIF_POS;
    67         SetScrollInfo(sb.h,SB_CTL,sb.si,true);
     88          npos := NewPos;
     89          FMask := SIF_POS;
     90          SetScrollInfo(sb.h, SB_CTL, sb.si, true);
    6891        end;
    6992      end
     
    7396function ProcessMouseWheel;
    7497var
    75 NewPos:integer;
     98  NewPos: integer;
    7699begin
    77 with sb.si do
    78   if nMax<integer(nPage) then result:=false
    79   else
     100  with sb.si do
     101    if nMax < integer(nPage) then
     102      result := false
     103    else
    80104    begin
    81     NewPos:=npos-m.wParam div (1 shl 16*40);
    82     if NewPos<0 then NewPos:=0;
    83     if NewPos>nMax-integer(nPage)+1 then NewPos:=nMax-integer(nPage)+1;
    84     result:=NewPos<>npos;
    85     if NewPos<>npos then
     105      NewPos := npos - m.wParam div (1 shl 16 * 40);
     106      if NewPos < 0 then
     107        NewPos := 0;
     108      if NewPos > nMax - integer(nPage) + 1 then
     109        NewPos := nMax - integer(nPage) + 1;
     110      result := NewPos <> npos;
     111      if NewPos <> npos then
    86112      begin
    87       npos:=NewPos;FMask:=SIF_POS;
    88       SetScrollInfo(sb.h,SB_CTL,sb.si,true);
     113        npos := NewPos;
     114        FMask := SIF_POS;
     115        SetScrollInfo(sb.h, SB_CTL, sb.si, true);
    89116      end
    90117    end
    91118end;
    92119
    93 procedure ShowPVSB(var sb:TPVScrollbar; Visible: boolean);
     120procedure ShowPVSB(var sb: TPVScrollbar; Visible: boolean);
    94121begin
    95 if not Visible or (sb.si.nMax<integer(sb.si.nPage)) then
    96   ShowWindow(sb.h,SW_HIDE)
    97 else ShowWindow(sb.h,SW_SHOW)
     122  if not Visible or (sb.si.nMax < integer(sb.si.nPage)) then
     123    ShowWindow(sb.h, SW_HIDE)
     124  else
     125    ShowWindow(sb.h, SW_SHOW)
    98126end;
    99127
    100 procedure EndPVSB(var sb:TPVScrollbar);
     128procedure EndPVSB(var sb: TPVScrollbar);
    101129begin
    102 with sb.si do
     130  with sb.si do
    103131  begin
    104   if nMax<integer(nPage) then npos:=0 // hidden
    105   else
     132    if nMax < integer(nPage) then
     133      npos := 0 // hidden
     134    else
    106135    begin
    107     sb.si.npos:=nMax-integer(nPage)+1;
    108     sb.si.FMask:=SIF_POS;
    109     SetScrollInfo(sb.h,SB_CTL,sb.si,true);
     136      sb.si.npos := nMax - integer(nPage) + 1;
     137      sb.si.FMask := SIF_POS;
     138      SetScrollInfo(sb.h, SB_CTL, sb.si, true);
    110139    end
    111140  end
     
    113142
    114143end.
    115 
  • trunk/LocalPlayer/Rates.pas

    r2 r6  
    55
    66uses
    7   Protocol,ScreenTools,BaseWin,
     7  Protocol, ScreenTools, BaseWin,
    88
    99  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    3232
    3333uses
    34   ClientTools,Term,Tribes;
     34  ClientTools, Term, Tribes;
    3535
    3636{$R *.DFM}
    3737
    3838const
    39 MessageLineSpacing=20;
    40 
     39  MessageLineSpacing = 20;
    4140
    4241procedure TRatesDlg.FormCreate(Sender: TObject);
    4342begin
    44 TitleHeight:=Screen.Height;
    45 InitButtons();
     43  TitleHeight := Screen.Height;
     44  InitButtons();
    4645end;
    4746
    4847procedure TRatesDlg.OffscreenPaint;
    4948var
    50 p,x,y,current,max,i: integer;
    51 s,s1: string;
    52 begin
    53 if (OffscreenUser<>nil) and (OffscreenUser<>self) then OffscreenUser.Update;
     49  p, x, y, current, max, i: integer;
     50  s, s1: string;
     51begin
     52  if (OffscreenUser <> nil) and (OffscreenUser <> self) then
     53    OffscreenUser.Update;
    5454  // complete working with old owner to prevent rebound
    55 OffscreenUser:=self;
    56 
    57 Fill(Offscreen.Canvas, 0,0,ClientWidth,ClientHeight,
    58   (wMaintexture-ClientWidth) div 2,(hMaintexture-ClientHeight) div 2);
    59 Frame(Offscreen.Canvas,0,0,ClientWidth-1,ClientHeight-1,0,0);
    60 Frame(Offscreen.Canvas,1,1,ClientWidth-2,ClientHeight-2,MainTexture.clBevelLight,MainTexture.clBevelShade);
    61 Frame(Offscreen.Canvas,2,2,ClientWidth-3,ClientHeight-3,MainTexture.clBevelLight,MainTexture.clBevelShade);
    62 
    63 BtnFrame(Offscreen.Canvas,CloseBtn.BoundsRect,MainTexture);
    64 Offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
    65 s:=Phrases.Lookup('TITLE_RATES');
    66 RisedTextOut(Offscreen.Canvas,(ClientWidth-BiColorTextWidth(Offscreen.Canvas,s)) div 2-1,7,s);
    67 
    68 if MyRO.Wonder[woLiberty].EffectiveOwner=me then
    69   s:=Phrases.Lookup('NORATES')
    70 else s:=Phrases.Lookup('RATES');
    71 Offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    72 p:=pos('\',s);
    73 if p=0 then
    74   RisedTextout(Offscreen.Canvas,(ClientWidth-BiColorTextWidth(Canvas,s)) div 2, 114, s)
    75 else
    76   begin
    77   s1:=copy(s,1,p-1);
    78   RisedTextout(Offscreen.Canvas,(ClientWidth-BiColorTextWidth(Offscreen.Canvas,s1)) div 2,
    79     114-MessageLineSpacing div 2, s1);
    80   s1:=copy(s,p+1,255);
    81   RisedTextout(Offscreen.Canvas,(ClientWidth-BiColorTextWidth(Offscreen.Canvas,s1)) div 2,
    82     114+(MessageLineSpacing-MessageLineSpacing div 2), s1);
    83   end;
    84 Offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    85 
    86 if MyRO.Wonder[woLiberty].EffectiveOwner=me then
    87   begin
    88   GlowFrame(Offscreen, ClientWidth div 2-xSizeBig div 2,
    89     52,xSizeBig, ySizeBig, Tribe[me].Color);
    90   BitBlt(Offscreen.Canvas.Handle, ClientWidth div 2-xSizeBig div 2,
    91     52, xSizeBig, ySizeBig,BigImp.Canvas.Handle,
    92     (woLiberty mod 7)*xSizeBig, (woLiberty div 7+SystemIconLines)*ySizeBig, SRCCOPY);
    93   end
    94 else
    95   begin
    96 //  ImageOp_CBC(Offscreen,Templates,260,40,145,112,36,36,$404000,$8B8BEB);
    97 
    98   s:=Phrases.Lookup('SCIENCE');
    99   RisedTextOut(Offscreen.Canvas,16+120-BiColorTextWidth(Offscreen.Canvas,s),44,s);
    100   s:=Format('%d%%',[100-MyRO.TaxRate-MyRO.LuxRate]);
    101   RisedTextOut(Offscreen.Canvas,16+120-BiColorTextWidth(Offscreen.Canvas,s),60,s);
    102   //PaintProgressBar(Offscreen.Canvas,2,16,81,(100-MyRO.LuxRate-MyRO.TaxRate)*120 div 100,0,120,MainTexture);
    103 
    104   // reverse progress bar for science
    105   x:=16;
    106   y:=81;
    107   current:=(100-MyRO.LuxRate-MyRO.TaxRate)*120 div 100;
    108   max:=120;
    109   Frame(Offscreen.Canvas,x-1,y-1,x+max,y+7,$000000,$000000);
    110   RFrame(Offscreen.Canvas,x-2,y-2,x+max+1,y+8,MainTexture.clBevelShade,MainTexture.clBevelLight);
    111   with Offscreen.Canvas do
     55  OffscreenUser := self;
     56
     57  Fill(Offscreen.Canvas, 0, 0, ClientWidth, ClientHeight,
     58    (wMaintexture - ClientWidth) div 2, (hMaintexture - ClientHeight) div 2);
     59  Frame(Offscreen.Canvas, 0, 0, ClientWidth - 1, ClientHeight - 1, 0, 0);
     60  Frame(Offscreen.Canvas, 1, 1, ClientWidth - 2, ClientHeight - 2,
     61    MainTexture.clBevelLight, MainTexture.clBevelShade);
     62  Frame(Offscreen.Canvas, 2, 2, ClientWidth - 3, ClientHeight - 3,
     63    MainTexture.clBevelLight, MainTexture.clBevelShade);
     64
     65  BtnFrame(Offscreen.Canvas, CloseBtn.BoundsRect, MainTexture);
     66  Offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
     67  s := Phrases.Lookup('TITLE_RATES');
     68  RisedTextOut(Offscreen.Canvas,
     69    (ClientWidth - BiColorTextWidth(Offscreen.Canvas, s)) div 2 - 1, 7, s);
     70
     71  if MyRO.Wonder[woLiberty].EffectiveOwner = me then
     72    s := Phrases.Lookup('NORATES')
     73  else
     74    s := Phrases.Lookup('RATES');
     75  Offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
     76  p := pos('\', s);
     77  if p = 0 then
     78    RisedTextOut(Offscreen.Canvas, (ClientWidth - BiColorTextWidth(Canvas, s))
     79      div 2, 114, s)
     80  else
     81  begin
     82    s1 := copy(s, 1, p - 1);
     83    RisedTextOut(Offscreen.Canvas,
     84      (ClientWidth - BiColorTextWidth(Offscreen.Canvas, s1)) div 2,
     85      114 - MessageLineSpacing div 2, s1);
     86    s1 := copy(s, p + 1, 255);
     87    RisedTextOut(Offscreen.Canvas,
     88      (ClientWidth - BiColorTextWidth(Offscreen.Canvas, s1)) div 2,
     89      114 + (MessageLineSpacing - MessageLineSpacing div 2), s1);
     90  end;
     91  Offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     92
     93  if MyRO.Wonder[woLiberty].EffectiveOwner = me then
     94  begin
     95    GlowFrame(Offscreen, ClientWidth div 2 - xSizeBig div 2, 52, xSizeBig,
     96      ySizeBig, Tribe[me].Color);
     97    BitBlt(Offscreen.Canvas.Handle, ClientWidth div 2 - xSizeBig div 2, 52,
     98      xSizeBig, ySizeBig, BigImp.Canvas.Handle, (woLiberty mod 7) * xSizeBig,
     99      (woLiberty div 7 + SystemIconLines) * ySizeBig, SRCCOPY);
     100  end
     101  else
     102  begin
     103    // ImageOp_CBC(Offscreen,Templates,260,40,145,112,36,36,$404000,$8B8BEB);
     104
     105    s := Phrases.Lookup('SCIENCE');
     106    RisedTextOut(Offscreen.Canvas,
     107      16 + 120 - BiColorTextWidth(Offscreen.Canvas, s), 44, s);
     108    s := Format('%d%%', [100 - MyRO.TaxRate - MyRO.LuxRate]);
     109    RisedTextOut(Offscreen.Canvas,
     110      16 + 120 - BiColorTextWidth(Offscreen.Canvas, s), 60, s);
     111    // PaintProgressBar(Offscreen.Canvas,2,16,81,(100-MyRO.LuxRate-MyRO.TaxRate)*120 div 100,0,120,MainTexture);
     112
     113    // reverse progress bar for science
     114    x := 16;
     115    y := 81;
     116    current := (100 - MyRO.LuxRate - MyRO.TaxRate) * 120 div 100;
     117    max := 120;
     118    Frame(Offscreen.Canvas, x - 1, y - 1, x + max, y + 7, $000000, $000000);
     119    RFrame(Offscreen.Canvas, x - 2, y - 2, x + max + 1, y + 8,
     120      MainTexture.clBevelShade, MainTexture.clBevelLight);
     121    with Offscreen.Canvas do
    112122    begin
    113     for i:=0 to current div 8-1 do
    114       BitBlt(Handle,x+max-8-i*8,y,8,7,GrExt[HGrSystem].Data.Canvas.Handle,104,
    115         9+8*2,SRCCOPY);
    116     BitBlt(Handle,x+max-current,y,
    117       current-8*(current div 8),7,GrExt[HGrSystem].Data.Canvas.Handle,104,9+8*2,SRCCOPY);
    118     Brush.Color:=$000000;
    119     FillRect(Rect(x,y,x+max-current,y+7));
    120     Brush.Style:=bsClear;
     123      for i := 0 to current div 8 - 1 do
     124        BitBlt(Handle, x + max - 8 - i * 8, y, 8, 7,
     125          GrExt[HGrSystem].Data.Canvas.Handle, 104, 9 + 8 * 2, SRCCOPY);
     126      BitBlt(Handle, x + max - current, y, current - 8 * (current div 8), 7,
     127        GrExt[HGrSystem].Data.Canvas.Handle, 104, 9 + 8 * 2, SRCCOPY);
     128      Brush.Color := $000000;
     129      FillRect(Rect(x, y, x + max - current, y + 7));
     130      Brush.Style := bsClear;
    121131    end;
    122132
    123   RisedTextOut(Offscreen.Canvas,16+160,44,Phrases.Lookup('LUX'));
    124   s:=Format('%d%%',[MyRO.LuxRate]);
    125   RisedTextOut(Offscreen.Canvas,16+160{+120-BiColorTextWidth(Offscreen.Canvas,s)},60,s);
    126   PaintProgressBar(Offscreen.Canvas,5,16+160,81,MyRO.LuxRate*120 div 100,0,120,MainTexture);
    127   RFrame(Offscreen.Canvas,ScienceBtn.Left-1,LuxBtn.Top-1,LuxBtn.Left+12,
    128     LuxBtn.Top+12,MainTexture.clBevelShade,MainTexture.clBevelLight);
    129   end;
    130 
    131 DLine(Offscreen.Canvas,1,ClientWidth-2,154, MainTexture.clBevelShade, MainTexture.clBevelLight);
    132 RisedTextOut(Offscreen.Canvas,16+80,164,Phrases.Lookup('TAXRATE'));
    133 s:=Format('%d%%',[MyRO.TaxRate]);
    134 RisedTextOut(Offscreen.Canvas,16+80{+120-BiColorTextWidth(Offscreen.Canvas,s)},180,s);
    135 PaintProgressBar(Offscreen.Canvas,0,16+80,201,MyRO.TaxRate*120 div 100,0,120,MainTexture);
    136 RFrame(Offscreen.Canvas,TaxUpBtn.Left-1,TaxUpBtn.Top-1,TaxUpBtn.Left+12,
    137   TaxDownBtn.Top+12,MainTexture.clBevelShade,MainTexture.clBevelLight);
    138 
    139 MarkUsedOffscreen(ClientWidth,ClientHeight);
     133    RisedTextOut(Offscreen.Canvas, 16 + 160, 44, Phrases.Lookup('LUX'));
     134    s := Format('%d%%', [MyRO.LuxRate]);
     135    RisedTextOut(Offscreen.Canvas,
     136      16 + 160 { +120-BiColorTextWidth(Offscreen.Canvas,s) } , 60, s);
     137    PaintProgressBar(Offscreen.Canvas, 5, 16 + 160, 81,
     138      MyRO.LuxRate * 120 div 100, 0, 120, MainTexture);
     139    RFrame(Offscreen.Canvas, ScienceBtn.Left - 1, LuxBtn.Top - 1,
     140      LuxBtn.Left + 12, LuxBtn.Top + 12, MainTexture.clBevelShade,
     141      MainTexture.clBevelLight);
     142  end;
     143
     144  DLine(Offscreen.Canvas, 1, ClientWidth - 2, 154, MainTexture.clBevelShade,
     145    MainTexture.clBevelLight);
     146  RisedTextOut(Offscreen.Canvas, 16 + 80, 164, Phrases.Lookup('TAXRATE'));
     147  s := Format('%d%%', [MyRO.TaxRate]);
     148  RisedTextOut(Offscreen.Canvas,
     149    16 + 80 { +120-BiColorTextWidth(Offscreen.Canvas,s) } , 180, s);
     150  PaintProgressBar(Offscreen.Canvas, 0, 16 + 80, 201,
     151    MyRO.TaxRate * 120 div 100, 0, 120, MainTexture);
     152  RFrame(Offscreen.Canvas, TaxUpBtn.Left - 1, TaxUpBtn.Top - 1,
     153    TaxUpBtn.Left + 12, TaxDownBtn.Top + 12, MainTexture.clBevelShade,
     154    MainTexture.clBevelLight);
     155
     156  MarkUsedOffscreen(ClientWidth, ClientHeight);
    140157end;
    141158
    142159procedure TRatesDlg.ShowNewContent(NewMode: integer);
    143160begin
    144 inherited ShowNewContent(NewMode);
     161  inherited ShowNewContent(NewMode);
    145162end;
    146163
    147164procedure TRatesDlg.FormShow(Sender: TObject);
    148165begin
    149 if MyRO.Wonder[woLiberty].EffectiveOwner=me then
    150   begin
    151   ScienceBtn.Visible:=false;
    152   LuxBtn.Visible:=false;
    153   end
    154 else
    155   begin
    156   ScienceBtn.Visible:=true;
    157   LuxBtn.Visible:=true;
    158   end;
    159 OffscreenPaint;
     166  if MyRO.Wonder[woLiberty].EffectiveOwner = me then
     167  begin
     168    ScienceBtn.Visible := false;
     169    LuxBtn.Visible := false;
     170  end
     171  else
     172  begin
     173    ScienceBtn.Visible := true;
     174    LuxBtn.Visible := true;
     175  end;
     176  OffscreenPaint;
    160177end;
    161178
    162179procedure TRatesDlg.CloseBtnClick(Sender: TObject);
    163180begin
    164 Close;
     181  Close;
    165182end;
    166183
    167184procedure TRatesDlg.TaxLuxBtnClick(Sender: TObject);
    168185var
    169 NewTax, NewLux: integer;
    170 begin
    171 NewTax:=MyRO.TaxRate div 10;
    172 NewLux:=MyRO.LuxRate div 10;
    173 if Sender=TaxUpBtn then
    174   begin
    175   if NewTax<10 then inc(NewTax);
    176   if NewTax+NewLux>10 then dec(NewLux);
    177   end
    178 else if (Sender=TaxDownBtn) and (NewTax>0) then
    179   dec(NewTax)
    180 else if (Sender=ScienceBtn) and (NewLux>0) then
    181   dec(NewLux)
    182 else if (Sender=LuxBtn) and (NewLux+NewTax<100) then
    183   inc(NewLux);
    184 if Server(sSetRates,me,NewTax+NewLux shl 4,nil^)<>eNotChanged then
    185   begin
    186   CityOptimizer_BeginOfTurn;
    187   SmartUpdateContent;
    188   MainScreen.UpdateViews(true);
     186  NewTax, NewLux: integer;
     187begin
     188  NewTax := MyRO.TaxRate div 10;
     189  NewLux := MyRO.LuxRate div 10;
     190  if Sender = TaxUpBtn then
     191  begin
     192    if NewTax < 10 then
     193      inc(NewTax);
     194    if NewTax + NewLux > 10 then
     195      dec(NewLux);
     196  end
     197  else if (Sender = TaxDownBtn) and (NewTax > 0) then
     198    dec(NewTax)
     199  else if (Sender = ScienceBtn) and (NewLux > 0) then
     200    dec(NewLux)
     201  else if (Sender = LuxBtn) and (NewLux + NewTax < 100) then
     202    inc(NewLux);
     203  if Server(sSetRates, me, NewTax + NewLux shl 4, nil^) <> eNotChanged then
     204  begin
     205    CityOptimizer_BeginOfTurn;
     206    SmartUpdateContent;
     207    MainScreen.UpdateViews(true);
    189208  end
    190209end;
    191210
    192211end.
    193 
  • trunk/LocalPlayer/Select.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Select;
    43
     
    65
    76uses
    8   Protocol,ClientTools,Term,ScreenTools,IsoEngine,PVSB,BaseWin,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,
    11   ExtCtrls,ButtonB, ButtonBase, Menus;
     7  Protocol, ClientTools, Term, ScreenTools, IsoEngine, PVSB, BaseWin,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     10  ExtCtrls, ButtonB, ButtonBase, Menus;
    1211
    1312const
    14 MaxLayer=3;
     13  MaxLayer = 3;
    1514
    1615type
    17   TListKind=(kProject,kAdvance,kFarAdvance,kCities,kCityEvents,kModels,kEModels,
    18     kAllEModels,kTribe,kScience,kShipPart,kEShipPart,kChooseTech,
    19     kChooseETech,kChooseModel,kChooseEModel,kChooseCity,kChooseECity,
    20     kStealTech,kGov,kMission);
     16  TListKind = (kProject, kAdvance, kFarAdvance, kCities, kCityEvents, kModels,
     17    kEModels, kAllEModels, kTribe, kScience, kShipPart, kEShipPart, kChooseTech,
     18    kChooseETech, kChooseModel, kChooseEModel, kChooseCity, kChooseECity,
     19    kStealTech, kGov, kMission);
    2120
    2221  TListDlg = class(TFramedDlg)
     
    2726    ToggleBtn: TButtonB;
    2827    Popup: TPopupMenu;
    29     procedure PaintBox1MouseMove(Sender:TObject;Shift:TShiftState;x,
    30       y:integer);
    31     procedure FormCreate(Sender:TObject);
    32     procedure PaintBox1MouseDown(Sender:TObject;Button:TMouseButton;
    33       Shift:TShiftState;x,y:integer);
    34     procedure FormPaint(Sender:TObject);
    35     procedure CloseBtnClick(Sender:TObject);
     28    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
     29      x, y: integer);
     30    procedure FormCreate(Sender: TObject);
     31    procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
     32      Shift: TShiftState; x, y: integer);
     33    procedure FormPaint(Sender: TObject);
     34    procedure CloseBtnClick(Sender: TObject);
    3635    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
    3736    procedure FormShow(Sender: TObject);
    3837    procedure ModeBtnClick(Sender: TObject);
    3938    procedure ToggleBtnClick(Sender: TObject);
    40     procedure FormKeyDown(Sender: TObject; var Key: word;
    41       Shift: TShiftState);
     39    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
    4240    procedure PlayerClick(Sender: TObject);
    4341    procedure FormDestroy(Sender: TObject);
     
    4543  public
    4644    result: integer;
    47     function OnlyChoice(TestKind: TListKind): integer; // -2=empty, -1=ambiguous, other=only choice
     45    function OnlyChoice(TestKind: TListKind): integer;
     46    // -2=empty, -1=ambiguous, other=only choice
    4847    procedure OffscreenPaint; override;
    4948    procedure ShowNewContent(NewMode: integer; ListKind: TListKind);
     
    5756  private
    5857    Kind: TListKind;
    59     LineDistance,MaxLines,cixProject,pView,Sel,DispLines,Layer,nColumn,
    60       TechNameSpace,ScienceNation: integer;
    61     sb:TPVScrollbar;
    62     Lines, FirstShrinkedLine: array[0..MaxLayer-1] of integer;
    63     code: array[0..MaxLayer-1,0..4095] of integer;
    64     Column: array[0..nPl-1] of integer;
    65     Closable,MultiPage: boolean;
     58    LineDistance, MaxLines, cixProject, pView, Sel, DispLines, Layer, nColumn,
     59      TechNameSpace, ScienceNation: integer;
     60    sb: TPVScrollbar;
     61    Lines, FirstShrinkedLine: array [0 .. MaxLayer - 1] of integer;
     62    code: array [0 .. MaxLayer - 1, 0 .. 4095] of integer;
     63    Column: array [0 .. nPl - 1] of integer;
     64    Closable, MultiPage: boolean;
    6665    ScienceNationDot: TBitmap;
    6766    procedure InitLines;
     
    6968    function RenameCity(cix: integer): boolean;
    7069    function RenameModel(mix: integer): boolean;
    71     procedure OnScroll(var m:TMessage); message WM_VSCROLL;
    72     procedure OnMouseWheel(var m:TMessage); message WM_MOUSEWHEEL;
    73     procedure OnMouseLeave(var Msg:TMessage); message CM_MOUSELEAVE;
    74   end;
    75 
    76   TModalSelectDlg=TListDlg;
     70    procedure OnScroll(var m: TMessage); message WM_VSCROLL;
     71    procedure OnMouseWheel(var m: TMessage); message WM_MOUSEWHEEL;
     72    procedure OnMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
     73  end;
     74
     75  TModalSelectDlg = TListDlg;
    7776
    7877const
    79 cpType=$10000;
    80 mixAll=$10000;
    81 adAll=$10000;
     78  cpType = $10000;
     79  mixAll = $10000;
     80  adAll = $10000;
    8281
    8382var
     
    8887
    8988uses
    90 CityScreen, Help, UnitStat, Tribes, Inp;
     89  CityScreen, Help, UnitStat, Tribes, Inp;
    9190
    9291{$R *.DFM}
    9392
    9493const
    95 CityNameSpace=127;
    96 
    97 MustChooseKind=[kTribe,kStealTech,kGov];
    98 
    99 
    100 procedure TListDlg.FormCreate(Sender:TObject);
    101 begin
    102 inherited;
    103 Canvas.Font.Assign(UniFont[ftNormal]);
    104 CreatePVSB(sb,Handle,2,361,2+422);
    105 InitButtons();
    106 Kind:=kMission;
    107 Layer0Btn.Hint:=Phrases.Lookup('BTN_IMPRS');
    108 Layer1Btn.Hint:=Phrases.Lookup('BTN_WONDERS');
    109 Layer2Btn.Hint:=Phrases.Lookup('BTN_CLASSES');
    110 ScienceNationDot:=TBitmap.Create;
    111 ScienceNationDot.PixelFormat:=pf24bit;
    112 ScienceNationDot.Width:=17; ScienceNationDot.Height:=17;
     94  CityNameSpace = 127;
     95
     96  MustChooseKind = [kTribe, kStealTech, kGov];
     97
     98procedure TListDlg.FormCreate(Sender: TObject);
     99begin
     100  inherited;
     101  Canvas.Font.Assign(UniFont[ftNormal]);
     102  CreatePVSB(sb, Handle, 2, 361, 2 + 422);
     103  InitButtons();
     104  Kind := kMission;
     105  Layer0Btn.Hint := Phrases.Lookup('BTN_IMPRS');
     106  Layer1Btn.Hint := Phrases.Lookup('BTN_WONDERS');
     107  Layer2Btn.Hint := Phrases.Lookup('BTN_CLASSES');
     108  ScienceNationDot := TBitmap.Create;
     109  ScienceNationDot.PixelFormat := pf24bit;
     110  ScienceNationDot.Width := 17;
     111  ScienceNationDot.Height := 17;
    113112end;
    114113
    115114procedure TListDlg.FormDestroy(Sender: TObject);
    116115begin
    117 ScienceNationDot.Free;
    118 end;
    119 
    120 procedure TListDlg.CloseBtnClick(Sender:TObject);
    121 begin
    122 Closable:=true; Close
    123 end;
    124 
    125 procedure TListDlg.FormCloseQuery(Sender: TObject;
    126   var CanClose: boolean);
    127 begin
    128 CanClose:=Closable or not(Kind in MustChooseKind)
    129 end;
    130 
    131 procedure TListDlg.OnScroll(var m:TMessage);
    132 begin
    133 if ProcessPVSB(sb,m) then
    134   begin Sel:=-2; SmartUpdateContent(true) end
    135 end;
    136 
    137 procedure TListDlg.OnMouseWheel(var m:TMessage);
    138 begin
    139 if ProcessMouseWheel(sb,m) then
    140   begin
    141   Sel:=-2;
    142   SmartUpdateContent(true);
    143   PaintBox1MouseMove(nil, [], m.lParam and $FFFF-Left, m.lParam shr 16-Top);
     116  ScienceNationDot.Free;
     117end;
     118
     119procedure TListDlg.CloseBtnClick(Sender: TObject);
     120begin
     121  Closable := true;
     122  Close
     123end;
     124
     125procedure TListDlg.FormCloseQuery(Sender: TObject; var CanClose: boolean);
     126begin
     127  CanClose := Closable or not(Kind in MustChooseKind)
     128end;
     129
     130procedure TListDlg.OnScroll(var m: TMessage);
     131begin
     132  if ProcessPVSB(sb, m) then
     133  begin
     134    Sel := -2;
     135    SmartUpdateContent(true)
    144136  end
    145137end;
    146138
    147 procedure TListDlg.OnMouseLeave(var Msg:TMessage);
    148 begin
    149 if not Closable and (Sel<>-2) then
    150   begin
    151   line(Canvas,Sel,false,false);
    152   Sel:=-2;
     139procedure TListDlg.OnMouseWheel(var m: TMessage);
     140begin
     141  if ProcessMouseWheel(sb, m) then
     142  begin
     143    Sel := -2;
     144    SmartUpdateContent(true);
     145    PaintBox1MouseMove(nil, [], m.lParam and $FFFF - Left,
     146      m.lParam shr 16 - Top);
    153147  end
    154148end;
    155149
    156 procedure TListDlg.FormPaint(Sender:TObject);
     150procedure TListDlg.OnMouseLeave(var Msg: TMessage);
     151begin
     152  if not Closable and (Sel <> -2) then
     153  begin
     154    line(Canvas, Sel, false, false);
     155    Sel := -2;
     156  end
     157end;
     158
     159procedure TListDlg.FormPaint(Sender: TObject);
    157160var
    158 s: string;
    159 begin
    160 inherited;
    161 Canvas.Font.Assign(UniFont[ftNormal]);
    162 if Sel<>-2 then line(Canvas,Sel,false,true);
    163 s:='';
    164 if (Kind=kAdvance) and (MyData.FarTech<>adNone) then
    165   s:=Format(Phrases.Lookup('TECHFOCUS'),
    166     [Phrases.Lookup('ADVANCES',MyData.FarTech)])
    167 else if Kind=kModels then s:=Tribe[me].TPhrase('SHORTNAME')
    168 else if Kind=kEModels then
    169   s:=Tribe[pView].TPhrase('SHORTNAME')
    170     +' ('+TurnToString(MyRO.EnemyReport[pView].TurnOfMilReport)+')';
    171 if s<>'' then
    172   LoweredTextOut(Canvas, -1, MainTexture,
    173     (ClientWidth-BiColorTextWidth(Canvas,s)) div 2, 31, s);
    174 if not MultiPage and (Kind in [kProject,kAdvance,kFarAdvance])
    175   and not Phrases2FallenBackToEnglish then
    176   begin
    177   s:=Phrases2.Lookup('SHIFTCLICK');
    178   LoweredTextOut(Canvas, -2, MainTexture,
    179     (ClientWidth-BiColorTextWidth(Canvas,s)) div 2, ClientHeight-29, s);
     161  s: string;
     162begin
     163  inherited;
     164  Canvas.Font.Assign(UniFont[ftNormal]);
     165  if Sel <> -2 then
     166    line(Canvas, Sel, false, true);
     167  s := '';
     168  if (Kind = kAdvance) and (MyData.FarTech <> adNone) then
     169    s := Format(Phrases.Lookup('TECHFOCUS'),
     170      [Phrases.Lookup('ADVANCES', MyData.FarTech)])
     171  else if Kind = kModels then
     172    s := Tribe[me].TPhrase('SHORTNAME')
     173  else if Kind = kEModels then
     174    s := Tribe[pView].TPhrase('SHORTNAME') + ' (' +
     175      TurnToString(MyRO.EnemyReport[pView].TurnOfMilReport) + ')';
     176  if s <> '' then
     177    LoweredTextOut(Canvas, -1, MainTexture,
     178      (ClientWidth - BiColorTextWidth(Canvas, s)) div 2, 31, s);
     179  if not MultiPage and (Kind in [kProject, kAdvance, kFarAdvance]) and not Phrases2FallenBackToEnglish
     180  then
     181  begin
     182    s := Phrases2.Lookup('SHIFTCLICK');
     183    LoweredTextOut(Canvas, -2, MainTexture,
     184      (ClientWidth - BiColorTextWidth(Canvas, s)) div 2, ClientHeight - 29, s);
    180185  end
    181186end;
     
    184189// paint a line
    185190
    186   procedure DisplayProject(x,y,pix: integer);
    187   begin
    188   if pix and (cpType or cpImp)=0 then
    189     with Tribe[me].ModelPicture[pix and cpIndex] do
    190       Sprite(offscreen,HGr,x,y,64,48,pix mod 10*65+1, pix div 10 *49+1)
    191   else
     191  procedure DisplayProject(x, y, pix: integer);
     192  begin
     193    if pix and (cpType or cpImp) = 0 then
     194      with Tribe[me].ModelPicture[pix and cpIndex] do
     195        Sprite(offscreen, HGr, x, y, 64, 48, pix mod 10 * 65 + 1,
     196          pix div 10 * 49 + 1)
     197    else
    192198    begin
    193     Frame(offscreen.Canvas,x+(16-1),y+(16-2),x+(16+xSizeSmall),
    194       y+(16-1+ySizeSmall),MainTexture.clBevelLight,MainTexture.clBevelShade);
    195     if pix and cpType=0 then
    196       if (pix and cpIndex=imPalace) and (MyRO.Government<>gAnarchy) then
    197         BitBlt(offscreen.Canvas.Handle,x+16,y+(16-1),xSizeSmall,ySizeSmall,
    198           SmallImp.Canvas.Handle,(MyRO.Government-1)*xSizeSmall,
    199           ySizeSmall,SRCCOPY)
    200       else BitBlt(offscreen.Canvas.Handle,x+16,y+(16-1),xSizeSmall,ySizeSmall,
    201         SmallImp.Canvas.Handle,pix and cpIndex mod 7*xSizeSmall,
    202         (pix and cpIndex+SystemIconLines*7) div 7*ySizeSmall,SRCCOPY)
    203     else BitBlt(offscreen.Canvas.Handle,x+16,y+(16-1),xSizeSmall,ySizeSmall,
    204       SmallImp.Canvas.Handle,(3+pix and cpIndex)*xSizeSmall, 0,SRCCOPY)
     199      Frame(offscreen.Canvas, x + (16 - 1), y + (16 - 2), x + (16 + xSizeSmall),
     200        y + (16 - 1 + ySizeSmall), MainTexture.clBevelLight,
     201        MainTexture.clBevelShade);
     202      if pix and cpType = 0 then
     203        if (pix and cpIndex = imPalace) and (MyRO.Government <> gAnarchy) then
     204          BitBlt(offscreen.Canvas.Handle, x + 16, y + (16 - 1), xSizeSmall,
     205            ySizeSmall, SmallImp.Canvas.Handle, (MyRO.Government - 1) *
     206            xSizeSmall, ySizeSmall, SRCCOPY)
     207        else
     208          BitBlt(offscreen.Canvas.Handle, x + 16, y + (16 - 1), xSizeSmall,
     209            ySizeSmall, SmallImp.Canvas.Handle, pix and cpIndex mod 7 *
     210            xSizeSmall, (pix and cpIndex + SystemIconLines * 7) div 7 *
     211            ySizeSmall, SRCCOPY)
     212      else
     213        BitBlt(offscreen.Canvas.Handle, x + 16, y + (16 - 1), xSizeSmall,
     214          ySizeSmall, SmallImp.Canvas.Handle, (3 + pix and cpIndex) *
     215          xSizeSmall, 0, SRCCOPY)
    205216    end;
    206217  end;
    207218
    208   procedure ReplaceText(x,y,Color: integer; s: string);
     219  procedure ReplaceText(x, y, Color: integer; s: string);
    209220  var
    210   TextSize: TSize;
    211   begin
    212   if ca=Canvas then
     221    TextSize: TSize;
     222  begin
     223    if ca = Canvas then
    213224    begin
    214     TextSize.cx:=BiColorTextWidth(ca,s);
    215     TextSize.cy:=ca.TextHeight(s);
    216     if y+TextSize.cy>=TitleHeight+InnerHeight then
    217       TextSize.cy:=TitleHeight+InnerHeight-y;
    218     Fill(ca,x,y,TextSize.cx,TextSize.cy,(wMaintexture-ClientWidth) div 2,
    219       (hMaintexture-ClientHeight) div 2);
     225      TextSize.cx := BiColorTextWidth(ca, s);
     226      TextSize.cy := ca.TextHeight(s);
     227      if y + TextSize.cy >= TitleHeight + InnerHeight then
     228        TextSize.cy := TitleHeight + InnerHeight - y;
     229      Fill(ca, x, y, TextSize.cx, TextSize.cy, (wMaintexture - ClientWidth)
     230        div 2, (hMaintexture - ClientHeight) div 2);
    220231    end;
    221   LoweredTextOut(ca,Color,MainTexture,x,y,s);
     232    LoweredTextOut(ca, Color, MainTexture, x, y, s);
    222233  end;
    223234
    224235var
    225 icon,ofs,x,y,y0,lix,i,j,TextColor,Available,first,test,FutureCount,
    226   growth,TrueFood,TrueProd:integer;
    227 CityReport: TCityReportNew;
    228 mox: ^TModelInfo;
    229 s,number: string;
    230 CanGrow: boolean;
    231 begin
    232 lix:=code[Layer,sb.si.npos+l];
    233 y0:=2+(l+1)*LineDistance;
    234 if sb.si.npos+l>=FirstShrinkedLine[Layer] then
    235   ofs:=(sb.si.npos+l-FirstShrinkedLine[Layer]) and 1 *33
    236 else {if FirstShrinkedLine[Layer]<Lines[Layer] then} ofs:=33;
    237 
    238 if Kind in [kCities,kCityEvents] then with MyCity[lix] do
    239   begin
    240   x:=104-76; y:=y0;
    241   if ca=Canvas then
    242     begin x:=x+SideFrame; y:=y+TitleHeight end;
    243   if lit then TextColor:=MainTexture.clLitText else TextColor:=-1;
    244   s:=CityName(ID);
    245   while BiColorTextWidth(ca,s)>CityNameSpace do
    246     delete(s,length(s),1);
    247   ReplaceText(x+15,y,TextColor,s);
    248 
    249   if NonText then with offscreen.canvas do
    250     begin // city size
    251     brush.color:=$000000;
    252     fillrect(rect(x-4-11,y+1,x-4+13,y+21));
    253     brush.color:=$FFFFFF;
    254     fillrect(rect(x-4-12,y,x-4+12,y+20));
    255     brush.style:=bsClear;
    256     font.color:=$000000;
    257     s:=inttostr(MyCity[lix].Size);
    258     TextOut(x-4-textwidth(s) div 2, y, s);
    259     end;
    260 
    261   if Kind=kCityEvents then
     236  icon, ofs, x, y, y0, lix, i, j, TextColor, Available, first, test,
     237    FutureCount, growth, TrueFood, TrueProd: integer;
     238  CityReport: TCityReportNew;
     239  mox: ^TModelInfo;
     240  s, number: string;
     241  CanGrow: boolean;
     242begin
     243  lix := code[Layer, sb.si.npos + l];
     244  y0 := 2 + (l + 1) * LineDistance;
     245  if sb.si.npos + l >= FirstShrinkedLine[Layer] then
     246    ofs := (sb.si.npos + l - FirstShrinkedLine[Layer]) and 1 * 33
     247  else { if FirstShrinkedLine[Layer]<Lines[Layer] then }
     248    ofs := 33;
     249
     250  if Kind in [kCities, kCityEvents] then
     251    with MyCity[lix] do
    262252    begin
    263     first:=-1;
    264     for j:=0 to nCityEventPriority-1 do
    265       if (Flags and CityRepMask and CityEventPriority[j]<>0) then
    266         begin first:=j; Break end;
    267     if first>=0 then
    268       begin
    269       i:=0;
    270       test:=1;
    271       while test<CityEventPriority[first] do
    272         begin inc(i); inc(test,test) end;
    273       s:=CityEventName(i);
    274 {      if CityEventPriority[first]=chNoGrowthWarning then
    275         if Built[imAqueduct]=0 then
    276           s:=Format(s,[Phrases.Lookup('IMPROVEMENTS',imAqueduct)])
    277         else begin s:=Format(s,[Phrases.Lookup('IMPROVEMENTS',imSewer)]); i:=17 end;}
    278       ReplaceText(x+(CityNameSpace+4+40+18+8),y,TextColor,s);
     253      x := 104 - 76;
     254      y := y0;
     255      if ca = Canvas then
     256      begin
     257        x := x + SideFrame;
     258        y := y + TitleHeight
     259      end;
     260      if lit then
     261        TextColor := MainTexture.clLitText
     262      else
     263        TextColor := -1;
     264      s := CityName(ID);
     265      while BiColorTextWidth(ca, s) > CityNameSpace do
     266        delete(s, length(s), 1);
     267      ReplaceText(x + 15, y, TextColor, s);
     268
    279269      if NonText then
    280         begin
    281         Sprite(offscreen,HGrSystem,105-76+CityNameSpace+4+40,y0+1,18,18,
    282           1+i mod 3 *19,1+i div 3 *19);
    283         x:=InnerWidth-26;
    284         for j:=nCityEventPriority-1 downto first+1 do
    285           if (Flags and CityRepMask and CityEventPriority[j]<>0) then
    286             begin
    287             i:=0;
    288             test:=1;
    289             while test<CityEventPriority[j] do
    290               begin inc(i); inc(test,test) end;
    291             if (CityEventPriority[j]=chNoGrowthWarning)
    292               and (Built[imAqueduct]>0) then
    293               i:=17;
    294             Sprite(offscreen,HGrSystem,x,y0+1,18,18,1+i mod 3 *19,
    295               1+i div 3 *19);
    296             dec(x,20)
    297             end
     270        with offscreen.Canvas do
     271        begin // city size
     272          brush.Color := $000000;
     273          fillrect(rect(x - 4 - 11, y + 1, x - 4 + 13, y + 21));
     274          brush.Color := $FFFFFF;
     275          fillrect(rect(x - 4 - 12, y, x - 4 + 12, y + 20));
     276          brush.style := bsClear;
     277          Font.Color := $000000;
     278          s := inttostr(MyCity[lix].Size);
     279          TextOut(x - 4 - textwidth(s) div 2, y, s);
     280        end;
     281
     282      if Kind = kCityEvents then
     283      begin
     284        first := -1;
     285        for j := 0 to nCityEventPriority - 1 do
     286          if (Flags and CityRepMask and CityEventPriority[j] <> 0) then
     287          begin
     288            first := j;
     289            Break
     290          end;
     291        if first >= 0 then
     292        begin
     293          i := 0;
     294          test := 1;
     295          while test < CityEventPriority[first] do
     296          begin
     297            inc(i);
     298            inc(test, test)
     299          end;
     300          s := CityEventName(i);
     301          { if CityEventPriority[first]=chNoGrowthWarning then
     302            if Built[imAqueduct]=0 then
     303            s:=Format(s,[Phrases.Lookup('IMPROVEMENTS',imAqueduct)])
     304            else begin s:=Format(s,[Phrases.Lookup('IMPROVEMENTS',imSewer)]); i:=17 end; }
     305          ReplaceText(x + (CityNameSpace + 4 + 40 + 18 + 8), y, TextColor, s);
     306          if NonText then
     307          begin
     308            Sprite(offscreen, HGrSystem, 105 - 76 + CityNameSpace + 4 + 40,
     309              y0 + 1, 18, 18, 1 + i mod 3 * 19, 1 + i div 3 * 19);
     310            x := InnerWidth - 26;
     311            for j := nCityEventPriority - 1 downto first + 1 do
     312              if (Flags and CityRepMask and CityEventPriority[j] <> 0) then
     313              begin
     314                i := 0;
     315                test := 1;
     316                while test < CityEventPriority[j] do
     317                begin
     318                  inc(i);
     319                  inc(test, test)
     320                end;
     321                if (CityEventPriority[j] = chNoGrowthWarning) and
     322                  (Built[imAqueduct] > 0) then
     323                  i := 17;
     324                Sprite(offscreen, HGrSystem, x, y0 + 1, 18, 18,
     325                  1 + i mod 3 * 19, 1 + i div 3 * 19);
     326                dec(x, 20)
     327              end
     328          end
    298329        end
    299330      end
     331      else
     332      begin
     333        CityReport.HypoTiles := -1;
     334        CityReport.HypoTaxRate := -1;
     335        CityReport.HypoLuxuryRate := -1;
     336        Server(sGetCityReportNew, me, lix, CityReport);
     337        TrueFood := Food;
     338        TrueProd := Prod;
     339        if supervising then
     340        begin // normalize city from after-turn state
     341          dec(TrueFood, CityReport.FoodSurplus);
     342          if TrueFood < 0 then
     343            TrueFood := 0; // shouldn't happen
     344          dec(TrueProd, CityReport.Production);
     345          if TrueProd < 0 then
     346            TrueProd := 0; // shouldn't happen
     347        end;
     348
     349        s := ''; // disorder info
     350        if Flags and chCaptured <> 0 then
     351          s := Phrases.Lookup('CITYEVENTS', 14)
     352        else if CityReport.HappinessBalance < 0 then
     353          s := Phrases.Lookup('CITYEVENTS', 0);
     354        if s <> '' then
     355        begin { disorder }
     356          if NonText then
     357          begin
     358            DarkGradient(offscreen.Canvas, 99 + 31 + CityNameSpace + 4,
     359              y0 + 2, 131, 3);
     360            ca.Font.Assign(UniFont[ftSmall]);
     361            RisedTextout(offscreen.Canvas, 103 + CityNameSpace + 4 + 31,
     362              y0 + 1, s);
     363            ca.Font.Assign(UniFont[ftNormal]);
     364          end
     365        end
     366        else
     367        begin
     368          { s:=IntToStr(CityReport.FoodSurplus);
     369            ReplaceText(x+(CityNameSpace+4+48)-BiColorTextWidth(ca,s),y,TextColor,s); }
     370          s := inttostr(CityReport.Science);
     371          ReplaceText(x + CityNameSpace + 4 + 370 + 48 - BiColorTextWidth(ca,
     372            s), y, TextColor, s);
     373          s := inttostr(CityReport.Production);
     374          ReplaceText(x + CityNameSpace + 4 + 132 - BiColorTextWidth(ca, s), y,
     375            TextColor, s);
     376          if NonText then
     377          begin
     378            // Sprite(offscreen,HGrSystem,x+CityNameSpace+4+333+1,y+6,10,10,66,115);
     379            Sprite(offscreen, HGrSystem, x + CityNameSpace + 4 + 370 + 48 + 1,
     380              y + 6, 10, 10, 77, 126);
     381            Sprite(offscreen, HGrSystem, x + CityNameSpace + 4 + 132 + 1, y + 6,
     382              10, 10, 88, 115);
     383          end
     384        end;
     385        s := inttostr(CityTaxBalance(lix, CityReport));
     386        ReplaceText(x + CityNameSpace + 4 + 370 - BiColorTextWidth(ca, s), y,
     387          TextColor, s);
     388        // if Project and (cpImp+cpIndex)<>cpImp+imTrGoods then
     389        // ReplaceText(x+CityNameSpace+4+333+1,y,TextColor,Format('%d/%d',[TrueProd,CityReport.ProjectCost]));
     390        if NonText then
     391        begin
     392          Sprite(offscreen, HGrSystem, x + CityNameSpace + 4 + 370 + 1, y + 6,
     393            10, 10, 132, 115);
     394
     395          // food progress
     396          CanGrow := (Size < MaxCitySize) and (MyRO.Government <> gFuture) and
     397            (CityReport.FoodSurplus > 0) and
     398            ((Size < NeedAqueductSize) or (Built[imAqueduct] = 1) and
     399            (Size < NeedSewerSize) or (Built[imSewer] = 1));
     400          PaintRelativeProgressBar(offscreen.Canvas, 1, x + 15 + CityNameSpace +
     401            4, y + 7, 68, TrueFood, CutCityFoodSurplus(CityReport.FoodSurplus,
     402            (MyRO.Government <> gAnarchy) and (Flags and chCaptured = 0),
     403            MyRO.Government, Size), CityReport.Storage, CanGrow, MainTexture);
     404
     405          if Project <> cpImp + imTrGoods then
     406          begin
     407            DisplayProject(ofs + 104 - 76 + x - 28 + CityNameSpace + 4 + 206 -
     408              60, y0 - 15, Project);
     409
     410            // production progress
     411            growth := CityReport.Production;
     412            if (growth < 0) or (MyRO.Government = gAnarchy) or
     413              (Flags and chCaptured <> 0) then
     414              growth := 0;
     415            PaintRelativeProgressBar(offscreen.Canvas, 4,
     416              x + CityNameSpace + 4 + 304 - 60 + 9, y + 7, 68, TrueProd, growth,
     417              CityReport.ProjectCost, true, MainTexture);
     418          end;
     419        end
     420      end;
    300421    end
     422  else if Kind in [kModels, kEModels] then
     423  begin
     424    x := 104;
     425    y := y0;
     426    if ca = Canvas then
     427    begin
     428      x := x + SideFrame;
     429      y := y + TitleHeight
     430    end;
     431    if lit then
     432      TextColor := MainTexture.clLitText
     433    else
     434      TextColor := -1;
     435    if Kind = kModels then
     436    begin
     437      Available := 0;
     438      for j := 0 to MyRO.nUn - 1 do
     439        if (MyUn[j].Loc >= 0) and (MyUn[j].mix = lix) then
     440          inc(Available);
     441      if MainScreen.mNames.Checked then
     442        s := Tribe[me].ModelName[lix]
     443      else
     444        s := Format(Tribe[me].TPhrase('GENMODEL'), [lix]);
     445      if NonText then
     446        DisplayProject(8 + ofs, y0 - 15, lix);
     447    end
     448    else
     449    begin
     450      Available := MyRO.EnemyReport[pView].UnCount[lix];
     451      if MainScreen.mNames.Checked then
     452        s := Tribe[pView].ModelName[lix]
     453      else
     454        s := Format(Tribe[pView].TPhrase('GENMODEL'), [lix]);
     455      if NonText then
     456        with Tribe[pView].ModelPicture[lix] do
     457          Sprite(offscreen, HGr, 8 + ofs, y0 - 15, 64, 48, pix mod 10 * 65 + 1,
     458            pix div 10 * 49 + 1);
     459    end;
     460    if Available > 0 then
     461      ReplaceText(x + 32 - BiColorTextWidth(ca, inttostr(Available)), y,
     462        TextColor, inttostr(Available));
     463    ReplaceText(x + 40, y, TextColor, s);
     464  end
    301465  else
    302     begin
    303     CityReport.HypoTiles:=-1;
    304     CityReport.HypoTaxRate:=-1;
    305     CityReport.HypoLuxuryRate:=-1;
    306     Server(sGetCityReportNew,me,lix,CityReport);
    307     TrueFood:=Food;
    308     TrueProd:=Prod;
    309     if supervising then
    310       begin // normalize city from after-turn state
    311       dec(TrueFood,CityReport.FoodSurplus);
    312       if TrueFood<0 then
    313         TrueFood:=0; // shouldn't happen
    314       dec(TrueProd,CityReport.Production);
    315       if TrueProd<0 then
    316         TrueProd:=0; // shouldn't happen
    317       end;
    318 
    319     s:=''; // disorder info
    320     if Flags and chCaptured<>0 then
    321       s:=Phrases.Lookup('CITYEVENTS',14)
    322     else if CityReport.HappinessBalance<0 then
    323       s:=Phrases.Lookup('CITYEVENTS',0);
    324     if s<>'' then
    325       begin {disorder}
    326       if NonText then
    327         begin
    328         DarkGradient(offscreen.Canvas,99+31+CityNameSpace+4,y0+2,131,3);
    329         ca.Font.Assign(UniFont[ftSmall]);
    330         RisedTextout(offscreen.canvas,103+CityNameSpace+4+31,y0+1,s);
    331         ca.Font.Assign(UniFont[ftNormal]);
    332         end
    333       end
    334     else
    335       begin
    336 {      s:=IntToStr(CityReport.FoodSurplus);
    337       ReplaceText(x+(CityNameSpace+4+48)-BiColorTextWidth(ca,s),y,TextColor,s);}
    338       s:=IntToStr(CityReport.Science);
    339       ReplaceText(x+CityNameSpace+4+370+48-BiColorTextWidth(ca,s),y,TextColor,s);
    340       s:=IntToStr(CityReport.Production);
    341       ReplaceText(x+CityNameSpace+4+132-BiColorTextWidth(ca,s),y,TextColor,s);
    342       if NonText then
    343         begin
    344         //Sprite(offscreen,HGrSystem,x+CityNameSpace+4+333+1,y+6,10,10,66,115);
    345         Sprite(offscreen,HGrSystem,x+CityNameSpace+4+370+48+1,y+6,10,10,77,126);
    346         Sprite(offscreen,HGrSystem,x+CityNameSpace+4+132+1,y+6,10,10,88,115);
    347         end
    348       end;
    349     s:=IntToStr(CityTaxBalance(lix, CityReport));
    350     ReplaceText(x+CityNameSpace+4+370-BiColorTextWidth(ca,s),y,TextColor,s);
    351     //if Project and (cpImp+cpIndex)<>cpImp+imTrGoods then
    352     //  ReplaceText(x+CityNameSpace+4+333+1,y,TextColor,Format('%d/%d',[TrueProd,CityReport.ProjectCost]));
    353     if NonText then
    354       begin
    355       Sprite(offscreen,HGrSystem,x+CityNameSpace+4+370+1,y+6,10,10,132,115);
    356 
    357       // food progress
    358       CanGrow:=(Size<MaxCitySize) and (MyRO.Government<>gFuture)
    359         and (CityReport.FoodSurplus>0)
    360         and ((Size<NeedAqueductSize)
    361           or (Built[imAqueduct]=1) and (Size<NeedSewerSize)
    362           or (Built[imSewer]=1));
    363       PaintRelativeProgressBar(offscreen.canvas,1,x+15+CityNameSpace+4,y+7,68,TrueFood,
    364         CutCityFoodSurplus(CityReport.FoodSurplus,
    365         (MyRO.Government<>gAnarchy) and (Flags and chCaptured=0),
    366         MyRO.Government,Size),CityReport.Storage,CanGrow,MainTexture);
    367 
    368       if Project<>cpImp+imTrGoods then
    369         begin
    370         DisplayProject(ofs+104-76+x-28+CityNameSpace+4+206-60,y0-15,Project);
    371 
    372         // production progress
    373         growth:=CityReport.Production;
    374         if (growth<0) or (MyRO.Government=gAnarchy)
    375           or (Flags and chCaptured<>0) then
    376           growth:=0;
    377         PaintRelativeProgressBar(offscreen.canvas,4,x+CityNameSpace+4+304-60+9,y+7,68,
    378           TrueProd,growth,CityReport.ProjectCost,true,MainTexture);
    379         end;
    380       end
    381     end;
    382   end
    383 else if Kind in [kModels,kEModels] then
    384   begin
    385   x:=104; y:=y0;
    386   if ca=Canvas then
    387     begin x:=x+SideFrame; y:=y+TitleHeight end;
    388   if lit then TextColor:=MainTexture.clLitText else TextColor:=-1;
    389   if Kind=kModels then
    390     begin
    391     Available:=0;
    392     for j:=0 to MyRO.nUn-1 do
    393       if (MyUn[j].Loc>=0) and (MyUn[j].mix=lix) then inc(Available);
    394     if MainScreen.mNames.Checked then
    395       s:=Tribe[me].ModelName[lix]
    396     else s:=Format(Tribe[me].TPhrase('GENMODEL'),[lix]);
    397     if NonText then DisplayProject(8+ofs,y0-15,lix);
    398     end
    399   else
    400     begin
    401     Available:=MyRO.EnemyReport[pView].UnCount[lix];
    402     if MainScreen.mNames.Checked then
    403       s:=Tribe[pView].ModelName[lix]
    404     else s:=Format(Tribe[pView].TPhrase('GENMODEL'),[lix]);
    405     if NonText then
    406       with Tribe[pView].ModelPicture[lix] do
    407         Sprite(offscreen,HGr,8+ofs,y0-15,64,48,pix mod 10*65+1, pix div 10 *49+1);
    408     end;
    409   if Available>0 then
    410     ReplaceText(x+32-BiColorTextWidth(ca,IntToStr(Available)),y,TextColor,
    411       IntToStr(Available));
    412   ReplaceText(x+40,y,TextColor,s);
    413   end
    414 else
    415   begin
    416   case Kind of
    417     kAllEModels, kChooseEModel:
    418       if lix=mixAll then s:=Phrases.Lookup('PRICECAT_ALLMODEL')
    419       else
    420         begin
    421         mox:=@MyRO.EnemyModel[lix];
    422         if MainScreen.mNames.Checked then
    423           begin
    424           s:=Tribe[mox.Owner].ModelName[mox.mix];
    425           if (Kind=kAllEModels) and (code[1,sb.si.npos+l]=0) then
    426             s:=Format(Tribe[mox.Owner].TPhrase('OWNED'), [s]);
     466  begin
     467    case Kind of
     468      kAllEModels, kChooseEModel:
     469        if lix = mixAll then
     470          s := Phrases.Lookup('PRICECAT_ALLMODEL')
     471        else
     472        begin
     473          mox := @MyRO.EnemyModel[lix];
     474          if MainScreen.mNames.Checked then
     475          begin
     476            s := Tribe[mox.Owner].ModelName[mox.mix];
     477            if (Kind = kAllEModels) and (code[1, sb.si.npos + l] = 0) then
     478              s := Format(Tribe[mox.Owner].TPhrase('OWNED'), [s]);
    427479          end
    428         else s:=Format(Tribe[mox.Owner].TPhrase('GENMODEL'),[mox.mix]);
    429         if NonText then
    430           with Tribe[mox.Owner].ModelPicture[mox.mix] do
    431             Sprite(offscreen,HGr,8+ofs,y0-15,64,48,pix mod 10*65+1, pix div 10 *49+1);
    432         end;
    433     kChooseModel:
    434       if lix=mixAll then s:=Phrases.Lookup('PRICECAT_ALLMODEL')
    435       else
    436         begin
    437         s:=Tribe[me].ModelName[lix];
    438         if NonText then DisplayProject(8+ofs,y0-15,lix);
    439         end;
    440     kProject:
    441       begin
    442       if lix and cpType<>0 then s:=Phrases.Lookup('CITYTYPE',lix and cpIndex)
    443       else if lix and cpImp=0 then with MyModel[lix and cpIndex] do
    444         begin
    445         s:=Tribe[me].ModelName[lix and cpIndex];
    446         if lix and cpConscripts<>0 then
    447           s:=Format(Phrases.Lookup('CONSCRIPTS'),[s]);
    448         end
    449       else
    450         begin
    451         s:=Phrases.Lookup('IMPROVEMENTS',lix and cpIndex);
    452         if (Imp[lix and cpIndex].Kind in [ikNatLocal,ikNatGlobal])
    453             and (MyRO.NatBuilt[lix and cpIndex]>0)
    454           or (lix and cpIndex in [imPower,imHydro,imNuclear])
    455             and (MyCity[cixProject].Built[imPower]
    456               +MyCity[cixProject].Built[imHydro]
    457               +MyCity[cixProject].Built[imNuclear]>0) then
    458           s:=Format(Phrases.Lookup('NATEXISTS'),[s]);
    459         end;
    460       if NonText then DisplayProject(8+ofs,y0-15,lix);
    461       end;
    462     kAdvance, kFarAdvance, kScience, kChooseTech, kChooseETech, kStealTech:
    463       begin
    464       if lix=adAll then s:=Phrases.Lookup('PRICECAT_ALLTECH')
    465       else
    466         begin
    467         if lix=adNexus then s:=Phrases.Lookup('NEXUS')
    468         else if lix=adNone then s:=Phrases.Lookup('NOFARTECH')
    469         else if lix=adMilitary then s:=Phrases.Lookup('INITUNIT')
     480          else
     481            s := Format(Tribe[mox.Owner].TPhrase('GENMODEL'), [mox.mix]);
     482          if NonText then
     483            with Tribe[mox.Owner].ModelPicture[mox.mix] do
     484              Sprite(offscreen, HGr, 8 + ofs, y0 - 15, 64, 48,
     485                pix mod 10 * 65 + 1, pix div 10 * 49 + 1);
     486        end;
     487      kChooseModel:
     488        if lix = mixAll then
     489          s := Phrases.Lookup('PRICECAT_ALLMODEL')
    470490        else
    471           begin
    472           s:=Phrases.Lookup('ADVANCES',lix);
    473           if (Kind=kAdvance) and (lix in FutureTech) then
    474             if MyRO.Tech[lix]<tsApplicable then s:=s+' 1'
    475             else s:=s+' '+IntToStr(MyRO.Tech[lix]+1);
    476           end;
    477         if BiColorTextWidth(ca,s)>TechNameSpace+8 then
    478           begin
    479           repeat
    480             delete(s,length(s),1);
    481           until BiColorTextWidth(ca,s)<=TechNameSpace+5;
    482           s:=s+'.';
    483           end;
    484 
    485         if NonText then
    486           begin // show tech icon
    487           if lix=adNexus then
     491        begin
     492          s := Tribe[me].ModelName[lix];
     493          if NonText then
     494            DisplayProject(8 + ofs, y0 - 15, lix);
     495        end;
     496      kProject:
     497        begin
     498          if lix and cpType <> 0 then
     499            s := Phrases.Lookup('CITYTYPE', lix and cpIndex)
     500          else if lix and cpImp = 0 then
     501            with MyModel[lix and cpIndex] do
    488502            begin
    489             Frame(offscreen.Canvas,(8+16-1),y0-1,(8+16+36),
    490               y0+20,MainTexture.clBevelLight,MainTexture.clBevelShade);
    491             Dump(offscreen,HGrSystem,(8+16),y0,36,20,223,295)
    492             end
    493           else if lix=adNone then
    494             begin
    495             Frame(offscreen.Canvas,(8+16-1),y0-1,(8+16+36),
    496               y0+20,MainTexture.clBevelLight,MainTexture.clBevelShade);
    497             Dump(offscreen,HGrSystem,(8+16),y0,36,20,260,295)
    498             end
    499           else if lix=adMilitary then
    500             begin
    501             Frame(offscreen.Canvas,(8+16-1),y0-1,(8+16+36),
    502               y0+20,MainTexture.clBevelLight,MainTexture.clBevelShade);
    503             Dump(offscreen,HGrSystem,(8+16),y0,36,20,38,295)
     503              s := Tribe[me].ModelName[lix and cpIndex];
     504              if lix and cpConscripts <> 0 then
     505                s := Format(Phrases.Lookup('CONSCRIPTS'), [s]);
    504506            end
    505507          else
     508          begin
     509            s := Phrases.Lookup('IMPROVEMENTS', lix and cpIndex);
     510            if (Imp[lix and cpIndex].Kind in [ikNatLocal, ikNatGlobal]) and
     511              (MyRO.NatBuilt[lix and cpIndex] > 0) or
     512              (lix and cpIndex in [imPower, imHydro, imNuclear]) and
     513              (MyCity[cixProject].Built[imPower] + MyCity[cixProject].Built
     514              [imHydro] + MyCity[cixProject].Built[imNuclear] > 0) then
     515              s := Format(Phrases.Lookup('NATEXISTS'), [s]);
     516          end;
     517          if NonText then
     518            DisplayProject(8 + ofs, y0 - 15, lix);
     519        end;
     520      kAdvance, kFarAdvance, kScience, kChooseTech, kChooseETech, kStealTech:
     521        begin
     522          if lix = adAll then
     523            s := Phrases.Lookup('PRICECAT_ALLTECH')
     524          else
     525          begin
     526            if lix = adNexus then
     527              s := Phrases.Lookup('NEXUS')
     528            else if lix = adNone then
     529              s := Phrases.Lookup('NOFARTECH')
     530            else if lix = adMilitary then
     531              s := Phrases.Lookup('INITUNIT')
     532            else
    506533            begin
    507             Frame(offscreen.Canvas,(8+16-1),y0-1,(8+16+xSizeSmall),
    508               y0+ySizeSmall,MainTexture.clBevelLight,MainTexture.clBevelShade);
    509             if AdvIcon[lix]<84 then
    510               BitBlt(offscreen.Canvas.Handle,(8+16),y0,xSizeSmall,ySizeSmall,
    511                 SmallImp.Canvas.Handle,(AdvIcon[lix]+SystemIconLines*7) mod 7*xSizeSmall,
    512                 (AdvIcon[lix]+SystemIconLines*7) div 7*ySizeSmall,SRCCOPY)
    513             else Dump(offscreen,HGrSystem,(8+16),y0,36,20,
    514               1+(AdvIcon[lix]-84) mod 8*37,295+(AdvIcon[lix]-84) div 8*21);
    515             j:=AdvValue[lix] div 1000;
    516             BitBlt(Handle,(8+16-4),y0+2,14,14,
    517               GrExt[HGrSystem].Mask.Canvas.Handle,127+j*15,85,SRCAND);
    518             Sprite(offscreen,HGrSystem,(8+16-5),y0+1,14,14,
    519               127+j*15,85);
     534              s := Phrases.Lookup('ADVANCES', lix);
     535              if (Kind = kAdvance) and (lix in FutureTech) then
     536                if MyRO.Tech[lix] < tsApplicable then
     537                  s := s + ' 1'
     538                else
     539                  s := s + ' ' + inttostr(MyRO.Tech[lix] + 1);
    520540            end;
    521           end;
    522         end;
    523 
    524       if NonText and (Kind in [kAdvance, kScience]) then
    525         begin // show research state
    526         for j:=0 to nColumn-1 do
    527           begin
    528           FutureCount:=0;
    529           if j=0 then // own science
    530             if lix=MyRO.ResearchTech then
     541            if BiColorTextWidth(ca, s) > TechNameSpace + 8 then
     542            begin
     543              repeat
     544                delete(s, length(s), 1);
     545              until BiColorTextWidth(ca, s) <= TechNameSpace + 5;
     546              s := s + '.';
     547            end;
     548
     549            if NonText then
     550            begin // show tech icon
     551              if lix = adNexus then
    531552              begin
    532               Server(sGetTechCost,me,0,icon);
    533               icon:=4+MyRO.Research*4 div icon;
    534               if icon>4+3 then icon:=4+3
     553                Frame(offscreen.Canvas, (8 + 16 - 1), y0 - 1, (8 + 16 + 36),
     554                  y0 + 20, MainTexture.clBevelLight, MainTexture.clBevelShade);
     555                Dump(offscreen, HGrSystem, (8 + 16), y0, 36, 20, 223, 295)
    535556              end
    536             else if (lix>=adMilitary) then
    537               icon:=-1
    538             else if lix in FutureTech then
     557              else if lix = adNone then
    539558              begin
    540               icon:=-1;
    541               FutureCount:=MyRO.Tech[lix];
     559                Frame(offscreen.Canvas, (8 + 16 - 1), y0 - 1, (8 + 16 + 36),
     560                  y0 + 20, MainTexture.clBevelLight, MainTexture.clBevelShade);
     561                Dump(offscreen, HGrSystem, (8 + 16), y0, 36, 20, 260, 295)
    542562              end
    543             else if MyRO.Tech[lix]=tsSeen then icon:=1
    544             else if MyRO.Tech[lix]>=tsApplicable then icon:=2
    545             else icon:=-1
    546           else with MyRO.EnemyReport[Column[j]]^ do // enemy science
    547             if (MyRO.Alive and (1 shl Column[j])<>0)
    548               and (TurnOfCivilReport>=0) and (lix=ResearchTech)
    549               and ((lix=adMilitary) or (lix in FutureTech)
    550                 or (Tech[lix]<tsApplicable)) then
     563              else if lix = adMilitary then
    551564              begin
    552               icon:=4+ResearchDone div 25;
    553               if icon>4+3 then icon:=4+3
     565                Frame(offscreen.Canvas, (8 + 16 - 1), y0 - 1, (8 + 16 + 36),
     566                  y0 + 20, MainTexture.clBevelLight, MainTexture.clBevelShade);
     567                Dump(offscreen, HGrSystem, (8 + 16), y0, 36, 20, 38, 295)
    554568              end
    555             else if lix=adMilitary then
    556               icon:=-1
    557             else if lix in FutureTech then
     569              else
    558570              begin
    559               icon:=-1;
    560               FutureCount:=Tech[lix]
     571                Frame(offscreen.Canvas, (8 + 16 - 1), y0 - 1,
     572                  (8 + 16 + xSizeSmall), y0 + ySizeSmall,
     573                  MainTexture.clBevelLight, MainTexture.clBevelShade);
     574                if AdvIcon[lix] < 84 then
     575                  BitBlt(offscreen.Canvas.Handle, (8 + 16), y0, xSizeSmall,
     576                    ySizeSmall, SmallImp.Canvas.Handle,
     577                    (AdvIcon[lix] + SystemIconLines * 7) mod 7 * xSizeSmall,
     578                    (AdvIcon[lix] + SystemIconLines * 7) div 7 *
     579                    ySizeSmall, SRCCOPY)
     580                else
     581                  Dump(offscreen, HGrSystem, (8 + 16), y0, 36, 20,
     582                    1 + (AdvIcon[lix] - 84) mod 8 * 37,
     583                    295 + (AdvIcon[lix] - 84) div 8 * 21);
     584                j := AdvValue[lix] div 1000;
     585                BitBlt(Handle, (8 + 16 - 4), y0 + 2, 14, 14,
     586                  GrExt[HGrSystem].Mask.Canvas.Handle, 127 + j * 15,
     587                  85, SRCAND);
     588                Sprite(offscreen, HGrSystem, (8 + 16 - 5), y0 + 1, 14, 14,
     589                  127 + j * 15, 85);
     590              end;
     591            end;
     592          end;
     593
     594          if NonText and (Kind in [kAdvance, kScience]) then
     595          begin // show research state
     596            for j := 0 to nColumn - 1 do
     597            begin
     598              FutureCount := 0;
     599              if j = 0 then // own science
     600                if lix = MyRO.ResearchTech then
     601                begin
     602                  Server(sGetTechCost, me, 0, icon);
     603                  icon := 4 + MyRO.Research * 4 div icon;
     604                  if icon > 4 + 3 then
     605                    icon := 4 + 3
     606                end
     607                else if (lix >= adMilitary) then
     608                  icon := -1
     609                else if lix in FutureTech then
     610                begin
     611                  icon := -1;
     612                  FutureCount := MyRO.Tech[lix];
     613                end
     614                else if MyRO.Tech[lix] = tsSeen then
     615                  icon := 1
     616                else if MyRO.Tech[lix] >= tsApplicable then
     617                  icon := 2
     618                else
     619                  icon := -1
     620              else
     621                with MyRO.EnemyReport[Column[j]]^ do // enemy science
     622                  if (MyRO.Alive and (1 shl Column[j]) <> 0) and
     623                    (TurnOfCivilReport >= 0) and (lix = ResearchTech) and
     624                    ((lix = adMilitary) or (lix in FutureTech) or
     625                    (Tech[lix] < tsApplicable)) then
     626                  begin
     627                    icon := 4 + ResearchDone div 25;
     628                    if icon > 4 + 3 then
     629                      icon := 4 + 3
     630                  end
     631                  else if lix = adMilitary then
     632                    icon := -1
     633                  else if lix in FutureTech then
     634                  begin
     635                    icon := -1;
     636                    FutureCount := Tech[lix]
     637                  end
     638                  else if Tech[lix] >= tsApplicable then
     639                    icon := 2
     640                  else if Tech[lix] = tsSeen then
     641                    icon := 1
     642                  else
     643                    icon := -1;
     644              if icon >= 0 then
     645                Sprite(offscreen, HGrSystem, 104 - 33 + 15 + 3 + TechNameSpace +
     646                  24 * j, y0 + 3, 14, 14, 67 + icon * 15, 85)
     647              else if (Kind = kScience) and (FutureCount > 0) then
     648              begin
     649                number := inttostr(FutureCount);
     650                RisedTextout(ca, 104 - 33 + 15 + 10 + TechNameSpace + 24 * j -
     651                  BiColorTextWidth(ca, number) div 2, y0, number);
    561652              end
    562             else if Tech[lix]>=tsApplicable then
    563               icon:=2
    564             else if Tech[lix]=tsSeen then
    565               icon:=1
    566             else icon:=-1;
    567           if icon>=0 then
    568             Sprite(offscreen,HGrSystem,104-33+15+3+TechNameSpace+24*j,y0+3,
    569               14,14,67+icon*15,85)
    570           else if (Kind=kScience) and (FutureCount>0) then
    571             begin
    572             number:=inttostr(FutureCount);
    573             RisedTextOut(ca,104-33+15+10+TechNameSpace+24*j
    574               -BiColorTextWidth(ca,number) div 2,y0,number);
    575653            end
     654          end;
     655        end; // kAdvance, kScience
     656      kTribe:
     657        s := TribeNames[lix];
     658      kShipPart:
     659        begin
     660          s := Phrases.Lookup('IMPROVEMENTS', imShipComp + lix) + ' (' +
     661            inttostr(MyRO.Ship[me].Parts[lix]) + ')';
     662          if NonText then
     663            DisplayProject(8 + ofs, y0 - 15, cpImp + imShipComp + lix);
     664        end;
     665      kEShipPart:
     666        begin
     667          s := Phrases.Lookup('IMPROVEMENTS', imShipComp + lix) + ' (' +
     668            inttostr(MyRO.Ship[DipMem[me].pContact].Parts[lix]) + ')';
     669          if NonText then
     670            DisplayProject(8 + ofs, y0 - 15, cpImp + imShipComp + lix);
     671        end;
     672      kGov:
     673        begin
     674          s := Phrases.Lookup('GOVERNMENT', lix);
     675          if NonText then
     676          begin
     677            Frame(offscreen.Canvas, 8 + 16 - 1, y0 - 15 + (16 - 2),
     678              8 + 16 + xSizeSmall, y0 - 15 + (16 - 1 + ySizeSmall),
     679              MainTexture.clBevelLight, MainTexture.clBevelShade);
     680            BitBlt(offscreen.Canvas.Handle, 8 + 16, y0 - 15 + (16 - 1),
     681              xSizeSmall, ySizeSmall, SmallImp.Canvas.Handle,
     682              (lix - 1) * xSizeSmall, ySizeSmall, SRCCOPY);
    576683          end
    577684        end;
    578       end; // kAdvance, kScience
    579     kTribe:
    580       s:=TribeNames[lix];
    581     kShipPart:
    582       begin
    583       s:=Phrases.Lookup('IMPROVEMENTS',imShipComp+lix)
    584         +' ('+inttostr(MyRO.Ship[me].Parts[lix])+')';
    585       if NonText then DisplayProject(8+ofs,y0-15,cpImp+imShipComp+lix);
    586       end;
    587     kEShipPart:
    588       begin
    589       s:=Phrases.Lookup('IMPROVEMENTS',imShipComp+lix)
    590         +' ('+inttostr(MyRO.Ship[DipMem[me].pContact].Parts[lix])+')';
    591       if NonText then DisplayProject(8+ofs,y0-15,cpImp+imShipComp+lix);
    592       end;
    593     kGov:
    594       begin
    595       s:=Phrases.Lookup('GOVERNMENT',lix);
    596       if NonText then
    597         begin
    598         Frame(offscreen.Canvas,8+16-1,y0-15+(16-2),8+16+xSizeSmall,
    599           y0-15+(16-1+ySizeSmall),MainTexture.clBevelLight,MainTexture.clBevelShade);
    600         BitBlt(offscreen.Canvas.Handle,8+16,y0-15+(16-1),xSizeSmall,ySizeSmall,
    601           SmallImp.Canvas.Handle,(lix-1)*xSizeSmall,ySizeSmall,SRCCOPY);
    602         end
    603       end;
    604     kMission:
    605       s:=Phrases.Lookup('SPYMISSION',lix);
     685      kMission:
     686        s := Phrases.Lookup('SPYMISSION', lix);
    606687    end;
    607   case Kind of
    608     kTribe,kMission: // center text
    609       if Lines[0]>MaxLines then
    610         x:=(InnerWidth-GetSystemMetrics(SM_CXVSCROLL)) div 2-BiColorTextWidth(ca,s) div 2
    611       else x:=InnerWidth div 2-BiColorTextWidth(ca,s) div 2;
    612     kAdvance, kFarAdvance, kScience, kChooseTech, kChooseETech, kStealTech, kGov:
    613       x:=104-33;
    614     kAllEModels: x:=104;
    615     else x:=104+15;
     688    case Kind of
     689      kTribe, kMission: // center text
     690        if Lines[0] > MaxLines then
     691          x := (InnerWidth - GetSystemMetrics(SM_CXVSCROLL)) div 2 -
     692            BiColorTextWidth(ca, s) div 2
     693        else
     694          x := InnerWidth div 2 - BiColorTextWidth(ca, s) div 2;
     695      kAdvance, kFarAdvance, kScience, kChooseTech, kChooseETech,
     696        kStealTech, kGov:
     697        x := 104 - 33;
     698      kAllEModels:
     699        x := 104;
     700    else
     701      x := 104 + 15;
    616702    end;
    617   y:=y0;
    618   if ca=Canvas then
    619     begin x:=x+SideFrame; y:=y+TitleHeight end;
    620   if lit then TextColor:=MainTexture.clLitText
    621   else TextColor:=-1;
    622 {  if Kind=kTribe then ReplaceText_Tribe(x,y,TextColor,
    623     integer(TribeNames.Objects[lix]),s)
    624   else} ReplaceText(x,y,TextColor,s);
     703    y := y0;
     704    if ca = Canvas then
     705    begin
     706      x := x + SideFrame;
     707      y := y + TitleHeight
     708    end;
     709    if lit then
     710      TextColor := MainTexture.clLitText
     711    else
     712      TextColor := -1;
     713    { if Kind=kTribe then ReplaceText_Tribe(x,y,TextColor,
     714      integer(TribeNames.Objects[lix]),s)
     715      else } ReplaceText(x, y, TextColor, s);
    625716  end
    626717end;
     
    628719procedure TListDlg.OffscreenPaint;
    629720var
    630 i,j: integer;
    631 begin
    632 case Kind of
    633   kCities: Caption:=Tribe[me].TPhrase('TITLE_CITIES');
    634   kCityEvents: Caption:=Format(Phrases.Lookup('TITLE_EVENTS'),[TurnToString(MyRO.Turn)]);
    635   end;
    636 
    637 inherited;
    638 offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    639 FillOffscreen(0,0,InnerWidth,InnerHeight);
    640 with offscreen.Canvas do
    641   begin
    642   if Kind=kScience then
    643     for i:=1 to nColumn-1 do
    644       begin
    645       Pen.Color:=$000000;
    646       MoveTo(104-33+15+TechNameSpace+24*i,0);
    647       LineTo(104-33+15+TechNameSpace+24*i,InnerHeight);
    648       MoveTo(104-33+15+TechNameSpace+9*2+24*i,0);
    649       LineTo(104-33+15+TechNameSpace+9*2+24*i,InnerHeight);
    650       if MyRO.EnemyReport[Column[i]].TurnOfCivilReport>=MyRO.Turn-1 then
    651         begin
    652         brush.color:=Tribe[Column[i]].Color;
    653         FillRect(Rect(104-33+14+TechNameSpace+24*i+1*2,0,
    654           104-33+17+TechNameSpace+24*i+8*2,InnerHeight));
    655         brush.style:=bsClear;
     721  i, j: integer;
     722begin
     723  case Kind of
     724    kCities:
     725      Caption := Tribe[me].TPhrase('TITLE_CITIES');
     726    kCityEvents:
     727      Caption := Format(Phrases.Lookup('TITLE_EVENTS'),
     728        [TurnToString(MyRO.Turn)]);
     729  end;
     730
     731  inherited;
     732  offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
     733  FillOffscreen(0, 0, InnerWidth, InnerHeight);
     734  with offscreen.Canvas do
     735  begin
     736    if Kind = kScience then
     737      for i := 1 to nColumn - 1 do
     738      begin
     739        Pen.Color := $000000;
     740        MoveTo(104 - 33 + 15 + TechNameSpace + 24 * i, 0);
     741        LineTo(104 - 33 + 15 + TechNameSpace + 24 * i, InnerHeight);
     742        MoveTo(104 - 33 + 15 + TechNameSpace + 9 * 2 + 24 * i, 0);
     743        LineTo(104 - 33 + 15 + TechNameSpace + 9 * 2 + 24 * i, InnerHeight);
     744        if MyRO.EnemyReport[Column[i]].TurnOfCivilReport >= MyRO.Turn - 1 then
     745        begin
     746          brush.Color := Tribe[Column[i]].Color;
     747          fillrect(rect(104 - 33 + 14 + TechNameSpace + 24 * i + 1 * 2, 0,
     748            104 - 33 + 17 + TechNameSpace + 24 * i + 8 * 2, InnerHeight));
     749          brush.style := bsClear;
    656750        end
    657       else
     751        else
    658752        begin // colored player columns
    659         Pen.Color:=Tribe[Column[i]].Color;
    660         for j:=1 to 8 do
    661           begin
    662           MoveTo(104-33+15+TechNameSpace+24*i+j*2,0);
    663           LineTo(104-33+15+TechNameSpace+24*i+j*2,InnerHeight);
     753          Pen.Color := Tribe[Column[i]].Color;
     754          for j := 1 to 8 do
     755          begin
     756            MoveTo(104 - 33 + 15 + TechNameSpace + 24 * i + j * 2, 0);
     757            LineTo(104 - 33 + 15 + TechNameSpace + 24 * i + j * 2, InnerHeight);
    664758          end
    665759        end;
    666760      end;
    667   for i:=-1 to DispLines do if (i+sb.si.npos>=0) and (i+sb.si.npos<Lines[Layer]) then
    668     line(offscreen.Canvas,i,true,false)
    669   end;
    670 MarkUsedOffscreen(InnerWidth,8+48+DispLines*LineDistance);
    671 end;
    672 
    673 procedure TListDlg.PaintBox1MouseMove(Sender:TObject;
    674   Shift:TShiftState;x,y:integer);
     761    for i := -1 to DispLines do
     762      if (i + sb.si.npos >= 0) and (i + sb.si.npos < Lines[Layer]) then
     763        line(offscreen.Canvas, i, true, false)
     764  end;
     765  MarkUsedOffscreen(InnerWidth, 8 + 48 + DispLines * LineDistance);
     766end;
     767
     768procedure TListDlg.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
     769  x, y: integer);
    675770var
    676 i0,Sel0,iColumn,OldScienceNation,xScreen: integer;
    677 s: string;
    678 begin
    679 y:=y-TitleHeight;
    680 i0:=sb.si.npos;
    681 Sel0:=Sel;
    682 if (x>=SideFrame) and (x<SideFrame+InnerWidth) and (y>=0) and (y<InnerHeight)
    683   and (y mod LineDistance>=4) and (y mod LineDistance<20) then
    684   Sel:=y div LineDistance-1
    685 else Sel:=-2;
    686 if (Sel<-1) or (Sel>DispLines) or (Sel+i0<0) or (Sel+i0>=Lines[Layer]) then
    687   Sel:=-2;
    688 if Sel<>Sel0 then
    689   begin
    690   if Sel0<>-2 then line(Canvas,Sel0,false,false);
    691   if Sel<>-2 then line(Canvas,Sel,false,true)
    692   end;
    693 
    694 if Kind=kScience then
     771  i0, Sel0, iColumn, OldScienceNation, xScreen: integer;
     772  s: string;
     773begin
     774  y := y - TitleHeight;
     775  i0 := sb.si.npos;
     776  Sel0 := Sel;
     777  if (x >= SideFrame) and (x < SideFrame + InnerWidth) and (y >= 0) and
     778    (y < InnerHeight) and (y mod LineDistance >= 4) and (y mod LineDistance < 20)
     779  then
     780    Sel := y div LineDistance - 1
     781  else
     782    Sel := -2;
     783  if (Sel < -1) or (Sel > DispLines) or (Sel + i0 < 0) or
     784    (Sel + i0 >= Lines[Layer]) then
     785    Sel := -2;
     786  if Sel <> Sel0 then
     787  begin
     788    if Sel0 <> -2 then
     789      line(Canvas, Sel0, false, false);
     790    if Sel <> -2 then
     791      line(Canvas, Sel, false, true)
     792  end;
     793
     794  if Kind = kScience then
    695795  begin // show nation under cursor position
    696   OldScienceNation:=ScienceNation;
    697   ScienceNation:=-1;
    698   if (x>=SideFrame+(104-33+15+TechNameSpace)) and ((x-SideFrame-(104-33+15+TechNameSpace)) mod 24<=18)
    699     and (y>=0) and (y<InnerHeight) then
     796    OldScienceNation := ScienceNation;
     797    ScienceNation := -1;
     798    if (x >= SideFrame + (104 - 33 + 15 + TechNameSpace)) and
     799      ((x - SideFrame - (104 - 33 + 15 + TechNameSpace)) mod 24 <= 18) and
     800      (y >= 0) and (y < InnerHeight) then
    700801    begin
    701     iColumn:=(x-SideFrame-(104-33+15+TechNameSpace)) div 24;
    702     if (iColumn>=1) and (iColumn<nColumn) then
    703       ScienceNation:=Column[iColumn];
     802      iColumn := (x - SideFrame - (104 - 33 + 15 + TechNameSpace)) div 24;
     803      if (iColumn >= 1) and (iColumn < nColumn) then
     804        ScienceNation := Column[iColumn];
    704805    end;
    705   if ScienceNation<>OldScienceNation then
     806    if ScienceNation <> OldScienceNation then
    706807    begin
    707     Fill(Canvas,9,ClientHeight-29,ClientWidth-18,24,
    708       (wMaintexture-ClientWidth) div 2,(hMaintexture-ClientHeight) div 2);
    709     if ScienceNation>=0 then
    710       begin
    711       s:=Tribe[ScienceNation].TPhrase('SHORTNAME');
    712       if MyRO.Alive and (1 shl ScienceNation)=0 then
    713         s:=Format(Phrases.Lookup('SCIENCEREPORT_EXTINCT'),[s]) // extinct
    714       else if MyRO.EnemyReport[ScienceNation].TurnOfCivilReport<MyRO.Turn-1 then
    715         s:=s+' ('+TurnToString(MyRO.EnemyReport[ScienceNation].TurnOfCivilReport)+')'; // old report
    716       xScreen:=(ClientWidth-BiColorTextWidth(Canvas,s)) div 2;
    717       LoweredTextOut(Canvas, -1, MainTexture, xScreen+10, ClientHeight-29, s);
    718       BitBlt(ScienceNationDot.Canvas.Handle,0,0,17,17,Canvas.Handle,xScreen-10,
    719         ClientHeight-27,SRCCOPY);
    720       ImageOp_BCC(ScienceNationDot,Templates,0,0,114,211,17,17,
    721         MainTexture.clBevelShade,Tribe[ScienceNation].Color);
    722       BitBlt(Canvas.Handle,xScreen-10,ClientHeight-27,17,17,
    723         ScienceNationDot.Canvas.Handle,0,0,SRCCOPY);
     808      Fill(Canvas, 9, ClientHeight - 29, ClientWidth - 18, 24,
     809        (wMaintexture - ClientWidth) div 2,
     810        (hMaintexture - ClientHeight) div 2);
     811      if ScienceNation >= 0 then
     812      begin
     813        s := Tribe[ScienceNation].TPhrase('SHORTNAME');
     814        if MyRO.Alive and (1 shl ScienceNation) = 0 then
     815          s := Format(Phrases.Lookup('SCIENCEREPORT_EXTINCT'), [s]) // extinct
     816        else if MyRO.EnemyReport[ScienceNation].TurnOfCivilReport < MyRO.Turn - 1
     817        then
     818          s := s + ' (' + TurnToString(MyRO.EnemyReport[ScienceNation]
     819            .TurnOfCivilReport) + ')'; // old report
     820        xScreen := (ClientWidth - BiColorTextWidth(Canvas, s)) div 2;
     821        LoweredTextOut(Canvas, -1, MainTexture, xScreen + 10,
     822          ClientHeight - 29, s);
     823        BitBlt(ScienceNationDot.Canvas.Handle, 0, 0, 17, 17, Canvas.Handle,
     824          xScreen - 10, ClientHeight - 27, SRCCOPY);
     825        ImageOp_BCC(ScienceNationDot, Templates, 0, 0, 114, 211, 17, 17,
     826          MainTexture.clBevelShade, Tribe[ScienceNation].Color);
     827        BitBlt(Canvas.Handle, xScreen - 10, ClientHeight - 27, 17, 17,
     828          ScienceNationDot.Canvas.Handle, 0, 0, SRCCOPY);
    724829      end;
    725830    end
     
    729834function TListDlg.RenameCity(cix: integer): boolean;
    730835var
    731 CityNameInfo: TCityNameInfo;
    732 begin
    733 InputDlg.Caption:=Phrases.Lookup('TITLE_CITYNAME');
    734 InputDlg.EInput.Text:=CityName(MyCity[cix].ID);
    735 InputDlg.CenterToRect(BoundsRect);
    736 InputDlg.ShowModal;
    737 if (InputDlg.ModalResult=mrOK) and (InputDlg.EInput.Text<>'')
    738   and (InputDlg.EInput.Text<>CityName(MyCity[cix].ID)) then
    739   begin
    740   CityNameInfo.ID:=MyCity[cix].ID;
    741   CityNameInfo.NewName:=InputDlg.EInput.Text;
    742   Server(cSetCityName+(Length(CityNameInfo.NewName)+8) div 4,me,0,CityNameInfo);
    743   if CityDlg.Visible then begin CityDlg.FormShow(nil); CityDlg.Invalidate end;
    744   result:=true
     836  CityNameInfo: TCityNameInfo;
     837begin
     838  InputDlg.Caption := Phrases.Lookup('TITLE_CITYNAME');
     839  InputDlg.EInput.Text := CityName(MyCity[cix].ID);
     840  InputDlg.CenterToRect(BoundsRect);
     841  InputDlg.ShowModal;
     842  if (InputDlg.ModalResult = mrOK) and (InputDlg.EInput.Text <> '') and
     843    (InputDlg.EInput.Text <> CityName(MyCity[cix].ID)) then
     844  begin
     845    CityNameInfo.ID := MyCity[cix].ID;
     846    CityNameInfo.NewName := InputDlg.EInput.Text;
     847    Server(cSetCityName + (length(CityNameInfo.NewName) + 8) div 4, me, 0,
     848      CityNameInfo);
     849    if CityDlg.Visible then
     850    begin
     851      CityDlg.FormShow(nil);
     852      CityDlg.Invalidate
     853    end;
     854    result := true
    745855  end
    746 else result:=false
     856  else
     857    result := false
    747858end;
    748859
    749860function TListDlg.RenameModel(mix: integer): boolean;
    750861var
    751 ModelNameInfo: TModelNameInfo;
    752 begin
    753 InputDlg.Caption:=Phrases.Lookup('TITLE_MODELNAME');
    754 InputDlg.EInput.Text:=Tribe[me].ModelName[mix];
    755 InputDlg.CenterToRect(BoundsRect);
    756 InputDlg.ShowModal;
    757 if (InputDlg.ModalResult=mrOK) and (InputDlg.EInput.Text<>'')
    758   and (InputDlg.EInput.Text<>Tribe[me].ModelName[mix]) then
    759   begin
    760   ModelNameInfo.mix:=mix;
    761   ModelNameInfo.NewName:=InputDlg.EInput.Text;
    762   Server(cSetModelName+(Length(ModelNameInfo.NewName)+1+4+3) div 4,
    763     me,0,ModelNameInfo);
    764   if UnitStatDlg.Visible then begin UnitStatDlg.FormShow(nil); UnitStatDlg.Invalidate end;
    765   result:=true
     862  ModelNameInfo: TModelNameInfo;
     863begin
     864  InputDlg.Caption := Phrases.Lookup('TITLE_MODELNAME');
     865  InputDlg.EInput.Text := Tribe[me].ModelName[mix];
     866  InputDlg.CenterToRect(BoundsRect);
     867  InputDlg.ShowModal;
     868  if (InputDlg.ModalResult = mrOK) and (InputDlg.EInput.Text <> '') and
     869    (InputDlg.EInput.Text <> Tribe[me].ModelName[mix]) then
     870  begin
     871    ModelNameInfo.mix := mix;
     872    ModelNameInfo.NewName := InputDlg.EInput.Text;
     873    Server(cSetModelName + (length(ModelNameInfo.NewName) + 1 + 4 + 3) div 4,
     874      me, 0, ModelNameInfo);
     875    if UnitStatDlg.Visible then
     876    begin
     877      UnitStatDlg.FormShow(nil);
     878      UnitStatDlg.Invalidate
     879    end;
     880    result := true
    766881  end
    767 else result:=false 
    768 end;
    769 
    770 procedure TListDlg.PaintBox1MouseDown(Sender:TObject;Button:TMouseButton;
    771   Shift:TShiftState;x,y:integer);
     882  else
     883    result := false
     884end;
     885
     886procedure TListDlg.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
     887  Shift: TShiftState; x, y: integer);
    772888var
    773 lix: integer;
    774 begin
    775 if sb.si.npos+Sel>=0 then lix:=code[Layer,sb.si.npos+Sel];
    776 if Kind in [kScience,kCities,kCityEvents,kModels,kEModels,kAllEModels] then
    777   include(Shift, ssShift); // don't close list window
    778 if (ssLeft in Shift) and not(ssShift in Shift) then
    779   begin
    780   if Sel<>-2 then
    781     begin result:=lix; Closable:=true; Close end
     889  lix: integer;
     890begin
     891  if sb.si.npos + Sel >= 0 then
     892    lix := code[Layer, sb.si.npos + Sel];
     893  if Kind in [kScience, kCities, kCityEvents, kModels, kEModels, kAllEModels]
     894  then
     895    include(Shift, ssShift); // don't close list window
     896  if (ssLeft in Shift) and not(ssShift in Shift) then
     897  begin
     898    if Sel <> -2 then
     899    begin
     900      result := lix;
     901      Closable := true;
     902      Close
     903    end
    782904  end
    783 else if (ssLeft in Shift) and (ssShift in Shift) then
     905  else if (ssLeft in Shift) and (ssShift in Shift) then
    784906  begin // show help/info popup
    785   if Sel<>-2 then
    786     case Kind of
    787       kCities:
    788         MainScreen.ZoomToCity(MyCity[lix].Loc);
    789       kCityEvents:
    790         MainScreen.ZoomToCity(MyCity[lix].Loc, false, MyCity[lix].Flags and CityRepMask);
    791       kModels,kChooseModel:
    792         if lix<>mixAll then
    793           UnitStatDlg.ShowNewContent_OwnModel(FWindowMode or wmPersistent, lix);
    794       kEModels:
    795         UnitStatDlg.ShowNewContent_EnemyModel(FWindowMode or wmPersistent, code[1,sb.si.npos+Sel]);
    796       kAllEModels,kChooseEModel:
    797         if lix<>mixAll then
    798           UnitStatDlg.ShowNewContent_EnemyModel(FWindowMode or wmPersistent, lix);
    799       kAdvance,kFarAdvance,kScience,kChooseTech,kChooseETech,kStealTech:
    800         if lix=adMilitary then
    801           HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText, HelpDlg.TextIndex('MILRES'))
    802         else if lix<adMilitary then
    803           HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkAdv, lix);
    804       kProject:
    805         if lix=cpImp+imTrGoods then
    806           HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText,HelpDlg.TextIndex('TRADINGGOODS'))
    807         else if lix and (cpImp+cpType)=0 then
    808           UnitStatDlg.ShowNewContent_OwnModel(FWindowMode or wmPersistent, lix and cpIndex)
    809         else if (lix and cpType=0) and (lix<>cpImp+imTrGoods) then
    810           HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp, lix and cpIndex);
    811       kGov:
    812         HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkMisc, miscGovList);
    813       kShipPart,kEShipPart:;
     907    if Sel <> -2 then
     908      case Kind of
     909        kCities:
     910          MainScreen.ZoomToCity(MyCity[lix].Loc);
     911        kCityEvents:
     912          MainScreen.ZoomToCity(MyCity[lix].Loc, false, MyCity[lix].Flags and
     913            CityRepMask);
     914        kModels, kChooseModel:
     915          if lix <> mixAll then
     916            UnitStatDlg.ShowNewContent_OwnModel(FWindowMode or
     917              wmPersistent, lix);
     918        kEModels:
     919          UnitStatDlg.ShowNewContent_EnemyModel(FWindowMode or wmPersistent,
     920            code[1, sb.si.npos + Sel]);
     921        kAllEModels, kChooseEModel:
     922          if lix <> mixAll then
     923            UnitStatDlg.ShowNewContent_EnemyModel(FWindowMode or
     924              wmPersistent, lix);
     925        kAdvance, kFarAdvance, kScience, kChooseTech, kChooseETech, kStealTech:
     926          if lix = adMilitary then
     927            HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText,
     928              HelpDlg.TextIndex('MILRES'))
     929          else if lix < adMilitary then
     930            HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkAdv, lix);
     931        kProject:
     932          if lix = cpImp + imTrGoods then
     933            HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText,
     934              HelpDlg.TextIndex('TRADINGGOODS'))
     935          else if lix and (cpImp + cpType) = 0 then
     936            UnitStatDlg.ShowNewContent_OwnModel(FWindowMode or wmPersistent,
     937              lix and cpIndex)
     938          else if (lix and cpType = 0) and (lix <> cpImp + imTrGoods) then
     939            HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp,
     940              lix and cpIndex);
     941        kGov:
     942          HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkMisc,
     943            miscGovList);
     944        kShipPart, kEShipPart:
     945          ;
    814946      end
    815947  end
    816 else if ssRight in Shift then
    817   begin
    818   if Sel<>-2 then
    819     case Kind of
    820       kCities, kCityEvents:
    821         if RenameCity(lix) then SmartUpdateContent;
    822       kModels:
    823         if RenameModel(lix) then SmartUpdateContent;
     948  else if ssRight in Shift then
     949  begin
     950    if Sel <> -2 then
     951      case Kind of
     952        kCities, kCityEvents:
     953          if RenameCity(lix) then
     954            SmartUpdateContent;
     955        kModels:
     956          if RenameModel(lix) then
     957            SmartUpdateContent;
    824958      end
    825959  end
     
    828962procedure TListDlg.InitLines;
    829963var
    830 required: array[0..nAdv-1] of integer;
    831 
    832   procedure TryAddImpLine(Layer,Project: integer);
    833   begin
    834   if Server(sSetCityProject-sExecute,me,cixProject,Project)>=rExecuted then
    835     begin code[Layer,Lines[Layer]]:=Project; inc(Lines[Layer]); end;
     964  required: array [0 .. nAdv - 1] of integer;
     965
     966  procedure TryAddImpLine(Layer, Project: integer);
     967  begin
     968    if Server(sSetCityProject - sExecute, me, cixProject, Project) >= rExecuted
     969    then
     970    begin
     971      code[Layer, Lines[Layer]] := Project;
     972      inc(Lines[Layer]);
     973    end;
    836974  end;
    837975
    838976  procedure SortTechs;
    839977  var
    840   i,j,swap: integer;
     978    i, j, swap: integer;
    841979  begin // sort by advancedness
    842   for i:=0 to Lines[0]-2 do if code[0,i]<adMilitary then
    843     for j:=i+1 to Lines[0]-1 do
    844       if AdvValue[code[0,i]]*nAdv+code[0,i]<AdvValue[code[0,j]]*nAdv+code[0,j] then
    845         begin swap:=code[0,i]; code[0,i]:=code[0,j]; code[0,j]:=swap end;
     980    for i := 0 to Lines[0] - 2 do
     981      if code[0, i] < adMilitary then
     982        for j := i + 1 to Lines[0] - 1 do
     983          if AdvValue[code[0, i]] * nAdv + code[0, i] < AdvValue[code[0, j]] *
     984            nAdv + code[0, j] then
     985          begin
     986            swap := code[0, i];
     987            code[0, i] := code[0, j];
     988            code[0, j] := swap
     989          end;
    846990  end;
    847991
    848992  procedure SortCities;
    849993  var
    850   i,j,swap: integer;
    851   begin
    852   for i:=0 to Lines[0]-2 do
    853     for j:=i+1 to Lines[0]-1 do
    854       if CityName(MyCity[code[0,i]].ID)>CityName(MyCity[code[0,j]].ID) then
    855         begin swap:=code[0,i]; code[0,i]:=code[0,j]; code[0,j]:=swap end;
    856   end;
    857 
    858   function ModelSortValue(const mi: TModelInfo; MixPlayers: boolean = false): integer;
    859   begin
    860   result:=(mi.Domain+1) shl 28 -mi.mix;
    861   if MixPlayers then dec(result, ModelCode(mi) shl 16);
     994    i, j, swap: integer;
     995  begin
     996    for i := 0 to Lines[0] - 2 do
     997      for j := i + 1 to Lines[0] - 1 do
     998        if CityName(MyCity[code[0, i]].ID) > CityName(MyCity[code[0, j]].ID)
     999        then
     1000        begin
     1001          swap := code[0, i];
     1002          code[0, i] := code[0, j];
     1003          code[0, j] := swap
     1004        end;
     1005  end;
     1006
     1007  function ModelSortValue(const mi: TModelInfo;
     1008    MixPlayers: boolean = false): integer;
     1009  begin
     1010    result := (mi.Domain + 1) shl 28 - mi.mix;
     1011    if MixPlayers then
     1012      dec(result, ModelCode(mi) shl 16);
    8621013  end;
    8631014
    8641015  procedure SortModels;
    8651016  var
    866   i,j,swap: integer;
     1017    i, j, swap: integer;
    8671018  begin // sort by code[2]
    868   for i:=0 to Lines[0]-2 do for j:=i+1 to Lines[0]-1 do
    869     if code[2,i]>code[2,j] then
    870       begin
    871       swap:=code[0,i]; code[0,i]:=code[0,j]; code[0,j]:=swap;
    872       swap:=code[1,i]; code[1,i]:=code[1,j]; code[1,j]:=swap;
    873       swap:=code[2,i]; code[2,i]:=code[2,j]; code[2,j]:=swap;
    874       end;
     1019    for i := 0 to Lines[0] - 2 do
     1020      for j := i + 1 to Lines[0] - 1 do
     1021        if code[2, i] > code[2, j] then
     1022        begin
     1023          swap := code[0, i];
     1024          code[0, i] := code[0, j];
     1025          code[0, j] := swap;
     1026          swap := code[1, i];
     1027          code[1, i] := code[1, j];
     1028          code[1, j] := swap;
     1029          swap := code[2, i];
     1030          code[2, i] := code[2, j];
     1031          code[2, j] := swap;
     1032        end;
    8751033  end;
    8761034
    8771035  procedure MarkPreqs(i: integer);
    8781036  begin
    879   required[i]:=1;
    880   if MyRO.Tech[i]<tsSeen then
     1037    required[i] := 1;
     1038    if MyRO.Tech[i] < tsSeen then
    8811039    begin
    882     if (AdvPreq[i,0]>=0) then MarkPreqs(AdvPreq[i,0]);
    883     if (AdvPreq[i,1]>=0) then MarkPreqs(AdvPreq[i,1]);
     1040      if (AdvPreq[i, 0] >= 0) then
     1041        MarkPreqs(AdvPreq[i, 0]);
     1042      if (AdvPreq[i, 1] >= 0) then
     1043        MarkPreqs(AdvPreq[i, 1]);
    8841044    end
    8851045  end;
    8861046
    8871047var
    888 Loc1,i,j,p1,dx,dy,mix,emix,EnemyType,TestEnemyType:integer;
    889 mi: TModelInfo;
    890 PPicture, PTestPicture: ^TModelPicture;
    891 ModelOk: array[0..4095] of boolean;
    892 ok: boolean;
    893 begin
    894 for i:=0 to MaxLayer-1 do
    895   begin Lines[i]:=0; FirstShrinkedLine[i]:=MaxInt end;
    896 case Kind of
    897   kProject:
    898     begin
    899     // improvements
    900     code[0,0]:=cpImp+imTrGoods;
    901     Lines[0]:=1;
    902     for i:=28 to nImp-1 do
    903       if Imp[i].Kind=ikCommon then
    904         TryAddImpLine(0,i+cpImp);
    905     for i:=28 to nImp-1 do
    906       if not (Imp[i].Kind in [ikCommon,ikTrGoods])
    907         and ((MyRO.NatBuilt[i]=0) or (Imp[i].Kind=ikNatLocal)) then
    908         TryAddImpLine(0,i+cpImp);
    909     for i:=0 to nCityType-1 do if MyData.ImpOrder[i,0]>=0 then
    910       begin code[0,Lines[0]]:=cpType+i; inc(Lines[0]); end;
    911 
    912     // wonders
    913     for i:=0 to 27 do
    914       TryAddImpLine(1,i+cpImp);
    915 
    916     // units
    917     for i:=0 to MyRO.nModel-1 do
    918       begin
    919 {      if MyModel[i].Kind=mkSlaves then
    920         ok:= MyRO.Wonder[woPyramids].EffectiveOwner=me
    921       else} if MyModel[i].Domain=dSea then
    922         begin
    923         ok:=false;
    924         for dx:=-2 to 2 do for dy:=-2 to 2 do if abs(dx)+abs(dy)=2 then
    925           begin
    926           Loc1:=dLoc(MyCity[cixProject].Loc,dx,dy);
    927           if (Loc1>=0) and (Loc1<G.lx*G.ly)
    928             and ((MyMap[Loc1] and fTerrain=fShore) or (MyMap[Loc1] and fCanal>0)) then
    929             ok:=true;
     1048  Loc1, i, j, p1, dx, dy, mix, emix, EnemyType, TestEnemyType: integer;
     1049  mi: TModelInfo;
     1050  PPicture, PTestPicture: ^TModelPicture;
     1051  ModelOk: array [0 .. 4095] of boolean;
     1052  ok: boolean;
     1053begin
     1054  for i := 0 to MaxLayer - 1 do
     1055  begin
     1056    Lines[i] := 0;
     1057    FirstShrinkedLine[i] := MaxInt
     1058  end;
     1059  case Kind of
     1060    kProject:
     1061      begin
     1062        // improvements
     1063        code[0, 0] := cpImp + imTrGoods;
     1064        Lines[0] := 1;
     1065        for i := 28 to nImp - 1 do
     1066          if Imp[i].Kind = ikCommon then
     1067            TryAddImpLine(0, i + cpImp);
     1068        for i := 28 to nImp - 1 do
     1069          if not(Imp[i].Kind in [ikCommon, ikTrGoods]) and
     1070            ((MyRO.NatBuilt[i] = 0) or (Imp[i].Kind = ikNatLocal)) then
     1071            TryAddImpLine(0, i + cpImp);
     1072        for i := 0 to nCityType - 1 do
     1073          if MyData.ImpOrder[i, 0] >= 0 then
     1074          begin
     1075            code[0, Lines[0]] := cpType + i;
     1076            inc(Lines[0]);
     1077          end;
     1078
     1079        // wonders
     1080        for i := 0 to 27 do
     1081          TryAddImpLine(1, i + cpImp);
     1082
     1083        // units
     1084        for i := 0 to MyRO.nModel - 1 do
     1085        begin
     1086          { if MyModel[i].Kind=mkSlaves then
     1087            ok:= MyRO.Wonder[woPyramids].EffectiveOwner=me
     1088            else } if MyModel[i].Domain = dSea then
     1089          begin
     1090            ok := false;
     1091            for dx := -2 to 2 do
     1092              for dy := -2 to 2 do
     1093                if abs(dx) + abs(dy) = 2 then
     1094                begin
     1095                  Loc1 := dLoc(MyCity[cixProject].Loc, dx, dy);
     1096                  if (Loc1 >= 0) and (Loc1 < G.lx * G.ly) and
     1097                    ((MyMap[Loc1] and fTerrain = fShore) or
     1098                    (MyMap[Loc1] and fCanal > 0)) then
     1099                    ok := true;
     1100                end
    9301101          end
     1102          else
     1103            ok := true;
     1104          if ok then
     1105          begin
     1106            if MyModel[i].Status and msObsolete = 0 then
     1107            begin
     1108              code[2, Lines[2]] := i;
     1109              inc(Lines[2])
     1110            end;
     1111            if MyModel[i].Status and msAllowConscripts <> 0 then
     1112            begin
     1113              code[2, Lines[2]] := i + cpConscripts;
     1114              inc(Lines[2])
     1115            end;
     1116          end;
     1117        end;
     1118        FirstShrinkedLine[2] := 0;
     1119      end;
     1120    kAdvance:
     1121      begin
     1122        nColumn := 1;
     1123        if MyData.FarTech <> adNone then
     1124        begin
     1125          FillChar(required, SizeOf(required), 0);
     1126          MarkPreqs(MyData.FarTech);
     1127        end;
     1128        for i := 0 to nAdv - 1 do
     1129          if ((i in FutureTech) or (MyRO.Tech[i] < tsApplicable)) and
     1130            (Server(sSetResearch - sExecute, me, i, nil^) >= rExecuted) and
     1131            ((MyData.FarTech = adNone) or (required[i] > 0)) then
     1132          begin
     1133            code[0, Lines[0]] := i;
     1134            inc(Lines[0]);
     1135          end;
     1136        SortTechs;
     1137        if Lines[0] = 0 then // no more techs -- offer nexus
     1138        begin
     1139          code[0, Lines[0]] := adNexus;
     1140          inc(Lines[0]);
     1141        end;
     1142        ok := false;
     1143        for i := 0 to nDomains - 1 do
     1144          if (upgrade[i, 0].Preq = preNone) or
     1145            (MyRO.Tech[upgrade[i, 0].Preq] >= tsApplicable) then
     1146            ok := true;
     1147        if ok then { new unit class }
     1148        begin
     1149          code[0, Lines[0]] := adMilitary;
     1150          inc(Lines[0])
     1151        end;
     1152      end;
     1153    kFarAdvance:
     1154      begin
     1155        code[0, Lines[0]] := adNone;
     1156        inc(Lines[0]);
     1157        for i := 0 to nAdv - 1 do
     1158          if not(i in FutureTech) and (MyRO.Tech[i] < tsApplicable) and
     1159            ((AdvValue[i] < 2000) or (MyRO.Tech[adMassProduction] > tsNA)) and
     1160            ((AdvValue[i] < 1000) or (MyRO.Tech[adScience] > tsNA)) then
     1161          begin
     1162            code[0, Lines[0]] := i;
     1163            inc(Lines[0]);
     1164          end;
     1165        SortTechs;
     1166      end;
     1167    kChooseTech:
     1168      begin
     1169        for i := 0 to nAdv - 1 do
     1170          if not(i in FutureTech) and (MyRO.Tech[i] >= tsApplicable) and
     1171            (MyRO.EnemyReport[DipMem[me].pContact].Tech[i] < tsSeen) then
     1172          begin
     1173            code[0, Lines[0]] := i;
     1174            inc(Lines[0]);
     1175          end;
     1176        SortTechs;
     1177        // if Lines[0]>1 then
     1178        begin
     1179          code[0, Lines[0]] := adAll;
     1180          inc(Lines[0]);
     1181        end;
     1182      end;
     1183    kChooseETech:
     1184      begin
     1185        for i := 0 to nAdv - 1 do
     1186          if not(i in FutureTech) and (MyRO.Tech[i] < tsSeen) and
     1187            (MyRO.EnemyReport[DipMem[me].pContact].Tech[i] >= tsApplicable) then
     1188          begin
     1189            code[0, Lines[0]] := i;
     1190            inc(Lines[0]);
     1191          end;
     1192        SortTechs;
     1193        // if Lines[0]>1 then
     1194        begin
     1195          code[0, Lines[0]] := adAll;
     1196          inc(Lines[0]);
     1197        end;
     1198      end;
     1199    kStealTech:
     1200      begin
     1201        for i := 0 to nAdv - 1 do
     1202          if Server(sStealTech - sExecute, me, i, nil^) >= rExecuted then
     1203          begin
     1204            code[0, Lines[0]] := i;
     1205            inc(Lines[0]);
     1206          end;
     1207        SortTechs;
     1208      end;
     1209    kScience:
     1210      begin
     1211        Column[0] := me;
     1212        nColumn := 1;
     1213        for EnemyType := 0 to 2 do
     1214          for p1 := 0 to nPl - 1 do
     1215            if (MyRO.EnemyReport[p1] <> nil) and
     1216              ((MyRO.EnemyReport[p1].TurnOfContact >= 0) or
     1217              (MyRO.EnemyReport[p1].TurnOfCivilReport >= 0)) then
     1218            begin
     1219              if MyRO.Alive and (1 shl p1) = 0 then
     1220                TestEnemyType := 2 // extinct enemy -- move to right end
     1221              else if MyRO.EnemyReport[p1].TurnOfCivilReport >= MyRO.Turn - 1
     1222              then
     1223                TestEnemyType := 0 // current report -- move to left end
     1224              else
     1225                TestEnemyType := 1;
     1226              if TestEnemyType = EnemyType then
     1227              begin
     1228                Column[nColumn] := p1;
     1229                inc(nColumn);
     1230              end;
     1231            end;
     1232        for i := 0 to nAdv - 1 do
     1233        begin
     1234          ok := (MyRO.Tech[i] <> tsNA) or (MyRO.ResearchTech = i);
     1235          for j := 1 to nColumn - 1 do
     1236            with MyRO.EnemyReport[Column[j]]^ do
     1237              if (Tech[i] <> tsNA) or (TurnOfCivilReport >= 0) and
     1238                (ResearchTech = i) then
     1239                ok := true;
     1240          if ok then
     1241          begin
     1242            code[0, Lines[0]] := i;
     1243            inc(Lines[0]);
     1244          end;
     1245        end;
     1246        SortTechs;
     1247
     1248        ok := MyRO.ResearchTech = adMilitary;
     1249        for j := 1 to nColumn - 1 do
     1250          with MyRO.EnemyReport[Column[j]]^ do
     1251            if (MyRO.Alive and (1 shl Column[j]) <> 0) and
     1252              (TurnOfCivilReport >= 0) and (ResearchTech = adMilitary) then
     1253              ok := true;
     1254        if ok then
     1255        begin
     1256          code[0, Lines[0]] := adMilitary;
     1257          inc(Lines[0]);
    9311258        end
    932       else ok:=true;
    933       if ok then
    934         begin
    935         if MyModel[i].Status and msObsolete=0 then
    936           begin code[2,Lines[2]]:=i; inc(Lines[2]) end;
    937         if MyModel[i].Status and msAllowConscripts<>0 then
    938           begin code[2,Lines[2]]:=i+cpConscripts; inc(Lines[2]) end;
    939         end;
    940       end;
    941     FirstShrinkedLine[2]:=0;
    942     end;
    943   kAdvance:
    944     begin
    945     nColumn:=1;
    946     if MyData.FarTech<>adNone then
    947       begin
    948       FillChar(required,SizeOf(required),0);
    949       MarkPreqs(MyData.FarTech);
    950       end;
    951     for i:=0 to nAdv-1 do
    952       if ((i in FutureTech) or (MyRO.Tech[i]<tsApplicable))
    953         and (Server(sSetResearch-sExecute,me,i,nil^)>=rExecuted)
    954         and ((MyData.FarTech=adNone) or (required[i]>0)) then
    955         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    956     SortTechs;
    957     if Lines[0]=0 then // no more techs -- offer nexus
    958       begin code[0,Lines[0]]:=adNexus; inc(Lines[0]); end;
    959     ok:=false;
    960     for i:=0 to nDomains-1 do
    961       if (upgrade[i,0].Preq=preNone)
    962         or (MyRO.Tech[upgrade[i,0].Preq]>=tsApplicable) then
    963         ok:=true;
    964     if ok then {new unit class}
    965       begin code[0,Lines[0]]:=adMilitary; inc(Lines[0]) end;
    966     end;
    967   kFarAdvance:
    968     begin
    969     code[0,Lines[0]]:=adNone; inc(Lines[0]);
    970     for i:=0 to nAdv-1 do
    971       if not (i in FutureTech) and (MyRO.Tech[i]<tsApplicable)
    972         and ((AdvValue[i]<2000) or (MyRO.Tech[adMassProduction]>tsNA))
    973         and ((AdvValue[i]<1000) or (MyRO.Tech[adScience]>tsNA)) then
    974         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    975     SortTechs;
    976     end;
    977   kChooseTech:
    978     begin
    979     for i:=0 to nAdv-1 do
    980       if not (i in FutureTech) and (MyRO.Tech[i]>=tsApplicable)
    981         and (MyRO.EnemyReport[DipMem[me].pContact].Tech[i]<tsSeen) then
    982         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    983     SortTechs;
    984 //    if Lines[0]>1 then
    985       begin code[0,Lines[0]]:=adAll; inc(Lines[0]); end;
    986     end;
    987   kChooseETech:
    988     begin
    989     for i:=0 to nAdv-1 do
    990       if not (i in FutureTech) and (MyRO.Tech[i]<tsSeen)
    991         and (MyRO.EnemyReport[DipMem[me].pContact].Tech[i]>=tsApplicable) then
    992         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    993     SortTechs;
    994 //    if Lines[0]>1 then
    995       begin code[0,Lines[0]]:=adAll; inc(Lines[0]); end;
    996     end;
    997   kStealTech:
    998     begin
    999     for i:=0 to nAdv-1 do
    1000       if Server(sStealTech-sExecute, me, i, nil^)>=rExecuted then
    1001         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    1002     SortTechs;
    1003     end;
    1004   kScience:
    1005     begin
    1006     Column[0]:=me;
    1007     nColumn:=1;
    1008     for EnemyType:=0 to 2 do
    1009       for p1:=0 to nPl-1 do
    1010         if (MyRO.EnemyReport[p1]<>nil)
    1011           and ((MyRO.EnemyReport[p1].TurnOfContact>=0)
    1012             or (MyRO.EnemyReport[p1].TurnOfCivilReport>=0)) then
    1013           begin
    1014           if MyRO.Alive and (1 shl p1)=0 then
    1015             TestEnemyType:=2 // extinct enemy -- move to right end
    1016           else if MyRO.EnemyReport[p1].TurnOfCivilReport>=MyRO.Turn-1 then
    1017             TestEnemyType:=0 // current report -- move to left end
    1018           else TestEnemyType:=1;
    1019           if TestEnemyType=EnemyType then
    1020             begin Column[nColumn]:=p1; inc(nColumn); end;
    1021           end;
    1022     for i:=0 to nAdv-1 do
    1023       begin
    1024       ok:= (MyRO.Tech[i]<>tsNA) or (MyRO.ResearchTech=i);
    1025       for j:=1 to nColumn-1 do with MyRO.EnemyReport[Column[j]]^ do
    1026         if (Tech[i]<>tsNA) or (TurnOfCivilReport>=0) and (ResearchTech=i) then
    1027           ok:=true;
    1028       if ok then
    1029         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    1030       end;
    1031     SortTechs;
    1032 
    1033     ok:= MyRO.ResearchTech=adMilitary;
    1034     for j:=1 to nColumn-1 do with MyRO.EnemyReport[Column[j]]^ do
    1035       if (MyRO.Alive and (1 shl Column[j])<>0)
    1036         and (TurnOfCivilReport>=0) and (ResearchTech=adMilitary) then
    1037         ok:=true;
    1038     if ok then
    1039       begin code[0,Lines[0]]:=adMilitary; inc(Lines[0]); end
    1040     end;
    1041   kCities{, kChooseCity}:
    1042     begin
    1043     if ClientMode<scContact then
    1044       for i:=0 to MyRO.nCity-1 do if MyCity[i].Loc>=0 then
    1045         begin code[0,Lines[0]]:=i; inc(Lines[0]) end;
    1046     SortCities;
    1047     FirstShrinkedLine[0]:=0
    1048     end;
    1049   kCityEvents:
    1050     begin
    1051     for i:=0 to MyRO.nCity-1 do
    1052       if (MyCity[i].Loc>=0) and (MyCity[i].Flags and CityRepMask<>0) then
    1053         begin code[0,Lines[0]]:=i; inc(Lines[0]) end;
    1054     SortCities;
    1055     FirstShrinkedLine[0]:=0
    1056     end;
    1057 {  kChooseECity:
    1058     begin
    1059     for i:=0 to MyRO.nEnemyCity-1 do
     1259      end;
     1260    kCities { , kChooseCity } :
     1261      begin
     1262        if ClientMode < scContact then
     1263          for i := 0 to MyRO.nCity - 1 do
     1264            if MyCity[i].Loc >= 0 then
     1265            begin
     1266              code[0, Lines[0]] := i;
     1267              inc(Lines[0])
     1268            end;
     1269        SortCities;
     1270        FirstShrinkedLine[0] := 0
     1271      end;
     1272    kCityEvents:
     1273      begin
     1274        for i := 0 to MyRO.nCity - 1 do
     1275          if (MyCity[i].Loc >= 0) and (MyCity[i].Flags and CityRepMask <> 0)
     1276          then
     1277          begin
     1278            code[0, Lines[0]] := i;
     1279            inc(Lines[0])
     1280          end;
     1281        SortCities;
     1282        FirstShrinkedLine[0] := 0
     1283      end;
     1284    { kChooseECity:
     1285      begin
     1286      for i:=0 to MyRO.nEnemyCity-1 do
    10601287      if (MyRO.EnemyCity[i].Loc>=0)
    1061         and (MyRO.EnemyCity[i].owner=DipMem[me].pContact) then
    1062         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    1063     FirstShrinkedLine:=0
    1064     end;}
    1065   kModels:
    1066     begin
    1067     for mix:=0 to MyRO.nModel-1 do
    1068       begin
    1069       code[0,mix]:=mix;
    1070       MakeModelInfo(me, mix, MyModel[mix], mi);
    1071       code[2,mix]:=ModelSortValue(mi);
    1072       end;
    1073     Lines[0]:=MyRO.nModel;
    1074     SortModels;
    1075     FirstShrinkedLine[0]:=0
    1076     end;
    1077   kChooseModel:
    1078     begin
    1079     for mix:=3 to MyRO.nModel-1 do
    1080       begin // check if opponent already has this model
    1081       MakeModelInfo(me,mix,MyModel[mix],mi);
    1082       ok:=true;
    1083       for emix:=0 to MyRO.nEnemyModel-1 do
    1084          if (MyRO.EnemyModel[emix].Owner=DipMem[me].pContact)
    1085            and IsSameModel(MyRO.EnemyModel[emix],mi) then
    1086            ok:=false;
    1087       if ok then
    1088         begin
    1089         code[0,Lines[0]]:=mix;
    1090         MakeModelInfo(me, mix, MyModel[mix], mi);
    1091         code[2,Lines[0]]:=ModelSortValue(mi);
    1092         inc(Lines[0]);
    1093         end;
    1094       end;
    1095     SortModels;
    1096 //    if Lines[0]>1 then
    1097       begin code[0,Lines[0]]:=mixAll; inc(Lines[0]); end;
    1098     FirstShrinkedLine[0]:=0
    1099     end;
    1100   kChooseEModel:
    1101     begin
    1102     if MyRO.TestFlags and tfUncover<>0 then
    1103       Server(sGetModels,me,0,nil^);
    1104     for emix:=0 to MyRO.nEnemyModel-1 do
    1105       ModelOk[emix]:= MyRO.EnemyModel[emix].Owner=DipMem[me].pContact;
    1106     for mix:=0 to MyRO.nModel-1 do
    1107       begin // don't list models I already have
    1108       MakeModelInfo(me,mix,MyModel[mix],mi);
    1109       for emix:=0 to MyRO.nEnemyModel-1 do
    1110         ModelOk[emix]:=ModelOk[emix]
    1111           and not IsSameModel(MyRO.EnemyModel[emix],mi);
    1112       end;
    1113     for emix:=0 to MyRO.nEnemyModel-1 do if ModelOk[emix] then
    1114       begin
    1115       if Tribe[DipMem[me].pContact].ModelPicture[MyRO.EnemyModel[emix].mix].HGr=0 then
    1116         InitEnemyModel(emix);
    1117       code[0,Lines[0]]:=emix;
    1118       code[2,Lines[0]]:=ModelSortValue(MyRO.EnemyModel[emix]);
    1119       inc(Lines[0]);
    1120       end;
    1121     SortModels;
    1122 //    if not IsMilReportNew(DipMem[me].pContact) or (Lines[0]>1) then
    1123       begin code[0,Lines[0]]:=mixAll; inc(Lines[0]); end;
    1124     FirstShrinkedLine[0]:=0
    1125     end;
    1126   kEModels:
    1127     begin
    1128     for i:=0 to MyRO.EnemyReport[pView].nModelCounted-1 do
    1129       begin
    1130       code[1,Lines[0]]:=MyRO.nEnemyModel-1;
    1131       while (code[1,Lines[0]]>=0)
    1132         and not ((MyRO.EnemyModel[code[1,Lines[0]]].Owner=pView)
    1133         and (MyRO.EnemyModel[code[1,Lines[0]]].mix=i)) do
    1134         dec(code[1,Lines[0]]);
    1135       if Tribe[pView].ModelPicture[i].HGr=0 then
    1136         InitEnemyModel(code[1,Lines[0]]);
    1137       code[0,Lines[0]]:=i;
    1138       code[2,Lines[0]]:=ModelSortValue(MyRO.EnemyModel[code[1,Lines[0]]]);
    1139       inc(Lines[0]);
    1140       end;
    1141     SortModels;
    1142     FirstShrinkedLine[0]:=0
    1143     end;
    1144   kAllEModels:
    1145     begin
    1146     if (MyRO.TestFlags and tfUncover<>0) or (G.Difficulty[me]=0) then
    1147       Server(sGetModels,me,0,nil^);
    1148     for emix:=0 to MyRO.nEnemyModel-1 do
    1149       if (MyRO.EnemyModel[emix].mix>=3)
    1150         and (MyRO.EnemyModel[emix].Kind in [mkSelfDeveloped,mkEnemyDeveloped]) then
    1151         begin
    1152         PPicture:=@Tribe[MyRO.EnemyModel[emix].Owner].ModelPicture[MyRO.EnemyModel[emix].mix];
    1153         if PPicture.HGr=0 then InitEnemyModel(emix);
    1154         ok:=true;
    1155         if MainScreen.mNames.Checked then
    1156           for j:=0 to Lines[0]-1 do
     1288      and (MyRO.EnemyCity[i].owner=DipMem[me].pContact) then
     1289      begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
     1290      FirstShrinkedLine:=0
     1291      end; }
     1292    kModels:
     1293      begin
     1294        for mix := 0 to MyRO.nModel - 1 do
     1295        begin
     1296          code[0, mix] := mix;
     1297          MakeModelInfo(me, mix, MyModel[mix], mi);
     1298          code[2, mix] := ModelSortValue(mi);
     1299        end;
     1300        Lines[0] := MyRO.nModel;
     1301        SortModels;
     1302        FirstShrinkedLine[0] := 0
     1303      end;
     1304    kChooseModel:
     1305      begin
     1306        for mix := 3 to MyRO.nModel - 1 do
     1307        begin // check if opponent already has this model
     1308          MakeModelInfo(me, mix, MyModel[mix], mi);
     1309          ok := true;
     1310          for emix := 0 to MyRO.nEnemyModel - 1 do
     1311            if (MyRO.EnemyModel[emix].Owner = DipMem[me].pContact) and
     1312              IsSameModel(MyRO.EnemyModel[emix], mi) then
     1313              ok := false;
     1314          if ok then
     1315          begin
     1316            code[0, Lines[0]] := mix;
     1317            MakeModelInfo(me, mix, MyModel[mix], mi);
     1318            code[2, Lines[0]] := ModelSortValue(mi);
     1319            inc(Lines[0]);
     1320          end;
     1321        end;
     1322        SortModels;
     1323        // if Lines[0]>1 then
     1324        begin
     1325          code[0, Lines[0]] := mixAll;
     1326          inc(Lines[0]);
     1327        end;
     1328        FirstShrinkedLine[0] := 0
     1329      end;
     1330    kChooseEModel:
     1331      begin
     1332        if MyRO.TestFlags and tfUncover <> 0 then
     1333          Server(sGetModels, me, 0, nil^);
     1334        for emix := 0 to MyRO.nEnemyModel - 1 do
     1335          ModelOk[emix] := MyRO.EnemyModel[emix].Owner = DipMem[me].pContact;
     1336        for mix := 0 to MyRO.nModel - 1 do
     1337        begin // don't list models I already have
     1338          MakeModelInfo(me, mix, MyModel[mix], mi);
     1339          for emix := 0 to MyRO.nEnemyModel - 1 do
     1340            ModelOk[emix] := ModelOk[emix] and
     1341              not IsSameModel(MyRO.EnemyModel[emix], mi);
     1342        end;
     1343        for emix := 0 to MyRO.nEnemyModel - 1 do
     1344          if ModelOk[emix] then
     1345          begin
     1346            if Tribe[DipMem[me].pContact].ModelPicture
     1347              [MyRO.EnemyModel[emix].mix].HGr = 0 then
     1348              InitEnemyModel(emix);
     1349            code[0, Lines[0]] := emix;
     1350            code[2, Lines[0]] := ModelSortValue(MyRO.EnemyModel[emix]);
     1351            inc(Lines[0]);
     1352          end;
     1353        SortModels;
     1354        // if not IsMilReportNew(DipMem[me].pContact) or (Lines[0]>1) then
     1355        begin
     1356          code[0, Lines[0]] := mixAll;
     1357          inc(Lines[0]);
     1358        end;
     1359        FirstShrinkedLine[0] := 0
     1360      end;
     1361    kEModels:
     1362      begin
     1363        for i := 0 to MyRO.EnemyReport[pView].nModelCounted - 1 do
     1364        begin
     1365          code[1, Lines[0]] := MyRO.nEnemyModel - 1;
     1366          while (code[1, Lines[0]] >= 0) and
     1367            not((MyRO.EnemyModel[code[1, Lines[0]]].Owner = pView) and
     1368            (MyRO.EnemyModel[code[1, Lines[0]]].mix = i)) do
     1369            dec(code[1, Lines[0]]);
     1370          if Tribe[pView].ModelPicture[i].HGr = 0 then
     1371            InitEnemyModel(code[1, Lines[0]]);
     1372          code[0, Lines[0]] := i;
     1373          code[2, Lines[0]] :=
     1374            ModelSortValue(MyRO.EnemyModel[code[1, Lines[0]]]);
     1375          inc(Lines[0]);
     1376        end;
     1377        SortModels;
     1378        FirstShrinkedLine[0] := 0
     1379      end;
     1380    kAllEModels:
     1381      begin
     1382        if (MyRO.TestFlags and tfUncover <> 0) or (G.Difficulty[me] = 0) then
     1383          Server(sGetModels, me, 0, nil^);
     1384        for emix := 0 to MyRO.nEnemyModel - 1 do
     1385          if (MyRO.EnemyModel[emix].mix >= 3) and
     1386            (MyRO.EnemyModel[emix].Kind in [mkSelfDeveloped, mkEnemyDeveloped])
     1387          then
     1388          begin
     1389            PPicture := @Tribe[MyRO.EnemyModel[emix].Owner].ModelPicture
     1390              [MyRO.EnemyModel[emix].mix];
     1391            if PPicture.HGr = 0 then
     1392              InitEnemyModel(emix);
     1393            ok := true;
     1394            if MainScreen.mNames.Checked then
     1395              for j := 0 to Lines[0] - 1 do
     1396              begin
     1397                PTestPicture := @Tribe[MyRO.EnemyModel[code[0, j]].Owner]
     1398                  .ModelPicture[MyRO.EnemyModel[code[0, j]].mix];
     1399                if (PPicture.HGr = PTestPicture.HGr) and
     1400                  (PPicture.pix = PTestPicture.pix) and
     1401                  (ModelHash(MyRO.EnemyModel[emix])
     1402                  = ModelHash(MyRO.EnemyModel[code[0, j]])) then
     1403                begin
     1404                  code[1, j] := 1;
     1405                  ok := false;
     1406                  Break
     1407                end;
     1408              end;
     1409            if ok then
    11571410            begin
    1158             PTestPicture:=@Tribe[MyRO.EnemyModel[code[0,j]].Owner].ModelPicture[MyRO.EnemyModel[code[0,j]].mix];
    1159             if (PPicture.HGr=PTestPicture.HGr) and (PPicture.pix=PTestPicture.pix)
    1160               and (ModelHash(MyRO.EnemyModel[emix])=ModelHash(MyRO.EnemyModel[code[0,j]])) then
    1161               begin code[1,j]:=1; ok:=false; Break end;
    1162             end;
    1163         if ok then
    1164           begin
    1165           code[0,Lines[0]]:=emix;
    1166           code[1,Lines[0]]:=0;
    1167           code[2,Lines[0]]:=ModelSortValue(MyRO.EnemyModel[emix],true);
    1168           inc(Lines[0]);
    1169           end
    1170         end;
    1171     SortModels;
    1172     FirstShrinkedLine[0]:=0
    1173     end;
    1174   kTribe:
    1175     for i:=0 to TribeNames.Count-1 do
    1176       begin code[0,Lines[0]]:=i; inc(Lines[0]) end;
    1177 (*  kDeliver:
    1178     if MyRO.Treaty[DipMem[me].pContact]<trAlliance then
     1411              code[0, Lines[0]] := emix;
     1412              code[1, Lines[0]] := 0;
     1413              code[2, Lines[0]] := ModelSortValue(MyRO.EnemyModel[emix], true);
     1414              inc(Lines[0]);
     1415            end
     1416          end;
     1417        SortModels;
     1418        FirstShrinkedLine[0] := 0
     1419      end;
     1420    kTribe:
     1421      for i := 0 to TribeNames.Count - 1 do
     1422      begin
     1423        code[0, Lines[0]] := i;
     1424        inc(Lines[0])
     1425      end;
     1426    (* kDeliver:
     1427      if MyRO.Treaty[DipMem[me].pContact]<trAlliance then
    11791428      begin // suggest next treaty level
    11801429      code[0,Lines[0]]:=opTreaty+MyRO.Treaty[DipMem[me].pContact]+1;
    11811430      inc(Lines[0]);
    11821431      end;
    1183     if MyRO.Treaty[DipMem[me].pContact]=trNone then
     1432      if MyRO.Treaty[DipMem[me].pContact]=trNone then
    11841433      begin // suggest peace
    11851434      code[0,Lines[0]]:=opTreaty+trPeace;
    11861435      inc(Lines[0]);
    11871436      end;
    1188     if MyRO.Treaty[DipMem[me].pContact]>trNone then
     1437      if MyRO.Treaty[DipMem[me].pContact]>trNone then
    11891438      begin // suggest next treaty level
    11901439      code[0,Lines[0]]:=opTreaty+MyRO.Treaty[DipMem[me].pContact]-1;
    11911440      inc(Lines[0]);
    1192       end;*)
    1193   kShipPart:
     1441      end; *)
     1442    kShipPart:
     1443      begin
     1444        Lines[0] := 0;
     1445        for i := 0 to nShipPart - 1 do
     1446          if MyRO.Ship[me].Parts[i] > 0 then
     1447          begin
     1448            code[0, Lines[0]] := i;
     1449            inc(Lines[0]);
     1450          end;
     1451      end;
     1452    kEShipPart:
     1453      begin
     1454        Lines[0] := 0;
     1455        for i := 0 to nShipPart - 1 do
     1456          if MyRO.Ship[DipMem[me].pContact].Parts[i] > 0 then
     1457          begin
     1458            code[0, Lines[0]] := i;
     1459            inc(Lines[0]);
     1460          end;
     1461      end;
     1462    kGov:
     1463      for i := 1 to nGov - 1 do
     1464        if (GovPreq[i] <> preNA) and
     1465          ((GovPreq[i] = preNone) or (MyRO.Tech[GovPreq[i]] >= tsApplicable))
     1466        then
     1467        begin
     1468          code[0, Lines[0]] := i;
     1469          inc(Lines[0])
     1470        end;
     1471    kMission:
     1472      for i := 0 to nSpyMission - 1 do
     1473      begin
     1474        code[0, Lines[0]] := i;
     1475        inc(Lines[0])
     1476      end;
     1477  end;
     1478
     1479  if Kind = kProject then // test if choice fitting to one screen
     1480    if Lines[0] + Lines[1] + Lines[2] <= MaxLines then
    11941481    begin
    1195     Lines[0]:=0;
    1196     for i:=0 to nShipPart-1 do
    1197       if MyRO.Ship[me].Parts[i]>0 then
    1198         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    1199     end;
    1200   kEShipPart:
    1201     begin
    1202     Lines[0]:=0;
    1203     for i:=0 to nShipPart-1 do
    1204       if MyRO.Ship[DipMem[me].pContact].Parts[i]>0 then
    1205         begin code[0,Lines[0]]:=i; inc(Lines[0]); end;
    1206     end;
    1207   kGov:
    1208     for i:=1 to nGov-1 do
    1209       if (GovPreq[i]<>preNA) and ((GovPreq[i]=preNone)
    1210         or (MyRO.Tech[GovPreq[i]]>=tsApplicable)) then
    1211         begin code[0,Lines[0]]:=i; inc(Lines[0]) end;
    1212   kMission:
    1213     for i:=0 to nSpyMission-1 do
    1214       begin code[0,Lines[0]]:=i; inc(Lines[0]) end;
    1215   end;
    1216 
    1217 if Kind=kProject then // test if choice fitting to one screen
    1218   if Lines[0]+Lines[1]+Lines[2]<=MaxLines then
    1219     begin
    1220     for i:=0 to Lines[1]-1 do // add wonders to first page
    1221       begin code[0,Lines[0]]:=code[1,i]; inc(Lines[0]); end;
    1222     Lines[1]:=0;
    1223     FirstShrinkedLine[0]:=Lines[0];
    1224     for i:=0 to Lines[2]-1 do // add models to first page
    1225       begin code[0,Lines[0]]:=code[2,i]; inc(Lines[0]); end;
    1226     Lines[2]:=0;
     1482      for i := 0 to Lines[1] - 1 do // add wonders to first page
     1483      begin
     1484        code[0, Lines[0]] := code[1, i];
     1485        inc(Lines[0]);
     1486      end;
     1487      Lines[1] := 0;
     1488      FirstShrinkedLine[0] := Lines[0];
     1489      for i := 0 to Lines[2] - 1 do // add models to first page
     1490      begin
     1491        code[0, Lines[0]] := code[2, i];
     1492        inc(Lines[0]);
     1493      end;
     1494      Lines[2] := 0;
    12271495    end;
    12281496end; // InitLines
     
    12301498function TListDlg.OnlyChoice(TestKind: TListKind): integer;
    12311499begin
    1232 Kind:=TestKind;
    1233 InitLines;
    1234 if Lines[0]=0 then result:=-2
    1235 else if Lines[0]>1 then result:=-1
    1236 else result:=code[0,0];
     1500  Kind := TestKind;
     1501  InitLines;
     1502  if Lines[0] = 0 then
     1503    result := -2
     1504  else if Lines[0] > 1 then
     1505    result := -1
     1506  else
     1507    result := code[0, 0];
    12371508end;
    12381509
    12391510procedure TListDlg.FormShow(Sender: TObject);
    12401511var
    1241 i: integer;
    1242 begin
    1243 result:=-1;
    1244 Closable:=false;
    1245 
    1246 if Kind=kTribe then
    1247   begin
    1248   LineDistance:=21; // looks ugly with scrollbar
    1249   MaxLines:=(hMainTexture-(24+TitleHeight+NarrowFrame)) div LineDistance -1;
     1512  i: integer;
     1513begin
     1514  result := -1;
     1515  Closable := false;
     1516
     1517  if Kind = kTribe then
     1518  begin
     1519    LineDistance := 21; // looks ugly with scrollbar
     1520    MaxLines := (hMaintexture - (24 + TitleHeight + NarrowFrame))
     1521      div LineDistance - 1;
    12501522  end
    1251 else
    1252   begin
    1253   LineDistance:=24;
    1254   MaxLines:=(hMainTexture-(24+TitleHeight+WideFrame)) div LineDistance -1;
    1255   end;
    1256 InitLines;
    1257 
    1258 MultiPage:=false;
    1259 for i:=1 to MaxLayer-1 do if Lines[i]>0 then MultiPage:=true;
    1260 WideBottom:=MultiPage or (Kind=kScience)
    1261   or not Phrases2FallenBackToEnglish
    1262     and (Kind in [kProject,kAdvance,kFarAdvance]);
    1263 if (Kind=kAdvance) and (MyData.FarTech<>adNone)
    1264   or (Kind=kModels) or (Kind=kEModels) then
    1265   TitleHeight:=WideFrame+20
    1266 else TitleHeight:=WideFrame;
    1267 
    1268 DispLines:=Lines[0];
    1269 for i:=0 to MaxLayer-1 do if Lines[i]>DispLines then DispLines:=Lines[i];
    1270 if WideBottom then
    1271   begin
    1272   if DispLines>MaxLines then
    1273     DispLines:=MaxLines;
    1274   InnerHeight:=LineDistance*(DispLines+1)+24;
    1275   ClientHeight:=InnerHeight+TitleHeight+WideFrame
     1523  else
     1524  begin
     1525    LineDistance := 24;
     1526    MaxLines := (hMaintexture - (24 + TitleHeight + WideFrame))
     1527      div LineDistance - 1;
     1528  end;
     1529  InitLines;
     1530
     1531  MultiPage := false;
     1532  for i := 1 to MaxLayer - 1 do
     1533    if Lines[i] > 0 then
     1534      MultiPage := true;
     1535  WideBottom := MultiPage or (Kind = kScience) or
     1536    not Phrases2FallenBackToEnglish and
     1537    (Kind in [kProject, kAdvance, kFarAdvance]);
     1538  if (Kind = kAdvance) and (MyData.FarTech <> adNone) or (Kind = kModels) or
     1539    (Kind = kEModels) then
     1540    TitleHeight := WideFrame + 20
     1541  else
     1542    TitleHeight := WideFrame;
     1543
     1544  DispLines := Lines[0];
     1545  for i := 0 to MaxLayer - 1 do
     1546    if Lines[i] > DispLines then
     1547      DispLines := Lines[i];
     1548  if WideBottom then
     1549  begin
     1550    if DispLines > MaxLines then
     1551      DispLines := MaxLines;
     1552    InnerHeight := LineDistance * (DispLines + 1) + 24;
     1553    ClientHeight := InnerHeight + TitleHeight + WideFrame
    12761554  end
    1277 else
    1278   begin
    1279   if DispLines>MaxLines then
    1280     DispLines:=MaxLines;
    1281   InnerHeight:=LineDistance*(DispLines+1)+24;
    1282   ClientHeight:=InnerHeight+TitleHeight+NarrowFrame;
    1283   end;
    1284 assert(ClientHeight<=hMainTexture);
    1285 
    1286 TechNameSpace:=224;
    1287 case Kind of
    1288   kGov: InnerWidth:=272;
    1289   kCities, kCityEvents: InnerWidth:=640-18;
    1290   kTribe:
    1291     if Lines[0]>MaxLines then InnerWidth:=280+GetSystemMetrics(SM_CXVSCROLL)
    1292     else InnerWidth:=280;
    1293   kScience:
    1294     begin
    1295     InnerWidth:=104-33+15+8+TechNameSpace+24*nColumn+GetSystemMetrics(SM_CXVSCROLL);
    1296     if InnerWidth+2*SideFrame>640 then
    1297       begin
    1298       TechNameSpace:=TechNameSpace+640-InnerWidth-2*SideFrame;
    1299       InnerWidth:=640-2*SideFrame
    1300       end
    1301     end;
    1302   kAdvance,kFarAdvance:
    1303     InnerWidth:=104-33+15+8+TechNameSpace+24+GetSystemMetrics(SM_CXVSCROLL);
    1304   kChooseTech, kChooseETech, kStealTech:
    1305     InnerWidth:=104-33+15+8+TechNameSpace+GetSystemMetrics(SM_CXVSCROLL);
    1306   else InnerWidth:=363;
    1307   end;
    1308 ClientWidth:=InnerWidth+2*SideFrame;
    1309 
    1310 CloseBtn.Left:=ClientWidth-38;
    1311 CaptionLeft:=ToggleBtn.Left+ToggleBtn.Width;
    1312 CaptionRight:=CloseBtn.Left;
    1313 SetWindowPos(sb.h,0,SideFrame+InnerWidth-GetSystemMetrics(SM_CXVSCROLL),
    1314   TitleHeight,GetSystemMetrics(SM_CXVSCROLL),LineDistance*DispLines+48,
    1315   SWP_NOZORDER or SWP_NOREDRAW);
    1316 
    1317 if WindowMode=wmModal then
    1318   begin {center on screen}
    1319   if Kind=kTribe then
    1320     Left:=(Screen.Width-800)*3 div 8+130
    1321   else Left:=(Screen.Width-Width) div 2;
    1322   Top:=(Screen.Height-Height) div 2;
    1323   if Kind=kProject then
    1324     Top:=Top+48;
    1325   end;
    1326 
    1327 Layer0Btn.Visible:= MultiPage and (Lines[0]>0);
    1328 Layer1Btn.Visible:= MultiPage and (Lines[1]>0);
    1329 Layer2Btn.Visible:= MultiPage and (Lines[2]>0);
    1330 if Kind=kProject then
    1331   begin
    1332   Layer0Btn.Top:=ClientHeight-31;
    1333   Layer0Btn.Left:=ClientWidth div 2-(12+29);
    1334   Layer0Btn.Down:=true;
    1335   Layer1Btn.Top:=ClientHeight-31;
    1336   Layer1Btn.Left:=ClientWidth div 2-(12-29);
    1337   Layer1Btn.Down:=false;
    1338   Layer2Btn.Top:=ClientHeight-31;
    1339   Layer2Btn.Left:=ClientWidth div 2-12;
    1340   Layer2Btn.Down:=false;
    1341   end;
    1342 
    1343 Layer:=0;
    1344 Sel:=-2;
    1345 ScienceNation:=-1;
    1346 InitPVSB(sb,Lines[Layer]-1,DispLines);
    1347 
    1348 OffscreenPaint;
     1555  else
     1556  begin
     1557    if DispLines > MaxLines then
     1558      DispLines := MaxLines;
     1559    InnerHeight := LineDistance * (DispLines + 1) + 24;
     1560    ClientHeight := InnerHeight + TitleHeight + NarrowFrame;
     1561  end;
     1562  assert(ClientHeight <= hMaintexture);
     1563
     1564  TechNameSpace := 224;
     1565  case Kind of
     1566    kGov:
     1567      InnerWidth := 272;
     1568    kCities, kCityEvents:
     1569      InnerWidth := 640 - 18;
     1570    kTribe:
     1571      if Lines[0] > MaxLines then
     1572        InnerWidth := 280 + GetSystemMetrics(SM_CXVSCROLL)
     1573      else
     1574        InnerWidth := 280;
     1575    kScience:
     1576      begin
     1577        InnerWidth := 104 - 33 + 15 + 8 + TechNameSpace + 24 * nColumn +
     1578          GetSystemMetrics(SM_CXVSCROLL);
     1579        if InnerWidth + 2 * SideFrame > 640 then
     1580        begin
     1581          TechNameSpace := TechNameSpace + 640 - InnerWidth - 2 * SideFrame;
     1582          InnerWidth := 640 - 2 * SideFrame
     1583        end
     1584      end;
     1585    kAdvance, kFarAdvance:
     1586      InnerWidth := 104 - 33 + 15 + 8 + TechNameSpace + 24 +
     1587        GetSystemMetrics(SM_CXVSCROLL);
     1588    kChooseTech, kChooseETech, kStealTech:
     1589      InnerWidth := 104 - 33 + 15 + 8 + TechNameSpace +
     1590        GetSystemMetrics(SM_CXVSCROLL);
     1591  else
     1592    InnerWidth := 363;
     1593  end;
     1594  ClientWidth := InnerWidth + 2 * SideFrame;
     1595
     1596  CloseBtn.Left := ClientWidth - 38;
     1597  CaptionLeft := ToggleBtn.Left + ToggleBtn.Width;
     1598  CaptionRight := CloseBtn.Left;
     1599  SetWindowPos(sb.h, 0, SideFrame + InnerWidth - GetSystemMetrics(SM_CXVSCROLL),
     1600    TitleHeight, GetSystemMetrics(SM_CXVSCROLL), LineDistance * DispLines + 48,
     1601    SWP_NOZORDER or SWP_NOREDRAW);
     1602
     1603  if WindowMode = wmModal then
     1604  begin { center on screen }
     1605    if Kind = kTribe then
     1606      Left := (Screen.Width - 800) * 3 div 8 + 130
     1607    else
     1608      Left := (Screen.Width - Width) div 2;
     1609    Top := (Screen.Height - Height) div 2;
     1610    if Kind = kProject then
     1611      Top := Top + 48;
     1612  end;
     1613
     1614  Layer0Btn.Visible := MultiPage and (Lines[0] > 0);
     1615  Layer1Btn.Visible := MultiPage and (Lines[1] > 0);
     1616  Layer2Btn.Visible := MultiPage and (Lines[2] > 0);
     1617  if Kind = kProject then
     1618  begin
     1619    Layer0Btn.Top := ClientHeight - 31;
     1620    Layer0Btn.Left := ClientWidth div 2 - (12 + 29);
     1621    Layer0Btn.Down := true;
     1622    Layer1Btn.Top := ClientHeight - 31;
     1623    Layer1Btn.Left := ClientWidth div 2 - (12 - 29);
     1624    Layer1Btn.Down := false;
     1625    Layer2Btn.Top := ClientHeight - 31;
     1626    Layer2Btn.Left := ClientWidth div 2 - 12;
     1627    Layer2Btn.Down := false;
     1628  end;
     1629
     1630  Layer := 0;
     1631  Sel := -2;
     1632  ScienceNation := -1;
     1633  InitPVSB(sb, Lines[Layer] - 1, DispLines);
     1634
     1635  OffscreenPaint;
    13491636end;
    13501637
    13511638procedure TListDlg.ShowNewContent(NewMode: integer; ListKind: TListKind);
    13521639var
    1353 i: integer;
    1354 ShowFocus, forceclose: boolean;
    1355 begin
    1356 forceclose:= (ListKind<>Kind)
    1357   and not ((Kind=kCities) and (ListKind=kCityEvents))
    1358   and not ((Kind=kCityEvents) and (ListKind=kCities))
    1359   and not ((Kind=kModels) and (ListKind=kEModels))
    1360   and not ((Kind=kEModels) and (ListKind=kModels));
    1361 
    1362 Kind:=ListKind;
    1363 ModalIndication:= not (Kind in MustChooseKind);
    1364 case Kind of
    1365   kProject: Caption:=Phrases.Lookup('TITLE_PROJECT');
    1366   kAdvance: Caption:=Phrases.Lookup('TITLE_TECHSELECT');
    1367   kFarAdvance: Caption:=Phrases.Lookup('TITLE_FARTECH');
    1368   kModels, kEModels: Caption:=Phrases.Lookup('FRMILREP');
    1369   kAllEModels: Caption:=Phrases.Lookup('TITLE_EMODELS');
    1370   kTribe: Caption:=Phrases.Lookup('TITLE_TRIBE');
    1371   kScience: Caption:=Phrases.Lookup('TITLE_SCIENCE');
    1372   kShipPart, kEShipPart: Caption:=Phrases.Lookup('TITLE_CHOOSESHIPPART');
    1373   kChooseTech, kChooseETech: Caption:=Phrases.Lookup('TITLE_CHOOSETECH');
    1374   kChooseModel, kChooseEModel: Caption:=Phrases.Lookup('TITLE_CHOOSEMODEL');
    1375   kStealTech: Caption:=Phrases.Lookup('TITLE_CHOOSETECH');
    1376   kGov: Caption:=Phrases.Lookup('TITLE_GOV');
    1377   kMission: Caption:=Phrases.Lookup('TITLE_SPYMISSION');
    1378   end;
    1379 
    1380 case Kind of
    1381   kMission: HelpContext:='SPYMISSIONS';
    1382   else HelpContext:='CONCEPTS'
    1383   end;
    1384 
    1385 if Kind=kAdvance then
    1386   begin
    1387   ToggleBtn.ButtonIndex:=13;
    1388   ToggleBtn.Hint:=Phrases.Lookup('FARTECH')
     1640  i: integer;
     1641  ShowFocus, forceclose: boolean;
     1642begin
     1643  forceclose := (ListKind <> Kind) and
     1644    not((Kind = kCities) and (ListKind = kCityEvents)) and
     1645    not((Kind = kCityEvents) and (ListKind = kCities)) and
     1646    not((Kind = kModels) and (ListKind = kEModels)) and
     1647    not((Kind = kEModels) and (ListKind = kModels));
     1648
     1649  Kind := ListKind;
     1650  ModalIndication := not(Kind in MustChooseKind);
     1651  case Kind of
     1652    kProject:
     1653      Caption := Phrases.Lookup('TITLE_PROJECT');
     1654    kAdvance:
     1655      Caption := Phrases.Lookup('TITLE_TECHSELECT');
     1656    kFarAdvance:
     1657      Caption := Phrases.Lookup('TITLE_FARTECH');
     1658    kModels, kEModels:
     1659      Caption := Phrases.Lookup('FRMILREP');
     1660    kAllEModels:
     1661      Caption := Phrases.Lookup('TITLE_EMODELS');
     1662    kTribe:
     1663      Caption := Phrases.Lookup('TITLE_TRIBE');
     1664    kScience:
     1665      Caption := Phrases.Lookup('TITLE_SCIENCE');
     1666    kShipPart, kEShipPart:
     1667      Caption := Phrases.Lookup('TITLE_CHOOSESHIPPART');
     1668    kChooseTech, kChooseETech:
     1669      Caption := Phrases.Lookup('TITLE_CHOOSETECH');
     1670    kChooseModel, kChooseEModel:
     1671      Caption := Phrases.Lookup('TITLE_CHOOSEMODEL');
     1672    kStealTech:
     1673      Caption := Phrases.Lookup('TITLE_CHOOSETECH');
     1674    kGov:
     1675      Caption := Phrases.Lookup('TITLE_GOV');
     1676    kMission:
     1677      Caption := Phrases.Lookup('TITLE_SPYMISSION');
     1678  end;
     1679
     1680  case Kind of
     1681    kMission:
     1682      HelpContext := 'SPYMISSIONS';
     1683  else
     1684    HelpContext := 'CONCEPTS'
     1685  end;
     1686
     1687  if Kind = kAdvance then
     1688  begin
     1689    ToggleBtn.ButtonIndex := 13;
     1690    ToggleBtn.Hint := Phrases.Lookup('FARTECH')
    13891691  end
    1390 else if Kind=kCities then
    1391   begin
    1392   ToggleBtn.ButtonIndex:=15;
    1393   ToggleBtn.Hint:=Phrases.Lookup('BTN_PAGE')
     1692  else if Kind = kCities then
     1693  begin
     1694    ToggleBtn.ButtonIndex := 15;
     1695    ToggleBtn.Hint := Phrases.Lookup('BTN_PAGE')
    13941696  end
    1395 else
    1396   begin
    1397   ToggleBtn.ButtonIndex:=28;
    1398   ToggleBtn.Hint:=Phrases.Lookup('BTN_SELECT')
    1399   end;
    1400 
    1401 if Kind=kAdvance then // show focus button?
    1402   if MyData.FarTech<>adNone then
    1403     ShowFocus:=true
    14041697  else
     1698  begin
     1699    ToggleBtn.ButtonIndex := 28;
     1700    ToggleBtn.Hint := Phrases.Lookup('BTN_SELECT')
     1701  end;
     1702
     1703  if Kind = kAdvance then // show focus button?
     1704    if MyData.FarTech <> adNone then
     1705      ShowFocus := true
     1706    else
    14051707    begin
    1406     ShowFocus:=false;
    1407     for i:=0 to nAdv-1 do
    1408       if not (i in FutureTech) and (MyRO.Tech[i]<tsApplicable)
    1409         and ((AdvValue[i]<2000) or (MyRO.Tech[adMassProduction]>tsNA))
    1410         and ((AdvValue[i]<1000) or (MyRO.Tech[adScience]>tsNA))
    1411         and (Server(sSetResearch-sExecute,me,i,nil^)<rExecuted) then
    1412         ShowFocus:=true;
     1708      ShowFocus := false;
     1709      for i := 0 to nAdv - 1 do
     1710        if not(i in FutureTech) and (MyRO.Tech[i] < tsApplicable) and
     1711          ((AdvValue[i] < 2000) or (MyRO.Tech[adMassProduction] > tsNA)) and
     1712          ((AdvValue[i] < 1000) or (MyRO.Tech[adScience] > tsNA)) and
     1713          (Server(sSetResearch - sExecute, me, i, nil^) < rExecuted) then
     1714          ShowFocus := true;
    14131715    end;
    1414 ToggleBtn.Visible:= (Kind=kCities) and not supervising
    1415   or (Kind=kAdvance) and ShowFocus
    1416   or (Kind=kModels)
    1417   or (Kind=kEModels);
    1418 CloseBtn.Visible:= not(Kind in MustChooseKind);
    1419 
    1420 inherited ShowNewContent(NewMode, forceclose);
     1716  ToggleBtn.Visible := (Kind = kCities) and not supervising or (Kind = kAdvance)
     1717    and ShowFocus or (Kind = kModels) or (Kind = kEModels);
     1718  CloseBtn.Visible := not(Kind in MustChooseKind);
     1719
     1720  inherited ShowNewContent(NewMode, forceclose);
    14211721end; // ShowNewContent
    14221722
    14231723procedure TListDlg.ShowNewContent_CityProject(NewMode, cix: integer);
    14241724begin
    1425 cixProject:=cix;
    1426 ShowNewContent(NewMode, kProject);
     1725  cixProject := cix;
     1726  ShowNewContent(NewMode, kProject);
    14271727end;
    14281728
    14291729procedure TListDlg.ShowNewContent_MilReport(NewMode, p: integer);
    14301730begin
    1431 pView:=p;
    1432 if p=me then ShowNewContent(NewMode, kModels)
    1433 else ShowNewContent(NewMode, kEModels)
     1731  pView := p;
     1732  if p = me then
     1733    ShowNewContent(NewMode, kModels)
     1734  else
     1735    ShowNewContent(NewMode, kEModels)
    14341736end;
    14351737
    14361738procedure TListDlg.PlayerClick(Sender: TObject);
    14371739begin
    1438 if TComponent(Sender).Tag=me then Kind:=kModels
    1439 else
    1440   begin
    1441   Kind:=kEModels;
    1442   pView:=TComponent(Sender).Tag;
    1443   end;
    1444 InitLines;
    1445 Sel:=-2;
    1446 InitPVSB(sb,Lines[Layer]-1,DispLines);
    1447 OffscreenPaint;
    1448 Invalidate
     1740  if TComponent(Sender).Tag = me then
     1741    Kind := kModels
     1742  else
     1743  begin
     1744    Kind := kEModels;
     1745    pView := TComponent(Sender).Tag;
     1746  end;
     1747  InitLines;
     1748  Sel := -2;
     1749  InitPVSB(sb, Lines[Layer] - 1, DispLines);
     1750  OffscreenPaint;
     1751  Invalidate
    14491752end;
    14501753
    14511754procedure TListDlg.ModeBtnClick(Sender: TObject);
    14521755begin
    1453 Layer0Btn.Down:= Sender=Layer0Btn;
    1454 Layer1Btn.Down:= Sender=Layer1Btn;
    1455 Layer2Btn.Down:= Sender=Layer2Btn;
    1456 Layer:=TComponent(Sender).Tag;
    1457 
    1458 Sel:=-2;
    1459 InitPVSB(sb,Lines[Layer]-1,DispLines);
    1460 SmartUpdateContent
     1756  Layer0Btn.Down := Sender = Layer0Btn;
     1757  Layer1Btn.Down := Sender = Layer1Btn;
     1758  Layer2Btn.Down := Sender = Layer2Btn;
     1759  Layer := TComponent(Sender).Tag;
     1760
     1761  Sel := -2;
     1762  InitPVSB(sb, Lines[Layer] - 1, DispLines);
     1763  SmartUpdateContent
    14611764end;
    14621765
    14631766procedure TListDlg.ToggleBtnClick(Sender: TObject);
    14641767var
    1465 p1: integer;
    1466 m: TMenuItem;
    1467 begin
    1468 case Kind of
    1469   kAdvance:
    1470     begin
    1471     result:=adFar;
    1472     Closable:=true;
    1473     Close
    1474     end;
    1475   kCities, kCityEvents:
    1476     begin
    1477     if Kind=kCities then Kind:=kCityEvents
    1478     else Kind:=kCities;
    1479     OffscreenPaint;
    1480     Invalidate;
    1481     end;
    1482   kModels, kEModels:
    1483     begin
    1484     EmptyMenu(Popup.Items);
    1485     if G.Difficulty[me]>0 then
    1486       begin
    1487       m:=TMenuItem.Create(Popup);
    1488       m.RadioItem:=true;
    1489       m.Caption:=Tribe[me].TPhrase('SHORTNAME');
    1490       m.Tag:=me;
    1491       m.OnClick:=PlayerClick;
    1492       if Kind=kModels then m.Checked:=true;
    1493       Popup.Items.Add(m);
    1494       end;
    1495     for p1:=0 to nPl-1 do
    1496       if (p1<>me) and (MyRO.EnemyReport[p1]<>nil)
    1497         and (MyRO.EnemyReport[p1].TurnOfMilReport>=0) then
    1498         begin
    1499         m:=TMenuItem.Create(Popup);
    1500         m.RadioItem:=true;
    1501         m.Caption:=Tribe[p1].TPhrase('SHORTNAME');
    1502         m.Tag:=p1;
    1503         m.OnClick:=PlayerClick;
    1504         if (Kind=kEModels) and (p1=pView) then m.Checked:=true;
    1505         Popup.Items.Add(m);
    1506         end;
    1507     Popup.Popup(Left+ToggleBtn.Left, Top+ToggleBtn.Top+ToggleBtn.Height);
    1508     end
     1768  p1: integer;
     1769  m: TMenuItem;
     1770begin
     1771  case Kind of
     1772    kAdvance:
     1773      begin
     1774        result := adFar;
     1775        Closable := true;
     1776        Close
     1777      end;
     1778    kCities, kCityEvents:
     1779      begin
     1780        if Kind = kCities then
     1781          Kind := kCityEvents
     1782        else
     1783          Kind := kCities;
     1784        OffscreenPaint;
     1785        Invalidate;
     1786      end;
     1787    kModels, kEModels:
     1788      begin
     1789        EmptyMenu(Popup.Items);
     1790        if G.Difficulty[me] > 0 then
     1791        begin
     1792          m := TMenuItem.Create(Popup);
     1793          m.RadioItem := true;
     1794          m.Caption := Tribe[me].TPhrase('SHORTNAME');
     1795          m.Tag := me;
     1796          m.OnClick := PlayerClick;
     1797          if Kind = kModels then
     1798            m.Checked := true;
     1799          Popup.Items.Add(m);
     1800        end;
     1801        for p1 := 0 to nPl - 1 do
     1802          if (p1 <> me) and (MyRO.EnemyReport[p1] <> nil) and
     1803            (MyRO.EnemyReport[p1].TurnOfMilReport >= 0) then
     1804          begin
     1805            m := TMenuItem.Create(Popup);
     1806            m.RadioItem := true;
     1807            m.Caption := Tribe[p1].TPhrase('SHORTNAME');
     1808            m.Tag := p1;
     1809            m.OnClick := PlayerClick;
     1810            if (Kind = kEModels) and (p1 = pView) then
     1811              m.Checked := true;
     1812            Popup.Items.Add(m);
     1813          end;
     1814        Popup.Popup(Left + ToggleBtn.Left, Top + ToggleBtn.Top +
     1815          ToggleBtn.Height);
     1816      end
    15091817  end
    15101818end;
     
    15131821  Shift: TShiftState);
    15141822begin
    1515 if (Key=VK_F2) and (Kind in [kModels,kEModels]) then // my key
    1516   // !!! toggle
    1517 else if (Key=VK_F3) and (Kind in [kCities,kCityEvents]) then // my key
    1518   ToggleBtnClick(nil)
    1519 else if ((Key=VK_ESCAPE) or (Key=VK_RETURN)) and not CloseBtn.Visible then // prevent closing
    1520 else inherited
     1823  if (Key = VK_F2) and (Kind in [kModels, kEModels]) then // my key
     1824    // !!! toggle
     1825  else if (Key = VK_F3) and (Kind in [kCities, kCityEvents]) then // my key
     1826    ToggleBtnClick(nil)
     1827  else if ((Key = VK_ESCAPE) or (Key = VK_RETURN)) and not CloseBtn.Visible then
     1828  // prevent closing
     1829  else
     1830    inherited
    15211831end;
    15221832
    15231833procedure TListDlg.EcoChange;
    15241834begin
    1525 if Visible and (Kind=kCities) then SmartUpdateContent
     1835  if Visible and (Kind = kCities) then
     1836    SmartUpdateContent
    15261837end;
    15271838
    15281839procedure TListDlg.TechChange;
    15291840begin
    1530 if Visible and (Kind=kScience) then
    1531   begin
    1532   FormShow(nil);
    1533   Invalidate;
     1841  if Visible and (Kind = kScience) then
     1842  begin
     1843    FormShow(nil);
     1844    Invalidate;
    15341845  end;
    15351846end;
     
    15371848procedure TListDlg.AddCity;
    15381849begin
    1539 if Visible and (Kind=kCities) then
    1540   begin
    1541   FormShow(nil);
    1542   Invalidate;
     1850  if Visible and (Kind = kCities) then
     1851  begin
     1852    FormShow(nil);
     1853    Invalidate;
    15431854  end;
    15441855end;
     
    15461857procedure TListDlg.RemoveUnit;
    15471858begin
    1548 if ListDlg.Visible and (Kind=kModels) then
    1549   SmartUpdateContent;
     1859  if ListDlg.Visible and (Kind = kModels) then
     1860    SmartUpdateContent;
    15501861end;
    15511862
    15521863end.
    1553 
  • trunk/LocalPlayer/TechTree.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit TechTree;
    43
     
    65
    76uses
    8   ScreenTools,Messg,
     7  ScreenTools, Messg,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    2120    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
    2221      Shift: TShiftState; X, Y: Integer);
    23     procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
    24       Y: Integer);
    25     procedure FormKeyDown(Sender: TObject; var Key: Word;
    26       Shift: TShiftState);
     22    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
     23    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    2724    procedure CloseBtnClick(Sender: TObject);
    2825  private
    29     xOffset, yOffset, xDown, yDown: integer;
     26    xOffset, yOffset, xDown, yDown: Integer;
    3027    Image: TBitmap;
    3128    dragging: boolean;
     
    4340
    4441const
    45 BlackBorder=4;
    46 LeftBorder=72; RightBorder=45; TopBorder=16; BottomBorder=48;
    47 xStart=0; yStart=40;
    48 xPitch=160; yPitch=90;
    49 xLegend=44; yLegend=79; yLegendPitch=32;
    50 
    51 function min(a,b: integer): integer;
    52 begin
    53 if a<b then
    54   result:=a
    55 else result:=b;
    56 end;
    57 
    58 function max(a,b: integer): integer;
    59 begin
    60 if a>b then
    61   result:=a
    62 else result:=b;
     42  BlackBorder = 4;
     43  LeftBorder = 72;
     44  RightBorder = 45;
     45  TopBorder = 16;
     46  BottomBorder = 48;
     47  xStart = 0;
     48  yStart = 40;
     49  xPitch = 160;
     50  yPitch = 90;
     51  xLegend = 44;
     52  yLegend = 79;
     53  yLegendPitch = 32;
     54
     55function min(a, b: Integer): Integer;
     56begin
     57  if a < b then
     58    result := a
     59  else
     60    result := b;
     61end;
     62
     63function max(a, b: Integer): Integer;
     64begin
     65  if a > b then
     66    result := a
     67  else
     68    result := b;
    6369end;
    6470
    6571procedure TTechTreeDlg.FormCreate(Sender: TObject);
    6672begin
    67 InitButtons;
    68 Image:=nil;
     73  InitButtons;
     74  Image := nil;
    6975end;
    7076
    7177procedure TTechTreeDlg.FormPaint(Sender: TObject);
    7278var
    73 x,w: integer;
    74 begin
    75 with Canvas do
    76   begin
    77   // black border
    78   brush.color:=$000000;
    79   fillrect(rect(0,0,BlackBorder,ClientHeight));
    80   fillrect(rect(BlackBorder,0,ClientWidth-BlackBorder,BlackBorder));
    81   fillrect(rect(ClientWidth-BlackBorder,0,ClientWidth,ClientHeight));
    82   fillrect(rect(BlackBorder,ClientHeight-BlackBorder,ClientWidth-BlackBorder,
    83     ClientHeight));
    84 
    85   // texturize empty space
    86   brush.color:=$FFFFFF;
    87   if xOffset>0 then
    88     FillRectSeamless(Canvas,BlackBorder,BlackBorder,BlackBorder+xOffset,
    89       ClientHeight-BlackBorder,-BlackBorder-xOffset,-BlackBorder-yOffset,Paper);
    90   if xOffset+Image.width<ClientWidth-2*BlackBorder then
    91     FillRectSeamless(Canvas,BlackBorder+xOffset+Image.width,BlackBorder,
    92       ClientWidth-BlackBorder,ClientHeight-BlackBorder,-BlackBorder-xOffset,
    93       -BlackBorder-yOffset,Paper);
    94   x:=max(BlackBorder,BlackBorder+xOffset);
    95   w:=min(BlackBorder+xOffset+Image.width,ClientWidth-BlackBorder);
    96   if yOffset>0 then
    97     FillRectSeamless(Canvas,x,BlackBorder,w,BlackBorder+yOffset,
    98       -BlackBorder-xOffset,-BlackBorder-yOffset,Paper);
    99   if yOffset+Image.height<ClientHeight-2*BlackBorder then
    100     FillRectSeamless(Canvas,x,BlackBorder+yOffset+Image.height,w,
    101       ClientHeight-BlackBorder,-BlackBorder-xOffset,-BlackBorder-yOffset,Paper);
     79  X, w: Integer;
     80begin
     81  with Canvas do
     82  begin
     83    // black border
     84    brush.color := $000000;
     85    fillrect(rect(0, 0, BlackBorder, ClientHeight));
     86    fillrect(rect(BlackBorder, 0, ClientWidth - BlackBorder, BlackBorder));
     87    fillrect(rect(ClientWidth - BlackBorder, 0, ClientWidth, ClientHeight));
     88    fillrect(rect(BlackBorder, ClientHeight - BlackBorder,
     89      ClientWidth - BlackBorder, ClientHeight));
     90
     91    // texturize empty space
     92    brush.color := $FFFFFF;
     93    if xOffset > 0 then
     94      FillRectSeamless(Canvas, BlackBorder, BlackBorder, BlackBorder + xOffset,
     95        ClientHeight - BlackBorder, -BlackBorder - xOffset,
     96        -BlackBorder - yOffset, Paper);
     97    if xOffset + Image.width < ClientWidth - 2 * BlackBorder then
     98      FillRectSeamless(Canvas, BlackBorder + xOffset + Image.width, BlackBorder,
     99        ClientWidth - BlackBorder, ClientHeight - BlackBorder,
     100        -BlackBorder - xOffset, -BlackBorder - yOffset, Paper);
     101    X := max(BlackBorder, BlackBorder + xOffset);
     102    w := min(BlackBorder + xOffset + Image.width, ClientWidth - BlackBorder);
     103    if yOffset > 0 then
     104      FillRectSeamless(Canvas, X, BlackBorder, w, BlackBorder + yOffset,
     105        -BlackBorder - xOffset, -BlackBorder - yOffset, Paper);
     106    if yOffset + Image.height < ClientHeight - 2 * BlackBorder then
     107      FillRectSeamless(Canvas, X, BlackBorder + yOffset + Image.height, w,
     108        ClientHeight - BlackBorder, -BlackBorder - xOffset,
     109        -BlackBorder - yOffset, Paper);
    102110  end;
    103 BitBlt(Canvas.Handle,max(BlackBorder,BlackBorder+xOffset),
    104   max(BlackBorder,BlackBorder+yOffset),
    105   min(Image.width,min(Image.width+xOffset,
    106     min(ClientWidth-2*BlackBorder,ClientWidth-2*BlackBorder-xOffset))),
    107   min(Image.Height,min(Image.height+yOffset,
    108     min(ClientHeight-2*BlackBorder,ClientHeight-2*BlackBorder-yOffset))),
    109   Image.Canvas.Handle,max(0,-xOffset),max(0,-yOffset),SRCCOPY);
     111  BitBlt(Canvas.Handle, max(BlackBorder, BlackBorder + xOffset),
     112    max(BlackBorder, BlackBorder + yOffset),
     113    min(Image.width, min(Image.width + xOffset,
     114    min(ClientWidth - 2 * BlackBorder, ClientWidth - 2 * BlackBorder - xOffset))
     115    ), min(Image.height, min(Image.height + yOffset,
     116    min(ClientHeight - 2 * BlackBorder, ClientHeight - 2 * BlackBorder -
     117    yOffset))), Image.Canvas.Handle, max(0, -xOffset),
     118    max(0, -yOffset), SRCCOPY);
    110119end;
    111120
    112121procedure TTechTreeDlg.FormShow(Sender: TObject);
    113122type
    114 TLine=array[0..9999,0..2] of Byte;
     123  TLine = array [0 .. 9999, 0 .. 2] of Byte;
    115124var
    116 x,y,ad,TexWidth,TexHeight: integer;
    117 s: string;
    118 SrcLine, DstLine: ^TLine;
    119 begin
    120 if Image=nil then
    121   begin
    122   Image:=TBitmap.Create;
    123   LoadGraphicFile(Image, HomeDir+'Help\AdvTree',gfNoGamma);
    124   Image.PixelFormat:=pf24bit;
    125 
    126   with Image.Canvas do
     125  X, Y, ad, TexWidth, TexHeight: Integer;
     126  s: string;
     127  SrcLine, DstLine: ^TLine;
     128begin
     129  if Image = nil then
     130  begin
     131    Image := TBitmap.Create;
     132    LoadGraphicFile(Image, HomeDir + 'Help\AdvTree', gfNoGamma);
     133    Image.PixelFormat := pf24bit;
     134
     135    with Image.Canvas do
    127136    begin
    128     // write advance names
    129     Font.Assign(UniFont[ftSmall]);
    130     Font.Color:=clBlack;
    131     Brush.Style:=bsClear;
    132     for x:=0 to (Image.width-xStart) div xPitch do
    133       for y:=0 to (Image.height-yStart) div yPitch do
     137      // write advance names
     138      Font.Assign(UniFont[ftSmall]);
     139      Font.color := clBlack;
     140      brush.Style := bsClear;
     141      for X := 0 to (Image.width - xStart) div xPitch do
     142        for Y := 0 to (Image.height - yStart) div yPitch do
    134143        begin
    135         ad:=Pixels[xStart+x*xPitch+10,yStart+y*yPitch-1];
    136         if ad and $FFFF00=0 then
     144          ad := Pixels[xStart + X * xPitch + 10, yStart + Y * yPitch - 1];
     145          if ad and $FFFF00 = 0 then
    137146          begin
    138           s:=Phrases.Lookup('ADVANCES',ad);
    139           while TextWidth(s)>112 do
    140             Delete(s,Length(s),1);
    141           TextOut(xStart+x*xPitch+2,yStart+y*yPitch,s);
    142           Pixels[xStart+x*xPitch+10,yStart+y*yPitch-1]:=$7F007F;
     147            s := Phrases.Lookup('ADVANCES', ad);
     148            while TextWidth(s) > 112 do
     149              Delete(s, Length(s), 1);
     150            TextOut(xStart + X * xPitch + 2, yStart + Y * yPitch, s);
     151            Pixels[xStart + X * xPitch + 10, yStart + Y * yPitch - 1]
     152              := $7F007F;
    143153          end
    144154        end;
    145155
    146     // write legend
    147     TextOut(xLegend,yLegend,Phrases2.Lookup('ADVTREE_UP0'));
    148     TextOut(xLegend,yLegend+yLegendPitch,Phrases2.Lookup('ADVTREE_UP1'));
    149     TextOut(xLegend,yLegend+2*yLegendPitch,Phrases2.Lookup('ADVTREE_UP2'));
    150     TextOut(xLegend,yLegend+3*yLegendPitch,Phrases2.Lookup('ADVTREE_GOV'));
    151     TextOut(xLegend,yLegend+4*yLegendPitch,Phrases2.Lookup('ADVTREE_OTHER'));
     156      // write legend
     157      TextOut(xLegend, yLegend, Phrases2.Lookup('ADVTREE_UP0'));
     158      TextOut(xLegend, yLegend + yLegendPitch, Phrases2.Lookup('ADVTREE_UP1'));
     159      TextOut(xLegend, yLegend + 2 * yLegendPitch,
     160        Phrases2.Lookup('ADVTREE_UP2'));
     161      TextOut(xLegend, yLegend + 3 * yLegendPitch,
     162        Phrases2.Lookup('ADVTREE_GOV'));
     163      TextOut(xLegend, yLegend + 4 * yLegendPitch,
     164        Phrases2.Lookup('ADVTREE_OTHER'));
    152165    end;
    153166
    154   // texturize background
    155   TexWidth:=Paper.width;
    156   TexHeight:=Paper.height;
    157   for y:=0 to Image.height-1 do
     167    // texturize background
     168    TexWidth := Paper.width;
     169    TexHeight := Paper.height;
     170    for Y := 0 to Image.height - 1 do
    158171    begin
    159     SrcLine:=Paper.ScanLine[y mod TexHeight];
    160     DstLine:=Image.ScanLine[y];
    161     for x:=0 to Image.Width-1 do
     172      SrcLine := Paper.ScanLine[Y mod TexHeight];
     173      DstLine := Image.ScanLine[Y];
     174      for X := 0 to Image.width - 1 do
    162175      begin
    163       if Cardinal((@DstLine[x])^) and $FFFFFF=$7F007F then // transparent
    164         DstLine[x]:=SrcLine[x mod TexWidth];
     176        if Cardinal((@DstLine[X])^) and $FFFFFF = $7F007F then // transparent
     177          DstLine[X] := SrcLine[X mod TexWidth];
    165178      end
    166179    end
    167180  end;
    168181
    169 // fit window to image, center image in window, center window to screen
    170 Width:=min(Screen.Width-40,Image.Width+LeftBorder+RightBorder+2*BlackBorder);
    171 Height:=min(Screen.Height-40,Image.Height+TopBorder+BottomBorder+2*BlackBorder);
    172 Left:=(Screen.Width-Width) div 2;
    173 Top:=(Screen.Height-Height) div 2;
    174 CloseBtn.Left:=Width-CloseBtn.Width-BlackBorder-8;
    175 CloseBtn.Top:=BlackBorder+8;
    176 xOffset:=(ClientWidth-Image.width+LeftBorder-RightBorder) div 2-BlackBorder;
    177 yOffset:=ClientHeight-2*BlackBorder-Image.height-BottomBorder;
     182  // fit window to image, center image in window, center window to screen
     183  width := min(Screen.width - 40, Image.width + LeftBorder + RightBorder + 2 *
     184    BlackBorder);
     185  height := min(Screen.height - 40, Image.height + TopBorder + BottomBorder + 2
     186    * BlackBorder);
     187  Left := (Screen.width - width) div 2;
     188  Top := (Screen.height - height) div 2;
     189  CloseBtn.Left := width - CloseBtn.width - BlackBorder - 8;
     190  CloseBtn.Top := BlackBorder + 8;
     191  xOffset := (ClientWidth - Image.width + LeftBorder - RightBorder) div 2 -
     192    BlackBorder;
     193  yOffset := ClientHeight - 2 * BlackBorder - Image.height - BottomBorder;
    178194end;
    179195
     
    181197  Shift: TShiftState; X, Y: Integer);
    182198begin
    183 if Button=mbLeft then
    184   begin
    185   dragging:=true;
    186   xDown:=x;
    187   yDown:=y;
     199  if Button = mbLeft then
     200  begin
     201    dragging := true;
     202    xDown := X;
     203    yDown := Y;
    188204  end
    189205end;
     
    192208  Shift: TShiftState; X, Y: Integer);
    193209begin
    194 dragging:=false;
     210  dragging := false;
    195211end;
    196212
     
    198214  X, Y: Integer);
    199215begin
    200 if dragging then
    201   begin
    202   xOffset:=xOffset+x-xDown;
    203   yOffset:=yOffset+y-yDown;
    204   xDown:=x;
    205   yDown:=y;
    206 
    207   if xOffset>LeftBorder then
    208     xOffset:=LeftBorder;
    209   if xOffset<ClientWidth-2*BlackBorder-Image.width-RightBorder then
    210     xOffset:=ClientWidth-2*BlackBorder-Image.width-RightBorder;
    211   if yOffset>TopBorder then
    212     yOffset:=TopBorder;
    213   if yOffset<ClientHeight-2*BlackBorder-Image.height-BottomBorder then
    214     yOffset:=ClientHeight-2*BlackBorder-Image.height-BottomBorder;
    215 
    216   SmartInvalidate;
     216  if dragging then
     217  begin
     218    xOffset := xOffset + X - xDown;
     219    yOffset := yOffset + Y - yDown;
     220    xDown := X;
     221    yDown := Y;
     222
     223    if xOffset > LeftBorder then
     224      xOffset := LeftBorder;
     225    if xOffset < ClientWidth - 2 * BlackBorder - Image.width - RightBorder then
     226      xOffset := ClientWidth - 2 * BlackBorder - Image.width - RightBorder;
     227    if yOffset > TopBorder then
     228      yOffset := TopBorder;
     229    if yOffset < ClientHeight - 2 * BlackBorder - Image.height - BottomBorder
     230    then
     231      yOffset := ClientHeight - 2 * BlackBorder - Image.height - BottomBorder;
     232
     233    SmartInvalidate;
    217234  end
    218235end;
     
    221238  Shift: TShiftState);
    222239begin
    223 if key=VK_ESCAPE then
    224   Close;
     240  if Key = VK_ESCAPE then
     241    Close;
    225242end;
    226243
    227244procedure TTechTreeDlg.CloseBtnClick(Sender: TObject);
    228245begin
    229 Close();
     246  Close();
    230247end;
    231248
  • trunk/LocalPlayer/Term.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Term;
    43
     
    65
    76uses
    8   Protocol,Tribes,PVSB,ClientTools,ScreenTools,BaseWin,Messg,ButtonBase,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Menus,ExtCtrls,
    11   ButtonA,ButtonB, ButtonC, EOTButton, Area;
     7  Protocol, Tribes, PVSB, ClientTools, ScreenTools, BaseWin, Messg, ButtonBase,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Menus,
     10  ExtCtrls,
     11  ButtonA, ButtonB, ButtonC, EOTButton, Area;
    1212
    1313const
    14 WM_EOT=WM_USER;
    15 
    16 pltsNormal=0; pltsBlink=1;
     14  WM_EOT = WM_USER;
     15
     16  pltsNormal = 0;
     17  pltsBlink = 1;
    1718
    1819type
    1920  TMainScreen = class(TDrawDlg)
    20     Timer1:TTimer;
     21    Timer1: TTimer;
    2122    GamePopup: TPopupMenu;
    22     UnitPopup:TPopupMenu;
    23     mIrrigation:TMenuItem;
    24     mCity:TMenuItem;
    25     mRoad:TMenuItem;
    26     mMine:TMenuItem;
    27     mPollution:TMenuItem;
    28     mHome:TMenuItem;
     23    UnitPopup: TPopupMenu;
     24    mIrrigation: TMenuItem;
     25    mCity: TMenuItem;
     26    mRoad: TMenuItem;
     27    mMine: TMenuItem;
     28    mPollution: TMenuItem;
     29    mHome: TMenuItem;
    2930    mStay: TMenuItem;
    30     mDisband:TMenuItem;
    31     mWait:TMenuItem;
    32     mNoOrders:TMenuItem;
    33     MTrans:TMenuItem;
     31    mDisband: TMenuItem;
     32    mWait: TMenuItem;
     33    mNoOrders: TMenuItem;
     34    MTrans: TMenuItem;
    3435    UnitBtn: TButtonB;
    3536    mResign: TMenuItem;
     
    163164    N12: TMenuItem;
    164165    mRep14: TMenuItem;
    165     procedure FormCreate(Sender:TObject);
    166     procedure FormDestroy(Sender:TObject);
    167     procedure Timer1Timer(Sender:TObject);
    168     procedure MapBoxMouseDown(Sender:TObject;Button:TMouseButton;
    169       Shift:TShiftState;x,y:integer);
    170     procedure EOTClick(Sender:TObject);
    171     procedure PanelBoxMouseDown(Sender:TObject;Button:TMouseButton;
    172       Shift:TShiftState;x,y:integer);
    173     procedure FormKeyDown(Sender:TObject;var Key:word;
    174       Shift:TShiftState);
    175     procedure MenuClick(Sender:TObject);
    176     procedure FormResize(Sender:TObject);
     166    procedure FormCreate(Sender: TObject);
     167    procedure FormDestroy(Sender: TObject);
     168    procedure Timer1Timer(Sender: TObject);
     169    procedure MapBoxMouseDown(Sender: TObject; Button: TMouseButton;
     170      Shift: TShiftState; x, y: integer);
     171    procedure EOTClick(Sender: TObject);
     172    procedure PanelBoxMouseDown(Sender: TObject; Button: TMouseButton;
     173      Shift: TShiftState; x, y: integer);
     174    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
     175    procedure MenuClick(Sender: TObject);
     176    procedure FormResize(Sender: TObject);
    177177    procedure PanelBtnClick(Sender: TObject);
    178178    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
    179179    procedure Toggle(Sender: TObject);
    180     procedure PanelBoxMouseMove(Sender: TObject; Shift: TShiftState; x,
    181       y: integer);
     180    procedure PanelBoxMouseMove(Sender: TObject; Shift: TShiftState;
     181      x, y: integer);
    182182    procedure PanelBoxMouseUp(Sender: TObject; Button: TMouseButton;
    183183      Shift: TShiftState; x, y: integer);
    184     procedure MapBoxMouseMove(Sender: TObject; Shift: TShiftState; x,
    185       y: integer);
     184    procedure MapBoxMouseMove(Sender: TObject; Shift: TShiftState;
     185      x, y: integer);
    186186    procedure mShowClick(Sender: TObject);
    187187    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
    188188      Shift: TShiftState; x, y: integer);
    189     procedure FormMouseMove(Sender: TObject; Shift: TShiftState; x,
    190       y: integer);
     189    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; x, y: integer);
    191190    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
    192191      Shift: TShiftState; x, y: integer);
     
    200199    procedure mNamesClick(Sender: TObject);
    201200    procedure MapBtnClick(Sender: TObject);
    202     procedure FormKeyUp(Sender: TObject; var Key: Word;
    203       Shift: TShiftState);
     201    procedure FormKeyUp(Sender: TObject; var Key: word; Shift: TShiftState);
    204202    procedure CreateUnitClick(Sender: TObject);
    205203    procedure mSoundOffClick(Sender: TObject);
     
    217215  public
    218216    procedure CreateParams(var p: TCreateParams); override;
    219     procedure Client(Command,NewPlayer:integer;var Data);
     217    procedure Client(Command, NewPlayer: integer; var Data);
    220218    procedure SetAIName(p: integer; Name: string);
    221219    function ZoomToCity(Loc: integer; NextUnitOnClose: boolean = false;
     
    229227
    230228  private
    231     xw,yw,xwd,ywd,xwMini,ywMini,xMidPanel,xRightPanel,xTroop,xTerrain,xMini,
    232       yMini,ywmax,ywcenter,TroopLoc,TrCnt,TrRow,TrPitch,MapWidth,MapOffset,
    233       MapHeight,BlinkTime,BrushLoc,EditLoc,xMouse,yMouse: integer;
     229    xw, yw, xwd, ywd, xwMini, ywMini, xMidPanel, xRightPanel, xTroop, xTerrain,
     230      xMini, yMini, ywmax, ywcenter, TroopLoc, TrCnt, TrRow, TrPitch, MapWidth,
     231      MapOffset, MapHeight, BlinkTime, BrushLoc, EditLoc, xMouse,
     232      yMouse: integer;
    234233    BrushType: Cardinal;
    235     trix:array[0..63] of integer;
    236     AILogo: array[0..nPl-1] of TBitmap;
    237     Mini,Panel,TopBar: TBitmap;
    238     sb:TPVScrollbar;
    239     Closable,RepaintOnResize,Tracking,TurnComplete,Edited,GoOnPhase,
     234    trix: array [0 .. 63] of integer;
     235    AILogo: array [0 .. nPl - 1] of TBitmap;
     236    Mini, Panel, TopBar: TBitmap;
     237    sb: TPVScrollbar;
     238    Closable, RepaintOnResize, Tracking, TurnComplete, Edited, GoOnPhase,
    240239      HaveStrategyAdvice, FirstMovieTurn: boolean;
    241240    procedure ArrangeMidPanel;
     
    246245    procedure CopyMiniToPanel;
    247246    procedure PanelPaint;
    248     procedure NextUnit(NearLoc:integer;AutoTurn:boolean);
    249     procedure Scroll(dx,dy: integer);
    250     procedure Centre(Loc:integer);
    251     procedure SetTroopLoc(Loc:integer);
    252     procedure ProcessRect(x0,y0,nx,ny,Options: integer);
     247    procedure NextUnit(NearLoc: integer; AutoTurn: boolean);
     248    procedure Scroll(dx, dy: integer);
     249    procedure Centre(Loc: integer);
     250    procedure SetTroopLoc(Loc: integer);
     251    procedure ProcessRect(x0, y0, nx, ny, Options: integer);
    253252    procedure PaintLoc(Loc: integer; Radius: integer = 0);
    254253    procedure PaintLoc_BeforeMove(FromLoc: integer);
    255254    procedure PaintLocTemp(Loc: integer; Style: integer = pltsNormal);
    256     procedure PaintBufferToScreen(xMap,yMap,width,height: integer);
     255    procedure PaintBufferToScreen(xMap, yMap, width, height: integer);
    257256    procedure PaintDestination;
    258     procedure SetUnFocus(uix:integer);
    259     function MoveUnit(dx,dy:integer; Options: integer = 0): integer;
     257    procedure SetUnFocus(uix: integer);
     258    function MoveUnit(dx, dy: integer; Options: integer = 0): integer;
    260259    procedure MoveToLoc(Loc: integer; CheckSuicide: boolean);
    261     procedure MoveOnScreen(ShowMove: TShowMove; Step0,Step1,nStep: integer;
     260    procedure MoveOnScreen(ShowMove: TShowMove; Step0, Step1, nStep: integer;
    262261      Restore: boolean = true);
    263     procedure FocusOnLoc(Loc:integer; Options: integer = 0);
     262    procedure FocusOnLoc(Loc: integer; Options: integer = 0);
    264263    function EndTurn(WasSkipped: boolean = false): boolean;
    265264    procedure EndNego;
    266     function IsPanelPixel(x,y: integer): boolean;
     265    function IsPanelPixel(x, y: integer): boolean;
    267266    procedure InitPopup(Popup: TPopupMenu);
    268267    procedure SetMapOptions;
     
    272271    procedure SetDebugMap(p: integer);
    273272    procedure SetViewpoint(p: integer);
    274     function LocationOfScreenPixel(x,y: integer): integer;
    275     procedure SetTileSize(x,y: integer);
    276     procedure RectInvalidate(Left,Top,Rigth,Bottom: integer);
    277     procedure SmartRectInvalidate(Left,Top,Rigth,Bottom: integer);
     273    function LocationOfScreenPixel(x, y: integer): integer;
     274    procedure SetTileSize(x, y: integer);
     275    procedure RectInvalidate(Left, Top, Rigth, Bottom: integer);
     276    procedure SmartRectInvalidate(Left, Top, Rigth, Bottom: integer);
    278277    procedure SaveSettings;
    279     procedure OnScroll(var m:TMessage); message WM_VSCROLL;
    280     procedure OnEOT(var Msg:TMessage); message WM_EOT;
     278    procedure OnScroll(var m: TMessage); message WM_VSCROLL;
     279    procedure OnEOT(var Msg: TMessage); message WM_EOT;
    281280  end;
    282281
    283282var
    284 MainScreen:TMainScreen;
     283  MainScreen: TMainScreen;
    285284
    286285type
    287 TTribeInfo=record
    288   trix: integer;
    289   FileName: ShortString;
     286  TTribeInfo = record
     287    trix: integer;
     288    FileName: ShortString;
    290289  end;
    291 TCityNameInfo=record
    292   ID: integer;
    293   NewName: ShortString
    294   end;
    295 TModelNameInfo=record
    296   mix: integer;
    297   NewName: ShortString
    298   end;
    299 TPriceSet=Set of $00..$FF;
    300 
    301 const
    302 crImpDrag=2;
    303 crFlatHand=3;
    304 
    305 xxu=32; yyu=24; // half of unit slot size x/y
    306 yyu_anchor=32;
    307 xxc=32; yyc=16; // 1/2 of city slot size in x, 1/2 of ground tile size in y (=1/3 of slot)
    308 
    309 // layout
    310 TopBarHeight=41;
    311 PanelHeight=168;
    312 MidPanelHeight=120; // TopBarHeight+MidPanelHeight should be same as BaseWin.yUnused
    313 MapCenterUp=(MidPanelHeight-TopBarHeight) div 2;
    314 
    315 nCityType=4;
    316 
    317 {client exclusive commands:}
    318 cSetTribe=$9000;cSetNewModelPicture=$9100;cSetModelName=$9110;
    319 cSetModelPicture=$9120;cSetSlaveIndex=$9131;
    320 cSetCityName=$9200;
    321 
    322 // city status flags
    323 csTypeMask=$0007; csToldDelay=$0008; csResourceWeightsMask=$00F0;
    324 csToldBombard=$0100;
    325 
    326 {unit status flags}
    327 usStay=$01; usWaiting=$02; usGoto=$04; usEnhance=$08; usRecover=$10;
    328 usToldNoReturn=$100;
    329 usPersistent=usStay or usGoto or usEnhance or usRecover or integer($FFFF0000);
    330 
    331 {model status flags}
    332 msObsolete=$1; msAllowConscripts=$2;
    333 
    334 {additional city happened flags}
    335 chTypeDel=$8000; chAllImpsMade=$4000;
    336 
    337 adNone=$801; adFar=$802; adNexus=$803;
    338 
    339 SpecialModelPictureCode: array[0..nSpecialModel-1] of integer=
    340 (10,11,40,41,21,30,{50,51,}64,74,{71,}73);
    341 
    342 pixSlaves=0; pixNoSlaves=1; // index of slaves in StdUnits
    343 
    344 // icons.bmp properties
    345 xSizeSmall=36; ySizeSmall=20;
    346 SystemIconLines=2; // lines of system icons in icons.bmp before improvements
    347 
    348 // save options apart from what's defined by SaveOption
    349 soTellAI=30;
    350 soExtraMask=$40000000;
    351 
    352 nCityEventPriority=16;
    353 CityEventPriority: array[0..nCityEventPriority-1] of integer=
    354 (chDisorder,chImprovementLost,chUnitLost,chAllImpsMade,chProduction,
    355 chOldWonder,chNoSettlerProd,chPopDecrease,chProductionSabotaged,
    356 chNoGrowthWarning,chPollution,chTypeDel,chFounded,chSiege,chAfterCapture,
    357 chPopIncrease);
    358 
    359 CityEventSoundItem: array[0..15] of string=
    360 ('CITY_DISORDER','','CITY_POPPLUS','CITY_POPMINUS','CITY_UNITLOST',
    361 'CITY_IMPLOST','CITY_SABOTAGE','CITY_GROWTHNEEDSIMP','CITY_POLLUTION',
    362 'CITY_SIEGE','CITY_WONDEREX','CITY_EMDELAY','CITY_FOUNDED','CITY_FOUNDED','',
    363 'CITY_INVALIDTYPE');
    364 
    365 type
    366 TPersistentData=record
    367   FarTech, ToldAge, ToldModels, ToldAlive, ToldContact, ToldOwnCredibility,
    368     ColdWarStart, PeaceEvaHappened: integer;
    369   EnhancementJobs: TEnhancementJobs;
    370   ImpOrder: array[0..nCityType-1] of TImpOrder;
    371   ToldWonders: array[0..27] of TWonderInfo;
    372   ToldTech: array[0..nAdv-1] of ShortInt;
    373   end;
    374 
    375 var
    376 MyData: ^TPersistentData;
    377 AdvIcon:array[0..nAdv-1] of integer; {icons displayed with the technologies}
    378 xxt,yyt, // half of tile size x/y
    379   GameMode,ClientMode,Age,UnFocus,OptionChecked,MapOptionChecked,nLostArmy,
    380   ScienceSum,TaxSum,SoundPreloadDone,MarkCityLoc,HGrTerrain,HGrCities,
    381   MovieSpeed: integer;
    382 CityRepMask: cardinal;
    383 ReceivedOffer: TOffer;
    384 Buffer,SmallImp: TBitmap;
    385 BlinkON,DestinationMarkON,StartRunning,StayOnTop_Ensured,supervising: boolean;
    386 UnusedTribeFiles, TribeNames: tstringlist;
    387 TribeOriginal: array[0..nPl-1] of boolean;
    388 LostArmy: array[0..nPl*nMmax-1] of integer;
    389 DipMem: array[0..nPl-1] of record
    390   pContact, SentCommand, FormerTreaty: integer;
    391   SentOffer: TOffer;
    392   DeliveredPrices, ReceivedPrices: TPriceSet;
     290
     291  TCityNameInfo = record
     292    ID: integer;
     293    NewName: ShortString end;
     294    TModelNameInfo = record mix: integer;
     295    NewName: ShortString end;
     296    TPriceSet = Set of $00 .. $FF;
     297
     298  const
     299    crImpDrag = 2;
     300    crFlatHand = 3;
     301
     302    xxu = 32;
     303    yyu = 24; // half of unit slot size x/y
     304    yyu_anchor = 32;
     305    xxc = 32;
     306    yyc = 16; // 1/2 of city slot size in x, 1/2 of ground tile size in y (=1/3 of slot)
     307
     308    // layout
     309    TopBarHeight = 41;
     310    PanelHeight = 168;
     311    MidPanelHeight = 120;
     312    // TopBarHeight+MidPanelHeight should be same as BaseWin.yUnused
     313    MapCenterUp = (MidPanelHeight - TopBarHeight) div 2;
     314
     315    nCityType = 4;
     316
     317    { client exclusive commands: }
     318    cSetTribe = $9000;
     319    cSetNewModelPicture = $9100;
     320    cSetModelName = $9110;
     321    cSetModelPicture = $9120;
     322    cSetSlaveIndex = $9131;
     323    cSetCityName = $9200;
     324
     325    // city status flags
     326    csTypeMask = $0007;
     327    csToldDelay = $0008;
     328    csResourceWeightsMask = $00F0;
     329    csToldBombard = $0100;
     330
     331    { unit status flags }
     332    usStay = $01;
     333    usWaiting = $02;
     334    usGoto = $04;
     335    usEnhance = $08;
     336    usRecover = $10;
     337    usToldNoReturn = $100;
     338    usPersistent = usStay or usGoto or usEnhance or usRecover or
     339      integer($FFFF0000);
     340
     341    { model status flags }
     342    msObsolete = $1;
     343    msAllowConscripts = $2;
     344
     345    { additional city happened flags }
     346    chTypeDel = $8000;
     347    chAllImpsMade = $4000;
     348
     349    adNone = $801;
     350    adFar = $802;
     351    adNexus = $803;
     352
     353    SpecialModelPictureCode: array [0 .. nSpecialModel - 1] of integer = (10,
     354      11, 40, 41, 21, 30, { 50,51, } 64, 74, { 71, } 73);
     355
     356    pixSlaves = 0;
     357    pixNoSlaves = 1; // index of slaves in StdUnits
     358
     359    // icons.bmp properties
     360    xSizeSmall = 36;
     361    ySizeSmall = 20;
     362    SystemIconLines = 2;
     363    // lines of system icons in icons.bmp before improvements
     364
     365    // save options apart from what's defined by SaveOption
     366    soTellAI = 30;
     367    soExtraMask = $40000000;
     368
     369    nCityEventPriority = 16;
     370    CityEventPriority: array [0 .. nCityEventPriority - 1] of integer =
     371      (chDisorder, chImprovementLost, chUnitLost, chAllImpsMade, chProduction,
     372      chOldWonder, chNoSettlerProd, chPopDecrease, chProductionSabotaged,
     373      chNoGrowthWarning, chPollution, chTypeDel, chFounded, chSiege,
     374      chAfterCapture, chPopIncrease);
     375
     376    CityEventSoundItem: array [0 .. 15] of string = ('CITY_DISORDER', '',
     377      'CITY_POPPLUS', 'CITY_POPMINUS', 'CITY_UNITLOST', 'CITY_IMPLOST',
     378      'CITY_SABOTAGE', 'CITY_GROWTHNEEDSIMP', 'CITY_POLLUTION', 'CITY_SIEGE',
     379      'CITY_WONDEREX', 'CITY_EMDELAY', 'CITY_FOUNDED', 'CITY_FOUNDED', '',
     380      'CITY_INVALIDTYPE');
     381
     382  type
     383    TPersistentData = record
     384      FarTech, ToldAge, ToldModels, ToldAlive, ToldContact, ToldOwnCredibility,
     385        ColdWarStart, PeaceEvaHappened: integer;
     386      EnhancementJobs: TEnhancementJobs;
     387      ImpOrder: array [0 .. nCityType - 1] of TImpOrder;
     388      ToldWonders: array [0 .. 27] of TWonderInfo;
     389      ToldTech: array [0 .. nAdv - 1] of ShortInt;
     390    end;
     391
     392  var
     393    MyData: ^TPersistentData;
     394    AdvIcon: array [0 .. nAdv - 1] of integer;
     395    { icons displayed with the technologies }
     396    xxt, yyt, // half of tile size x/y
     397    GameMode, ClientMode, Age, UnFocus, OptionChecked, MapOptionChecked,
     398      nLostArmy, ScienceSum, TaxSum, SoundPreloadDone, MarkCityLoc, HGrTerrain,
     399      HGrCities, MovieSpeed: integer;
     400    CityRepMask: Cardinal;
     401    ReceivedOffer: TOffer;
     402    Buffer, SmallImp: TBitmap;
     403    BlinkON, DestinationMarkON, StartRunning, StayOnTop_Ensured,
     404      supervising: boolean;
     405    UnusedTribeFiles, TribeNames: tstringlist;
     406    TribeOriginal: array [0 .. nPl - 1] of boolean;
     407    LostArmy: array [0 .. nPl * nMmax - 1] of integer;
     408    DipMem: array [0 .. nPl - 1] of record pContact, SentCommand,
     409      FormerTreaty: integer;
     410    SentOffer: TOffer;
     411    DeliveredPrices, ReceivedPrices: TPriceSet;
    393412  end;
    394413
     
    400419procedure InitMyModel(mix: integer; final: boolean);
    401420
    402 procedure ImpImage(ca: TCanvas; x,y,iix: integer; Government: integer = -1;
     421procedure ImpImage(ca: TCanvas; x, y, iix: integer; Government: integer = -1;
    403422  IsControl: boolean = false);
    404423procedure HelpOnTerrain(Loc, NewMode: integer);
    405424
    406 
    407425implementation
    408426
    409427uses
    410   Directories,IsoEngine,CityScreen,Draft,MessgEx,Select,CityType,Help,
    411   UnitStat,Diplomacy,Inp,log,Diagram,NatStat,Wonders,Enhance,Nego,Battle,Rates,
     428  Directories, IsoEngine, CityScreen, Draft, MessgEx, Select, CityType, Help,
     429  UnitStat, Diplomacy, Inp, log, Diagram, NatStat, Wonders, Enhance, Nego,
     430  Battle, Rates,
    412431  TechTree,
    413432
    414   Registry,ShellAPI;
    415 
     433  Registry, ShellAPI;
    416434
    417435{$R *.DFM}
     
    419437
    420438const
    421 lxmax_xxx=130;
    422 LeftPanelWidth=70;
    423 LeftPanelWidth_Editor=46;
    424 overlap=PanelHeight-MidPanelHeight;
    425 yTroop=PanelHeight-83;
    426 xPalace=66; yPalace=24; //120;
    427 xAdvisor=108; yAdvisor=48;
    428 xUnitText=80;
    429 PaperShade=3;
    430 BlinkOnTime=12; BlinkOffTime=6;
    431 MoveTime=300; // {time for moving a unit in ms}
    432 WaitAfterShowMove=32;
    433 FastScrolling=false; // causes problems with overlapping windows
    434 
    435 nBrushTypes=26;
    436 BrushTypes: array[0..nBrushTypes-1] of Cardinal=
    437 (fPrefStartPos,fStartPos,
    438 fShore,fGrass,fTundra,fPrairie,fDesert,fSwamp,fForest,fHills,fMountains,fArctic,
    439 fDeadLands,fDeadLands or fCobalt,fDeadLands or fUranium,
    440 fDeadLands or fMercury,fRiver,
    441 fRoad,fRR,fCanal,tiIrrigation,tiFarm,tiMine,fPoll,tiFort,tiBase);
    442 
    443 // MoveUnit options:
    444 muAutoNoWait=$0001; muAutoNext=$0002; muNoSuicideCheck=$0004;
    445 
    446 // ProcessRect options:
    447 prPaint=$0001; prAutoBounds=$0002; prInvalidate=$0004;
    448 
    449 // FocusOnLoc options:
    450 flRepaintPanel=$0001; flImmUpdate=$0002;
    451 
    452 nSaveOption=22;
     439  lxmax_xxx = 130;
     440  LeftPanelWidth = 70;
     441  LeftPanelWidth_Editor = 46;
     442  overlap = PanelHeight - MidPanelHeight;
     443  yTroop = PanelHeight - 83;
     444  xPalace = 66;
     445  yPalace = 24; // 120;
     446  xAdvisor = 108;
     447  yAdvisor = 48;
     448  xUnitText = 80;
     449  PaperShade = 3;
     450  BlinkOnTime = 12;
     451  BlinkOffTime = 6;
     452  MoveTime = 300; // {time for moving a unit in ms}
     453  WaitAfterShowMove = 32;
     454  FastScrolling = false; // causes problems with overlapping windows
     455
     456  nBrushTypes = 26;
     457  BrushTypes: array [0 .. nBrushTypes - 1] of Cardinal = (fPrefStartPos,
     458    fStartPos, fShore, fGrass, fTundra, fPrairie, fDesert, fSwamp, fForest,
     459    fHills, fMountains, fArctic, fDeadLands, fDeadLands or fCobalt,
     460    fDeadLands or fUranium, fDeadLands or fMercury, fRiver, fRoad, fRR, fCanal,
     461    tiIrrigation, tiFarm, tiMine, fPoll, tiFort, tiBase);
     462
     463  // MoveUnit options:
     464  muAutoNoWait = $0001;
     465  muAutoNext = $0002;
     466  muNoSuicideCheck = $0004;
     467
     468  // ProcessRect options:
     469  prPaint = $0001;
     470  prAutoBounds = $0002;
     471  prInvalidate = $0004;
     472
     473  // FocusOnLoc options:
     474  flRepaintPanel = $0001;
     475  flImmUpdate = $0002;
     476
     477  nSaveOption = 22;
    453478
    454479var
    455 Jump: array[0..nPl-1] of integer;
    456 pTurn,pLogo,UnStartLoc,ToldSlavery: integer;
    457 PerfFreq: int64;
    458 SmallScreen, GameOK, MapValid, skipped, idle: boolean;
    459 
    460 SaveOption: array[0..nSaveOption-1] of integer;
    461 MiniColors: array[0..11,0..1] of TColor;
    462 MainMap: TIsoMap;
    463 CurrentMoveInfo: record
    464   AfterMovePaintRadius,AfterAttackExpeller: integer;
    465   DoShow,IsAlly: boolean;
    466   end;
    467 
     480  Jump: array [0 .. nPl - 1] of integer;
     481  pTurn, pLogo, UnStartLoc, ToldSlavery: integer;
     482  PerfFreq: int64;
     483  SmallScreen, GameOK, MapValid, skipped, idle: boolean;
     484
     485  SaveOption: array [0 .. nSaveOption - 1] of integer;
     486  MiniColors: array [0 .. 11, 0 .. 1] of TColor;
     487  MainMap: TIsoMap;
     488  CurrentMoveInfo: record AfterMovePaintRadius, AfterAttackExpeller: integer;
     489  DoShow, IsAlly: boolean;
     490end;
    468491
    469492function CityEventName(i: integer): string;
    470493begin
    471 if i=14 then // chAllImpsMade
    472   if not Phrases2FallenBackToEnglish then
    473     result:=Phrases2.Lookup('CITYEVENT_ALLIMPSMADE')
    474   else result:=Phrases.Lookup('CITYEVENTS',1)
    475 else result:=Phrases.Lookup('CITYEVENTS',i);
     494  if i = 14 then // chAllImpsMade
     495    if not Phrases2FallenBackToEnglish then
     496      result := Phrases2.Lookup('CITYEVENT_ALLIMPSMADE')
     497    else
     498      result := Phrases.Lookup('CITYEVENTS', 1)
     499  else
     500    result := Phrases.Lookup('CITYEVENTS', i);
    476501end;
    477502
    478503procedure InitSmallImp;
    479504const
    480 cut=4;
    481 Sharpen=80;
     505  cut = 4;
     506  Sharpen = 80;
    482507type
    483 TLine=array[0..99999,0..2] of Byte;
    484 TBuffer=array[0..99999,0..2] of integer;
     508  TLine = array [0 .. 99999, 0 .. 2] of Byte;
     509  TBuffer = array [0 .. 99999, 0 .. 2] of integer;
    485510var
    486 sum,Cnt,dx,dy,nx,ny,ix,iy,ir,x,y,c,ch,xdivider,ydivider: integer;
    487 resampled: ^TBuffer;
    488 line: ^TLine;
     511  sum, Cnt, dx, dy, nx, ny, ix, iy, ir, x, y, c, ch, xdivider,
     512    ydivider: integer;
     513  resampled: ^TBuffer;
     514  line: ^TLine;
    489515begin
    490 nx:=BigImp.Width div xSizeBig *xSizeSmall;
    491 ny:=BigImp.Height div ySizeBig *ySizeSmall;
    492 
    493 // resample icons
    494 GetMem(resampled,nx*ny*12);
    495 FillChar(resampled^,nx*ny*12,0);
    496 for ix:=0 to BigImp.Width div xSizeBig-1 do for iy:=0 to BigImp.Height div ySizeBig-1 do
    497   for y:=0 to ySizeBig-2*cut-1 do
    498     begin
    499     ydivider:=(y*ySizeSmall div (ySizeBig-2*cut)+1)*(ySizeBig-2*cut)-y*ySizeSmall;
    500     if ydivider>ySizeSmall then ydivider:=ySizeSmall;
    501     line:=BigImp.ScanLine[cut+iy*ySizeBig+y];
    502     for x:=0 to xSizeBig-1 do
    503       begin
    504       ir:=ix*xSizeSmall+iy*nx*ySizeSmall
    505         +x*xSizeSmall div xSizeBig + y*ySizeSmall div (ySizeBig-2*cut) *nx;
    506       xdivider:=(x*xSizeSmall div xSizeBig+1)*xSizeBig-x*xSizeSmall;
    507       if xdivider>xSizeSmall then xdivider:=xSizeSmall;
    508       for ch:=0 to 2 do
    509         begin
    510         c:=line[ix*xSizeBig+x,ch];
    511         inc(resampled[ir,ch],c*xdivider*ydivider);
    512         if xdivider<xSizeSmall then
    513           inc(resampled[ir+1,ch],c*(xSizeSmall-xdivider)*ydivider);
    514         if ydivider<ySizeSmall then
    515           inc(resampled[ir+nx,ch],c*xdivider*(ySizeSmall-ydivider));
    516         if (xdivider<xSizeSmall) and (ydivider<ySizeSmall) then
    517           inc(resampled[ir+nx+1,ch],c*(xSizeSmall-xdivider)*(ySizeSmall-ydivider));
     516  nx := BigImp.width div xSizeBig * xSizeSmall;
     517  ny := BigImp.height div ySizeBig * ySizeSmall;
     518
     519  // resample icons
     520  GetMem(resampled, nx * ny * 12);
     521  FillChar(resampled^, nx * ny * 12, 0);
     522  for ix := 0 to BigImp.width div xSizeBig - 1 do
     523    for iy := 0 to BigImp.height div ySizeBig - 1 do
     524      for y := 0 to ySizeBig - 2 * cut - 1 do
     525      begin
     526        ydivider := (y * ySizeSmall div (ySizeBig - 2 * cut) + 1) *
     527          (ySizeBig - 2 * cut) - y * ySizeSmall;
     528        if ydivider > ySizeSmall then
     529          ydivider := ySizeSmall;
     530        line := BigImp.ScanLine[cut + iy * ySizeBig + y];
     531        for x := 0 to xSizeBig - 1 do
     532        begin
     533          ir := ix * xSizeSmall + iy * nx * ySizeSmall + x *
     534            xSizeSmall div xSizeBig + y *
     535            ySizeSmall div (ySizeBig - 2 * cut) * nx;
     536          xdivider := (x * xSizeSmall div xSizeBig + 1) * xSizeBig - x *
     537            xSizeSmall;
     538          if xdivider > xSizeSmall then
     539            xdivider := xSizeSmall;
     540          for ch := 0 to 2 do
     541          begin
     542            c := line[ix * xSizeBig + x, ch];
     543            inc(resampled[ir, ch], c * xdivider * ydivider);
     544            if xdivider < xSizeSmall then
     545              inc(resampled[ir + 1, ch], c * (xSizeSmall - xdivider) *
     546                ydivider);
     547            if ydivider < ySizeSmall then
     548              inc(resampled[ir + nx, ch],
     549                c * xdivider * (ySizeSmall - ydivider));
     550            if (xdivider < xSizeSmall) and (ydivider < ySizeSmall) then
     551              inc(resampled[ir + nx + 1, ch], c * (xSizeSmall - xdivider) *
     552                (ySizeSmall - ydivider));
     553          end
    518554        end
    519       end
    520     end;
    521 
    522 // sharpen resampled icons
    523 SmallImp.Width:=nx; SmallImp.Height:=ny;
    524 for y:=0 to ny-1 do
     555      end;
     556
     557  // sharpen resampled icons
     558  SmallImp.width := nx;
     559  SmallImp.height := ny;
     560  for y := 0 to ny - 1 do
    525561  begin
    526   line:=SmallImp.ScanLine[y];
    527   for x:=0 to nx-1 do
    528     for ch:=0 to 2 do
    529       begin
    530       sum:=0;
    531       Cnt:=0;
    532       for dy:=-1 to 1 do
    533         if ((dy>=0) or (y mod ySizeSmall>0)) and ((dy<=0) or (y mod ySizeSmall<ySizeSmall-1)) then
    534           for dx:=-1 to 1 do
    535             if ((dx>=0) or (x mod xSizeSmall>0)) and ((dx<=0) or (x mod xSizeSmall<xSizeSmall-1)) then
     562    line := SmallImp.ScanLine[y];
     563    for x := 0 to nx - 1 do
     564      for ch := 0 to 2 do
     565      begin
     566        sum := 0;
     567        Cnt := 0;
     568        for dy := -1 to 1 do
     569          if ((dy >= 0) or (y mod ySizeSmall > 0)) and
     570            ((dy <= 0) or (y mod ySizeSmall < ySizeSmall - 1)) then
     571            for dx := -1 to 1 do
     572              if ((dx >= 0) or (x mod xSizeSmall > 0)) and
     573                ((dx <= 0) or (x mod xSizeSmall < xSizeSmall - 1)) then
    536574              begin
    537               inc(sum,resampled[x+dx+nx*(y+dy),ch]);
    538               inc(Cnt);
     575                inc(sum, resampled[x + dx + nx * (y + dy), ch]);
     576                inc(Cnt);
    539577              end;
    540       sum:=((Cnt*Sharpen+800)*resampled[x+nx*y,ch]-sum*Sharpen) div (800*xSizeBig*(ySizeBig-2*cut));
    541       if sum<0 then sum:=0;
    542       if sum>255 then sum:=255;
    543       line[x][ch]:=sum;
     578        sum := ((Cnt * Sharpen + 800) * resampled[x + nx * y, ch] - sum *
     579          Sharpen) div (800 * xSizeBig * (ySizeBig - 2 * cut));
     580        if sum < 0 then
     581          sum := 0;
     582        if sum > 255 then
     583          sum := 255;
     584        line[x][ch] := sum;
    544585      end;
    545586  end;
    546 FreeMem(resampled);
    547 //smallimp.savetofile(homedir+'smallimp.bmp'); //!!!
     587  FreeMem(resampled);
     588  // smallimp.savetofile(homedir+'smallimp.bmp'); //!!!
    548589end;
    549590
    550 procedure ImpImage(ca: TCanvas; x,y,iix: integer; Government: integer;
     591procedure ImpImage(ca: TCanvas; x, y, iix: integer; Government: integer;
    551592  IsControl: boolean);
    552593begin
    553 if Government<0 then
    554   Government:=MyRO.Government;
    555 if (iix=imPalace) and (Government<>gAnarchy) then
    556   iix:=Government-8;
    557 FrameImage(ca, BigImp, x, y, xSizeBig, ySizeBig,
    558   (iix+SystemIconLines*7) mod 7*xSizeBig,
    559   (iix+SystemIconLines*7) div 7*ySizeBig, IsControl);
     594  if Government < 0 then
     595    Government := MyRO.Government;
     596  if (iix = imPalace) and (Government <> gAnarchy) then
     597    iix := Government - 8;
     598  FrameImage(ca, BigImp, x, y, xSizeBig, ySizeBig, (iix + SystemIconLines * 7)
     599    mod 7 * xSizeBig, (iix + SystemIconLines * 7) div 7 * ySizeBig, IsControl);
    560600end;
    561601
    562602procedure HelpOnTerrain(Loc, NewMode: integer);
    563603begin
    564 if MyMap[Loc] and fDeadLands<>0 then
    565   HelpDlg.ShowNewContent(NewMode, hkTer, 3*12)
    566 else if (MyMap[Loc] and fTerrain=fForest) and IsJungle(Loc div G.lx) then
    567   HelpDlg.ShowNewContent(NewMode, hkTer, fJungle + (MyMap[Loc] shr 5 and 3)*12)
    568 else HelpDlg.ShowNewContent(NewMode, hkTer,
    569   MyMap[Loc] and fTerrain + (MyMap[Loc] shr 5 and 3)*12);
     604  if MyMap[Loc] and fDeadLands <> 0 then
     605    HelpDlg.ShowNewContent(NewMode, hkTer, 3 * 12)
     606  else if (MyMap[Loc] and fTerrain = fForest) and IsJungle(Loc div G.lx) then
     607    HelpDlg.ShowNewContent(NewMode, hkTer,
     608      fJungle + (MyMap[Loc] shr 5 and 3) * 12)
     609  else
     610    HelpDlg.ShowNewContent(NewMode, hkTer, MyMap[Loc] and fTerrain +
     611      (MyMap[Loc] shr 5 and 3) * 12);
    570612end;
    571613
    572 
    573 {*** tribe management procedures ***}
     614{ *** tribe management procedures *** }
    574615
    575616function RoughCredibility(Credibility: integer): integer;
    576617begin
    577 case Credibility of
    578   0..69: result:=0; 70..89: result:=1; 90..99: result:=2; 100: result:=3 end;
    579 end;
    580 
    581 procedure ChooseModelPicture(p,mix,code,Hash,Turn: integer;
    582   ForceNew,final: boolean);
    583 var
    584 i: integer;
    585 Picture: TModelPictureInfo;
    586 IsNew: boolean;
    587 begin
    588 Picture.trix:=p;
    589 Picture.mix:=mix;
    590 if code=74 then
    591   begin // use correct pictures for slaves
    592   if Tribe[p].mixSlaves<0 then
    593     if not TribeOriginal[p] then Tribe[p].mixSlaves:=mix
    594     else begin i:=mix+p shl 16; Server(cSetSlaveIndex,0,0,i); end;
    595   if ToldSlavery=1 then Picture.pix:=pixSlaves else Picture.pix:=pixNoSlaves;
    596   Picture.Hash:=0;
    597   Picture.GrName:='StdUnits';
    598   IsNew:=true;
    599   end
    600 else
    601   begin
    602   Picture.Hash:=Hash;
    603   IsNew:=Tribe[p].ChooseModelPicture(Picture,code,Turn,ForceNew);
    604   end;
    605 if final then
    606   if not TribeOriginal[p] then
    607     Tribe[p].SetModelPicture(Picture, IsNew)
    608   else if IsNew then
    609     Server(cSetNewModelPicture+(Length(Picture.GrName)+1+16+3) div 4,0,
    610       0,Picture)
    611   else Server(cSetModelPicture+(Length(Picture.GrName)+1+16+3) div 4,0,
    612     0,Picture)
    613 else with Tribe[p].ModelPicture[mix] do
    614   begin
    615   HGr:=LoadGraphicSet(Picture.GrName);
    616   pix:=Picture.pix;
     618  case Credibility of
     619    0 .. 69:
     620      result := 0;
     621    70 .. 89:
     622      result := 1;
     623    90 .. 99:
     624      result := 2;
     625    100:
     626      result := 3
    617627  end;
    618628end;
    619629
     630procedure ChooseModelPicture(p, mix, code, Hash, Turn: integer;
     631  ForceNew, final: boolean);
     632var
     633  i: integer;
     634  Picture: TModelPictureInfo;
     635  IsNew: boolean;
     636begin
     637  Picture.trix := p;
     638  Picture.mix := mix;
     639  if code = 74 then
     640  begin // use correct pictures for slaves
     641    if Tribe[p].mixSlaves < 0 then
     642      if not TribeOriginal[p] then
     643        Tribe[p].mixSlaves := mix
     644      else
     645      begin
     646        i := mix + p shl 16;
     647        Server(cSetSlaveIndex, 0, 0, i);
     648      end;
     649    if ToldSlavery = 1 then
     650      Picture.pix := pixSlaves
     651    else
     652      Picture.pix := pixNoSlaves;
     653    Picture.Hash := 0;
     654    Picture.GrName := 'StdUnits';
     655    IsNew := true;
     656  end
     657  else
     658  begin
     659    Picture.Hash := Hash;
     660    IsNew := Tribe[p].ChooseModelPicture(Picture, code, Turn, ForceNew);
     661  end;
     662  if final then
     663    if not TribeOriginal[p] then
     664      Tribe[p].SetModelPicture(Picture, IsNew)
     665    else if IsNew then
     666      Server(cSetNewModelPicture + (Length(Picture.GrName) + 1 + 16 + 3) div 4,
     667        0, 0, Picture)
     668    else
     669      Server(cSetModelPicture + (Length(Picture.GrName) + 1 + 16 + 3) div 4, 0,
     670        0, Picture)
     671  else
     672    with Tribe[p].ModelPicture[mix] do
     673    begin
     674      HGr := LoadGraphicSet(Picture.GrName);
     675      pix := Picture.pix;
     676    end;
     677end;
     678
    620679function InitEnemyModel(emix: integer): boolean;
    621680begin
    622 if GameMode=cMovie then
    623   begin result:=false; exit end;
    624 with MyRO.EnemyModel[emix] do
    625   ChooseModelPicture(Owner, mix, ModelCode(MyRO.EnemyModel[emix]),
    626     ModelHash(MyRO.EnemyModel[emix]), MyRO.Turn, false, true);
    627 result:=true
     681  if GameMode = cMovie then
     682  begin
     683    result := false;
     684    exit
     685  end;
     686  with MyRO.EnemyModel[emix] do
     687    ChooseModelPicture(Owner, mix, ModelCode(MyRO.EnemyModel[emix]),
     688      ModelHash(MyRO.EnemyModel[emix]), MyRO.Turn, false, true);
     689  result := true
    628690end;
    629691
    630692procedure InitAllEnemyModels;
    631693var
    632 emix: integer;
     694  emix: integer;
    633695begin
    634 for emix:=0 to MyRO.nEnemyModel-1 do
    635   with MyRO.EnemyModel[emix] do
    636     if Tribe[Owner].ModelPicture[mix].HGr=0 then
    637       InitEnemyModel(emix);
     696  for emix := 0 to MyRO.nEnemyModel - 1 do
     697    with MyRO.EnemyModel[emix] do
     698      if Tribe[Owner].ModelPicture[mix].HGr = 0 then
     699        InitEnemyModel(emix);
    638700end;
    639701
    640702procedure InitMyModel(mix: integer; final: boolean);
    641703var
    642 mi: TModelInfo;
     704  mi: TModelInfo;
    643705begin
    644 if (GameMode=cMovie) and (MyModel[mix].Kind<$08) then exit;
     706  if (GameMode = cMovie) and (MyModel[mix].Kind < $08) then
     707    exit;
    645708  // don't exit for special units because cSetModelPicture comes after TellNewModels
    646 MakeModelInfo(me,mix,MyModel[mix],mi);
    647 ChooseModelPicture(me, mix, ModelCode(mi), ModelHash(mi), MyRO.Turn, false,
    648   final);
     709  MakeModelInfo(me, mix, MyModel[mix], mi);
     710  ChooseModelPicture(me, mix, ModelCode(mi), ModelHash(mi), MyRO.Turn,
     711    false, final);
    649712end;
    650713
    651714function AttackSound(code: integer): string;
    652715begin
    653 result:='ATTACK_'+char(48+code div 100 mod 10)+char(48+code div 10 mod 10)
    654   +char(48+code mod 10);
     716  result := 'ATTACK_' + char(48 + code div 100 mod 10) +
     717    char(48 + code div 10 mod 10) + char(48 + code mod 10);
    655718end;
    656719
     
    658721// check whether aircraft survived low-fuel warning
    659722begin
    660 assert(not supervising);
    661 with MyUn[uix] do
    662   if (Status and usToldNoReturn<>0)
    663     and ((MyMap[Loc] and fCity<>0) or (MyMap[Loc] and fTerImp=tiBase)
    664       or (Master>=0)) then
    665     Status:=Status and not usToldNoReturn;
     723  assert(not supervising);
     724  with MyUn[uix] do
     725    if (Status and usToldNoReturn <> 0) and
     726      ((MyMap[Loc] and fCity <> 0) or (MyMap[Loc] and fTerImp = tiBase) or
     727      (Master >= 0)) then
     728      Status := Status and not usToldNoReturn;
    666729end;
    667730
    668 function CreateTribe(p:integer; FileName:string; Original: boolean): boolean;
     731function CreateTribe(p: integer; FileName: string; Original: boolean): boolean;
    669732begin
    670 if not FileExists(LocalizedFilePath('Tribes\'+FileName+'.tribe.txt')) then
    671   begin result:=false; exit end;
    672 
    673 TribeOriginal[p]:=Original;
    674 Tribe[p]:=TTribe.Create(FileName);
    675 with Tribe[p] do
     733  if not FileExists(LocalizedFilePath('Tribes\' + FileName + '.tribe.txt')) then
    676734  begin
    677   if (GameMode=cNewGame) or not Original then
    678     begin
    679     Term.ChooseModelPicture(p,0,010,1,0,true,true);
    680     Term.ChooseModelPicture(p,1,040,1,0,true,true);
    681     Term.ChooseModelPicture(p,2,041,1,0,true,true);
    682     Term.ChooseModelPicture(p,-1,017,1,0,true,true);
    683     end;
    684   DipMem[p].pContact:=-1;
     735    result := false;
     736    exit
    685737  end;
    686 result:=true;
     738
     739  TribeOriginal[p] := Original;
     740  Tribe[p] := TTribe.Create(FileName);
     741  with Tribe[p] do
     742  begin
     743    if (GameMode = cNewGame) or not Original then
     744    begin
     745      Term.ChooseModelPicture(p, 0, 010, 1, 0, true, true);
     746      Term.ChooseModelPicture(p, 1, 040, 1, 0, true, true);
     747      Term.ChooseModelPicture(p, 2, 041, 1, 0, true, true);
     748      Term.ChooseModelPicture(p, -1, 017, 1, 0, true, true);
     749    end;
     750    DipMem[p].pContact := -1;
     751  end;
     752  result := true;
    687753end;
    688754
    689755procedure TellNewContacts;
    690756var
    691 p1: integer;
     757  p1: integer;
    692758begin
    693 if not supervising then
    694   for p1:=0 to nPl-1 do
    695     if (p1<>me) and (1 shl p1 and MyData.ToldContact=0)
    696       and (1 shl p1 and MyRO.Alive<>0) and (MyRO.Treaty[p1]>trNoContact) then
    697       begin
    698       TribeMessage(p1, Tribe[p1].TPhrase('FRNEWNATION'), '');
    699       MyData.ToldContact:=MyData.ToldContact or (1 shl p1);
     759  if not supervising then
     760    for p1 := 0 to nPl - 1 do
     761      if (p1 <> me) and (1 shl p1 and MyData.ToldContact = 0) and
     762        (1 shl p1 and MyRO.Alive <> 0) and (MyRO.Treaty[p1] > trNoContact) then
     763      begin
     764        TribeMessage(p1, Tribe[p1].TPhrase('FRNEWNATION'), '');
     765        MyData.ToldContact := MyData.ToldContact or (1 shl p1);
    700766      end
    701767end;
     
    703769procedure TellNewModels;
    704770var
    705 mix: integer;
    706 ModelNameInfo: TModelNameInfo;
     771  mix: integer;
     772  ModelNameInfo: TModelNameInfo;
    707773begin
    708 if supervising then
    709   exit;
    710 with Tribe[me] do while MyData.ToldModels<MyRO.nModel do
    711   begin {new Unit class available}
    712   if (ModelPicture[MyData.ToldModels].HGr>0)
    713     and (MyModel[MyData.ToldModels].Kind<>mkSelfDeveloped) then
    714     begin // save picture of DevModel
    715     ModelPicture[MyData.ToldModels+1]:=ModelPicture[MyData.ToldModels];
    716     ModelName[MyData.ToldModels+1]:=ModelName[MyData.ToldModels];
    717     ModelPicture[MyData.ToldModels].HGr:=0
    718     end;
    719   if ModelPicture[MyData.ToldModels].HGr=0 then
    720     InitMyModel(MyData.ToldModels,true); {only run if no researched model}
    721   with MessgExDlg do
    722     begin
    723 {    MakeModelInfo(me,MyData.ToldModels,MyModel[MyData.ToldModels],mi);
    724     if mi.Attack=0 then OpenSound:='MSG_DEFAULT'
    725     else OpenSound:=AttackSound(ModelCode(mi));}
    726     if MyModel[MyData.ToldModels].Kind=mkSelfDeveloped then
    727       OpenSound:='NEWMODEL_'+char(48+Age);
    728     MessgText:=Phrases.Lookup('MODELAVAILABLE');
    729     if GameMode=cMovie then
    730       begin
    731       Kind:=mkOkHelp; // doesn't matter
    732       MessgText:=MessgText+'\'+ModelName[MyData.ToldModels];
     774  if supervising then
     775    exit;
     776  with Tribe[me] do
     777    while MyData.ToldModels < MyRO.nModel do
     778    begin { new Unit class available }
     779      if (ModelPicture[MyData.ToldModels].HGr > 0) and
     780        (MyModel[MyData.ToldModels].Kind <> mkSelfDeveloped) then
     781      begin // save picture of DevModel
     782        ModelPicture[MyData.ToldModels + 1] := ModelPicture[MyData.ToldModels];
     783        ModelName[MyData.ToldModels + 1] := ModelName[MyData.ToldModels];
     784        ModelPicture[MyData.ToldModels].HGr := 0
     785      end;
     786      if ModelPicture[MyData.ToldModels].HGr = 0 then
     787        InitMyModel(MyData.ToldModels, true);
     788      { only run if no researched model }
     789      with MessgExDlg do
     790      begin
     791        { MakeModelInfo(me,MyData.ToldModels,MyModel[MyData.ToldModels],mi);
     792          if mi.Attack=0 then OpenSound:='MSG_DEFAULT'
     793          else OpenSound:=AttackSound(ModelCode(mi)); }
     794        if MyModel[MyData.ToldModels].Kind = mkSelfDeveloped then
     795          OpenSound := 'NEWMODEL_' + char(48 + Age);
     796        MessgText := Phrases.Lookup('MODELAVAILABLE');
     797        if GameMode = cMovie then
     798        begin
     799          Kind := mkOkHelp; // doesn't matter
     800          MessgText := MessgText + '\' + ModelName[MyData.ToldModels];
     801        end
     802        else
     803        begin
     804          Kind := mkModel;
     805          EInput.Text := ModelName[MyData.ToldModels];
     806        end;
     807        IconKind := mikModel;
     808        IconIndex := MyData.ToldModels;
     809        ShowModal;
     810        if (EInput.Text <> '') and (EInput.Text <> ModelName[MyData.ToldModels])
     811        then
     812        begin // user renamed model
     813          ModelNameInfo.mix := MyData.ToldModels;
     814          ModelNameInfo.NewName := EInput.Text;
     815          Server(cSetModelName + (Length(ModelNameInfo.NewName) + 1 + 4 + 3)
     816            div 4, me, 0, ModelNameInfo);
     817        end
     818      end;
     819      if MyModel[MyData.ToldModels].Kind = mkSettler then
     820      begin // engineers make settlers obsolete
     821        for mix := 0 to MyData.ToldModels - 1 do
     822          if MyModel[mix].Kind = mkSettler then
     823            MyModel[mix].Status := MyModel[mix].Status or msObsolete;
     824      end;
     825      inc(MyData.ToldModels)
     826    end;
     827end;
     828
     829procedure PaintZoomedTile(dst: TBitmap; x, y, Loc: integer);
     830
     831  procedure TSprite(xDst, yDst, xSrc, ySrc: integer);
     832  begin
     833    Sprite(dst, HGrTerrain, x + xDst, y + yDst, xxt * 2, yyt * 3,
     834      1 + xSrc * (xxt * 2 + 1), 1 + ySrc * (yyt * 3 + 1));
     835  end;
     836
     837  procedure TSprite4(xSrc, ySrc: integer);
     838  begin
     839    Sprite(dst, HGrTerrain, x + xxt, y + yyt + 2, xxt * 2, yyt * 2 - 2,
     840      1 + xSrc * (xxt * 2 + 1), 3 + yyt + ySrc * (yyt * 3 + 1));
     841    Sprite(dst, HGrTerrain, x + 4, y + 2 * yyt, xxt * 2 - 4, yyt * 2,
     842      5 + xSrc * (xxt * 2 + 1), 1 + yyt + ySrc * (yyt * 3 + 1));
     843    Sprite(dst, HGrTerrain, x + xxt * 2, y + 2 * yyt, xxt * 2 - 4, yyt * 2,
     844      1 + xSrc * (xxt * 2 + 1), 1 + yyt + ySrc * (yyt * 3 + 1));
     845    Sprite(dst, HGrTerrain, x + xxt, y + yyt * 3, xxt * 2, yyt * 2 - 2,
     846      1 + xSrc * (xxt * 2 + 1), 1 + yyt + ySrc * (yyt * 3 + 1));
     847  end;
     848
     849var
     850  cix, ySrc, Tile: integer;
     851begin
     852  Tile := MyMap[Loc];
     853  if Tile and fCity <> 0 then
     854  begin
     855    if MyRO.Tech[adRailroad] >= tsApplicable then
     856      Tile := Tile or fRR
     857    else
     858      Tile := Tile or fRoad;
     859    if Tile and fOwned <> 0 then
     860    begin
     861      cix := MyRO.nCity - 1;
     862      while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
     863        dec(cix);
     864      assert(cix >= 0);
     865      if MyCity[cix].Built[imSupermarket] > 0 then
     866        Tile := Tile or tiFarm
     867      else
     868        Tile := Tile or tiIrrigation;
     869    end
     870    else
     871      Tile := Tile or tiIrrigation;
     872  end;
     873
     874  if Tile and fTerrain >= fForest then
     875    TSprite4(2, 2)
     876  else
     877    TSprite4(Tile and fTerrain, 0);
     878  if Tile and fTerrain >= fForest then
     879  begin
     880    if (Tile and fTerrain = fForest) and IsJungle(Loc div G.lx) then
     881      ySrc := 18
     882    else
     883      ySrc := 3 + 2 * (Tile and fTerrain - fForest);
     884    TSprite(xxt, 0, 6, ySrc);
     885    TSprite(0, yyt, 3, ySrc);
     886    TSprite((xxt * 2), yyt, 4, ySrc + 1);
     887    TSprite(xxt, (yyt * 2), 1, ySrc + 1);
     888  end;
     889
     890  // irrigation
     891  case Tile and fTerImp of
     892    tiIrrigation:
     893      begin
     894        TSprite(xxt, 0, 0, 12);
     895        TSprite(xxt * 2, yyt, 0, 12);
     896      end;
     897    tiFarm:
     898      begin
     899        TSprite(xxt, 0, 1, 12);
     900        TSprite(xxt * 2, yyt, 1, 12);
    733901      end
     902  end;
     903
     904  // river/canal/road/railroad
     905  if Tile and fRiver <> 0 then
     906  begin
     907    TSprite(0, yyt, 2, 14);
     908    TSprite(xxt, (yyt * 2), 2, 14);
     909  end;
     910  if Tile and fCanal <> 0 then
     911  begin
     912    TSprite(xxt, 0, 7, 11);
     913    TSprite(xxt, 0, 3, 11);
     914    TSprite(xxt * 2, yyt, 7, 11);
     915    TSprite(xxt * 2, yyt, 3, 11);
     916  end;
     917  if Tile and fRR <> 0 then
     918  begin
     919    TSprite((xxt * 2), yyt, 1, 10);
     920    TSprite((xxt * 2), yyt, 5, 10);
     921    TSprite(xxt, (yyt * 2), 1, 10);
     922    TSprite(xxt, (yyt * 2), 5, 10);
     923  end
     924  else if Tile and fRoad <> 0 then
     925  begin
     926    TSprite((xxt * 2), yyt, 8, 9);
     927    TSprite((xxt * 2), yyt, 5, 9);
     928    TSprite(xxt, (yyt * 2), 1, 9);
     929    TSprite(xxt, (yyt * 2), 5, 9);
     930  end;
     931
     932  if Tile and fPoll <> 0 then
     933    TSprite(xxt, (yyt * 2), 6, 12);
     934
     935  // special
     936  if Tile and (fTerrain or fSpecial) = fGrass or fSpecial1 then
     937    TSprite4(2, 1)
     938  else if Tile and fSpecial <> 0 then
     939    if Tile and fTerrain < fForest then
     940      TSprite(0, yyt, Tile and fTerrain, Tile and fSpecial shr 5)
     941    else if (Tile and fTerrain = fForest) and IsJungle(Loc div G.lx) then
     942      TSprite(0, yyt, 8, 17 + Tile and fSpecial shr 5)
    734943    else
    735       begin
    736       Kind:=mkModel;
    737       EInput.Text:=ModelName[MyData.ToldModels];
     944      TSprite(0, yyt, 8, 2 + (Tile and fTerrain - fForest) * 2 + Tile and
     945        fSpecial shr 5)
     946  else if Tile and fDeadLands <> 0 then
     947  begin
     948    TSprite4(6, 2);
     949    TSprite(xxt, yyt, 8, 12 + Tile shr 25 and 3);
     950  end;
     951
     952  // other improvements
     953  case Tile and fTerImp of
     954    tiMine:
     955      TSprite(xxt, 0, 2, 12);
     956    tiFort:
     957      begin
     958        TSprite(xxt, 0, 7, 12);
     959        TSprite(xxt, 0, 3, 12);
    738960      end;
    739     IconKind:=mikModel;
    740     IconIndex:=MyData.ToldModels;
    741     ShowModal;
    742     if (EInput.Text<>'') and (EInput.Text<>ModelName[MyData.ToldModels]) then
    743       begin // user renamed model
    744       ModelNameInfo.mix:=MyData.ToldModels;
    745       ModelNameInfo.NewName:=EInput.Text;
    746       Server(cSetModelName+(Length(ModelNameInfo.NewName)+1+4+3) div 4,
    747         me,0,ModelNameInfo);
    748       end
    749     end;
    750   if MyModel[MyData.ToldModels].kind=mkSettler then
    751     begin // engineers make settlers obsolete
    752     for mix:=0 to MyData.ToldModels-1 do
    753       if MyModel[mix].Kind=mkSettler then
    754         MyModel[mix].Status:=MyModel[mix].Status or msObsolete;
    755     end;
    756   inc(MyData.ToldModels)
     961    tiBase:
     962      TSprite(xxt, 0, 4, 12);
    757963  end;
    758964end;
    759965
    760 procedure PaintZoomedTile(dst: TBitmap; x,y,Loc: integer);
    761 
    762   procedure TSprite(xDst, yDst, xSrc, ySrc: integer);
     966function ChooseResearch: boolean;
     967var
     968  ChosenResearch: integer;
     969begin
     970  if (MyData.FarTech <> adNone) and (MyRO.Tech[MyData.FarTech] >= tsApplicable)
     971  then
     972    MyData.FarTech := adNone;
     973  repeat
     974    { research complete -- select new }
     975    repeat
     976      ModalSelectDlg.ShowNewContent(wmModal, kAdvance);
     977      if ModalSelectDlg.result < 0 then
     978      begin
     979        result := false;
     980        exit
     981      end;
     982      ChosenResearch := ModalSelectDlg.result;
     983      if ChosenResearch = adMilitary then
     984      begin
     985        DraftDlg.ShowNewContent(wmModal);
     986        if DraftDlg.ModalResult <> mrOK then
     987          Tribe[me].ModelPicture[MyRO.nModel].HGr := 0
     988      end
     989      until (ChosenResearch <> adMilitary) or (DraftDlg.ModalResult = mrOK);
     990
     991      if ChosenResearch = adMilitary then
     992        InitMyModel(MyRO.nModel, true)
     993      else if ChosenResearch = adFar then
     994      begin
     995        ModalSelectDlg.ShowNewContent(wmModal, kFarAdvance);
     996        if ModalSelectDlg.result >= 0 then
     997          if (ModalSelectDlg.result = adNone) or
     998            (Server(sSetResearch - sExecute, me, ModalSelectDlg.result, nil^) <
     999            rExecuted) then
     1000            MyData.FarTech := ModalSelectDlg.result
     1001          else
     1002          begin
     1003            ChosenResearch := ModalSelectDlg.result;
     1004            // can be researched immediately
     1005            MyData.FarTech := adNone
     1006          end
     1007      end;
     1008    until ChosenResearch <> adFar;
     1009    if ChosenResearch = adNexus then
     1010      MyData.FarTech := adNexus
     1011    else
     1012      Server(sSetResearch, me, ChosenResearch, nil^);
     1013    ListDlg.TechChange;
     1014    result := true;
     1015  end;
     1016
     1017  (* ** client function handling ** *)
     1018
     1019  function TMainScreen.DipCall(Command: integer): integer;
     1020  var
     1021    i: integer;
     1022    IsTreatyDeal: boolean;
    7631023  begin
    764   Sprite(dst, HGrTerrain, x+xDst, y+yDst, xxt*2, yyt*3, 1+xSrc*(xxt*2+1),
    765     1+ySrc*(yyt*3+1));
    766   end;
    767 
    768   procedure TSprite4(xSrc, ySrc: integer);
    769   begin
    770   Sprite(dst, HGrTerrain, x+xxt, y+yyt+2, xxt*2, yyt*2-2, 1+xSrc*(xxt*2+1),
    771     3+yyt+ySrc*(yyt*3+1));
    772   Sprite(dst, HGrTerrain, x+4, y+2*yyt, xxt*2-4, yyt*2, 5+xSrc*(xxt*2+1),
    773     1+yyt+ySrc*(yyt*3+1));
    774   Sprite(dst, HGrTerrain, x+xxt*2, y+2*yyt, xxt*2-4, yyt*2, 1+xSrc*(xxt*2+1),
    775     1+yyt+ySrc*(yyt*3+1));
    776   Sprite(dst, HGrTerrain, x+xxt, y+yyt*3, xxt*2, yyt*2-2, 1+xSrc*(xxt*2+1),
    777     1+yyt+ySrc*(yyt*3+1));
    778   end;
    779 
    780 var
    781 cix, ySrc, Tile: integer;
    782 begin
    783 Tile:=MyMap[Loc];
    784 if Tile and fCity<>0 then
    785   begin
    786   if MyRO.Tech[adRailroad]>=tsApplicable then
    787     Tile:=Tile or fRR
    788   else Tile:=Tile or fRoad;
    789   if Tile and fOwned<>0 then
    790     begin
    791     cix:=MyRO.nCity-1;
    792     while (cix>=0) and (MyCity[cix].Loc<>Loc) do dec(cix);
    793     assert(cix>=0);
    794     if MyCity[cix].Built[imSupermarket]>0 then
    795       Tile:=Tile or tiFarm
    796     else Tile:=Tile or tiIrrigation;
    797     end
    798   else Tile:=Tile or tiIrrigation;
    799   end;
    800 
    801 if Tile and fTerrain>=fForest then TSprite4(2,2)
    802 else TSprite4(Tile and fTerrain,0);
    803 if Tile and fTerrain>=fForest then
    804   begin
    805   if (Tile and fTerrain=fForest) and IsJungle(Loc div G.lx) then ySrc:=18
    806   else ySrc:=3+2*(Tile and fTerrain-fForest);
    807   TSprite(xxt, 0, 6, ySrc);
    808   TSprite(0, yyt, 3, ySrc);
    809   TSprite((xxt*2), yyt, 4, ySrc+1);
    810   TSprite(xxt, (yyt*2), 1, ySrc+1);
    811   end;
    812 
    813 // irrigation
    814 case Tile and fTerImp of
    815   tiIrrigation:
    816     begin
    817     TSprite(xxt,0,0,12);
    818     TSprite(xxt*2,yyt,0,12);
    819     end;
    820   tiFarm:
    821     begin
    822     TSprite(xxt,0,1,12);
    823     TSprite(xxt*2,yyt,1,12);
     1024    result := Server(Command, me, 0, nil^);
     1025    if result >= rExecuted then
     1026    begin
     1027      if Command and $FF0F = scContact then
     1028      begin
     1029        DipMem[me].pContact := Command shr 4 and $F;
     1030        NegoDlg.Initiate;
     1031        DipMem[me].DeliveredPrices := [];
     1032        DipMem[me].ReceivedPrices := [];
     1033      end;
     1034
     1035      DipMem[me].SentCommand := Command;
     1036      DipMem[me].FormerTreaty := MyRO.Treaty[DipMem[me].pContact];
     1037      if Command = scDipCancelTreaty then
     1038        Play('CANCELTREATY')
     1039      else if Command = scDipAccept then
     1040      begin // remember delivered and received prices
     1041        for i := 0 to ReceivedOffer.nDeliver - 1 do
     1042          include(DipMem[me].ReceivedPrices, ReceivedOffer.Price[i] shr 24);
     1043        for i := 0 to ReceivedOffer.nCost - 1 do
     1044          include(DipMem[me].DeliveredPrices,
     1045            ReceivedOffer.Price[ReceivedOffer.nDeliver + i] shr 24);
     1046        IsTreatyDeal := false;
     1047        for i := 0 to ReceivedOffer.nDeliver + ReceivedOffer.nCost - 1 do
     1048          if ReceivedOffer.Price[i] and opMask = opTreaty then
     1049            IsTreatyDeal := true;
     1050        if IsTreatyDeal then
     1051          Play('NEWTREATY')
     1052        else
     1053          Play('ACCEPTOFFER');
     1054      end;
     1055      CityDlg.CloseAction := None;
     1056      if G.RO[DipMem[me].pContact] <> nil then
     1057      begin // close windows for next player
     1058        for i := 0 to Screen.FormCount - 1 do
     1059          if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg)
     1060          then
     1061            Screen.Forms[i].Close;
     1062      end
     1063      else
     1064      begin
     1065        if CityDlg.Visible then
     1066          CityDlg.Close;
     1067        if UnitStatDlg.Visible then
     1068          UnitStatDlg.Close;
     1069      end
    8241070    end
    8251071  end;
    8261072
    827 // river/canal/road/railroad
    828 if Tile and fRiver<>0 then
     1073  function TMainScreen.OfferCall(var Offer: TOffer): integer;
     1074  var
     1075    i: integer;
    8291076  begin
    830   TSprite(0, yyt, 2, 14);
    831   TSprite(xxt, (yyt*2), 2, 14);
     1077    result := Server(scDipOffer, me, 0, Offer);
     1078    if result >= rExecuted then
     1079    begin
     1080      DipMem[me].SentCommand := scDipOffer;
     1081      DipMem[me].FormerTreaty := MyRO.Treaty[DipMem[me].pContact];
     1082      DipMem[me].SentOffer := Offer;
     1083      CityDlg.CloseAction := None;
     1084      if G.RO[DipMem[me].pContact] <> nil then
     1085      begin // close windows for next player
     1086        for i := 0 to Screen.FormCount - 1 do
     1087          if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg)
     1088          then
     1089            Screen.Forms[i].Close;
     1090      end
     1091      else
     1092      begin
     1093        if CityDlg.Visible then
     1094          CityDlg.Close;
     1095        if UnitStatDlg.Visible then
     1096          UnitStatDlg.Close;
     1097      end
     1098    end
    8321099  end;
    833 if Tile and fCanal<>0 then
     1100
     1101  procedure TMainScreen.SetUnFocus(uix: integer);
     1102  var
     1103    Loc0: integer;
    8341104  begin
    835   TSprite(xxt, 0, 7, 11);
    836   TSprite(xxt, 0, 3, 11);
    837   TSprite(xxt*2,yyt,7,11);
    838   TSprite(xxt*2,yyt,3,11);
    839   end;
    840 if Tile and fRR<>0 then
    841   begin
    842   TSprite((xxt*2), yyt, 1, 10);
    843   TSprite((xxt*2), yyt, 5, 10);
    844   TSprite(xxt, (yyt*2), 1, 10);
    845   TSprite(xxt, (yyt*2), 5, 10);
    846   end
    847 else if Tile and fRoad<>0 then
    848   begin
    849   TSprite((xxt*2), yyt, 8, 9);
    850   TSprite((xxt*2), yyt, 5, 9);
    851   TSprite(xxt, (yyt*2), 1, 9);
    852   TSprite(xxt, (yyt*2), 5, 9);
    853   end;
    854 
    855 if Tile and fPoll<>0 then
    856   TSprite(xxt,(yyt*2),6,12);
    857 
    858 // special
    859 if Tile and (fTerrain or fSpecial)=fGrass or fSpecial1 then
    860   TSprite4(2,1)
    861 else if Tile and fSpecial<>0 then
    862   if Tile and fTerrain<fForest then
    863     TSprite(0, yyt, Tile and fTerrain, Tile and fSpecial shr 5)
    864   else if (Tile and fTerrain=fForest) and IsJungle(Loc div G.lx) then
    865     TSprite(0, yyt, 8, 17+Tile and fSpecial shr 5)
    866   else TSprite(0, yyt, 8, 2+(Tile and fTerrain-fForest)*2+Tile and fSpecial shr 5)
    867 else if Tile and fDeadLands<>0 then
    868   begin
    869   TSprite4(6,2);
    870   TSprite(xxt, yyt, 8, 12+Tile shr 25 and 3);
    871   end;
    872 
    873 // other improvements
    874 case Tile and fTerImp of
    875   tiMine: TSprite(xxt, 0, 2, 12);
    876   tiFort: begin TSprite(xxt, 0, 7, 12); TSprite(xxt, 0, 3, 12); end;
    877   tiBase: TSprite(xxt, 0, 4, 12);
    878   end;
    879 end;
    880 
    881 function ChooseResearch: boolean;
    882 var
    883 ChosenResearch: integer;
    884 begin
    885 if (MyData.FarTech<>adNone) and (MyRO.Tech[MyData.FarTech]>=tsApplicable) then
    886   MyData.FarTech:=adNone;
    887 repeat
    888   {research complete -- select new}
    889   repeat
    890     ModalSelectDlg.ShowNewContent(wmModal,kAdvance);
    891     if ModalSelectDlg.result<0 then
    892       begin result:=false; exit end;
    893     ChosenResearch:=ModalSelectDlg.result;
    894     if ChosenResearch=adMilitary then
    895       begin
    896       DraftDlg.ShowNewContent(wmModal);
    897       if DraftDlg.ModalResult<>mrOK then
    898         Tribe[me].ModelPicture[MyRO.nModel].HGr:=0
    899       end
    900   until (ChosenResearch<>adMilitary) or (DraftDlg.ModalResult=mrOK);
    901 
    902   if ChosenResearch=adMilitary then InitMyModel(MyRO.nModel,true)
    903   else if ChosenResearch=adFar then
    904     begin
    905     ModalSelectDlg.ShowNewContent(wmModal,kFarAdvance);
    906     if ModalSelectDlg.result>=0 then
    907       if (ModalSelectDlg.Result=adNone) or
    908         (Server(sSetResearch-sExecute,me,ModalSelectDlg.Result,nil^)<rExecuted) then
    909         MyData.FarTech:=ModalSelectDlg.result
    910       else
    911         begin
    912         ChosenResearch:=ModalSelectDlg.result; // can be researched immediately
    913         MyData.FarTech:=adNone
    914         end
    915     end;
    916 until ChosenResearch<>adFar;
    917 if ChosenResearch=adNexus then MyData.FarTech:=adNexus
    918 else Server(sSetResearch,me,ChosenResearch,nil^);
    919 ListDlg.TechChange;
    920 result:=true;
    921 end;
    922 
    923 
    924 (*** client function handling ***)
    925 
    926 function TMainScreen.DipCall(Command: integer): integer;
    927 var
    928 i: integer;
    929 IsTreatyDeal: boolean;
    930 begin
    931 result:=Server(Command,me,0,nil^);
    932 if result>=rExecuted then
    933   begin
    934   if Command and $FF0F=scContact then
    935     begin
    936     DipMem[me].pContact:=Command shr 4 and $f;
    937     NegoDlg.Initiate;
    938     DipMem[me].DeliveredPrices:=[];
    939     DipMem[me].ReceivedPrices:=[];
    940     end;
    941 
    942   DipMem[me].SentCommand:=Command;
    943   DipMem[me].FormerTreaty:=MyRO.Treaty[DipMem[me].pContact];
    944   if Command=scDipCancelTreaty then Play('CANCELTREATY')
    945   else if Command=scDipAccept then
    946     begin // remember delivered and received prices
    947     for i:=0 to ReceivedOffer.nDeliver-1 do
    948       include(DipMem[me].ReceivedPrices,ReceivedOffer.Price[i] shr 24);
    949     for i:=0 to ReceivedOffer.nCost-1 do
    950       include(DipMem[me].DeliveredPrices,
    951         ReceivedOffer.Price[ReceivedOffer.nDeliver+i] shr 24);
    952     IsTreatyDeal:=false;
    953     for i:=0 to ReceivedOffer.nDeliver+ReceivedOffer.nCost-1 do
    954       if ReceivedOffer.Price[i] and opMask=opTreaty then
    955         IsTreatyDeal:=true;
    956     if IsTreatyDeal then Play('NEWTREATY')
    957     else Play('ACCEPTOFFER');     
    958     end;
    959   CityDlg.CloseAction:=None;
    960   if G.RO[DipMem[me].pContact]<>nil then
    961     begin // close windows for next player
    962     for i:=0 to Screen.FormCount-1 do
    963       if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    964         Screen.Forms[i].Close;
    965     end
    966   else
    967     begin
    968     if CityDlg.Visible then CityDlg.Close;
    969     if UnitStatDlg.Visible then UnitStatDlg.Close;
    970     end
    971   end
    972 end;
    973 
    974 function TMainScreen.OfferCall(var Offer: TOffer): integer;
    975 var
    976 i: integer;
    977 begin
    978 result:=Server(scDipOffer,me,0,Offer);
    979 if result>=rExecuted then
    980   begin
    981   DipMem[me].SentCommand:=scDipOffer;
    982   DipMem[me].FormerTreaty:=MyRO.Treaty[DipMem[me].pContact];
    983   DipMem[me].SentOffer:=Offer;
    984   CityDlg.CloseAction:=None;
    985   if G.RO[DipMem[me].pContact]<>nil then
    986     begin // close windows for next player
    987     for i:=0 to Screen.FormCount-1 do
    988       if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    989         Screen.Forms[i].Close;
    990     end
    991   else
    992     begin
    993     if CityDlg.Visible then CityDlg.Close;
    994     if UnitStatDlg.Visible then UnitStatDlg.Close;
    995     end
    996   end
    997 end;
    998 
    999 procedure TMainScreen.SetUnFocus(uix:integer);
    1000 var
    1001 Loc0: integer;
    1002 begin
    1003 assert(not ((uix>=0) and supervising));
    1004 if uix<>UnFocus then
    1005   begin
    1006   DestinationMarkON:=false;
    1007   PaintDestination;
    1008   if uix>=0 then UnStartLoc:=MyUn[uix].Loc;
    1009   BlinkON:=false;
    1010   BlinkTime:=-1;
    1011   if UnFocus>=0 then
    1012     begin
    1013     Loc0:=MyUn[UnFocus].Loc;
    1014     if (uix<0) or (Loc0<>MyUn[uix].Loc) then
    1015       begin
    1016       UnFocus:=-1;
    1017       PaintLoc(Loc0);
    1018       end
    1019     end;
    1020   UnFocus:=uix;
    1021   end;
    1022 UnitInfoBtn.Visible:= UnFocus>=0;
    1023 UnitBtn.Visible:= UnFocus>=0;
    1024 CheckTerrainBtnVisible;
    1025 end;
    1026 
    1027 procedure TMainScreen.CheckTerrainBtnVisible;
    1028 var
    1029 Tile: integer;
    1030 mox: ^TModel;
    1031 begin
    1032 if UnFocus>=0 then
    1033   begin
    1034   mox:=@MyModel[MyUn[UnFocus].mix];
    1035   Tile:=MyMap[MyUn[UnFocus].Loc];
    1036   TerrainBtn.Visible:= (Tile and fCity=0) and (MyUn[UnFocus].Master<0)
    1037     and ((mox.Kind=mkSettler) or (mox.Kind=mkSlaves) and (MyRO.Wonder[woPyramids].EffectiveOwner>=0));
    1038   end
    1039 else TerrainBtn.Visible:=false;
    1040 end;
    1041 
    1042 procedure TMainScreen.CheckMovieSpeedBtnState;
    1043 begin
    1044 if GameMode=cMovie then
    1045   begin
    1046   MovieSpeed1Btn.Down:= MovieSpeed=1;
    1047   MovieSpeed1Btn.Visible:=true;
    1048   MovieSpeed2Btn.Down:= MovieSpeed=2;
    1049   MovieSpeed2Btn.Visible:=true;
    1050   MovieSpeed3Btn.Down:= MovieSpeed=3;
    1051   MovieSpeed3Btn.Visible:=true;
    1052   MovieSpeed4Btn.Down:= MovieSpeed=4;
    1053   MovieSpeed4Btn.Visible:=true;
    1054   end
    1055 else
    1056   begin
    1057   MovieSpeed1Btn.Visible:=false;
    1058   MovieSpeed2Btn.Visible:=false;
    1059   MovieSpeed3Btn.Visible:=false;
    1060   MovieSpeed4Btn.Visible:=false;
    1061   end
    1062 end;
    1063 
    1064 procedure TMainScreen.SetMapOptions;
    1065 begin
    1066 IsoEngine.Options:=MapOptionChecked;
    1067 if ClientMode=cEditMap then
    1068   IsoEngine.Options:=IsoEngine.Options or (1 shl moEditMode);
    1069 if mLocCodes.Checked then
    1070   IsoEngine.Options:=IsoEngine.Options or (1 shl moLocCodes);
    1071 end;
    1072 
    1073 procedure TMainScreen.UpdateViews(UpdateCityScreen: boolean);
    1074 begin
    1075 SumCities(TaxSum,ScienceSum);
    1076 PanelPaint; // TopBar was enough!!!
    1077 ListDlg.EcoChange;
    1078 NatStatDlg.EcoChange;
    1079 if UpdateCityScreen then
    1080   CityDlg.SmartUpdateContent;
    1081 end;
    1082 
    1083 procedure TMainScreen.SetAIName(p: integer; Name: string);
    1084 begin
    1085 if Name='' then
    1086   begin
    1087   if AILogo[p]<>nil then
    1088     begin AILogo[p].free; AILogo[p]:=nil end
    1089   end
    1090 else
    1091   begin
    1092   if AILogo[p]=nil then
    1093     AILogo[p]:=TBitmap.Create;
    1094   if not LoadGraphicFile(AILogo[p], HomeDir+Name, gfNoError) then
    1095     begin AILogo[p].free; AILogo[p]:=nil end
    1096   end
    1097 end;
    1098 
    1099 function TMainScreen.ContactRefused(p: integer; Item: String): boolean;
    1100 // return whether treaty was cancelled
    1101 var
    1102 s: string;
    1103 begin
    1104 assert(MyRO.Treaty[p]>=trPeace);
    1105 s:=Tribe[p].TPhrase(Item);
    1106 if MyRO.Turn<MyRO.LastCancelTreaty[p]+CancelTreatyTurns then
    1107   begin
    1108   SimpleMessage(s);
    1109   result:=false;
    1110   end
    1111 else
    1112   begin
    1113   case MyRO.Treaty[p] of
    1114     trPeace: s:=s+' '+Phrases.Lookup('FRCANCELQUERY_PEACE');
    1115     trFriendlyContact: s:=s+' '+Phrases.Lookup('FRCANCELQUERY_FRIENDLY');
    1116     trAlliance: s:=s+' '+Phrases.Lookup('FRCANCELQUERY_ALLIANCE');
    1117     end;
    1118   result:= SimpleQuery(mkYesNo,s,'NEGO_REJECTED')=mrOK;
    1119   if result then
    1120     begin
    1121     Play('CANCELTREATY');
    1122     Server(sCancelTreaty,me,0,nil^);
    1123     if MyRO.Treaty[p]=trNone then
    1124       CityOptimizer_BeginOfTurn; // peace treaty was cancelled -- use formerly forbidden tiles
    1125     MapValid:=false;
    1126     PaintAllMaps;
    1127     end
    1128   end
    1129 end;
    1130 
    1131 procedure TMainScreen.RememberPeaceViolation;
    1132 var
    1133 uix,p1: integer;
    1134 begin
    1135 MyData.PeaceEvaHappened:=0;
    1136 for uix:=0 to MyRO.nUn-1 do with MyUn[uix] do if Loc>=0 then
    1137   begin
    1138   p1:=MyRO.Territory[Loc];
    1139   if (p1<>me) and (p1>=0) and (MyRO.Turn=MyRO.EvaStart[p1]+(PeaceEvaTurns-1)) then
    1140     MyData.PeaceEvaHappened:=MyData.PeaceEvaHappened or (1 shl p1);
    1141   end;
    1142 end;
    1143 
    1144 procedure TMainScreen.Client(Command,NewPlayer:integer;var Data);
    1145 
    1146   procedure GetTribeList;
    1147   var
    1148   SearchRec: TSearchRec;
    1149   Color: TColor;
    1150   Name: string;
    1151   ok: boolean;
    1152   begin
    1153   UnusedTribeFiles.Clear;
    1154   ok:= FindFirst(DataDir+'Localization\'+'Tribes\*.tribe.txt',
    1155     faArchive+faReadOnly,SearchRec)=0;
    1156   if not ok then
    1157     begin
    1158     FindClose(SearchRec);
    1159     ok:= FindFirst(HomeDir+'Tribes\*.tribe.txt',
    1160       faArchive+faReadOnly,SearchRec)=0;
    1161     end;
    1162   if ok then
    1163     repeat
    1164       SearchRec.Name:=Copy(SearchRec.Name,1,Length(SearchRec.Name)-10);
    1165       if GetTribeInfo(SearchRec.Name,Name,Color) then
    1166         UnusedTribeFiles.AddObject(SearchRec.Name, TObject(Color));
    1167     until FindNext(SearchRec)<>0;
    1168   FindClose(SearchRec);
    1169   end;
    1170 
    1171   function ChooseUnusedTribe: integer;
    1172   var
    1173   i,j,ColorDistance, BestColorDistance, TestColorDistance, CountBest: integer;
    1174   begin
    1175   Assert(UnusedTribeFiles.Count>0);
    1176   result:=-1;
    1177   BestColorDistance:=-1;
    1178   for j:=0 to UnusedTribeFiles.Count-1 do
    1179     begin
    1180     ColorDistance:=250; // consider differences more than this infinite
    1181     for i:=0 to nPl-1 do if Tribe[i]<>nil then
    1182       begin
    1183       TestColorDistance:=abs(integer(UnusedTribeFiles.Objects[j]) shr 16 and $FF - Tribe[i].Color shr 16 and $FF)
    1184         +abs(integer(UnusedTribeFiles.Objects[j]) shr 8 and $FF - Tribe[i].Color shr 8 and $FF)*3
    1185         +abs(integer(UnusedTribeFiles.Objects[j]) and $FF - Tribe[i].Color and $FF)*2;
    1186       if TestColorDistance<ColorDistance then
    1187         ColorDistance:=TestColorDistance
    1188       end;
    1189     if ColorDistance>BestColorDistance then
    1190       begin CountBest:=0; BestColorDistance:=ColorDistance end;
    1191     if ColorDistance=BestColorDistance then
    1192       begin inc(CountBest); if random(CountBest)=0 then result:=j end
    1193     end;
    1194   end;
    1195 
    1196   procedure ShowEnemyShipChange(ShowShipChange: TShowShipChange);
    1197   var
    1198   i,TestCost,MostCost: integer;
    1199   Ship1Plus,Ship2Plus: boolean;
    1200   begin
    1201   with ShowShipChange, MessgExDlg do
    1202     begin
    1203     case Reason of
    1204       scrProduction:
    1205         begin
    1206         OpenSound:='SHIP_BUILT';
    1207         MessgText:=Tribe[Ship1Owner].TPhrase('SHIPBUILT');
    1208         IconKind:=mikShip;
    1209         IconIndex:=Ship1Owner;
    1210         end;
    1211 
    1212       scrDestruction:
    1213         begin
    1214         OpenSound:='SHIP_DESTROYED';
    1215         MessgText:=Tribe[Ship1Owner].TPhrase('SHIPDESTROYED');
    1216         IconKind:=mikImp;
    1217         end;
    1218 
    1219       scrTrade:
    1220         begin
    1221         OpenSound:='SHIP_TRADED';
    1222         Ship1Plus:=false;
    1223         Ship2Plus:=false;
    1224         for i:=0 to nShipPart-1 do
    1225           begin
    1226           if Ship1Change[i]>0 then Ship1Plus:=true;
    1227           if Ship2Change[i]>0 then Ship2Plus:=true;
    1228           end;
    1229         if Ship1Plus and Ship2Plus then
    1230           MessgText:=Tribe[Ship1Owner].TPhrase('SHIPBITRADE1')
    1231             +' '+Tribe[Ship2Owner].TPhrase('SHIPBITRADE2')
    1232         else if Ship1Plus then
    1233           MessgText:=Tribe[Ship1Owner].TPhrase('SHIPUNITRADE1')
    1234             +' '+Tribe[Ship2Owner].TPhrase('SHIPUNITRADE2')
    1235         else //if Ship2Plus then
    1236           MessgText:=Tribe[Ship2Owner].TPhrase('SHIPUNITRADE1')
    1237             +' '+Tribe[Ship1Owner].TPhrase('SHIPUNITRADE2');
    1238         IconKind:=mikImp;
    1239         end;
    1240 
    1241       scrCapture:
    1242         begin
    1243         OpenSound:='SHIP_CAPTURED';
    1244         MessgText:=Tribe[Ship2Owner].TPhrase('SHIPCAPTURE1')
    1245           +' '+Tribe[Ship1Owner].TPhrase('SHIPCAPTURE2');
    1246         IconKind:=mikShip;
    1247         IconIndex:=Ship2Owner;
     1105    assert(not((uix >= 0) and supervising));
     1106    if uix <> UnFocus then
     1107    begin
     1108      DestinationMarkON := false;
     1109      PaintDestination;
     1110      if uix >= 0 then
     1111        UnStartLoc := MyUn[uix].Loc;
     1112      BlinkON := false;
     1113      BlinkTime := -1;
     1114      if UnFocus >= 0 then
     1115      begin
     1116        Loc0 := MyUn[UnFocus].Loc;
     1117        if (uix < 0) or (Loc0 <> MyUn[uix].Loc) then
     1118        begin
     1119          UnFocus := -1;
     1120          PaintLoc(Loc0);
    12481121        end
    12491122      end;
    1250 
    1251     if IconKind=mikImp then
    1252       begin
    1253       MostCost:=0;
    1254       for i:=0 to nShipPart-1 do
    1255         begin
    1256         TestCost:=abs(Ship1Change[i])*Imp[imShipComp+i].Cost;
    1257         if TestCost>MostCost then
    1258           begin MostCost:=TestCost; IconIndex:=imShipComp+i end
    1259         end;
     1123      UnFocus := uix;
     1124    end;
     1125    UnitInfoBtn.Visible := UnFocus >= 0;
     1126    UnitBtn.Visible := UnFocus >= 0;
     1127    CheckTerrainBtnVisible;
     1128  end;
     1129
     1130  procedure TMainScreen.CheckTerrainBtnVisible;
     1131  var
     1132    Tile: integer;
     1133    mox: ^TModel;
     1134  begin
     1135    if UnFocus >= 0 then
     1136    begin
     1137      mox := @MyModel[MyUn[UnFocus].mix];
     1138      Tile := MyMap[MyUn[UnFocus].Loc];
     1139      TerrainBtn.Visible := (Tile and fCity = 0) and (MyUn[UnFocus].Master < 0)
     1140        and ((mox.Kind = mkSettler) or (mox.Kind = mkSlaves) and
     1141        (MyRO.Wonder[woPyramids].EffectiveOwner >= 0));
     1142    end
     1143    else
     1144      TerrainBtn.Visible := false;
     1145  end;
     1146
     1147  procedure TMainScreen.CheckMovieSpeedBtnState;
     1148  begin
     1149    if GameMode = cMovie then
     1150    begin
     1151      MovieSpeed1Btn.Down := MovieSpeed = 1;
     1152      MovieSpeed1Btn.Visible := true;
     1153      MovieSpeed2Btn.Down := MovieSpeed = 2;
     1154      MovieSpeed2Btn.Visible := true;
     1155      MovieSpeed3Btn.Down := MovieSpeed = 3;
     1156      MovieSpeed3Btn.Visible := true;
     1157      MovieSpeed4Btn.Down := MovieSpeed = 4;
     1158      MovieSpeed4Btn.Visible := true;
     1159    end
     1160    else
     1161    begin
     1162      MovieSpeed1Btn.Visible := false;
     1163      MovieSpeed2Btn.Visible := false;
     1164      MovieSpeed3Btn.Visible := false;
     1165      MovieSpeed4Btn.Visible := false;
     1166    end
     1167  end;
     1168
     1169  procedure TMainScreen.SetMapOptions;
     1170  begin
     1171    IsoEngine.Options := MapOptionChecked;
     1172    if ClientMode = cEditMap then
     1173      IsoEngine.Options := IsoEngine.Options or (1 shl moEditMode);
     1174    if mLocCodes.Checked then
     1175      IsoEngine.Options := IsoEngine.Options or (1 shl moLocCodes);
     1176  end;
     1177
     1178  procedure TMainScreen.UpdateViews(UpdateCityScreen: boolean);
     1179  begin
     1180    SumCities(TaxSum, ScienceSum);
     1181    PanelPaint; // TopBar was enough!!!
     1182    ListDlg.EcoChange;
     1183    NatStatDlg.EcoChange;
     1184    if UpdateCityScreen then
     1185      CityDlg.SmartUpdateContent;
     1186  end;
     1187
     1188  procedure TMainScreen.SetAIName(p: integer; Name: string);
     1189  begin
     1190    if Name = '' then
     1191    begin
     1192      if AILogo[p] <> nil then
     1193      begin
     1194        AILogo[p].free;
     1195        AILogo[p] := nil
     1196      end
     1197    end
     1198    else
     1199    begin
     1200      if AILogo[p] = nil then
     1201        AILogo[p] := TBitmap.Create;
     1202      if not LoadGraphicFile(AILogo[p], HomeDir + Name, gfNoError) then
     1203      begin
     1204        AILogo[p].free;
     1205        AILogo[p] := nil
     1206      end
     1207    end
     1208  end;
     1209
     1210  function TMainScreen.ContactRefused(p: integer; Item: String): boolean;
     1211  // return whether treaty was cancelled
     1212  var
     1213    s: string;
     1214  begin
     1215    assert(MyRO.Treaty[p] >= trPeace);
     1216    s := Tribe[p].TPhrase(Item);
     1217    if MyRO.Turn < MyRO.LastCancelTreaty[p] + CancelTreatyTurns then
     1218    begin
     1219      SimpleMessage(s);
     1220      result := false;
     1221    end
     1222    else
     1223    begin
     1224      case MyRO.Treaty[p] of
     1225        trPeace:
     1226          s := s + ' ' + Phrases.Lookup('FRCANCELQUERY_PEACE');
     1227        trFriendlyContact:
     1228          s := s + ' ' + Phrases.Lookup('FRCANCELQUERY_FRIENDLY');
     1229        trAlliance:
     1230          s := s + ' ' + Phrases.Lookup('FRCANCELQUERY_ALLIANCE');
    12601231      end;
    1261 
    1262     Kind:=mkOk;
    1263     ShowModal;
    1264     end;
     1232      result := SimpleQuery(mkYesNo, s, 'NEGO_REJECTED') = mrOK;
     1233      if result then
     1234      begin
     1235        Play('CANCELTREATY');
     1236        Server(sCancelTreaty, me, 0, nil^);
     1237        if MyRO.Treaty[p] = trNone then
     1238          CityOptimizer_BeginOfTurn;
     1239        // peace treaty was cancelled -- use formerly forbidden tiles
     1240        MapValid := false;
     1241        PaintAllMaps;
     1242      end
     1243    end
    12651244  end;
    12661245
    1267   procedure InitModule;
     1246  procedure TMainScreen.RememberPeaceViolation;
    12681247  var
    1269   x,y,i,j,Domain:integer;
     1248    uix, p1: integer;
    12701249  begin
    1271   {search icons for advances:}
    1272   for i:=0 to nAdv-1 do
    1273     if i in FutureTech then AdvIcon[i]:=96+i-futResearchTechnology
    1274     else
    1275       begin
    1276       AdvIcon[i]:=-1;
    1277       for Domain:=0 to nDomains-1 do
    1278         for j:=0 to nUpgrade-1 do if upgrade[Domain,j].Preq=i then
    1279           if AdvIcon[i]>=0 then AdvIcon[i]:=85
    1280           else AdvIcon[i]:=86+Domain;
    1281       for j:=0 to nFeature-1 do if Feature[j].Preq=i then
    1282         for Domain:=0 to nDomains-1 do
    1283           if 1 shl Domain and Feature[j].Domains<>0 then
    1284             if (AdvIcon[i]>=0) and (AdvIcon[i]<>86+Domain) then AdvIcon[i]:=85
    1285             else AdvIcon[i]:=86+Domain;
    1286       for j:=28 to nImp-1 do if Imp[j].Preq=i then AdvIcon[i]:=j;
    1287       for j:=28 to nImp-1 do
    1288         if (Imp[j].Preq=i) and (Imp[j].Kind<>ikCommon) then AdvIcon[i]:=j;
    1289       for j:=0 to nJob-1 do if i=JobPreq[j] then AdvIcon[i]:=84;
    1290       for j:=0 to 27 do if Imp[j].Preq=i then AdvIcon[i]:=j;
    1291       if AdvIcon[i]<0 then
    1292         if AdvValue[i]<1000 then AdvIcon[i]:=-7
    1293         else AdvIcon[i]:=24+AdvValue[i] div 1000;
    1294       for j:=2 to nGov-1 do if GovPreq[j]=i then AdvIcon[i]:=j-8;
     1250    MyData.PeaceEvaHappened := 0;
     1251    for uix := 0 to MyRO.nUn - 1 do
     1252      with MyUn[uix] do
     1253        if Loc >= 0 then
     1254        begin
     1255          p1 := MyRO.Territory[Loc];
     1256          if (p1 <> me) and (p1 >= 0) and
     1257            (MyRO.Turn = MyRO.EvaStart[p1] + (PeaceEvaTurns - 1)) then
     1258            MyData.PeaceEvaHappened := MyData.PeaceEvaHappened or (1 shl p1);
     1259        end;
     1260  end;
     1261
     1262  procedure TMainScreen.Client(Command, NewPlayer: integer; var Data);
     1263
     1264    procedure GetTribeList;
     1265    var
     1266      SearchRec: TSearchRec;
     1267      Color: TColor;
     1268      Name: string;
     1269      ok: boolean;
     1270    begin
     1271      UnusedTribeFiles.Clear;
     1272      ok := FindFirst(DataDir + 'Localization\' + 'Tribes\*.tribe.txt',
     1273        faArchive + faReadOnly, SearchRec) = 0;
     1274      if not ok then
     1275      begin
     1276        FindClose(SearchRec);
     1277        ok := FindFirst(HomeDir + 'Tribes\*.tribe.txt', faArchive + faReadOnly,
     1278          SearchRec) = 0;
    12951279      end;
    1296   AdvIcon[adConscription]:=86+dGround;
    1297 
    1298   UnusedTribeFiles:=tstringlist.Create;
    1299   UnusedTribeFiles.Sorted:=true;
    1300   TribeNames:=tstringlist.Create;
    1301 
    1302   for x:=0 to 11 do for y:=0 to 1 do
    1303     MiniColors[x,y]:=GrExt[HGrSystem].Data.Canvas.Pixels[66+x,67+y];
    1304   IsoEngine.Init(InitEnemyModel);
    1305   if not IsoEngine.ApplyTileSize(xxt,yyt) and ((xxt<>48) or (yyt<>24)) then
    1306     ApplyTileSize(48,24); // non-default tile size is missing a file, switch to default
    1307   MainMap:=TIsoMap.Create;
    1308   MainMap.SetOutput(offscreen);
    1309 
    1310   HGrStdUnits:=LoadGraphicSet('StdUnits');
    1311   SmallImp:=TBitmap.Create;
    1312   SmallImp.PixelFormat:=pf24bit;
    1313   InitSmallImp;
    1314   SoundPreloadDone:=0;
    1315   StartRunning:=false;
    1316   StayOnTop_Ensured:=false;
    1317 
    1318   CreatePVSB(sb,Handle,100-200,122,100+MidPanelHeight-16-200);
    1319   end;{InitModule}
     1280      if ok then
     1281        repeat
     1282          SearchRec.Name := Copy(SearchRec.Name, 1,
     1283            Length(SearchRec.Name) - 10);
     1284          if GetTribeInfo(SearchRec.Name, Name, Color) then
     1285            UnusedTribeFiles.AddObject(SearchRec.Name, TObject(Color));
     1286        until FindNext(SearchRec) <> 0;
     1287      FindClose(SearchRec);
     1288    end;
     1289
     1290    function ChooseUnusedTribe: integer;
     1291    var
     1292      i, j, ColorDistance, BestColorDistance, TestColorDistance,
     1293        CountBest: integer;
     1294    begin
     1295      assert(UnusedTribeFiles.Count > 0);
     1296      result := -1;
     1297      BestColorDistance := -1;
     1298      for j := 0 to UnusedTribeFiles.Count - 1 do
     1299      begin
     1300        ColorDistance := 250; // consider differences more than this infinite
     1301        for i := 0 to nPl - 1 do
     1302          if Tribe[i] <> nil then
     1303          begin
     1304            TestColorDistance :=
     1305              abs(integer(UnusedTribeFiles.Objects[j]) shr 16 and
     1306              $FF - Tribe[i].Color shr 16 and $FF) +
     1307              abs(integer(UnusedTribeFiles.Objects[j]) shr 8 and
     1308              $FF - Tribe[i].Color shr 8 and $FF) * 3 +
     1309              abs(integer(UnusedTribeFiles.Objects[j]) and
     1310              $FF - Tribe[i].Color and $FF) * 2;
     1311            if TestColorDistance < ColorDistance then
     1312              ColorDistance := TestColorDistance
     1313          end;
     1314        if ColorDistance > BestColorDistance then
     1315        begin
     1316          CountBest := 0;
     1317          BestColorDistance := ColorDistance
     1318        end;
     1319        if ColorDistance = BestColorDistance then
     1320        begin
     1321          inc(CountBest);
     1322          if random(CountBest) = 0 then
     1323            result := j
     1324        end
     1325      end;
     1326    end;
     1327
     1328    procedure ShowEnemyShipChange(ShowShipChange: TShowShipChange);
     1329    var
     1330      i, TestCost, MostCost: integer;
     1331      Ship1Plus, Ship2Plus: boolean;
     1332    begin
     1333      with ShowShipChange, MessgExDlg do
     1334      begin
     1335        case Reason of
     1336          scrProduction:
     1337            begin
     1338              OpenSound := 'SHIP_BUILT';
     1339              MessgText := Tribe[Ship1Owner].TPhrase('SHIPBUILT');
     1340              IconKind := mikShip;
     1341              IconIndex := Ship1Owner;
     1342            end;
     1343
     1344          scrDestruction:
     1345            begin
     1346              OpenSound := 'SHIP_DESTROYED';
     1347              MessgText := Tribe[Ship1Owner].TPhrase('SHIPDESTROYED');
     1348              IconKind := mikImp;
     1349            end;
     1350
     1351          scrTrade:
     1352            begin
     1353              OpenSound := 'SHIP_TRADED';
     1354              Ship1Plus := false;
     1355              Ship2Plus := false;
     1356              for i := 0 to nShipPart - 1 do
     1357              begin
     1358                if Ship1Change[i] > 0 then
     1359                  Ship1Plus := true;
     1360                if Ship2Change[i] > 0 then
     1361                  Ship2Plus := true;
     1362              end;
     1363              if Ship1Plus and Ship2Plus then
     1364                MessgText := Tribe[Ship1Owner].TPhrase('SHIPBITRADE1') + ' ' +
     1365                  Tribe[Ship2Owner].TPhrase('SHIPBITRADE2')
     1366              else if Ship1Plus then
     1367                MessgText := Tribe[Ship1Owner].TPhrase('SHIPUNITRADE1') + ' ' +
     1368                  Tribe[Ship2Owner].TPhrase('SHIPUNITRADE2')
     1369              else // if Ship2Plus then
     1370                MessgText := Tribe[Ship2Owner].TPhrase('SHIPUNITRADE1') + ' ' +
     1371                  Tribe[Ship1Owner].TPhrase('SHIPUNITRADE2');
     1372              IconKind := mikImp;
     1373            end;
     1374
     1375          scrCapture:
     1376            begin
     1377              OpenSound := 'SHIP_CAPTURED';
     1378              MessgText := Tribe[Ship2Owner].TPhrase('SHIPCAPTURE1') + ' ' +
     1379                Tribe[Ship1Owner].TPhrase('SHIPCAPTURE2');
     1380              IconKind := mikShip;
     1381              IconIndex := Ship2Owner;
     1382            end
     1383        end;
     1384
     1385        if IconKind = mikImp then
     1386        begin
     1387          MostCost := 0;
     1388          for i := 0 to nShipPart - 1 do
     1389          begin
     1390            TestCost := abs(Ship1Change[i]) * Imp[imShipComp + i].Cost;
     1391            if TestCost > MostCost then
     1392            begin
     1393              MostCost := TestCost;
     1394              IconIndex := imShipComp + i
     1395            end
     1396          end;
     1397        end;
     1398
     1399        Kind := mkOk;
     1400        ShowModal;
     1401      end;
     1402    end;
     1403
     1404    procedure InitModule;
     1405    var
     1406      x, y, i, j, Domain: integer;
     1407    begin
     1408      { search icons for advances: }
     1409      for i := 0 to nAdv - 1 do
     1410        if i in FutureTech then
     1411          AdvIcon[i] := 96 + i - futResearchTechnology
     1412        else
     1413        begin
     1414          AdvIcon[i] := -1;
     1415          for Domain := 0 to nDomains - 1 do
     1416            for j := 0 to nUpgrade - 1 do
     1417              if upgrade[Domain, j].Preq = i then
     1418                if AdvIcon[i] >= 0 then
     1419                  AdvIcon[i] := 85
     1420                else
     1421                  AdvIcon[i] := 86 + Domain;
     1422          for j := 0 to nFeature - 1 do
     1423            if Feature[j].Preq = i then
     1424              for Domain := 0 to nDomains - 1 do
     1425                if 1 shl Domain and Feature[j].Domains <> 0 then
     1426                  if (AdvIcon[i] >= 0) and (AdvIcon[i] <> 86 + Domain) then
     1427                    AdvIcon[i] := 85
     1428                  else
     1429                    AdvIcon[i] := 86 + Domain;
     1430          for j := 28 to nImp - 1 do
     1431            if Imp[j].Preq = i then
     1432              AdvIcon[i] := j;
     1433          for j := 28 to nImp - 1 do
     1434            if (Imp[j].Preq = i) and (Imp[j].Kind <> ikCommon) then
     1435              AdvIcon[i] := j;
     1436          for j := 0 to nJob - 1 do
     1437            if i = JobPreq[j] then
     1438              AdvIcon[i] := 84;
     1439          for j := 0 to 27 do
     1440            if Imp[j].Preq = i then
     1441              AdvIcon[i] := j;
     1442          if AdvIcon[i] < 0 then
     1443            if AdvValue[i] < 1000 then
     1444              AdvIcon[i] := -7
     1445            else
     1446              AdvIcon[i] := 24 + AdvValue[i] div 1000;
     1447          for j := 2 to nGov - 1 do
     1448            if GovPreq[j] = i then
     1449              AdvIcon[i] := j - 8;
     1450        end;
     1451      AdvIcon[adConscription] := 86 + dGround;
     1452
     1453      UnusedTribeFiles := tstringlist.Create;
     1454      UnusedTribeFiles.Sorted := true;
     1455      TribeNames := tstringlist.Create;
     1456
     1457      for x := 0 to 11 do
     1458        for y := 0 to 1 do
     1459          MiniColors[x, y] := GrExt[HGrSystem].Data.Canvas.Pixels
     1460            [66 + x, 67 + y];
     1461      IsoEngine.Init(InitEnemyModel);
     1462      if not IsoEngine.ApplyTileSize(xxt, yyt) and ((xxt <> 48) or (yyt <> 24))
     1463      then
     1464        ApplyTileSize(48, 24);
     1465      // non-default tile size is missing a file, switch to default
     1466      MainMap := TIsoMap.Create;
     1467      MainMap.SetOutput(offscreen);
     1468
     1469      HGrStdUnits := LoadGraphicSet('StdUnits');
     1470      SmallImp := TBitmap.Create;
     1471      SmallImp.PixelFormat := pf24bit;
     1472      InitSmallImp;
     1473      SoundPreloadDone := 0;
     1474      StartRunning := false;
     1475      StayOnTop_Ensured := false;
     1476
     1477      CreatePVSB(sb, Handle, 100 - 200, 122, 100 + MidPanelHeight - 16 - 200);
     1478    end; { InitModule }
    13201479
    13211480  // sound blocks for preload
    13221481  const
    1323   sbStart=$01; sbWonder=$02; sbScience=$04; sbContact=$08;
    1324   sbTurn=$10; sbAll=$FF;
    1325 
    1326   procedure SoundPreload(Check: integer);
    1327   const
    1328   nStartBlock=27;
    1329   StartBlock: array[0..nStartBlock-1] of string=
    1330   ('INVALID','TURNEND','DISBAND','CHEAT','MSG_DEFAULT','WARNING_DISORDER',
    1331   'WARNING_FAMINE','WARNING_LOWSUPPORT','WARNING_LOWFUNDS','MOVE_MOUNTAIN',
    1332   'MOVE_LOAD','MOVE_UNLOAD','MOVE_DIE','NOMOVE_TIME','NOMOVE_DOMAIN',
    1333   'NOMOVE_DEFAULT','CITY_SELLIMP','CITY_REBUILDIMP','CITY_BUYPROJECT',
    1334   'CITY_UTILIZE','NEWMODEL_0','NEWADVANCE_0','AGE_0','REVOLUTION','NEWGOV',
    1335   'CITY_INVALIDTYPE','MSG_GAMEOVER');
    1336 
    1337   nWonderBlock=6;
    1338   WonderBlock: array[0..nWonderBlock-1] of string=
    1339   ('WONDER_BUILT','WONDER_CAPTURED','WONDER_EXPIRED','WONDER_DESTROYED',
    1340   'MSG_COLDWAR','NEWADVANCE_GRLIB');
    1341 
    1342   nScienceBlock=17;
    1343   ScienceBlock: array[0..nScienceBlock-1] of string=
    1344   ('MOVE_PARACHUTE','MOVE_PLANESTART','MOVE_PLANELANDING','MOVE_COVERT',
    1345   'NEWMODEL_1','NEWMODEL_2','NEWMODEL_3','NEWADVANCE_1','NEWADVANCE_2',
    1346   'NEWADVANCE_3','AGE_1','AGE_2','AGE_3','SHIP_BUILT','SHIP_TRADED',
    1347   'SHIP_CAPTURED','SHIP_DESTROYED');
    1348 
    1349   nContactBlock=20;
    1350   ContactBlock: array[0..nContactBlock-1] of string=
    1351   ('NEWTREATY','CANCELTREATY','ACCEPTOFFER','MSG_WITHDRAW','MSG_BANKRUPT',
    1352   'CONTACT_0','CONTACT_1','CONTACT_2','CONTACT_3','CONTACT_4','CONTACT_5',
    1353   'CONTACT_5','CONTACT_6','NEGO_REJECTED','MOVE_CAPTURE','MOVE_EXPEL',
    1354   'NOMOVE_TREATY','NOMOVE_ZOC','NOMOVE_SUBMARINE','NOMOVE_STEALTH');
    1355 
    1356   var
    1357   i,cix,mix: integer;
    1358   need: boolean;
    1359   mi: TModelInfo;
    1360   begin
    1361   if Check and sbStart and not SoundPreloadDone<>0 then
    1362     begin
    1363     for i:=0 to nStartBlock-1 do PreparePlay(StartBlock[i]);
    1364     SoundPreloadDone:=SoundPreloadDone or sbStart;
    1365     end;
    1366   if Check and sbWonder and not SoundPreloadDone<>0 then
    1367     begin
    1368     need:=false;
    1369     for i:=0 to 27 do if MyRO.Wonder[i].CityID<>-1 then need:=true;
    1370     if need then
    1371       begin
    1372       for i:=0 to nWonderBlock-1 do PreparePlay(WonderBlock[i]);
    1373       SoundPreloadDone:=SoundPreloadDone or sbWonder;
     1482    sbStart = $01;
     1483    sbWonder = $02;
     1484    sbScience = $04;
     1485    sbContact = $08;
     1486    sbTurn = $10;
     1487    sbAll = $FF;
     1488
     1489    procedure SoundPreload(Check: integer);
     1490    const
     1491      nStartBlock = 27;
     1492      StartBlock: array [0 .. nStartBlock - 1] of string = ('INVALID',
     1493        'TURNEND', 'DISBAND', 'CHEAT', 'MSG_DEFAULT', 'WARNING_DISORDER',
     1494        'WARNING_FAMINE', 'WARNING_LOWSUPPORT', 'WARNING_LOWFUNDS',
     1495        'MOVE_MOUNTAIN', 'MOVE_LOAD', 'MOVE_UNLOAD', 'MOVE_DIE', 'NOMOVE_TIME',
     1496        'NOMOVE_DOMAIN', 'NOMOVE_DEFAULT', 'CITY_SELLIMP', 'CITY_REBUILDIMP',
     1497        'CITY_BUYPROJECT', 'CITY_UTILIZE', 'NEWMODEL_0', 'NEWADVANCE_0',
     1498        'AGE_0', 'REVOLUTION', 'NEWGOV', 'CITY_INVALIDTYPE', 'MSG_GAMEOVER');
     1499
     1500      nWonderBlock = 6;
     1501      WonderBlock: array [0 .. nWonderBlock - 1] of string = ('WONDER_BUILT',
     1502        'WONDER_CAPTURED', 'WONDER_EXPIRED', 'WONDER_DESTROYED', 'MSG_COLDWAR',
     1503        'NEWADVANCE_GRLIB');
     1504
     1505      nScienceBlock = 17;
     1506      ScienceBlock: array [0 .. nScienceBlock - 1] of string =
     1507        ('MOVE_PARACHUTE', 'MOVE_PLANESTART', 'MOVE_PLANELANDING',
     1508        'MOVE_COVERT', 'NEWMODEL_1', 'NEWMODEL_2', 'NEWMODEL_3', 'NEWADVANCE_1',
     1509        'NEWADVANCE_2', 'NEWADVANCE_3', 'AGE_1', 'AGE_2', 'AGE_3', 'SHIP_BUILT',
     1510        'SHIP_TRADED', 'SHIP_CAPTURED', 'SHIP_DESTROYED');
     1511
     1512      nContactBlock = 20;
     1513      ContactBlock: array [0 .. nContactBlock - 1] of string = ('NEWTREATY',
     1514        'CANCELTREATY', 'ACCEPTOFFER', 'MSG_WITHDRAW', 'MSG_BANKRUPT',
     1515        'CONTACT_0', 'CONTACT_1', 'CONTACT_2', 'CONTACT_3', 'CONTACT_4',
     1516        'CONTACT_5', 'CONTACT_5', 'CONTACT_6', 'NEGO_REJECTED', 'MOVE_CAPTURE',
     1517        'MOVE_EXPEL', 'NOMOVE_TREATY', 'NOMOVE_ZOC', 'NOMOVE_SUBMARINE',
     1518        'NOMOVE_STEALTH');
     1519
     1520    var
     1521      i, cix, mix: integer;
     1522      need: boolean;
     1523      mi: TModelInfo;
     1524    begin
     1525      if Check and sbStart and not SoundPreloadDone <> 0 then
     1526      begin
     1527        for i := 0 to nStartBlock - 1 do
     1528          PreparePlay(StartBlock[i]);
     1529        SoundPreloadDone := SoundPreloadDone or sbStart;
    13741530      end;
    1375     end;
    1376   if (Check and sbScience and not SoundPreloadDone<>0)
    1377     and (MyRO.Tech[adScience]>=tsApplicable) then
    1378     begin
    1379     for i:=0 to nScienceBlock-1 do PreparePlay(ScienceBlock[i]);
    1380     SoundPreloadDone:=SoundPreloadDone or sbScience;
    1381     end;
    1382   if (Check and sbContact and not SoundPreloadDone<>0)
    1383     and (MyRO.nEnemyModel+MyRO.nEnemyCity>0) then
    1384     begin
    1385     for i:=0 to nContactBlock-1 do PreparePlay(ContactBlock[i]);
    1386     SoundPreloadDone:=SoundPreloadDone or sbContact;
    1387     end;
    1388   if Check and sbTurn<>0 then
    1389     begin
    1390     if MyRO.Happened and phShipComplete<>0 then
    1391       PreparePlay('MSG_YOUWIN');
    1392     if MyData.ToldAlive<>MyRO.Alive then PreparePlay('MSG_EXTINCT');
    1393     for cix:=0 to MyRO.nCity-1 do with MyCity[cix] do
    1394       if (Loc>=0) and (Flags and CityRepMask<>0) then
    1395         for i:=0 to 12 do if 1 shl i and Flags and CityRepMask<>0 then
    1396           PreparePlay(CityEventSoundItem[i]);
    1397      for mix:=0 to MyRO.nModel-1 do with MyModel[mix] do if Attack>0 then
    1398        begin
    1399        MakeModelInfo(me,mix,MyModel[mix],mi);
    1400        PreparePlay(AttackSound(ModelCode(mi)));
    1401        end
    1402      end
    1403   end;
    1404 
    1405   procedure InitTurn(p: integer);
    1406   const
    1407   nAdvBookIcon=16;
    1408   AdvBookIcon: array[0..nAdvBookIcon-1] of record Adv,Icon: integer end=
    1409   ((Adv:adPolyTheism;Icon:woZeus),(Adv:adBronzeWorking;Icon:woColossus),
    1410   (Adv:adMapMaking;Icon:woLighthouse),(Adv:adPoetry;Icon:imTheater),
    1411   (Adv:adMonotheism;Icon:woMich),(Adv:adPhilosophy;Icon:woLeo),
    1412   (Adv:adTheoryOfGravity;Icon:woNewton),(Adv:adSteel;Icon:woEiffel),
    1413   (Adv:adDemocracy;Icon:woLiberty),(Adv:adAutomobile;Icon:imHighways),
    1414   (Adv:adSanitation;Icon:imSewer),(Adv:adElectronics;Icon:woHoover),
    1415   (Adv:adNuclearFission;Icon:woManhattan),(Adv:adRecycling;Icon:imRecycling),
    1416   (Adv:adComputers;Icon:imResLab),(Adv:adSpaceFlight;Icon:woMIR));
    1417   var
    1418   Domain,p1,i,ad,uix,cix,MoveOptions,MoveResult,Loc1,Dist,NewAgeCenterTo,
    1419     Bankrupt,ShipMore,Winners,NewGovAvailable,dx,dy:integer;
    1420   MoveAdviceData: TMoveAdviceData;
    1421   Picture: TModelPictureInfo;
    1422   s, Item, Item2: string;
    1423   UpdatePanel, OwnWonder, ok, Stop, ShowCityList, WondersOnly,AllowCityScreen: boolean;
    1424   begin
    1425   if IsMultiPlayerGame and (p<>me) then
    1426     begin
    1427     UnitInfoBtn.Visible:=false;
    1428     UnitBtn.Visible:=false;
    1429     TerrainBtn.Visible:=false;
    1430     EOT.Visible:=false;
    1431     end;
    1432   if IsMultiPlayerGame and (p<>me) and (G.RO[0].Happened and phShipComplete=0) then
    1433     begin //inter player screen
    1434     for i:=0 to ControlCount-1 do if Controls[i] is TButtonC then
    1435       Controls[i].visible:=false;
    1436     me:=-1;
    1437     SetMainTextureByAge(-1);
    1438     with Panel.Canvas do
    1439       begin
    1440       Brush.Color:=$000000;
    1441       FillRect(Rect(0,0,Panel.Width,Panel.Height));
    1442       Brush.Style:=bsClear;
     1531      if Check and sbWonder and not SoundPreloadDone <> 0 then
     1532      begin
     1533        need := false;
     1534        for i := 0 to 27 do
     1535          if MyRO.Wonder[i].CityID <> -1 then
     1536            need := true;
     1537        if need then
     1538        begin
     1539          for i := 0 to nWonderBlock - 1 do
     1540            PreparePlay(WonderBlock[i]);
     1541          SoundPreloadDone := SoundPreloadDone or sbWonder;
     1542        end;
    14431543      end;
    1444     with TopBar.Canvas do
    1445       begin
    1446       Brush.Color:=$000000;
    1447       FillRect(Rect(0,0,TopBar.Width,TopBar.Height));
    1448       Brush.Style:=bsClear;
     1544      if (Check and sbScience and not SoundPreloadDone <> 0) and
     1545        (MyRO.Tech[adScience] >= tsApplicable) then
     1546      begin
     1547        for i := 0 to nScienceBlock - 1 do
     1548          PreparePlay(ScienceBlock[i]);
     1549        SoundPreloadDone := SoundPreloadDone or sbScience;
    14491550      end;
    1450     Invalidate;
    1451 
    1452     s:=TurnToString(G.RO[0].Turn);
    1453     if supervising then
    1454       SimpleMessage(Format(Phrases.Lookup('SUPERTURN'),[s]))
    1455     else SimpleMessage(Format(Tribe[NewPlayer].TPhrase('TURN'),[s]));
    1456     end;
    1457   for i:=0 to ControlCount-1 do if Controls[i] is TButtonC then
    1458     Controls[i].visible:=true;
    1459 
    1460   ItsMeAgain(p);
    1461   MyData:=G.RO[p].Data;
    1462   if not supervising then
    1463     SoundPreload(sbAll);
    1464   if (me=0) and ((MyRO.Turn=0) or (ClientMode=cResume)) then
    1465     Invalidate; // colorize empty space
    1466 
    1467   if not supervising then
    1468     begin
    1469 {    if MyRO.Happened and phGameEnd<>0 then
    1470       begin
    1471       Age:=3;
    1472       SetMainTextureByAge(-1);
     1551      if (Check and sbContact and not SoundPreloadDone <> 0) and
     1552        (MyRO.nEnemyModel + MyRO.nEnemyCity > 0) then
     1553      begin
     1554        for i := 0 to nContactBlock - 1 do
     1555          PreparePlay(ContactBlock[i]);
     1556        SoundPreloadDone := SoundPreloadDone or sbContact;
     1557      end;
     1558      if Check and sbTurn <> 0 then
     1559      begin
     1560        if MyRO.Happened and phShipComplete <> 0 then
     1561          PreparePlay('MSG_YOUWIN');
     1562        if MyData.ToldAlive <> MyRO.Alive then
     1563          PreparePlay('MSG_EXTINCT');
     1564        for cix := 0 to MyRO.nCity - 1 do
     1565          with MyCity[cix] do
     1566            if (Loc >= 0) and (Flags and CityRepMask <> 0) then
     1567              for i := 0 to 12 do
     1568                if 1 shl i and Flags and CityRepMask <> 0 then
     1569                  PreparePlay(CityEventSoundItem[i]);
     1570        for mix := 0 to MyRO.nModel - 1 do
     1571          with MyModel[mix] do
     1572            if Attack > 0 then
     1573            begin
     1574              MakeModelInfo(me, mix, MyModel[mix], mi);
     1575              PreparePlay(AttackSound(ModelCode(mi)));
     1576            end
    14731577      end
    1474     else}
    1475       begin
    1476       Age:=GetAge(me);
    1477       if SetMainTextureByAge(Age) then
    1478         EOT.Invalidate; // has visible background parts in its bounds
     1578    end;
     1579
     1580    procedure InitTurn(p: integer);
     1581    const
     1582      nAdvBookIcon = 16;
     1583      AdvBookIcon: array [0 .. nAdvBookIcon - 1] of record Adv,
     1584        Icon: integer end = ((Adv: adPolyTheism; Icon: woZeus),
     1585        (Adv: adBronzeWorking; Icon: woColossus), (Adv: adMapMaking;
     1586        Icon: woLighthouse), (Adv: adPoetry; Icon: imTheater),
     1587        (Adv: adMonotheism; Icon: woMich), (Adv: adPhilosophy; Icon: woLeo),
     1588        (Adv: adTheoryOfGravity; Icon: woNewton), (Adv: adSteel;
     1589        Icon: woEiffel), (Adv: adDemocracy; Icon: woLiberty),
     1590        (Adv: adAutomobile; Icon: imHighways), (Adv: adSanitation;
     1591        Icon: imSewer), (Adv: adElectronics; Icon: woHoover),
     1592        (Adv: adNuclearFission; Icon: woManhattan), (Adv: adRecycling;
     1593        Icon: imRecycling), (Adv: adComputers; Icon: imResLab),
     1594        (Adv: adSpaceFlight; Icon: woMIR));
     1595    var
     1596      Domain, p1, i, ad, uix, cix, MoveOptions, MoveResult, Loc1, Dist,
     1597        NewAgeCenterTo, Bankrupt, ShipMore, Winners, NewGovAvailable, dx,
     1598        dy: integer;
     1599      MoveAdviceData: TMoveAdviceData;
     1600      Picture: TModelPictureInfo;
     1601      s, Item, Item2: string;
     1602      UpdatePanel, OwnWonder, ok, Stop, ShowCityList, WondersOnly,
     1603        AllowCityScreen: boolean;
     1604    begin
     1605      if IsMultiPlayerGame and (p <> me) then
     1606      begin
     1607        UnitInfoBtn.Visible := false;
     1608        UnitBtn.Visible := false;
     1609        TerrainBtn.Visible := false;
     1610        EOT.Visible := false;
    14791611      end;
    1480 //    age:=MyRO.Turn mod 4; //!!!
    1481     if ClientMode=cMovieTurn then
    1482       EOT.ButtonIndex:=eotCancel
    1483     else if ClientMode<scContact then
    1484       EOT.ButtonIndex:=eotGray
    1485     else EOT.ButtonIndex:=eotBackToNego;
    1486     end
    1487   else
    1488     begin
    1489     Age:=0;
    1490     SetMainTextureByAge(-1);
    1491     if ClientMode=cMovieTurn then
    1492       EOT.ButtonIndex:=eotCancel
    1493     else EOT.ButtonIndex:=eotBlinkOn;
    1494     end;
    1495   InitCityMark(MainTexture);
    1496   CityDlg.CheckAge;
    1497   NatStatDlg.CheckAge;
    1498   UnitStatDlg.CheckAge;
    1499   HelpDlg.Difficulty:=G.Difficulty[me];
    1500 
    1501   UnFocus:=-1;
    1502   MarkCityLoc:=-1;
    1503   BlinkON:=false;
    1504   BlinkTime:=-1;
    1505   Tracking:=false;
    1506   TurnComplete:=false;
    1507 
    1508   if (ToldSlavery<0)
    1509     or ((ToldSlavery=1)<>(MyRO.Wonder[woPyramids].EffectiveOwner>=0)) then
    1510     begin
    1511     if MyRO.Wonder[woPyramids].EffectiveOwner>=0 then ToldSlavery:=1
    1512     else ToldSlavery:=0;
    1513     for p1:=0 to nPl-1 do
    1514       if (Tribe[p1]<>nil) and (Tribe[p1].mixSlaves>=0) then
    1515         with Picture do
    1516           begin // replace unit picture
    1517           mix:=Tribe[p1].mixSlaves;
    1518           if ToldSlavery=1 then pix:=pixSlaves else pix:=pixNoSlaves;
    1519           Hash:=0;
    1520           GrName:='StdUnits';
    1521           Tribe[p1].SetModelPicture(Picture, true);
     1612      if IsMultiPlayerGame and (p <> me) and
     1613        (G.RO[0].Happened and phShipComplete = 0) then
     1614      begin // inter player screen
     1615        for i := 0 to ControlCount - 1 do
     1616          if Controls[i] is TButtonC then
     1617            Controls[i].Visible := false;
     1618        me := -1;
     1619        SetMainTextureByAge(-1);
     1620        with Panel.Canvas do
     1621        begin
     1622          Brush.Color := $000000;
     1623          FillRect(Rect(0, 0, Panel.width, Panel.height));
     1624          Brush.Style := bsClear;
     1625        end;
     1626        with TopBar.Canvas do
     1627        begin
     1628          Brush.Color := $000000;
     1629          FillRect(Rect(0, 0, TopBar.width, TopBar.height));
     1630          Brush.Style := bsClear;
     1631        end;
     1632        Invalidate;
     1633
     1634        s := TurnToString(G.RO[0].Turn);
     1635        if supervising then
     1636          SimpleMessage(Format(Phrases.Lookup('SUPERTURN'), [s]))
     1637        else
     1638          SimpleMessage(Format(Tribe[NewPlayer].TPhrase('TURN'), [s]));
     1639      end;
     1640      for i := 0 to ControlCount - 1 do
     1641        if Controls[i] is TButtonC then
     1642          Controls[i].Visible := true;
     1643
     1644      ItsMeAgain(p);
     1645      MyData := G.RO[p].Data;
     1646      if not supervising then
     1647        SoundPreload(sbAll);
     1648      if (me = 0) and ((MyRO.Turn = 0) or (ClientMode = cResume)) then
     1649        Invalidate; // colorize empty space
     1650
     1651      if not supervising then
     1652      begin
     1653
     1654        { if MyRO.Happened and phGameEnd<>0 then
     1655          begin
     1656          Age:=3;
     1657          SetMainTextureByAge(-1);
    15221658          end
    1523     end;
    1524 
    1525   if not supervising and (ClientMode=cTurn) then
    1526     begin
    1527     for cix:=0 to MyRO.nCity-1 do
    1528       if (MyCity[cix].Loc>=0)
    1529         and ((MyRO.Turn=0) or (MyCity[cix].Flags and chFounded<>0)) then
    1530         MyCity[cix].Status:=MyCity[cix].Status
    1531           and not csResourceWeightsMask or (3 shl 4); // new city, set to maximum growth
    1532     end;
    1533   if (ClientMode=cTurn) or (ClientMode=cContinue) then
    1534     CityOptimizer_BeginOfTurn; // maybe peace was made or has ended
    1535   SumCities(TaxSum,ScienceSum);
    1536 
    1537   if ClientMode=cMovieTurn then
    1538     begin
    1539     UnitInfoBtn.Visible:=false;
    1540     UnitBtn.Visible:=false;
    1541     TerrainBtn.Visible:=false;
    1542     EOT.Hint:=Phrases.Lookup('BTN_STOP');
    1543     EOT.Visible:=true;
    1544     end
    1545   else if ClientMode<scContact then
    1546     begin
    1547     UnitInfoBtn.Visible:= UnFocus>=0;
    1548     UnitBtn.Visible:= UnFocus>=0;
    1549     CheckTerrainBtnVisible;
    1550     TurnComplete:=supervising;
    1551     EOT.Hint:=Phrases.Lookup('BTN_ENDTURN');
    1552     EOT.Visible:= Server(sTurn-sExecute,me,0,nil^)>=rExecuted;
    1553     end
    1554   else
    1555     begin
    1556     UnitInfoBtn.Visible:=false;
    1557     UnitBtn.Visible:=false;
    1558     TerrainBtn.Visible:=false;
    1559     EOT.Hint:=Phrases.Lookup('BTN_NEGO');
    1560     EOT.Visible:=true;
    1561     end;
    1562   SetTroopLoc(-1);
    1563   MapValid:=false;
    1564   NewAgeCenterTo:=0;
    1565   if ((MyRO.Turn=0) and not supervising or IsMultiPlayerGame
    1566     or (ClientMode=cResume)) and (MyRO.nCity>0) then
    1567     begin
    1568     Loc1:=MyCity[0].Loc;
    1569     if (ClientMode=cTurn) and (MyRO.Turn=0) then
    1570       begin // move city out of center to not be covered by welcome screen
    1571       dx:=MapWidth div (xxt*5);
    1572       if dx>5 then
    1573         dx:=5;
    1574       dy:=MapHeight div (yyt*5);
    1575       if dy>5 then
    1576         dy:=5;
    1577       if Loc1>=G.lx*G.ly div 2 then
    1578         begin
    1579         NewAgeCenterTo:=-1;
    1580         Loc1:=dLoc(Loc1,-dx,-dy)
    1581         end
     1659          else }
     1660        begin
     1661          Age := GetAge(me);
     1662          if SetMainTextureByAge(Age) then
     1663            EOT.Invalidate; // has visible background parts in its bounds
     1664        end;
     1665        // age:=MyRO.Turn mod 4; //!!!
     1666        if ClientMode = cMovieTurn then
     1667          EOT.ButtonIndex := eotCancel
     1668        else if ClientMode < scContact then
     1669          EOT.ButtonIndex := eotGray
     1670        else
     1671          EOT.ButtonIndex := eotBackToNego;
     1672      end
    15821673      else
    1583         begin
    1584         NewAgeCenterTo:=1;
    1585         Loc1:=dLoc(Loc1,-dx,dy);
    1586         end
     1674      begin
     1675        Age := 0;
     1676        SetMainTextureByAge(-1);
     1677        if ClientMode = cMovieTurn then
     1678          EOT.ButtonIndex := eotCancel
     1679        else
     1680          EOT.ButtonIndex := eotBlinkOn;
    15871681      end;
    1588     Centre(Loc1)
    1589     end;
    1590 
    1591   for i:=0 to Screen.FormCount-1 do
    1592     if Screen.Forms[i] is TBufferedDrawDlg then
    1593       Screen.Forms[i].Enabled:=true;
    1594 
    1595   if ClientMode<>cResume then
    1596     begin
    1597     PaintAll;
    1598     if (MyRO.Happened and phChangeGov<>0) and (MyRO.NatBuilt[imPalace]>0) then
    1599       ImpImage(Panel.Canvas, ClientWidth-xPalace, yPalace, imPalace, gAnarchy{, GameMode<>cMovie});
    1600       // first turn after anarchy -- don't show despotism palace!
    1601     Update;
    1602     for i:=0 to Screen.FormCount-1 do
    1603       if (Screen.Forms[i].Visible) and (Screen.Forms[i] is TBufferedDrawDlg) then
    1604         begin
    1605         if @Screen.Forms[i].OnShow<>nil then
    1606           Screen.Forms[i].OnShow(nil);
    1607         Screen.Forms[i].Invalidate;
    1608         Screen.Forms[i].Update;
    1609         end;
    1610 
    1611     if MyRO.Happened and phGameEnd<>0 then
    1612       with MessgExDlg do
    1613         begin // game ended
    1614         if MyRO.Happened and phExtinct<>0 then
     1682      InitCityMark(MainTexture);
     1683      CityDlg.CheckAge;
     1684      NatStatDlg.CheckAge;
     1685      UnitStatDlg.CheckAge;
     1686      HelpDlg.Difficulty := G.Difficulty[me];
     1687
     1688      UnFocus := -1;
     1689      MarkCityLoc := -1;
     1690      BlinkON := false;
     1691      BlinkTime := -1;
     1692      Tracking := false;
     1693      TurnComplete := false;
     1694
     1695      if (ToldSlavery < 0) or
     1696        ((ToldSlavery = 1) <> (MyRO.Wonder[woPyramids].EffectiveOwner >= 0))
     1697      then
     1698      begin
     1699        if MyRO.Wonder[woPyramids].EffectiveOwner >= 0 then
     1700          ToldSlavery := 1
     1701        else
     1702          ToldSlavery := 0;
     1703        for p1 := 0 to nPl - 1 do
     1704          if (Tribe[p1] <> nil) and (Tribe[p1].mixSlaves >= 0) then
     1705            with Picture do
     1706            begin // replace unit picture
     1707              mix := Tribe[p1].mixSlaves;
     1708              if ToldSlavery = 1 then
     1709                pix := pixSlaves
     1710              else
     1711                pix := pixNoSlaves;
     1712              Hash := 0;
     1713              GrName := 'StdUnits';
     1714              Tribe[p1].SetModelPicture(Picture, true);
     1715            end
     1716      end;
     1717
     1718      if not supervising and (ClientMode = cTurn) then
     1719      begin
     1720        for cix := 0 to MyRO.nCity - 1 do
     1721          if (MyCity[cix].Loc >= 0) and
     1722            ((MyRO.Turn = 0) or (MyCity[cix].Flags and chFounded <> 0)) then
     1723            MyCity[cix].Status := MyCity[cix].Status and
     1724              not csResourceWeightsMask or (3 shl 4);
     1725        // new city, set to maximum growth
     1726      end;
     1727      if (ClientMode = cTurn) or (ClientMode = cContinue) then
     1728        CityOptimizer_BeginOfTurn; // maybe peace was made or has ended
     1729      SumCities(TaxSum, ScienceSum);
     1730
     1731      if ClientMode = cMovieTurn then
     1732      begin
     1733        UnitInfoBtn.Visible := false;
     1734        UnitBtn.Visible := false;
     1735        TerrainBtn.Visible := false;
     1736        EOT.Hint := Phrases.Lookup('BTN_STOP');
     1737        EOT.Visible := true;
     1738      end
     1739      else if ClientMode < scContact then
     1740      begin
     1741        UnitInfoBtn.Visible := UnFocus >= 0;
     1742        UnitBtn.Visible := UnFocus >= 0;
     1743        CheckTerrainBtnVisible;
     1744        TurnComplete := supervising;
     1745        EOT.Hint := Phrases.Lookup('BTN_ENDTURN');
     1746        EOT.Visible := Server(sTurn - sExecute, me, 0, nil^) >= rExecuted;
     1747      end
     1748      else
     1749      begin
     1750        UnitInfoBtn.Visible := false;
     1751        UnitBtn.Visible := false;
     1752        TerrainBtn.Visible := false;
     1753        EOT.Hint := Phrases.Lookup('BTN_NEGO');
     1754        EOT.Visible := true;
     1755      end;
     1756      SetTroopLoc(-1);
     1757      MapValid := false;
     1758      NewAgeCenterTo := 0;
     1759      if ((MyRO.Turn = 0) and not supervising or IsMultiPlayerGame or
     1760        (ClientMode = cResume)) and (MyRO.nCity > 0) then
     1761      begin
     1762        Loc1 := MyCity[0].Loc;
     1763        if (ClientMode = cTurn) and (MyRO.Turn = 0) then
     1764        begin // move city out of center to not be covered by welcome screen
     1765          dx := MapWidth div (xxt * 5);
     1766          if dx > 5 then
     1767            dx := 5;
     1768          dy := MapHeight div (yyt * 5);
     1769          if dy > 5 then
     1770            dy := 5;
     1771          if Loc1 >= G.lx * G.ly div 2 then
    16151772          begin
    1616           OpenSound:='MSG_GAMEOVER';
    1617           MessgText:=Tribe[me].TPhrase('GAMEOVER');
    1618           IconKind:=mikBigIcon;
    1619           IconIndex:=8;
     1773            NewAgeCenterTo := -1;
     1774            Loc1 := dLoc(Loc1, -dx, -dy)
    16201775          end
    1621         else if MyRO.Happened and phShipComplete<>0 then
     1776          else
    16221777          begin
    1623           Winners:=0;
    1624           for p1:=0 to nPl-1 do if 1 shl p1 and MyRO.Alive<>0 then
     1778            NewAgeCenterTo := 1;
     1779            Loc1 := dLoc(Loc1, -dx, dy);
     1780          end
     1781        end;
     1782        Centre(Loc1)
     1783      end;
     1784
     1785      for i := 0 to Screen.FormCount - 1 do
     1786        if Screen.Forms[i] is TBufferedDrawDlg then
     1787          Screen.Forms[i].Enabled := true;
     1788
     1789      if ClientMode <> cResume then
     1790      begin
     1791        PaintAll;
     1792        if (MyRO.Happened and phChangeGov <> 0) and (MyRO.NatBuilt[imPalace] > 0)
     1793        then
     1794          ImpImage(Panel.Canvas, ClientWidth - xPalace, yPalace, imPalace,
     1795            gAnarchy { , GameMode<>cMovie } );
     1796        // first turn after anarchy -- don't show despotism palace!
     1797        Update;
     1798        for i := 0 to Screen.FormCount - 1 do
     1799          if (Screen.Forms[i].Visible) and (Screen.Forms[i] is TBufferedDrawDlg)
     1800          then
     1801          begin
     1802            if @Screen.Forms[i].OnShow <> nil then
     1803              Screen.Forms[i].OnShow(nil);
     1804            Screen.Forms[i].Invalidate;
     1805            Screen.Forms[i].Update;
     1806          end;
     1807
     1808        if MyRO.Happened and phGameEnd <> 0 then
     1809          with MessgExDlg do
     1810          begin // game ended
     1811            if MyRO.Happened and phExtinct <> 0 then
    16251812            begin
    1626             Winners:=Winners or 1 shl p1;
    1627             for i:=0 to nShipPart-1 do
    1628               if MyRO.Ship[p1].Parts[i]<ShipNeed[i] then
    1629                 Winners:=Winners and not (1 shl p1);
     1813              OpenSound := 'MSG_GAMEOVER';
     1814              MessgText := Tribe[me].TPhrase('GAMEOVER');
     1815              IconKind := mikBigIcon;
     1816              IconIndex := 8;
     1817            end
     1818            else if MyRO.Happened and phShipComplete <> 0 then
     1819            begin
     1820              Winners := 0;
     1821              for p1 := 0 to nPl - 1 do
     1822                if 1 shl p1 and MyRO.Alive <> 0 then
     1823                begin
     1824                  Winners := Winners or 1 shl p1;
     1825                  for i := 0 to nShipPart - 1 do
     1826                    if MyRO.Ship[p1].Parts[i] < ShipNeed[i] then
     1827                      Winners := Winners and not(1 shl p1);
     1828                end;
     1829              assert(Winners <> 0);
     1830              if Winners and (1 shl me) <> 0 then
     1831              begin
     1832                s := '';
     1833                for p1 := 0 to nPl - 1 do
     1834                  if (p1 <> me) and (1 shl p1 and Winners <> 0) then
     1835                    if s = '' then
     1836                      s := Tribe[p1].TPhrase('SHORTNAME')
     1837                    else
     1838                      s := Format(Phrases.Lookup('SHAREDWIN_CONCAT'),
     1839                        [s, Tribe[p1].TPhrase('SHORTNAME')]);
     1840
     1841                OpenSound := 'MSG_YOUWIN';
     1842                MessgText := Tribe[me].TPhrase('MYSPACESHIP');
     1843                if s <> '' then
     1844                  MessgText := MessgText + '\' +
     1845                    Format(Phrases.Lookup('SHAREDWIN'), [s]);
     1846                IconKind := mikBigIcon;
     1847                IconIndex := 9;
     1848              end
     1849              else
     1850              begin
     1851                assert(me = 0);
     1852                OpenSound := 'MSG_GAMEOVER';
     1853                MessgText := '';
     1854                for p1 := 0 to nPl - 1 do
     1855                  if Winners and (1 shl p1) <> 0 then
     1856                    MessgText := MessgText + Tribe[p1].TPhrase('SPACESHIP1');
     1857                MessgText := MessgText + '\' + Phrases.Lookup('SPACESHIP2');
     1858                IconKind := mikEnemyShipComplete;
     1859              end
     1860            end
     1861            else { if MyRO.Happened and fTimeUp<>0 then }
     1862            begin
     1863              assert(me = 0);
     1864              OpenSound := 'MSG_GAMEOVER';
     1865              if not supervising then
     1866                MessgText := Tribe[me].TPhrase('TIMEUP')
     1867              else
     1868                MessgText := Phrases.Lookup('TIMEUPSUPER');
     1869              IconKind := mikImp;
     1870              IconIndex := 22;
    16301871            end;
    1631           assert(Winners<>0);
    1632           if Winners and (1 shl me)<>0 then
     1872            Kind := mkOk;
     1873            ShowModal;
     1874            if MyRO.Happened and phExtinct = 0 then
    16331875            begin
    1634             s:='';
    1635             for p1:=0 to nPl-1 do
    1636               if (p1<>me) and (1 shl p1 and Winners<>0) then
    1637                 if s='' then s:=Tribe[p1].TPhrase('SHORTNAME')
    1638                 else s:=Format(Phrases.Lookup('SHAREDWIN_CONCAT'),
    1639                   [s,Tribe[p1].TPhrase('SHORTNAME')]);
    1640 
    1641             OpenSound:='MSG_YOUWIN';
    1642             MessgText:=Tribe[me].TPhrase('MYSPACESHIP');
    1643             if s<>'' then
    1644               MessgText:=MessgText+'\'+Format(Phrases.Lookup('SHAREDWIN'),[s]);
    1645             IconKind:=mikBigIcon;
    1646             IconIndex:=9;
     1876              p1 := 0;
     1877              while (p1 < nPl - 1) and (Winners and (1 shl p1) = 0) do
     1878                inc(p1);
     1879              if MyRO.Happened and phShipComplete = 0 then
     1880                DiaDlg.ShowNewContent_Charts(wmModal);
     1881            end;
     1882            TurnComplete := true;
     1883            exit;
     1884          end;
     1885        if not supervising and (1 shl me and MyRO.Alive = 0) then
     1886        begin
     1887          TurnComplete := true;
     1888          exit;
     1889        end;
     1890
     1891        if (ClientMode = cContinue) and
     1892          (DipMem[me].SentCommand and $FF0F = scContact) then
     1893          // contact was refused
     1894          if MyRO.Treaty[DipMem[me].pContact] >= trPeace then
     1895            ContactRefused(DipMem[me].pContact, 'FRREJECTED')
     1896          else
     1897            SoundMessage(Tribe[DipMem[me].pContact].TPhrase('FRREJECTED'),
     1898              'NEGO_REJECTED');
     1899
     1900        if not supervising and (Age > MyData.ToldAge) and
     1901          ((Age > 0) or (ClientMode <> cMovieTurn)) then
     1902          with MessgExDlg do
     1903          begin
     1904            if Age = 0 then
     1905            begin
     1906              if Phrases2FallenBackToEnglish then
     1907              begin
     1908                s := Tribe[me].TPhrase('AGE0');
     1909                MessgText :=
     1910                  Format(s, [TurnToString(MyRO.Turn), CityName(MyCity[0].ID)])
     1911              end
     1912              else
     1913              begin
     1914                s := Tribe[me].TString(Phrases2.Lookup('AGE0'));
     1915                MessgText := Format(s, [TurnToString(MyRO.Turn)]);
     1916              end
    16471917            end
    1648           else
     1918            else
    16491919            begin
    1650             assert(me=0);
    1651             OpenSound:='MSG_GAMEOVER';
    1652             MessgText:='';
    1653             for p1:=0 to nPl-1 do if Winners and (1 shl p1)<>0 then
    1654               MessgText:=MessgText+Tribe[p1].TPhrase('SPACESHIP1');
    1655             MessgText:=MessgText+'\'+Phrases.Lookup('SPACESHIP2');
    1656             IconKind:=mikEnemyShipComplete;
     1920              s := Tribe[me].TPhrase('AGE' + char(48 + Age));
     1921              MessgText := Format(s, [TurnToString(MyRO.Turn)]);
     1922            end;
     1923            IconKind := mikAge;
     1924            IconIndex := Age;
     1925            { if age=0 then } Kind := mkOk
     1926            { else begin Kind:=mkOkHelp; HelpKind:=hkAdv; HelpNo:=AgePreq[age]; end };
     1927            CenterTo := NewAgeCenterTo;
     1928            OpenSound := 'AGE_' + char(48 + Age);
     1929            ShowModal;
     1930            MyData.ToldAge := Age;
     1931            if Age > 0 then
     1932              MyData.ToldTech[AgePreq[Age]] := MyRO.Tech[AgePreq[Age]];
     1933          end;
     1934
     1935        if MyData.ToldAlive <> MyRO.Alive then
     1936        begin
     1937          for p1 := 0 to nPl - 1 do
     1938            if (MyData.ToldAlive - MyRO.Alive) and (1 shl p1) <> 0 then
     1939              with MessgExDlg do
     1940              begin
     1941                OpenSound := 'MSG_EXTINCT';
     1942                s := Tribe[p1].TPhrase('EXTINCT');
     1943                MessgText := Format(s, [TurnToString(MyRO.Turn)]);
     1944                if MyRO.Alive = 1 shl me then
     1945                  MessgText := MessgText + Phrases.Lookup('EXTINCTALL');
     1946                Kind := mkOk;
     1947                IconKind := mikImp;
     1948                IconIndex := 21;
     1949                ShowModal;
     1950              end;
     1951          if (ClientMode <> cMovieTurn) and not supervising then
     1952            DiaDlg.ShowNewContent_Charts(wmModal);
     1953        end;
     1954
     1955        // tell changes of own credibility
     1956        if not supervising then
     1957        begin
     1958          if RoughCredibility(MyRO.Credibility) <>
     1959            RoughCredibility(MyData.ToldOwnCredibility) then
     1960          begin
     1961            if RoughCredibility(MyRO.Credibility) >
     1962              RoughCredibility(MyData.ToldOwnCredibility) then
     1963              s := Phrases.Lookup('CREDUP')
     1964            else
     1965              s := Phrases.Lookup('CREDDOWN');
     1966            TribeMessage(me,
     1967              Format(s, [Phrases.Lookup('CREDIBILITY',
     1968              RoughCredibility(MyRO.Credibility))]), '');
     1969          end;
     1970          MyData.ToldOwnCredibility := MyRO.Credibility;
     1971        end;
     1972
     1973        for i := 0 to 27 do
     1974        begin
     1975          OwnWonder := false;
     1976          for cix := 0 to MyRO.nCity - 1 do
     1977            if (MyCity[cix].Loc >= 0) and
     1978              (MyCity[cix].ID = MyRO.Wonder[i].CityID) then
     1979              OwnWonder := true;
     1980          if MyRO.Wonder[i].CityID <> MyData.ToldWonders[i].CityID then
     1981          begin
     1982            if MyRO.Wonder[i].CityID = -2 then
     1983              with MessgExDlg do
     1984              begin { tell about destroyed wonders }
     1985                OpenSound := 'WONDER_DESTROYED';
     1986                MessgText := Format(Phrases.Lookup('WONDERDEST'),
     1987                  [Phrases.Lookup('IMPROVEMENTS', i)]);
     1988                Kind := mkOkHelp;
     1989                HelpKind := hkImp;
     1990                HelpNo := i;
     1991                IconKind := mikImp;
     1992                IconIndex := i;
     1993                ShowModal;
     1994              end
     1995            else
     1996            begin
     1997              if i = woManhattan then
     1998                if MyRO.Wonder[i].EffectiveOwner > me then
     1999                  MyData.ColdWarStart := MyRO.Turn - 1
     2000                else
     2001                  MyData.ColdWarStart := MyRO.Turn;
     2002              if not OwnWonder then
     2003                with MessgExDlg do
     2004                begin { tell about newly built wonders }
     2005                  if i = woManhattan then
     2006                  begin
     2007                    OpenSound := 'MSG_COLDWAR';
     2008                    s := Tribe[MyRO.Wonder[i].EffectiveOwner].TPhrase('COLDWAR')
     2009                  end
     2010                  else if MyRO.Wonder[i].EffectiveOwner >= 0 then
     2011                  begin
     2012                    OpenSound := 'WONDER_BUILT';
     2013                    s := Tribe[MyRO.Wonder[i].EffectiveOwner]
     2014                      .TPhrase('WONDERBUILT')
     2015                  end
     2016                  else
     2017                  begin
     2018                    OpenSound := 'MSG_DEFAULT';
     2019                    s := Phrases.Lookup('WONDERBUILTEXP');
     2020                    // already expired when built
     2021                  end;
     2022                  MessgText := Format(s, [Phrases.Lookup('IMPROVEMENTS', i),
     2023                    CityName(MyRO.Wonder[i].CityID)]);
     2024                  Kind := mkOkHelp;
     2025                  HelpKind := hkImp;
     2026                  HelpNo := i;
     2027                  IconKind := mikImp;
     2028                  IconIndex := i;
     2029                  ShowModal;
     2030                end
    16572031            end
    16582032          end
    1659         else {if MyRO.Happened and fTimeUp<>0 then}
     2033          else if (MyRO.Wonder[i].EffectiveOwner <> MyData.ToldWonders[i]
     2034            .EffectiveOwner) and (MyRO.Wonder[i].CityID > -2) then
     2035            if MyRO.Wonder[i].EffectiveOwner < 0 then
     2036            begin
     2037              if i <> woMIR then
     2038                with MessgExDlg do
     2039                begin { tell about expired wonders }
     2040                  OpenSound := 'WONDER_EXPIRED';
     2041                  MessgText := Format(Phrases.Lookup('WONDEREXP'),
     2042                    [Phrases.Lookup('IMPROVEMENTS', i),
     2043                    CityName(MyRO.Wonder[i].CityID)]);
     2044                  Kind := mkOkHelp;
     2045                  HelpKind := hkImp;
     2046                  HelpNo := i;
     2047                  IconKind := mikImp;
     2048                  IconIndex := i;
     2049                  ShowModal;
     2050                end
     2051            end
     2052            else if (MyData.ToldWonders[i].EffectiveOwner >= 0) and not OwnWonder
     2053            then
     2054              with MessgExDlg do
     2055              begin { tell about capture of wonders }
     2056                OpenSound := 'WONDER_CAPTURED';
     2057                s := Tribe[MyRO.Wonder[i].EffectiveOwner].TPhrase('WONDERCAPT');
     2058                MessgText := Format(s, [Phrases.Lookup('IMPROVEMENTS', i),
     2059                  CityName(MyRO.Wonder[i].CityID)]);
     2060                Kind := mkOkHelp;
     2061                HelpKind := hkImp;
     2062                HelpNo := i;
     2063                IconKind := mikImp;
     2064                IconIndex := i;
     2065                ShowModal;
     2066              end;
     2067        end;
     2068
     2069        if MyRO.Turn = MyData.ColdWarStart + ColdWarTurns then
     2070        begin
     2071          SoundMessageEx(Phrases.Lookup('COLDWAREND'), 'MSG_DEFAULT');
     2072          MyData.ColdWarStart := -ColdWarTurns - 1
     2073        end;
     2074
     2075        TellNewModels;
     2076      end; // ClientMode<>cResume
     2077      MyData.ToldAlive := MyRO.Alive;
     2078      move(MyRO.Wonder, MyData.ToldWonders, SizeOf(MyData.ToldWonders));
     2079
     2080      NewGovAvailable := -1;
     2081      if ClientMode <> cResume then
     2082      begin // tell about new techs
     2083        for ad := 0 to nAdv - 1 do
     2084          if (MyRO.TestFlags and tfAllTechs = 0) and
     2085            ((MyRO.Tech[ad] >= tsApplicable) <>
     2086            (MyData.ToldTech[ad] >= tsApplicable)) or (ad in FutureTech) and
     2087            (MyRO.Tech[ad] <> MyData.ToldTech[ad]) then
     2088            with MessgExDlg do
     2089            begin
     2090              Item := 'RESEARCH_GENERAL';
     2091              if GameMode <> cMovie then
     2092                OpenSound := 'NEWADVANCE_' + char(48 + Age);
     2093              Item2 := Phrases.Lookup('ADVANCES', ad);
     2094              if ad in FutureTech then
     2095                Item2 := Item2 + ' ' + IntToStr(MyRO.Tech[ad]);
     2096              MessgText := Format(Phrases.Lookup(Item), [Item2]);
     2097              Kind := mkOkHelp;
     2098              HelpKind := hkAdv;
     2099              HelpNo := ad;
     2100              IconKind := mikBook;
     2101              IconIndex := -1;
     2102              for i := 0 to nAdvBookIcon - 1 do
     2103                if AdvBookIcon[i].Adv = ad then
     2104                  IconIndex := AdvBookIcon[i].Icon;
     2105              ShowModal;
     2106              MyData.ToldTech[ad] := MyRO.Tech[ad];
     2107              for i := gMonarchy to nGov - 1 do
     2108                if GovPreq[i] = ad then
     2109                  NewGovAvailable := i;
     2110            end;
     2111      end;
     2112
     2113      ShowCityList := false;
     2114      if ClientMode = cTurn then
     2115      begin
     2116        if (MyRO.Happened and phTech <> 0) and (MyData.FarTech <> adNexus) then
     2117          ChooseResearch;
     2118
     2119        UpdatePanel := false;
     2120        if MyRO.Happened and phChangeGov <> 0 then
     2121        begin
     2122          ModalSelectDlg.ShowNewContent(wmModal, kGov);
     2123          Play('NEWGOV');
     2124          Server(sSetGovernment, me, ModalSelectDlg.result, nil^);
     2125          CityOptimizer_BeginOfTurn;
     2126          UpdatePanel := true;
     2127        end;
     2128      end; // ClientMode=cTurn
     2129
     2130      if not supervising and ((ClientMode = cTurn) or (ClientMode = cMovieTurn))
     2131      then
     2132        for cix := 0 to MyRO.nCity - 1 do
     2133          with MyCity[cix] do
     2134            Status := Status and not csToldBombard;
     2135
     2136      if ((ClientMode = cTurn) or (ClientMode = cMovieTurn)) and
     2137        (MyRO.Government <> gAnarchy) then
     2138      begin
     2139        // tell what happened in cities
     2140        for WondersOnly := true downto false do
     2141          for cix := 0 to MyRO.nCity - 1 do
     2142            with MyCity[cix] do
     2143              if (MyRO.Turn > 0) and (Loc >= 0) and (Flags and chCaptured = 0)
     2144                and (WondersOnly = (Flags and chProduction <> 0) and
     2145                (Project0 and cpImp <> 0) and (Project0 and cpIndex < 28)) then
     2146              begin
     2147                if WondersOnly then
     2148                  with MessgExDlg do
     2149                  begin { tell about newly built wonder }
     2150                    OpenSound := 'WONDER_BUILT';
     2151                    s := Tribe[me].TPhrase('WONDERBUILTOWN');
     2152                    MessgText :=
     2153                      Format(s, [Phrases.Lookup('IMPROVEMENTS',
     2154                      Project0 and cpIndex), CityName(ID)]);
     2155                    Kind := mkOkHelp;
     2156                    HelpKind := hkImp;
     2157                    HelpNo := Project0 and cpIndex;
     2158                    IconKind := mikImp;
     2159                    IconIndex := Project0 and cpIndex;
     2160                    ShowModal;
     2161                  end;
     2162                if not supervising and (ClientMode = cTurn) then
     2163                begin
     2164                  AllowCityScreen := true;
     2165                  if (Status and 7 <> 0) and
     2166                    (Project and (cpImp + cpIndex) = cpImp + imTrGoods) then
     2167                    if (MyData.ImpOrder[Status and 7 - 1, 0] >= 0) then
     2168                    begin
     2169                      if AutoBuild(cix, MyData.ImpOrder[Status and 7 - 1]) then
     2170                        AllowCityScreen := false
     2171                      else if Flags and chProduction <> 0 then
     2172                        Flags := (Flags and not chProduction) or chAllImpsMade
     2173                    end
     2174                    else
     2175                      Flags := Flags or chTypeDel;
     2176                  if (Size >= NeedAqueductSize) and
     2177                    (MyRO.Tech[Imp[imAqueduct].Preq] < tsApplicable) or
     2178                    (Size >= NeedSewerSize) and
     2179                    (MyRO.Tech[Imp[imSewer].Preq] < tsApplicable) then
     2180                    Flags := Flags and not chNoGrowthWarning;
     2181                  // don't remind of unknown building
     2182                  if Flags and chNoSettlerProd = 0 then
     2183                    Status := Status and not csToldDelay
     2184                  else if Status and csToldDelay = 0 then
     2185                    Status := Status or csToldDelay
     2186                  else
     2187                    Flags := Flags and not chNoSettlerProd;
     2188                  if mRepScreens.Checked then
     2189                  begin
     2190                    if (Flags and CityRepMask <> 0) and AllowCityScreen then
     2191                    begin { show what happened in cities }
     2192                      SetTroopLoc(MyCity[cix].Loc);
     2193                      MarkCityLoc := MyCity[cix].Loc;
     2194                      PanelPaint;
     2195                      CityDlg.CloseAction := None;
     2196                      CityDlg.ShowNewContent(wmModal, MyCity[cix].Loc,
     2197                        Flags and CityRepMask);
     2198                      UpdatePanel := true;
     2199                    end
     2200                  end
     2201                  else { if mRepList.Checked then }
     2202                  begin
     2203                    if Flags and CityRepMask <> 0 then
     2204                      ShowCityList := true
     2205                  end
     2206                end
     2207              end; { city loop }
     2208      end; // ClientMode=cTurn
     2209
     2210      if ClientMode = cTurn then
     2211      begin
     2212        if NewGovAvailable >= 0 then
     2213          with MessgExDlg do
    16602214          begin
    1661           assert(me=0);
    1662           OpenSound:='MSG_GAMEOVER';
    1663           if not supervising then MessgText:=Tribe[me].TPhrase('TIMEUP')
    1664           else MessgText:=Phrases.Lookup('TIMEUPSUPER');
    1665           IconKind:=mikImp;
    1666           IconIndex:=22;
     2215            MessgText := Format(Phrases.Lookup('AUTOREVOLUTION'),
     2216              [Phrases.Lookup('GOVERNMENT', NewGovAvailable)]);
     2217            Kind := mkYesNo;
     2218            IconKind := mikPureIcon;
     2219            IconIndex := 6 + NewGovAvailable;
     2220            ShowModal;
     2221            if ModalResult = mrOK then
     2222            begin
     2223              Play('REVOLUTION');
     2224              Server(sRevolution, me, 0, nil^);
     2225            end
    16672226          end;
    1668         Kind:=mkOK;
    1669         ShowModal;
    1670         if MyRO.Happened and phExtinct=0 then
     2227      end; // ClientMode=cTurn
     2228
     2229      if (ClientMode = cTurn) or (ClientMode = cMovieTurn) then
     2230      begin
     2231        if MyRO.Happened and phGliderLost <> 0 then
     2232          ContextMessage(Phrases.Lookup('GLIDERLOST'), 'MSG_DEFAULT',
     2233            hkModel, 200);
     2234        if MyRO.Happened and phPlaneLost <> 0 then
     2235          ContextMessage(Phrases.Lookup('PLANELOST'), 'MSG_DEFAULT',
     2236            hkFeature, mcFuel);
     2237        if MyRO.Happened and phPeaceEvacuation <> 0 then
     2238          for p1 := 0 to nPl - 1 do
     2239            if 1 shl p1 and MyData.PeaceEvaHappened <> 0 then
     2240              SoundMessageEx(Tribe[p1].TPhrase('WITHDRAW'), 'MSG_DEFAULT');
     2241        if MyRO.Happened and phPeaceViolation <> 0 then
     2242          for p1 := 0 to nPl - 1 do
     2243            if (1 shl p1 and MyRO.Alive <> 0) and (MyRO.EvaStart[p1] = MyRO.Turn)
     2244            then
     2245              SoundMessageEx(Format(Tribe[p1].TPhrase('VIOLATION'),
     2246                [TurnToString(MyRO.Turn + PeaceEvaTurns - 1)]), 'MSG_WITHDRAW');
     2247        TellNewContacts;
     2248      end;
     2249
     2250      if ClientMode = cMovieTurn then
     2251        Update
     2252      else if ClientMode = cTurn then
     2253      begin
     2254        if UpdatePanel then
     2255          UpdateViews;
     2256        Application.ProcessMessages;
     2257
     2258        if not supervising then
     2259          for uix := 0 to MyRO.nUn - 1 do
     2260            with MyUn[uix] do
     2261              if Loc >= 0 then
     2262              begin
     2263                if Flags and unWithdrawn <> 0 then
     2264                  Status := 0;
     2265                if Health = 100 then
     2266                  Status := Status and not usRecover;
     2267                if (Master >= 0) or UnitExhausted(uix) then
     2268                  Status := Status and not usWaiting
     2269                else
     2270                  Status := Status or usWaiting;
     2271                CheckToldNoReturn(uix);
     2272                if Status and usGoto <> 0 then
     2273                begin { continue multi-turn goto }
     2274                  SetUnFocus(uix);
     2275                  SetTroopLoc(Loc);
     2276                  FocusOnLoc(TroopLoc, flRepaintPanel or flImmUpdate);
     2277                  if Status shr 16 = $7FFF then
     2278                    MoveResult := GetMoveAdvice(UnFocus, maNextCity,
     2279                      MoveAdviceData)
     2280                  else
     2281                    MoveResult := GetMoveAdvice(UnFocus, Status shr 16,
     2282                      MoveAdviceData);
     2283                  if MoveResult >= rExecuted then
     2284                  begin // !!! Shinkansen
     2285                    MoveResult := eOK;
     2286                    ok := true;
     2287                    for i := 0 to MoveAdviceData.nStep - 1 do
     2288                    begin
     2289                      Loc1 := dLoc(Loc, MoveAdviceData.dx[i],
     2290                        MoveAdviceData.dy[i]);
     2291                      if (MyMap[Loc1] and (fCity or fOwned) = fCity)
     2292                      // don't capture cities during auto move
     2293                        or (MyMap[Loc1] and (fUnit or fOwned) = fUnit) then
     2294                      // don't attack during auto move
     2295                      begin
     2296                        ok := false;
     2297                        Break
     2298                      end
     2299                      else
     2300                      begin
     2301                        if (Loc1 = MoveAdviceData.ToLoc) or
     2302                          (MoveAdviceData.ToLoc = maNextCity) and
     2303                          (MyMap[dLoc(Loc, MoveAdviceData.dx[i],
     2304                          MoveAdviceData.dy[i])] and fCity <> 0) then
     2305                          MoveOptions := muAutoNoWait
     2306                        else
     2307                          MoveOptions := 0;
     2308                        MoveResult := MoveUnit(MoveAdviceData.dx[i],
     2309                          MoveAdviceData.dy[i], MoveOptions);
     2310                        if (MoveResult < rExecuted) or
     2311                          (MoveResult = eEnemySpotted) then
     2312                        begin
     2313                          ok := false;
     2314                          Break
     2315                        end;
     2316                      end
     2317                    end;
     2318                    Stop := not ok or (Loc = MoveAdviceData.ToLoc) or
     2319                      (MoveAdviceData.ToLoc = maNextCity) and
     2320                      (MyMap[Loc] and fCity <> 0)
     2321                  end
     2322                  else
     2323                  begin
     2324                    MoveResult := eOK;
     2325                    Stop := true;
     2326                  end;
     2327
     2328                  if MoveResult <> eDied then
     2329                    if Stop then
     2330                      Status := Status and ($FFFF - usGoto)
     2331                    else
     2332                      Status := Status and not usWaiting;
     2333                end;
     2334
     2335                if Status and (usEnhance or usGoto) = usEnhance then
     2336                // continue terrain enhancement
     2337                begin
     2338                  MoveResult := ProcessEnhancement(uix, MyData.EnhancementJobs);
     2339                  if MoveResult <> eDied then
     2340                    if MoveResult = eJobDone then
     2341                      Status := Status and not usEnhance
     2342                    else
     2343                      Status := Status and not usWaiting;
     2344                end
     2345              end;
     2346      end; // ClientMode=cTurn
     2347
     2348      HaveStrategyAdvice := false;
     2349      // (GameMode<>cMovie) and not supervising
     2350      // and AdvisorDlg.HaveStrategyAdvice;
     2351      GoOnPhase := true;
     2352      if supervising or (GameMode = cMovie) then
     2353      begin
     2354        SetTroopLoc(-1);
     2355        PaintAll
     2356      end { supervisor }
     2357      { else if (ClientMode=cTurn) and (MyRO.Turn=0) then
     2358        begin
     2359        SetUnFocus(0);
     2360        ZoomToCity(MyCity[0].Loc)
     2361        end }
     2362      else
     2363      begin
     2364        if ClientMode >= scContact then
     2365          SetUnFocus(-1)
     2366        else
     2367          NextUnit(-1, false);
     2368        if UnFocus < 0 then
     2369        begin
     2370          UnStartLoc := -1;
     2371          if IsMultiPlayerGame or (ClientMode = cResume) then
     2372            if MyRO.nCity > 0 then
     2373              FocusOnLoc(MyCity[0].Loc)
     2374            else
     2375              FocusOnLoc(G.lx * G.ly div 2);
     2376          SetTroopLoc(-1);
     2377          PanelPaint
     2378        end;
     2379        if ShowCityList then
     2380          ListDlg.ShowNewContent(wmPersistent, kCityEvents);
     2381      end;
     2382    end; { InitTurn }
     2383
     2384  var
     2385    i, j, p1, mix, ToLoc, AnimationSpeed, ShowMoveDomain, cix, ecix: integer;
     2386    Color: TColor;
     2387    Name, s: string;
     2388    TribeInfo: TTribeInfo;
     2389    mi: TModelInfo;
     2390    SkipTurn, IsAlpine, IsTreatyDeal: boolean;
     2391
     2392  begin { >>>client }
     2393    case Command of
     2394      cTurn, cResume, cContinue, cMovieTurn, scContact,
     2395        scDipStart .. scDipBreak:
     2396        begin
     2397          supervising := G.Difficulty[NewPlayer] = 0;
     2398          ArrangeMidPanel;
     2399        end
     2400    end;
     2401    case Command of
     2402      cDebugMessage:
     2403        LogDlg.Add(NewPlayer, G.RO[0].Turn, pchar(@Data));
     2404
     2405      cShowNego:
     2406        with TShowNegoData(Data) do
     2407        begin
     2408          s := Format('P%d to P%d: ', [pSender, pTarget]);
     2409          if (Action = scDipOffer) and (Offer.nDeliver + Offer.nCost > 0) then
    16712410          begin
    1672           p1:=0;
    1673           while (p1<nPl-1) and (Winners and (1 shl p1)=0) do inc(p1);
    1674           if MyRO.Happened and phShipComplete=0 then
    1675             DiaDlg.ShowNewContent_Charts(wmModal);
     2411            s := s + 'Offer ';
     2412            for i := 0 to Offer.nDeliver + Offer.nCost - 1 do
     2413            begin
     2414              if i = Offer.nDeliver then
     2415                s := s + ' for '
     2416              else if i > 0 then
     2417                s := s + '+';
     2418              case Offer.Price[i] and opMask of
     2419                opChoose:
     2420                  s := s + 'Price of choice';
     2421                opCivilReport:
     2422                  s := s + 'State report';
     2423                opMilReport:
     2424                  s := s + 'Military report';
     2425                opMap:
     2426                  s := s + 'Map';
     2427                opTreaty:
     2428                  s := s + 'Treaty';
     2429                opShipParts:
     2430                  s := s + 'Ship part';
     2431                opMoney:
     2432                  s := s + IntToStr(Offer.Price[i] and $FFFFFF) + 'o';
     2433                opTribute:
     2434                  s := s + IntToStr(Offer.Price[i] and $FFFFFF) + 'o tribute';
     2435                opTech:
     2436                  s := s + Phrases.Lookup('ADVANCES',
     2437                    Offer.Price[i] and $FFFFFF);
     2438                opAllTech:
     2439                  s := s + 'All advances';
     2440                opModel:
     2441                  s := s + Tribe[pSender].ModelName[Offer.Price[i] and $FFFFFF];
     2442                opAllModel:
     2443                  s := s + 'All models';
     2444              end
     2445            end;
     2446            LogDlg.Add(NewPlayer, G.RO[0].Turn, pchar(s));
     2447          end
     2448          else if Action = scDipAccept then
     2449          begin
     2450            s := s + '--- ACCEPTED! ---';
     2451            LogDlg.Add(NewPlayer, G.RO[0].Turn, pchar(s));
     2452          end
     2453        end;
     2454
     2455      cInitModule:
     2456        begin
     2457          Server := TInitModuleData(Data).Server;
     2458          // AdvisorDlg.Init;
     2459          InitModule;
     2460          TInitModuleData(Data).DataSize := SizeOf(TPersistentData);
     2461          TInitModuleData(Data).Flags := aiThreaded;
     2462        end;
     2463
     2464      cReleaseModule:
     2465        begin
     2466          SmallImp.free;
     2467          UnusedTribeFiles.free;
     2468          TribeNames.free;
     2469          MainMap.free;
     2470          IsoEngine.Done;
     2471          // AdvisorDlg.DeInit;
     2472        end;
     2473
     2474      cHelpOnly, cStartHelp, cStartCredits:
     2475        begin
     2476          Age := 0;
     2477          if Command = cHelpOnly then
     2478            SetMainTextureByAge(-1);
     2479          Tribes.Init;
     2480          HelpDlg.UserLeft := (Screen.width - HelpDlg.width) div 2;
     2481          HelpDlg.UserTop := (Screen.height - HelpDlg.height) div 2;
     2482          HelpDlg.Difficulty := 0;
     2483          if Command = cStartCredits then
     2484            HelpDlg.ShowNewContent(wmModal, hkMisc, miscCredits)
     2485          else
     2486            HelpDlg.ShowNewContent(wmModal, hkMisc, miscMain);
     2487          Tribes.Done;
     2488        end;
     2489
     2490      cNewGame, cLoadGame, cMovie, cNewMap:
     2491        begin
     2492          { if (Command=cNewGame) or (Command=cLoadGame) then
     2493            AdvisorDlg.NewGame(Data); }
     2494          GenerateNames := mNames.Checked;
     2495          GameOK := true;
     2496          G := TNewGameData(Data);
     2497          me := -1;
     2498          pLogo := -1;
     2499          ClientMode := -1;
     2500          SetMapOptions;
     2501          IsoEngine.pDebugMap := -1;
     2502          idle := false;
     2503          FillChar(Jump, SizeOf(Jump), 0);
     2504          if StartRunning then
     2505            Jump[0] := 999999;
     2506          GameMode := Command;
     2507          for i := 0 to nGrExt - 1 do
     2508            FillChar(GrExt[i].pixUsed, GrExt[i].Data.height div 49 * 10, 0);
     2509          IsoEngine.Reset;
     2510          Tribes.Init;
     2511          GetTribeList;
     2512          for p1 := 0 to nPl - 1 do
     2513            if (G.RO[p1] <> nil) and (G.RO[p1].Data <> nil) then
     2514              with TPersistentData(G.RO[p1].Data^) do
     2515              begin
     2516                FarTech := adNone;
     2517                FillChar(EnhancementJobs, SizeOf(EnhancementJobs), jNone);
     2518                FillChar(ImpOrder, SizeOf(ImpOrder), -1);
     2519                ColdWarStart := -ColdWarTurns - 1;
     2520                ToldAge := -1;
     2521                ToldModels := 3;
     2522                ToldAlive := 0;
     2523                ToldContact := 0;
     2524                ToldOwnCredibility := InitialCredibility;
     2525                for i := 0 to nPl - 1 do
     2526                  if G.Difficulty[i] > 0 then
     2527                    inc(ToldAlive, 1 shl i);
     2528                PeaceEvaHappened := 0;
     2529                for i := 0 to 27 do
     2530                  with ToldWonders[i] do
     2531                  begin
     2532                    CityID := -1;
     2533                    EffectiveOwner := -1
     2534                  end;
     2535                FillChar(ToldTech, SizeOf(ToldTech), tsNA);
     2536                if G.Difficulty[p1] > 0 then
     2537                  SoundPreload(sbStart);
     2538              end;
     2539
     2540          // arrange dialogs
     2541          ListDlg.UserLeft := 8;
     2542          ListDlg.UserTop := TopBarHeight + 8;
     2543          HelpDlg.UserLeft := Screen.width - HelpDlg.width - 8;
     2544          HelpDlg.UserTop := TopBarHeight + 8;
     2545          UnitStatDlg.UserLeft := 397;
     2546          UnitStatDlg.UserTop := TopBarHeight + 64;
     2547          DiaDlg.UserLeft := (Screen.width - DiaDlg.width) div 2;
     2548          DiaDlg.UserTop := (Screen.height - DiaDlg.height) div 2;
     2549          NatStatDlg.UserLeft := Screen.width - NatStatDlg.width - 8;
     2550          NatStatDlg.UserTop := Screen.height - PanelHeight -
     2551            NatStatDlg.height - 8;
     2552          if NatStatDlg.UserTop < 8 then
     2553            NatStatDlg.UserTop := 8;
     2554
     2555          Age := 0;
     2556          MovieSpeed := 1;
     2557          LogDlg.mSlot.Visible := true;
     2558          LogDlg.Host := self;
     2559          HelpDlg.ClearHistory;
     2560          CityDlg.Reset;
     2561
     2562          Mini.width := G.lx * 2;
     2563          Mini.height := G.ly;
     2564          for i := 0 to nPl - 1 do
     2565          begin
     2566            Tribe[i] := nil;
     2567            TribeOriginal[i] := false;
    16762568          end;
    1677         TurnComplete:=true;
    1678         exit;
    1679         end;
    1680     if not supervising and (1 shl me and MyRO.Alive=0) then
    1681       begin TurnComplete:=true; exit; end;
    1682 
    1683     if (ClientMode=cContinue) and (DipMem[me].SentCommand and $FF0F=scContact) then
    1684       // contact was refused
    1685       if MyRO.Treaty[DipMem[me].pContact]>=trPeace then
    1686         ContactRefused(DipMem[me].pContact, 'FRREJECTED')
    1687       else SoundMessage(Tribe[DipMem[me].pContact].TPhrase('FRREJECTED'),'NEGO_REJECTED');
    1688 
    1689     if not supervising and (Age>MyData.ToldAge)
    1690       and ((Age>0) or (ClientMode<>cMovieTurn)) then with MessgExDlg do
    1691       begin
    1692       if Age=0 then
    1693         begin
    1694         if Phrases2FallenBackToEnglish then
     2569          ToldSlavery := -1;
     2570          RepaintOnResize := false;
     2571          Closable := false;
     2572          FirstMovieTurn := true;
     2573
     2574          MenuArea.Visible := GameMode <> cMovie;
     2575          TreasuryArea.Visible := GameMode < cMovie;
     2576          ResearchArea.Visible := GameMode < cMovie;
     2577          ManagementArea.Visible := GameMode < cMovie;
     2578        end;
     2579
     2580      cGetReady, cReplay:
     2581        if NewPlayer = 0 then
     2582        begin
     2583          i := 0;
     2584          for p1 := 0 to nPl - 1 do
     2585            if (G.Difficulty[p1] > 0) and (Tribe[p1] = nil) then
     2586              inc(i);
     2587          if i > UnusedTribeFiles.Count then
    16952588          begin
    1696           s:=Tribe[me].TPhrase('AGE0');
    1697           MessgText:=Format(s,[TurnToString(MyRO.Turn),CityName(MyCity[0].ID)])
     2589            GameOK := false;
     2590            SimpleMessage(Phrases.Lookup('TOOFEWTRIBES'));
    16982591          end
     2592          else
     2593          begin
     2594            for p1 := 0 to nPl - 1 do
     2595              if (G.Difficulty[p1] > 0) and (Tribe[p1] = nil) and
     2596                (G.RO[p1] <> nil) then
     2597              begin // let player select own tribes
     2598                TribeInfo.trix := p1;
     2599                TribeNames.Clear;
     2600                for j := 0 to UnusedTribeFiles.Count - 1 do
     2601                begin
     2602                  GetTribeInfo(UnusedTribeFiles[j], Name, Color);
     2603                  TribeNames.AddObject(Name, TObject(Color));
     2604                end;
     2605                assert(TribeNames.Count > 0);
     2606                ModalSelectDlg.ShowNewContent(wmModal, kTribe);
     2607                Application.ProcessMessages;
     2608                TribeInfo.FileName := UnusedTribeFiles[ModalSelectDlg.result];
     2609                UnusedTribeFiles.Delete(ModalSelectDlg.result);
     2610
     2611                if GameMode = cLoadGame then
     2612                  CreateTribe(TribeInfo.trix, TribeInfo.FileName, false)
     2613                else
     2614                  Server(cSetTribe + (Length(TribeInfo.FileName) + 1 + 7) div 4,
     2615                    0, 0, TribeInfo);
     2616              end;
     2617
     2618            for p1 := 0 to nPl - 1 do
     2619              if (G.Difficulty[p1] > 0) and (Tribe[p1] = nil) and
     2620                (G.RO[p1] = nil) then
     2621              begin // autoselect enemy tribes
     2622                j := ChooseUnusedTribe;
     2623                TribeInfo.FileName := UnusedTribeFiles[j];
     2624                UnusedTribeFiles.Delete(j);
     2625                TribeInfo.trix := p1;
     2626                if GameMode = cLoadGame then
     2627                  CreateTribe(TribeInfo.trix, TribeInfo.FileName, false)
     2628                else
     2629                  Server(cSetTribe + (Length(TribeInfo.FileName) + 1 + 7) div 4,
     2630                    0, 0, TribeInfo);
     2631              end;
     2632          end;
     2633          if not mNames.Checked then
     2634            for p1 := 0 to nPl - 1 do
     2635              if Tribe[p1] <> nil then
     2636                Tribe[p1].NumberName := p1;
     2637        end;
     2638
     2639      cBreakGame:
     2640        begin
     2641          SaveSettings;
     2642          CityDlg.CloseAction := None;
     2643          for i := 0 to Screen.FormCount - 1 do
     2644            if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg)
     2645            then
     2646              Screen.Forms[i].Close;
     2647          if LogDlg.Visible then
     2648            LogDlg.Close;
     2649          LogDlg.List.Clear;
     2650          StartRunning := not idle and (Jump[0] > 0); // AI called Reload
     2651          me := -1;
     2652          idle := false;
     2653          ClientMode := -1;
     2654          UnitInfoBtn.Visible := false;
     2655          UnitBtn.Visible := false;
     2656          TerrainBtn.Visible := false;
     2657          MovieSpeed1Btn.Visible := false;
     2658          MovieSpeed2Btn.Visible := false;
     2659          MovieSpeed3Btn.Visible := false;
     2660          MovieSpeed4Btn.Visible := false;
     2661          EOT.Visible := false;
     2662          for i := 0 to ControlCount - 1 do
     2663            if Controls[i] is TButtonC then
     2664              Controls[i].Visible := false;
     2665          InitPVSB(sb, 0, 1);
     2666          for p1 := 0 to nPl - 1 do
     2667            if Tribe[p1] <> nil then
     2668              Tribe[p1].free;
     2669          Tribes.Done;
     2670          RepaintOnResize := false;
     2671          Closable := true;
     2672          Close;
     2673          { if (GameMode=cNewGame) or (GameMode=cLoadGame) then
     2674            AdvisorDlg.BreakGame; }
     2675        end;
     2676
     2677      cShowGame:
     2678        begin
     2679          with Panel.Canvas do
     2680          begin
     2681            Brush.Color := $000000;
     2682            FillRect(Rect(0, 0, Panel.width, Panel.height));
     2683            Brush.Style := bsClear;
     2684          end;
     2685          with TopBar.Canvas do
     2686          begin
     2687            Brush.Color := $000000;
     2688            FillRect(Rect(0, 0, TopBar.width, TopBar.height));
     2689            Brush.Style := bsClear;
     2690          end;
     2691          FormResize(nil); // place mini map correctly according to its size
     2692          Show;
     2693          Update;
     2694          RepaintOnResize := true;
     2695          xw := 0;
     2696          yw := ywcenter;
     2697          if not StayOnTop_Ensured then
     2698          begin
     2699            StayOnTop_Ensured := true;
     2700            CityDlg.StayOnTop_Workaround;
     2701            CityTypeDlg.StayOnTop_Workaround;
     2702            DiaDlg.StayOnTop_Workaround;
     2703            DraftDlg.StayOnTop_Workaround;
     2704            EnhanceDlg.StayOnTop_Workaround;
     2705            HelpDlg.StayOnTop_Workaround;
     2706            NatStatDlg.StayOnTop_Workaround;
     2707            NegoDlg.StayOnTop_Workaround;
     2708            ModalSelectDlg.StayOnTop_Workaround;
     2709            ListDlg.StayOnTop_Workaround;
     2710            UnitStatDlg.StayOnTop_Workaround;
     2711            WondersDlg.StayOnTop_Workaround;
     2712            RatesDlg.StayOnTop_Workaround;
     2713          end;
     2714        end;
     2715
     2716      cShowTurnChange:
     2717        begin
     2718          if integer(Data) >= 0 then
     2719          begin
     2720            pLogo := integer(Data);
     2721            if G.RO[pLogo] = nil then
     2722            begin
     2723              if AILogo[pLogo] <> nil then
     2724                BitBlt(Canvas.Handle, (xRightPanel + 10) - (16 + 64),
     2725                  ClientHeight - PanelHeight, 64, 64,
     2726                  AILogo[pLogo].Canvas.Handle, 0, 0, SRCCOPY);
     2727            end
     2728          end
     2729        end;
     2730
     2731      cTurn, cResume, cContinue:
     2732        if not GameOK then
     2733          Server(sResign, NewPlayer, 0, nil^)
    16992734        else
     2735        begin
     2736          ClientMode := Command;
     2737          pTurn := NewPlayer;
     2738          pLogo := NewPlayer;
     2739
     2740          if Command = cResume then
     2741          begin // init non-original model pictures (maybe tribes not found)
     2742            for p1 := 0 to nPl - 1 do
     2743              if G.RO[p1] <> nil then
     2744              begin
     2745                ItsMeAgain(p1);
     2746                for mix := 0 to MyRO.nModel - 1 do
     2747                  if Tribe[me].ModelPicture[mix].HGr = 0 then
     2748                    InitMyModel(mix, true);
     2749              end;
     2750            me := -1;
     2751          end;
     2752
     2753          if Jump[pTurn] > 0 then
     2754            Application.ProcessMessages;
     2755          if Jump[pTurn] > 0 then
     2756            if G.RO[NewPlayer].Happened and phGameEnd <> 0 then
     2757              Jump[pTurn] := 0
     2758            else
     2759              dec(Jump[pTurn]);
     2760          SkipTurn := Jump[pTurn] > 0;
     2761          if SkipTurn then
    17002762          begin
    1701           s:=Tribe[me].TString(Phrases2.Lookup('AGE0'));
    1702           MessgText:=Format(s,[TurnToString(MyRO.Turn)]);
     2763            ItsMeAgain(NewPlayer);
     2764            MyData := G.RO[NewPlayer].Data;
     2765            SetTroopLoc(-1);
     2766            MiniPaint;
     2767            InitAllEnemyModels; // necessary for correct replay
     2768            if not EndTurn(true) then
     2769              SkipTurn := false;
     2770          end;
     2771          if not SkipTurn then
     2772          begin
     2773            if ((ClientMode < scDipStart) or (ClientMode > scDipBreak)) and
     2774              NegoDlg.Visible then
     2775              NegoDlg.Close;
     2776            skipped := false; // always show my moves during my turn
     2777            idle := true;
     2778            InitTurn(NewPlayer);
     2779            DipMem[me].pContact := -1;
     2780            (* if (me=0) and (MyRO.Alive and (1 shl me)=0)} then
     2781              begin
     2782              if SimpleQuery(Phrases.Lookup('RESIGN'))=mrIgnore then
     2783              Server(sResign,me,0,nil^)
     2784              else Server(sBreak,me,0,nil^)
     2785              end
     2786              else Play('TURNSTART'); *)
     2787          end;
     2788        end;
     2789
     2790      cMovieTurn:
     2791        begin
     2792          ClientMode := Command;
     2793          pTurn := NewPlayer;
     2794          pLogo := -1;
     2795          skipped := false; // always show my moves during my turn
     2796          idle := true;
     2797          if FirstMovieTurn then
     2798          begin
     2799            CheckMovieSpeedBtnState;
     2800            FirstMovieTurn := false;
     2801          end;
     2802          InitTurn(NewPlayer);
     2803          Application.ProcessMessages;
     2804          if MovieSpeed = 4 then
     2805          begin
     2806            Sleep(75);
     2807            // this break will ensure speed of fast forward does not depend on cpu speed
     2808            Application.ProcessMessages;
     2809          end
     2810        end;
     2811
     2812      cMovieEndTurn:
     2813        begin
     2814          RememberPeaceViolation;
     2815          pTurn := -1;
     2816          pLogo := -1;
     2817          MapValid := false;
     2818          ClientMode := -1;
     2819          idle := false;
     2820          skipped := false;
     2821        end;
     2822
     2823      cEditMap:
     2824        begin
     2825          ClientMode := cEditMap;
     2826          SetMapOptions;
     2827          IsoEngine.pDebugMap := -1;
     2828          ItsMeAgain(0);
     2829          MyData := nil;
     2830          UnitInfoBtn.Visible := false;
     2831          UnitBtn.Visible := false;
     2832          TerrainBtn.Visible := false;
     2833          MovieSpeed1Btn.Visible := false;
     2834          MovieSpeed2Btn.Visible := false;
     2835          MovieSpeed3Btn.Visible := false;
     2836          MovieSpeed4Btn.Visible := false;
     2837          EOT.Visible := false;
     2838          HelpDlg.Difficulty := 0;
     2839          BrushType := fGrass;
     2840          BrushLoc := -1;
     2841          Edited := false;
     2842          UnFocus := -1;
     2843          MarkCityLoc := -1;
     2844          Tracking := false;
     2845          TurnComplete := false;
     2846          MapValid := false;
     2847          FormResize(nil); // calculate geometrics and paint all
     2848          SetTroopLoc(-1);
     2849          idle := true
     2850        end;
     2851
     2852      (* cNewContact:
     2853        begin
     2854        end;
     2855      *)
     2856
     2857      scContact:
     2858        begin
     2859          DipMem[NewPlayer].pContact := integer(Data);
     2860          if Jump[NewPlayer] > 0 then
     2861            DipCall(scReject)
     2862          else
     2863          begin
     2864            ClientMode := Command;
     2865            InitTurn(NewPlayer);
     2866            MyData.ToldContact := MyData.ToldContact or (1 shl integer(Data));
     2867            // don't tell about new nation when already contacted by them
     2868            with MessgExDlg do
     2869            begin
     2870              OpenSound := 'CONTACT_' + char(48 + MyRO.EnemyReport[integer(Data)
     2871                ].Attitude);
     2872              MessgText := Tribe[integer(Data)].TPhrase('FRCONTACT');
     2873              Kind := mkYesNo;
     2874              IconKind := mikTribe;
     2875              IconIndex := integer(Data);
     2876              ShowModal;
     2877              if ModalResult = mrOK then
     2878              begin
     2879                NegoDlg.Respond;
     2880                DipMem[me].DeliveredPrices := [];
     2881                DipMem[me].ReceivedPrices := [];
     2882                DipCall(scDipStart)
     2883              end
     2884              else
     2885              begin
     2886                DipCall(scReject);
     2887                EndNego
     2888              end
     2889            end
     2890          end;
     2891        end;
     2892
     2893      scDipStart .. scDipBreak:
     2894        begin
     2895          ClientMode := Command;
     2896          InitTurn(NewPlayer);
     2897          if Command = scDipStart then
     2898            Play('CONTACT_' + char(48 + MyRO.Attitude[DipMem[NewPlayer]
     2899              .pContact]))
     2900          else if Command = scDipCancelTreaty then
     2901            Play('CANCELTREATY')
     2902          else if Command = scDipOffer then
     2903          begin
     2904            ReceivedOffer := TOffer(Data);
     2905            InitAllEnemyModels;
     2906          end
     2907          else if Command = scDipAccept then
     2908          begin // remember delivered and received prices
     2909            for i := 0 to DipMem[me].SentOffer.nDeliver - 1 do
     2910              include(DipMem[me].DeliveredPrices,
     2911                DipMem[me].SentOffer.Price[i] shr 24);
     2912            for i := 0 to DipMem[me].SentOffer.nCost - 1 do
     2913              include(DipMem[me].ReceivedPrices,
     2914                DipMem[me].SentOffer.Price[DipMem[me].SentOffer.nDeliver +
     2915                i] shr 24);
     2916            IsTreatyDeal := false;
     2917            for i := 0 to ReceivedOffer.nDeliver + ReceivedOffer.nCost - 1 do
     2918              if DipMem[me].SentOffer.Price[i] and opMask = opTreaty then
     2919                IsTreatyDeal := true;
     2920            if IsTreatyDeal then
     2921              Play('NEWTREATY')
     2922            else
     2923              Play('ACCEPTOFFER');
     2924          end;
     2925          NegoDlg.Start;
     2926          idle := true
     2927        end;
     2928
     2929      cShowCancelTreaty:
     2930        if not IsMultiPlayerGame then
     2931        begin
     2932          case G.RO[NewPlayer].Treaty[integer(Data)] of
     2933            trPeace:
     2934              s := Tribe[integer(Data)].TPhrase('FRCANCELBYREJECT_PEACE');
     2935            trFriendlyContact:
     2936              s := Tribe[integer(Data)].TPhrase('FRCANCELBYREJECT_FRIENDLY');
     2937            trAlliance:
     2938              s := Tribe[integer(Data)].TPhrase('FRCANCELBYREJECT_ALLIANCE');
     2939          end;
     2940          TribeMessage(integer(Data), s, 'CANCELTREATY');
     2941        end;
     2942
     2943      cShowCancelTreatyByAlliance:
     2944        if idle and (NewPlayer = me) then
     2945          TribeMessage(integer(Data), Tribe[integer(Data)
     2946            ].TPhrase('FRENEMYALLIANCE'), 'CANCELTREATY');
     2947
     2948      cShowSupportAllianceAgainst:
     2949        if not IsMultiPlayerGame and (Jump[0] = 0) then
     2950          TribeMessage(integer(Data) and $F,
     2951            Tribe[integer(Data) and $F].TPhrase('FRMYALLIANCE1') + ' ' +
     2952            Tribe[integer(Data) shr 4].TPhrase('FRMYALLIANCE2'),
     2953            'CANCELTREATY');
     2954
     2955      cShowPeaceViolation:
     2956        if not IsMultiPlayerGame and (Jump[0] = 0) then
     2957          TribeMessage(integer(Data),
     2958            Format(Tribe[integer(Data)].TPhrase('EVIOLATION'),
     2959            [TurnToString(MyRO.Turn + PeaceEvaTurns - 1)]), 'MSG_WITHDRAW');
     2960
     2961      cShowEndContact:
     2962        EndNego;
     2963
     2964      cShowUnitChanged, cShowCityChanged, cShowAfterMove, cShowAfterAttack:
     2965        if (idle and (NewPlayer = me) or not idle and not skipped) and
     2966          not((GameMode = cMovie) and (MovieSpeed = 4)) then
     2967        begin
     2968          assert(NewPlayer = me);
     2969          if not idle or (GameMode = cMovie) then
     2970            Application.ProcessMessages;
     2971          if Command = cShowCityChanged then
     2972          begin
     2973            CurrentMoveInfo.DoShow := false;
     2974            if idle then
     2975              CurrentMoveInfo.DoShow := true
     2976            else if CurrentMoveInfo.IsAlly then
     2977              CurrentMoveInfo.DoShow := not mAlNoMoves.Checked
     2978            else
     2979              CurrentMoveInfo.DoShow := not mEnNoMoves.Checked
     2980          end
     2981          else if Command = cShowUnitChanged then
     2982          begin
     2983            CurrentMoveInfo.DoShow := false;
     2984            if idle then
     2985              CurrentMoveInfo.DoShow := not mEffectiveMovesOnly.Checked
     2986            else if CurrentMoveInfo.IsAlly then
     2987              CurrentMoveInfo.DoShow :=
     2988                not(mAlNoMoves.Checked or mAlEffectiveMovesOnly.Checked)
     2989            else
     2990              CurrentMoveInfo.DoShow :=
     2991                not(mEnNoMoves.Checked or mEnAttacks.Checked)
     2992          end;
     2993          // else keep DoShow from cShowMove/cShowAttack
     2994
     2995          if CurrentMoveInfo.DoShow then
     2996          begin
     2997            if Command = cShowCityChanged then
     2998              MapValid := false;
     2999            FocusOnLoc(integer(Data), flImmUpdate);
     3000            // OldUnFocus:=UnFocus;
     3001            // UnFocus:=-1;
     3002            if Command = cShowAfterMove then
     3003              PaintLoc(integer(Data), CurrentMoveInfo.AfterMovePaintRadius)
     3004              // show discovered areas
     3005            else
     3006              PaintLoc(integer(Data), 1);
     3007            // UnFocus:=OldUnFocus;
     3008            if (Command = cShowAfterAttack) and
     3009              (CurrentMoveInfo.AfterAttackExpeller >= 0) then
     3010            begin
     3011              SoundMessageEx(Tribe[CurrentMoveInfo.AfterAttackExpeller]
     3012                .TPhrase('EXPEL'), '');
     3013              CurrentMoveInfo.AfterAttackExpeller := -1;
     3014              Update; // remove message box from screen
     3015            end
     3016            else if not idle then
     3017              if Command = cShowCityChanged then
     3018                Sleep(MoveTime * WaitAfterShowMove div 16)
     3019              else if (Command = cShowUnitChanged) and
     3020                (MyMap[integer(Data)] and fUnit <> 0) then
     3021                Sleep(MoveTime * WaitAfterShowMove div 32)
     3022          end // if CurrentMoveInfo.DoShow
     3023          else
     3024            MapValid := false;
     3025        end;
     3026
     3027      cShowMoving, cShowCapturing:
     3028        if (idle and (NewPlayer = me) or not idle and not skipped and
     3029          (TShowMove(Data).emix <> $FFFF)) and
     3030          not((GameMode = cMovie) and (MovieSpeed = 4)) then
     3031        begin
     3032          assert(NewPlayer = me);
     3033          if not idle or (GameMode = cMovie) then
     3034            Application.ProcessMessages;
     3035          with TShowMove(Data) do
     3036          begin
     3037            CurrentMoveInfo.DoShow := false;
     3038            if not idle and (Tribe[Owner].ModelPicture[mix].HGr = 0) then
     3039              InitEnemyModel(emix);
     3040
     3041            ToLoc := dLoc(FromLoc, dx, dy);
     3042            if idle then
     3043            begin // own unit -- make discovered land visible
     3044              assert(Owner = me); // no foreign moves during my turn!
     3045              CurrentMoveInfo.DoShow := not mEffectiveMovesOnly.Checked or
     3046                (Command = cShowCapturing);
     3047              if CurrentMoveInfo.DoShow then
     3048              begin
     3049                if GameMode = cMovie then
     3050                begin
     3051                  if MovieSpeed = 3 then
     3052                    AnimationSpeed := 4
     3053                  else if MovieSpeed = 2 then
     3054                    AnimationSpeed := 8
     3055                  else
     3056                    AnimationSpeed := 16;
     3057                end
     3058                else
     3059                begin
     3060                  if mVeryFastMoves.Checked then
     3061                    AnimationSpeed := 4
     3062                  else if mFastMoves.Checked then
     3063                    AnimationSpeed := 8
     3064                  else
     3065                    AnimationSpeed := 16;
     3066                end;
     3067                with MyModel[mix] do
     3068                begin
     3069                  if (Kind = mkDiplomat) or (Domain = dAir) or
     3070                    (Cap[mcRadar] + Cap[mcCarrier] + Cap[mcAcademy] > 0) or
     3071                    (MyMap[ToLoc] and fTerrain = fMountains) or
     3072                    (MyMap[ToLoc] and fTerImp = tiFort) or
     3073                    (MyMap[ToLoc] and fTerImp = tiBase) then
     3074                    CurrentMoveInfo.AfterMovePaintRadius := 2
     3075                  else
     3076                    CurrentMoveInfo.AfterMovePaintRadius := 1;
     3077                  if (MyRO.Wonder[woShinkansen].EffectiveOwner = me) and
     3078                    (Domain = dGround) and
     3079                    (MyMap[FromLoc] and (fRR or fCity) <> 0) and
     3080                    (MyMap[ToLoc] and (fRR or fCity) <> 0) and
     3081                    (Flags and umPlaneUnloading = 0) then
     3082                    AnimationSpeed := 4;
     3083                  ShowMoveDomain := Domain;
     3084                  IsAlpine := Cap[mcAlpine] > 0;
     3085                end
     3086              end
     3087            end
     3088            else
     3089            begin
     3090              CurrentMoveInfo.IsAlly := MyRO.Treaty[Owner] = trAlliance;
     3091              if GameMode = cMovie then
     3092                CurrentMoveInfo.DoShow := true
     3093              else if CurrentMoveInfo.IsAlly then
     3094                CurrentMoveInfo.DoShow := not mAlNoMoves.Checked and
     3095                  not(mAlEffectiveMovesOnly.Checked and
     3096                  (Command <> cShowCapturing))
     3097              else
     3098                CurrentMoveInfo.DoShow := not mEnNoMoves.Checked and
     3099                  not(mEnAttacks.Checked and (Command <> cShowCapturing));
     3100              if CurrentMoveInfo.DoShow then
     3101              begin
     3102                if Command = cShowCapturing then
     3103                begin // show capture message
     3104                  if MyMap[ToLoc] and fOwned <> 0 then
     3105                  begin // own city, search
     3106                    cix := MyRO.nCity - 1;
     3107                    while (cix >= 0) and (MyCity[cix].Loc <> ToLoc) do
     3108                      dec(cix);
     3109                    s := CityName(MyCity[cix].ID);
     3110                  end
     3111                  else
     3112                  begin // foreign city, search
     3113                    ecix := MyRO.nEnemyCity - 1;
     3114                    while (ecix >= 0) and (MyRO.EnemyCity[ecix].Loc <> ToLoc) do
     3115                      dec(ecix);
     3116                    s := CityName(MyRO.EnemyCity[ecix].ID);
     3117                  end;
     3118                  TribeMessage(Owner, Format(Tribe[Owner].TPhrase('CAPTURE'),
     3119                    [s]), '');
     3120                  Update; // remove message box from screen
     3121                end;
     3122
     3123                if CurrentMoveInfo.IsAlly then
     3124                begin // allied unit -- make discovered land visible
     3125                  if mAlFastMoves.Checked then
     3126                    AnimationSpeed := 8
     3127                  else
     3128                    AnimationSpeed := 16;
     3129                  with MyRO.EnemyModel[emix] do
     3130                    if (Kind = mkDiplomat) or (Domain = dAir) or
     3131                      (ATrans_Fuel > 0) or
     3132                      (Cap and (1 shl (mcRadar - mcFirstNonCap) or
     3133                      1 shl (mcAcademy - mcFirstNonCap)) <> 0) or
     3134                      (MyMap[ToLoc] and fTerrain = fMountains) or
     3135                      (MyMap[ToLoc] and fTerImp = tiFort) or
     3136                      (MyMap[ToLoc] and fTerImp = tiBase) then
     3137                      CurrentMoveInfo.AfterMovePaintRadius := 2
     3138                    else
     3139                      CurrentMoveInfo.AfterMovePaintRadius := 1
     3140                end
     3141                else
     3142                begin
     3143                  if mEnFastMoves.Checked then
     3144                    AnimationSpeed := 8
     3145                  else
     3146                    AnimationSpeed := 16;
     3147                  CurrentMoveInfo.AfterMovePaintRadius := 0;
     3148                  // enemy unit, nothing discovered
     3149                end;
     3150                if GameMode = cMovie then
     3151                begin
     3152                  if MovieSpeed = 3 then
     3153                    AnimationSpeed := 4
     3154                  else if MovieSpeed = 2 then
     3155                    AnimationSpeed := 8
     3156                  else
     3157                    AnimationSpeed := 16;
     3158                end;
     3159                ShowMoveDomain := MyRO.EnemyModel[emix].Domain;
     3160                IsAlpine := MyRO.EnemyModel[emix].Cap and
     3161                  (1 shl (mcAlpine - mcFirstNonCap)) <> 0;
     3162              end
     3163            end;
     3164
     3165            if CurrentMoveInfo.DoShow then
     3166            begin
     3167              if Command = cShowCapturing then
     3168                Play('MOVE_CAPTURE')
     3169              else if EndHealth <= 0 then
     3170                Play('MOVE_DIE')
     3171              else if Flags and umSpyMission <> 0 then
     3172                Play('MOVE_COVERT')
     3173              else if Flags and umShipLoading <> 0 then
     3174                if ShowMoveDomain = dAir then
     3175                  Play('MOVE_PLANELANDING')
     3176                else
     3177                  Play('MOVE_LOAD')
     3178              else if Flags and umPlaneLoading <> 0 then
     3179                Play('MOVE_LOAD')
     3180              else if Flags and umShipUnloading <> 0 then
     3181                if ShowMoveDomain = dAir then
     3182                  Play('MOVE_PLANESTART')
     3183                else
     3184                  Play('MOVE_UNLOAD')
     3185              else if Flags and umPlaneUnloading <> 0 then
     3186                if (MyMap[FromLoc] and fCity = 0) and
     3187                  (MyMap[FromLoc] and fTerImp <> tiBase) then
     3188                  Play('MOVE_PARACHUTE')
     3189                else
     3190                  Play('MOVE_UNLOAD')
     3191              else if (ShowMoveDomain = dGround) and not IsAlpine and
     3192                (MyMap[ToLoc] and fTerrain = fMountains) and
     3193                ((MyMap[FromLoc] and (fRoad or fRR or fCity) = 0) or
     3194                (MyMap[ToLoc] and (fRoad or fRR or fCity) = 0)) then
     3195                Play('MOVE_MOUNTAIN');
     3196
     3197              FocusOnLoc(FromLoc, flImmUpdate);
     3198              PaintLoc_BeforeMove(FromLoc);
     3199              if Command = cShowCapturing then
     3200                MoveOnScreen(TShowMove(Data), 1, 32, 32)
     3201              else
     3202                MoveOnScreen(TShowMove(Data), 1, AnimationSpeed, AnimationSpeed)
     3203            end // if CurrentMoveInfo.DoShow
     3204            else
     3205              MapValid := false;
     3206          end
     3207        end;
     3208
     3209      cShowAttacking:
     3210        if (idle and (NewPlayer = me) or not idle and not skipped and
     3211          (TShowMove(Data).emix <> $FFFF)) and
     3212          not((GameMode = cMovie) and (MovieSpeed = 4)) then
     3213        begin
     3214          assert(NewPlayer = me);
     3215          if not idle or (GameMode = cMovie) then
     3216            Application.ProcessMessages;
     3217          with TShowMove(Data) do
     3218          begin
     3219            CurrentMoveInfo.AfterAttackExpeller := -1;
     3220            CurrentMoveInfo.DoShow := false;
     3221            if idle then
     3222              CurrentMoveInfo.DoShow := true // own unit -- always show attacks
     3223            else
     3224            begin
     3225              CurrentMoveInfo.IsAlly := MyRO.Treaty[Owner] = trAlliance;
     3226              if CurrentMoveInfo.IsAlly then
     3227                CurrentMoveInfo.DoShow := not mAlNoMoves.Checked
     3228              else
     3229                CurrentMoveInfo.DoShow := not mEnNoMoves.Checked;
     3230            end;
     3231            if CurrentMoveInfo.DoShow then
     3232            begin
     3233              ToLoc := dLoc(FromLoc, dx, dy);
     3234              if Tribe[Owner].ModelPicture[mix].HGr = 0 then
     3235                InitEnemyModel(emix);
     3236
     3237              if (MyMap[ToLoc] and (fCity or fUnit or fOwned) = fCity or fOwned)
     3238              then
     3239              begin // tell about bombardment
     3240                cix := MyRO.nCity - 1;
     3241                while (cix >= 0) and (MyCity[cix].Loc <> ToLoc) do
     3242                  dec(cix);
     3243                if MyCity[cix].Status and csToldBombard = 0 then
     3244                begin
     3245                  if not supervising then
     3246                    MyCity[cix].Status := MyCity[cix].Status or csToldBombard;
     3247                  s := CityName(MyCity[cix].ID);
     3248                  SoundMessageEx(Format(Tribe[Owner].TPhrase('BOMBARD'),
     3249                    [s]), '');
     3250                  Update; // remove message box from screen
     3251                end;
     3252              end
     3253              else if Flags and umExpelling <> 0 then
     3254                CurrentMoveInfo.AfterAttackExpeller := Owner;
     3255
     3256              if Flags and umExpelling <> 0 then
     3257                Play('MOVE_EXPEL')
     3258              else if Owner = me then
     3259              begin
     3260                MakeModelInfo(me, mix, MyModel[mix], mi);
     3261                Play(AttackSound(ModelCode(mi)));
     3262              end
     3263              else
     3264                Play(AttackSound(ModelCode(MyRO.EnemyModel[emix])));
     3265
     3266              FocusOnLoc(FromLoc, flImmUpdate);
     3267
     3268              // before combat
     3269              MainMap.AttackBegin(TShowMove(Data));
     3270              if MyMap[ToLoc] and fCity <> 0 then
     3271                PaintLoc(ToLoc);
     3272              PaintLoc(FromLoc);
     3273              MoveOnScreen(TShowMove(Data), 1, 9, 16);
     3274              MoveOnScreen(TShowMove(Data), 17, 12, 32);
     3275              MoveOnScreen(TShowMove(Data), 7, 11, 16);
     3276
     3277              // after combat
     3278              MainMap.AttackEffect(TShowMove(Data));
     3279              PaintLoc(ToLoc);
     3280              if EndHealth > 0 then
     3281              begin
     3282                Health := EndHealth;
     3283                MoveOnScreen(TShowMove(Data), 10, 0, 16);
     3284              end
     3285              else if not idle then
     3286                Sleep(MoveTime div 2);
     3287              MainMap.AttackEnd;
     3288            end // if CurrentMoveInfo.DoShow
     3289            else
     3290              MapValid := false;
     3291          end
     3292        end;
     3293
     3294      cShowMissionResult:
     3295        if Cardinal(Data) = 0 then
     3296          SoundMessageEx(Phrases.Lookup('NOFOREIGNINFO'), '')
     3297        else
     3298        begin
     3299          s := Phrases.Lookup('FOREIGNINFO');
     3300          for p1 := 0 to nPl - 1 do
     3301            if 3 shl (p1 * 2) and Cardinal(Data) <> 0 then
     3302              s := s + '\' + Tribe[p1].TPhrase('SHORTNAME');
     3303          SoundMessageEx(s, '')
     3304        end;
     3305
     3306      cShowShipChange:
     3307        if not IsMultiPlayerGame and (Jump[0] = 0) then
     3308          ShowEnemyShipChange(TShowShipChange(Data));
     3309
     3310      cShowGreatLibTech:
     3311        if not IsMultiPlayerGame and (Jump[0] = 0) then
     3312          with MessgExDlg do
     3313          begin
     3314            MessgText := Format(Phrases.Lookup('GRLIB_GENERAL'),
     3315              [Phrases.Lookup('ADVANCES', integer(Data))]);
     3316            OpenSound := 'NEWADVANCE_GRLIB';
     3317            Kind := mkOk;
     3318            IconKind := mikImp;
     3319            IconIndex := woGrLibrary;
     3320            ShowModal;
     3321          end;
     3322
     3323      cRefreshDebugMap:
     3324        begin
     3325          if integer(Data) = IsoEngine.pDebugMap then
     3326          begin
     3327            MapValid := false;
     3328            MainOffscreenPaint;
     3329            Update;
     3330          end
     3331        end;
     3332
     3333    else
     3334      if Command >= cClientEx then
     3335        case Command and $FFF0 of
     3336
     3337          cSetTribe:
     3338            with TTribeInfo(Data) do
     3339            begin
     3340              i := UnusedTribeFiles.Count - 1;
     3341              while (i >= 0) and
     3342                (AnsiCompareFileName(UnusedTribeFiles[i], FileName) <> 0) do
     3343                dec(i);
     3344              if i >= 0 then
     3345                UnusedTribeFiles.Delete(i);
     3346              CreateTribe(trix, FileName, true);
     3347            end;
     3348
     3349          cSetNewModelPicture, cSetModelPicture:
     3350            if TribeOriginal[TModelPictureInfo(Data).trix] then
     3351              Tribe[TModelPictureInfo(Data).trix].SetModelPicture
     3352                (TModelPictureInfo(Data), Command and
     3353                $FFF0 = cSetNewModelPicture);
     3354
     3355          cSetSlaveIndex and $FFF0:
     3356            Tribe[integer(Data) shr 16].mixSlaves := integer(Data) and $FFFF;
     3357
     3358          cSetCityName:
     3359            with TCityNameInfo(Data) do
     3360              if TribeOriginal[ID shr 12] then
     3361                Tribe[ID shr 12].SetCityName(ID and $FFF, NewName);
     3362
     3363          cSetModelName:
     3364            with TModelNameInfo(Data) do
     3365              if TribeOriginal[NewPlayer] then
     3366                Tribe[NewPlayer].ModelName[mix] := NewName;
     3367        end
     3368    end
     3369  end; { <<<client }
     3370
     3371  { *** main part *** }
     3372
     3373  procedure TMainScreen.CreateParams(var p: TCreateParams);
     3374  var
     3375    DefaultOptionChecked: integer;
     3376    Reg: TRegistry;
     3377    doinit: boolean;
     3378  begin
     3379    inherited;
     3380
     3381    // define which menu settings to save
     3382    SaveOption[0] := mAlEffectiveMovesOnly.Tag;
     3383    SaveOption[1] := mEnMoves.Tag;
     3384    SaveOption[2] := mEnAttacks.Tag;
     3385    SaveOption[3] := mEnNoMoves.Tag;
     3386    SaveOption[4] := mWaitTurn.Tag;
     3387    SaveOption[5] := mEffectiveMovesOnly.Tag;
     3388    SaveOption[6] := mEnFastMoves.Tag;
     3389    SaveOption[7] := mSlowMoves.Tag;
     3390    SaveOption[8] := mFastMoves.Tag;
     3391    SaveOption[9] := mVeryFastMoves.Tag;
     3392    SaveOption[10] := mNames.Tag;
     3393    SaveOption[11] := mRepList.Tag;
     3394    SaveOption[12] := mRepScreens.Tag;
     3395    SaveOption[13] := mSoundOff.Tag;
     3396    SaveOption[14] := mSoundOn.Tag;
     3397    SaveOption[15] := mSoundOnAlt.Tag;
     3398    SaveOption[16] := mScrollSlow.Tag;
     3399    SaveOption[17] := mScrollFast.Tag;
     3400    SaveOption[18] := mScrollOff.Tag;
     3401    SaveOption[19] := mAlSlowMoves.Tag;
     3402    SaveOption[20] := mAlFastMoves.Tag;
     3403    SaveOption[21] := mAlNoMoves.Tag;
     3404    DefaultOptionChecked := 1 shl 1 + 1 shl 7 + 1 shl 10 + 1 shl 12 + 1 shl 14 +
     3405      1 shl 18 + 1 shl 19;
     3406
     3407    Reg := TRegistry.Create;
     3408    doinit := true;
     3409    if Reg.KeyExists('SOFTWARE\cevo\RegVer9') then
     3410    begin
     3411      doinit := false;
     3412      Reg.OpenKey('SOFTWARE\cevo\RegVer9', false);
     3413      try
     3414        xxt := Reg.ReadInteger('TileWidth') div 2;
     3415        yyt := Reg.ReadInteger('TileHeight') div 2;
     3416        OptionChecked := Reg.ReadInteger('OptionChecked');
     3417        MapOptionChecked := Reg.ReadInteger('MapOptionChecked');
     3418        CityRepMask := Cardinal(Reg.ReadInteger('CityReport'));
     3419      except
     3420        doinit := true;
     3421      end;
     3422      Reg.closekey;
     3423      if OptionChecked and (7 shl 16) = 0 then
     3424        OptionChecked := OptionChecked or (1 shl 16);
     3425      // old regver with no scrolling
     3426    end;
     3427    Reg.free;
     3428    if doinit then
     3429    begin
     3430      xxt := 48;
     3431      yyt := 24;
     3432      OptionChecked := DefaultOptionChecked;
     3433      MapOptionChecked := 1 shl moCityNames;
     3434      CityRepMask := Cardinal(not chPopIncrease and not chNoGrowthWarning and
     3435        not chCaptured);
     3436    end;
     3437
     3438    if FullScreen then
     3439    begin
     3440      p.Style := $87000000;
     3441      BorderStyle := bsNone;
     3442      BorderIcons := [];
     3443    end;
     3444
     3445    if 1 shl 13 and OptionChecked <> 0 then
     3446      SoundMode := smOff
     3447    else if 1 shl 15 and OptionChecked <> 0 then
     3448      SoundMode := smOnAlt
     3449    else
     3450      SoundMode := smOn
     3451  end;
     3452
     3453  procedure TMainScreen.FormCreate(Sender: TObject);
     3454  var
     3455    i, j: integer;
     3456  begin
     3457    Screen.Cursors[crImpDrag] := LoadCursor(HInstance, 'DRAG');
     3458    Screen.Cursors[crFlatHand] := LoadCursor(HInstance, 'FLATHAND');
     3459
     3460    // tag-controlled language
     3461    for i := 0 to ComponentCount - 1 do
     3462      if Components[i].Tag and $FF <> 0 then
     3463        if Components[i] is TMenuItem then
     3464        begin
     3465          TMenuItem(Components[i]).Caption := Phrases.Lookup('CONTROLS',
     3466            -1 + Components[i].Tag and $FF);
     3467          for j := 0 to nSaveOption - 1 do
     3468            if Components[i].Tag and $FF = SaveOption[j] then
     3469              TMenuItem(Components[i]).Checked := 1 shl j and
     3470                OptionChecked <> 0;
     3471        end
     3472        else if Components[i] is TButtonBase then
     3473        begin
     3474          TButtonBase(Components[i]).Hint := Phrases.Lookup('CONTROLS',
     3475            -1 + Components[i].Tag and $FF);
     3476          if (Components[i] is TButtonC) and
     3477            (TButtonC(Components[i]).ButtonIndex <> 1) then
     3478            TButtonC(Components[i]).ButtonIndex :=
     3479              MapOptionChecked shr (Components[i].Tag shr 8) and 1 + 2
     3480        end;
     3481
     3482    // non-tag-controlled language
     3483    mTechTree.Caption := Phrases2.Lookup('MENU_ADVTREE');
     3484    mViewpoint.Caption := Phrases2.Lookup('MENU_VIEWPOINT');
     3485    if not Phrases2FallenBackToEnglish then
     3486    begin
     3487      MenuArea.Hint := Phrases2.Lookup('BTN_MENU');
     3488      TreasuryArea.Hint := Phrases2.Lookup('TIP_TREASURY');
     3489      ResearchArea.Hint := Phrases.Lookup('SCIENCE');
     3490      ManagementArea.Hint := Phrases2.Lookup('BTN_MANAGE');
     3491    end;
     3492    for i := 0 to mRep.Count - 1 do
     3493    begin
     3494      j := mRep[i].Tag shr 8;
     3495      mRep[i].Caption := CityEventName(j);
     3496      mRep[i].Checked := CityRepMask and (1 shl j) <> 0;
     3497    end;
     3498
     3499    Mini := TBitmap.Create;
     3500    Mini.PixelFormat := pf24bit;
     3501    Panel := TBitmap.Create;
     3502    Panel.PixelFormat := pf24bit;
     3503    Panel.Canvas.Font.Assign(UniFont[ftSmall]);
     3504    Panel.Canvas.Brush.Style := bsClear;
     3505    TopBar := TBitmap.Create;
     3506    TopBar.PixelFormat := pf24bit;
     3507    TopBar.Canvas.Font.Assign(UniFont[ftNormal]);
     3508    TopBar.Canvas.Brush.Style := bsClear;
     3509    Buffer := TBitmap.Create;
     3510    Buffer.PixelFormat := pf24bit;
     3511    if 2 * lxmax > 3 * xSizeBig then
     3512      Buffer.width := 2 * lxmax
     3513    else
     3514      Buffer.width := 3 * xSizeBig;
     3515    if lymax > 3 * ySizeBig then
     3516      Buffer.height := lymax
     3517    else
     3518      Buffer.height := 3 * ySizeBig;
     3519    Buffer.Canvas.Font.Assign(UniFont[ftSmall]);
     3520    for i := 0 to nPl - 1 do
     3521      AILogo[i] := nil;
     3522    Canvas.Font.Assign(UniFont[ftSmall]);
     3523    InitButtons();
     3524    EOT.Template := Templates;
     3525  end;
     3526
     3527  procedure TMainScreen.FormDestroy(Sender: TObject);
     3528  var
     3529    i: integer;
     3530  begin
     3531    Mini.free;
     3532    Buffer.free;
     3533    Panel.free;
     3534    for i := 0 to nPl - 1 do
     3535      if AILogo[i] <> nil then
     3536        AILogo[i].free;
     3537  end;
     3538
     3539  procedure TMainScreen.FormResize(Sender: TObject);
     3540  var
     3541    MiniFrame, MaxMapWidth: integer;
     3542  begin
     3543    SmallScreen := ClientWidth < 1024;
     3544    MaxMapWidth := (G.lx * 2 - 3) * xxt;
     3545    // avoide the same tile being visible left and right
     3546    if ClientWidth <= MaxMapWidth then
     3547    begin
     3548      MapWidth := ClientWidth;
     3549      MapOffset := 0;
     3550    end
     3551    else
     3552    begin
     3553      MapWidth := MaxMapWidth;
     3554      MapOffset := (ClientWidth - MapWidth) div 2;
     3555    end;
     3556    MapHeight := ClientHeight - TopBarHeight - PanelHeight + overlap;
     3557    Panel.width := ClientWidth;
     3558    Panel.height := PanelHeight;
     3559    TopBar.width := ClientWidth;
     3560    TopBar.height := TopBarHeight;
     3561    MiniFrame := (lxmax_xxx - G.ly) div 2;
     3562    xMidPanel := (G.lx + MiniFrame) * 2 + 1;
     3563    xRightPanel := ClientWidth - LeftPanelWidth - 10;
     3564    if ClientMode = cEditMap then
     3565      TrPitch := 2 * xxt
     3566    else
     3567      TrPitch := 66;
     3568    xMini := MiniFrame - 5;
     3569    yMini := (PanelHeight - 26 - lxmax_xxx) div 2 + MiniFrame;
     3570    ywmax := (G.ly - MapHeight div yyt + 1) and not 1;
     3571    ywcenter := -((MapHeight - yyt * (G.ly - 1)) div (4 * yyt)) * 2;
     3572    // only for ywmax<=0
     3573    if ywmax <= 0 then
     3574      yw := ywcenter
     3575    else if yw < 0 then
     3576      yw := 0
     3577    else if yw > ywmax then
     3578      yw := ywmax;
     3579    UnitInfoBtn.Top := ClientHeight - 29;
     3580    UnitInfoBtn.Left := xMidPanel + 7 + 99;
     3581    UnitBtn.Top := ClientHeight - 29;
     3582    UnitBtn.Left := xMidPanel + 7 + 99 + 31;
     3583    TerrainBtn.Top := ClientHeight - 29;
     3584    TerrainBtn.Left := xMidPanel + 7 + 99 + 62;
     3585    MovieSpeed1Btn.Top := ClientHeight - 91;
     3586    MovieSpeed1Btn.Left := ClientWidth div 2 - 62;
     3587    MovieSpeed2Btn.Top := ClientHeight - 91;
     3588    MovieSpeed2Btn.Left := ClientWidth div 2 - 62 + 29;
     3589    MovieSpeed3Btn.Top := ClientHeight - 91;
     3590    MovieSpeed3Btn.Left := ClientWidth div 2 - 62 + 2 * 29;
     3591    MovieSpeed4Btn.Top := ClientHeight - 91;
     3592    MovieSpeed4Btn.Left := ClientWidth div 2 - 62 + 3 * 29 + 12;
     3593    EOT.Top := ClientHeight - 64;
     3594    EOT.Left := ClientWidth - 62;
     3595    SetWindowPos(sb.h, 0, xRightPanel + 10 - 14 -
     3596      GetSystemMetrics(SM_CXVSCROLL), ClientHeight - MidPanelHeight + 8, 0, 0,
     3597      SWP_NOSIZE or SWP_NOZORDER);
     3598    MapBtn0.Left := xMini + G.lx - 44;
     3599    MapBtn0.Top := ClientHeight - 15;
     3600    MapBtn1.Left := xMini + G.lx - 28;
     3601    MapBtn1.Top := ClientHeight - 15;
     3602    { MapBtn2.Left:=xMini+G.lx-20;
     3603      MapBtn2.Top:=ClientHeight-15;
     3604      MapBtn3.Left:=xMini+G.lx-4;
     3605      MapBtn3.Top:=ClientHeight-15; }
     3606    MapBtn5.Left := xMini + G.lx - 12;
     3607    MapBtn5.Top := ClientHeight - 15;
     3608    MapBtn4.Left := xMini + G.lx + 20;
     3609    MapBtn4.Top := ClientHeight - 15;
     3610    MapBtn6.Left := xMini + G.lx + 36;
     3611    MapBtn6.Top := ClientHeight - 15;
     3612    TreasuryArea.Left := ClientWidth div 2 - 172;
     3613    ResearchArea.Left := ClientWidth div 2;
     3614    ManagementArea.Left := ClientWidth - xPalace;
     3615    ManagementArea.Top := TopBarHeight + MapHeight - overlap + yPalace;
     3616    ArrangeMidPanel;
     3617    if RepaintOnResize then
     3618    begin
     3619      RectInvalidate(0, TopBarHeight, ClientWidth, TopBarHeight + MapHeight);
     3620      MapValid := false;
     3621      PaintAll
     3622    end
     3623  end;
     3624
     3625  procedure TMainScreen.FormCloseQuery(Sender: TObject; var CanClose: boolean);
     3626  begin
     3627    CanClose := Closable;
     3628    if not Closable and idle and (me = 0) and (ClientMode < scContact) then
     3629      MenuClick(mResign)
     3630  end;
     3631
     3632  procedure TMainScreen.OnScroll(var m: TMessage);
     3633  begin
     3634    if ProcessPVSB(sb, m) then
     3635    begin
     3636      PanelPaint;
     3637      Update
     3638    end
     3639  end;
     3640
     3641  procedure TMainScreen.OnEOT(var Msg: TMessage);
     3642  begin
     3643    EndTurn
     3644  end;
     3645
     3646  procedure TMainScreen.EOTClick(Sender: TObject);
     3647  begin
     3648    if GameMode = cMovie then
     3649    begin
     3650      MessgExDlg.CancelMovie;
     3651      Server(sBreak, me, 0, nil^)
     3652    end
     3653    else if ClientMode < 0 then
     3654      skipped := true
     3655    else if ClientMode >= scContact then
     3656      NegoDlg.ShowNewContent(wmPersistent)
     3657    else if Jump[pTurn] > 0 then
     3658    begin
     3659      Jump[pTurn] := 0;
     3660      StartRunning := false
     3661    end
     3662    else
     3663      EndTurn
     3664  end;
     3665
     3666  // set xTerrain, xTroop, and TrRow
     3667  procedure TMainScreen.ArrangeMidPanel;
     3668  begin
     3669    if ClientMode = cEditMap then
     3670      xTroop := xMidPanel + 15
     3671    else
     3672    begin
     3673      if supervising then
     3674        xTerrain := xMidPanel + 2 * xxt + 14
     3675      else if ClientWidth < 1280 then
     3676        xTerrain := ClientWidth div 2 + (1280 - ClientWidth) div 3
     3677      else
     3678        xTerrain := ClientWidth div 2;
     3679      xTroop := xTerrain + 2 * xxt + 12;
     3680      if SmallScreen and not supervising then
     3681        xTroop := xRightPanel + 10 - 3 * 66 -
     3682          GetSystemMetrics(SM_CXVSCROLL) - 19 - 4;
     3683      // not perfect but we assume almost no one is still playing on a 800x600 screen
     3684    end;
     3685    TrRow := (xRightPanel + 10 - xTroop - GetSystemMetrics(SM_CXVSCROLL) - 19)
     3686      div TrPitch;
     3687  end;
     3688
     3689  function TMainScreen.EndTurn(WasSkipped: boolean): boolean;
     3690
     3691    function IsResourceUnused(cix, NeedFood, NeedProd: integer): boolean;
     3692    var
     3693      dx, dy, fix: integer;
     3694      CityAreaInfo: TCityAreaInfo;
     3695      TileInfo: TTileInfo;
     3696    begin
     3697      Server(sGetCityAreaInfo, me, cix, CityAreaInfo);
     3698      for dy := -3 to 3 do
     3699        for dx := -3 to 3 do
     3700          if ((dx + dy) and 1 = 0) and (dx * dx * dy * dy < 81) then
     3701          begin
     3702            fix := (dy + 3) shl 2 + (dx + 3) shr 1;
     3703            if (MyCity[cix].Tiles and (1 shl fix) = 0) // not used yet
     3704              and (CityAreaInfo.Available[fix] = faAvailable) then // usable
     3705            begin
     3706              TileInfo.ExplCity := cix;
     3707              Server(sGetHypoCityTileInfo, me, dLoc(MyCity[cix].Loc, dx, dy),
     3708                TileInfo);
     3709              if (TileInfo.Food >= NeedFood) and (TileInfo.Prod >= NeedProd)
     3710              then
     3711              begin
     3712                result := true;
     3713                exit
     3714              end;
     3715            end
     3716          end;
     3717      result := false;
     3718    end;
     3719
     3720  var
     3721    i, p1, uix, cix, CenterLoc: integer;
     3722    MsgItem: string;
     3723    CityReport: TCityReport;
     3724    PlaneReturnData: TPlaneReturnData;
     3725    Zoom: boolean;
     3726  begin
     3727    result := false;
     3728    if ClientMode >= scDipOffer then
     3729      exit;
     3730
     3731    if supervising and (me <> 0) then
     3732    begin
     3733      for i := 0 to Screen.FormCount - 1 do
     3734        if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg)
     3735        then
     3736          Screen.Forms[i].Close; // close windows
     3737      ItsMeAgain(0);
     3738    end;
     3739
     3740    CityOptimizer_EndOfTurn;
     3741
     3742    if not WasSkipped then // check warnings
     3743    begin
     3744      // need to move planes home?
     3745      for uix := 0 to MyRO.nUn - 1 do
     3746        with MyUn[uix] do
     3747          if (Loc >= 0) and (MyModel[mix].Domain = dAir) and
     3748            (Status and usToldNoReturn = 0) and (Master < 0) and
     3749            (MyMap[Loc] and fCity = 0) and (MyMap[Loc] and fTerImp <> tiBase)
     3750          then
     3751          begin
     3752            PlaneReturnData.Fuel := Fuel;
     3753            PlaneReturnData.Loc := Loc;
     3754            PlaneReturnData.Movement := 0; // end turn without further movement?
     3755            if Server(sGetPlaneReturn, me, uix, PlaneReturnData) = eNoWay then
     3756            begin
     3757              CenterLoc := Loc + G.lx * 6;
     3758              // centering the unit itself would make it covered by the query dialog
     3759              while CenterLoc >= G.lx * G.ly do
     3760                dec(CenterLoc, G.lx * 2);
     3761              Centre(CenterLoc);
     3762              SetTroopLoc(-1);
     3763              PaintAll;
     3764
     3765              if MyModel[mix].Kind = mkSpecial_Glider then
     3766                MsgItem := 'LOWFUEL_GLIDER'
     3767              else
     3768                MsgItem := 'LOWFUEL';
     3769              if SimpleQuery(mkYesNo, Phrases.Lookup(MsgItem),
     3770                'WARNING_LOWSUPPORT') <> mrOK then
     3771              begin
     3772                SetUnFocus(uix);
     3773                SetTroopLoc(Loc);
     3774                PanelPaint;
     3775                exit;
     3776              end;
     3777              MyUn[uix].Status := MyUn[uix].Status or usToldNoReturn;
     3778            end
     3779          end;
     3780
     3781      if not supervising and (MyRO.TestFlags and tfImmImprove = 0) and
     3782        (MyRO.Government <> gAnarchy) and (MyRO.Money + TaxSum < 0) and
     3783        (MyRO.TaxRate < 100) then // low funds!
     3784        with MessgExDlg do
     3785        begin
     3786          OpenSound := 'WARNING_LOWFUNDS';
     3787          MessgText := Phrases.Lookup('LOWFUNDS');
     3788          Kind := mkYesNo;
     3789          IconKind := mikImp;
     3790          IconIndex := imTrGoods;
     3791          ShowModal;
     3792          if ModalResult <> mrOK then
     3793            exit
     3794        end;
     3795
     3796      if MyRO.Government <> gAnarchy then
     3797        for cix := 0 to MyRO.nCity - 1 do
     3798          with MyCity[cix] do
     3799            if (Loc >= 0) and (Flags and chCaptured = 0) then
     3800            begin
     3801              Zoom := false;
     3802              CityReport.HypoTiles := -1;
     3803              CityReport.HypoTax := -1;
     3804              CityReport.HypoLux := -1;
     3805              Server(sGetCityReport, me, cix, CityReport);
     3806
     3807              if (CityReport.Working - CityReport.Happy > Size shr 1) and
     3808                (Flags and chCaptured <= $10000) then
     3809                with MessgExDlg do
     3810                begin
     3811                  OpenSound := 'WARNING_DISORDER';
     3812                  if Status and csResourceWeightsMask = 0 then
     3813                    MsgItem := 'DISORDER'
     3814                  else
     3815                    MsgItem := 'DISORDER_UNREST';
     3816                  MessgText := Format(Phrases.Lookup(MsgItem), [CityName(ID)]);
     3817                  Kind := mkYesNo;
     3818                  // BigIcon:=29;
     3819                  ShowModal;
     3820                  Zoom := ModalResult <> mrOK;
     3821                end;
     3822              if not Zoom and (Food + CityReport.FoodRep - CityReport.Eaten < 0)
     3823              then
     3824                with MessgExDlg do
     3825                begin
     3826                  OpenSound := 'WARNING_FAMINE';
     3827                  if Status and csResourceWeightsMask = 0 then
     3828                    MsgItem := 'FAMINE'
     3829                  else if (CityReport.Deployed <> 0) and
     3830                    IsResourceUnused(cix, 1, 0) then
     3831                    MsgItem := 'FAMINE_UNREST'
     3832                  else
     3833                    MsgItem := 'FAMINE_TILES';
     3834                  MessgText := Format(Phrases.Lookup(MsgItem), [CityName(ID)]);
     3835                  Kind := mkYesNo;
     3836                  IconKind := mikImp;
     3837                  IconIndex := 22;
     3838                  ShowModal;
     3839                  Zoom := ModalResult <> mrOK;
     3840                end;
     3841              if not Zoom and (CityReport.ProdRep < CityReport.Support) then
     3842                with MessgExDlg do
     3843                begin
     3844                  OpenSound := 'WARNING_LOWSUPPORT';
     3845                  if Status and csResourceWeightsMask = 0 then
     3846                    MsgItem := 'LOWSUPPORT'
     3847                  else if (CityReport.Deployed <> 0) and
     3848                    IsResourceUnused(cix, 0, 1) then
     3849                    MsgItem := 'LOWSUPPORT_UNREST'
     3850                  else
     3851                    MsgItem := 'LOWSUPPORT_TILES';
     3852                  MessgText := Format(Phrases.Lookup(MsgItem), [CityName(ID)]);
     3853                  Kind := mkYesNo;
     3854                  IconKind := mikImp;
     3855                  IconIndex := 29;
     3856                  ShowModal;
     3857                  Zoom := ModalResult <> mrOK;
     3858                end;
     3859              if Zoom then
     3860              begin // zoom to city
     3861                ZoomToCity(Loc);
     3862                exit
     3863              end
     3864            end;
     3865
     3866      if (MyRO.Happened and phTech <> 0) and (MyRO.ResearchTech < 0) and
     3867        (MyData.FarTech <> adNexus) then
     3868        if not ChooseResearch then
     3869          exit;
     3870    end;
     3871
     3872    RememberPeaceViolation;
     3873
     3874    SetUnFocus(-1);
     3875    for uix := 0 to MyRO.nUn - 1 do
     3876      MyUn[uix].Status := MyUn[uix].Status and usPersistent;
     3877
     3878    CityDlg.CloseAction := None;
     3879    if IsMultiPlayerGame then
     3880    begin // close windows for next player
     3881      for i := 0 to Screen.FormCount - 1 do
     3882        if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg)
     3883        then
     3884          Screen.Forms[i].Close;
     3885    end
     3886    else
     3887    begin
     3888      if CityDlg.Visible then
     3889        CityDlg.Close;
     3890      if UnitStatDlg.Visible then
     3891        UnitStatDlg.Close;
     3892    end;
     3893    for i := 0 to Screen.FormCount - 1 do
     3894      if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
     3895        Screen.Forms[i].Enabled := false;
     3896
     3897    if Server(sTurn, pTurn, 0, nil^) >= rExecuted then
     3898    begin
     3899      if Jump[pTurn] > 0 then
     3900        EOT.Hint := Phrases.Lookup('BTN_STOP')
     3901      else
     3902        EOT.Hint := Phrases.Lookup('BTN_SKIP');
     3903      result := true;
     3904      SetTroopLoc(-1);
     3905      pTurn := -1;
     3906      pLogo := -1;
     3907      UnitInfoBtn.Visible := false;
     3908      UnitBtn.Visible := false;
     3909      TerrainBtn.Visible := false;
     3910      EOT.ButtonIndex := eotCancel;
     3911      EOT.Visible := true;
     3912      MapValid := false;
     3913      PanelPaint;
     3914      Update;
     3915      ClientMode := -1;
     3916      idle := false;
     3917      skipped := WasSkipped;
     3918      for p1 := 1 to nPl - 1 do
     3919        if G.RO[p1] <> nil then
     3920          skipped := true; // don't show enemy moves in hotseat mode
     3921    end
     3922    else
     3923      PanelPaint
     3924  end; // EndTurn
     3925
     3926  procedure TMainScreen.EndNego;
     3927  begin
     3928    if NegoDlg.Visible then
     3929      NegoDlg.Close;
     3930    HaveStrategyAdvice := false;
     3931    // AdvisorDlg.HaveStrategyAdvice;
     3932    // negotiation might have changed advices
     3933    EOT.ButtonIndex := eotCancel;
     3934    EOT.Visible := true;
     3935    PanelPaint;
     3936    Update;
     3937    ClientMode := -1;
     3938    idle := false;
     3939  end;
     3940
     3941  procedure TMainScreen.ProcessRect(x0, y0, nx, ny, Options: integer);
     3942  var
     3943    xs, ys, xl, yl: integer;
     3944  begin
     3945    xl := nx * xxt + xxt;
     3946    yl := ny * yyt + yyt * 2;
     3947    xs := (x0 - xw) * (xxt * 2) + y0 and 1 * xxt - G.lx * (xxt * 2);
     3948    // |xs+xl/2-MapWidth/2| -> min
     3949    while abs(2 * (xs + G.lx * (xxt * 2)) + xl - MapWidth) <
     3950      abs(2 * xs + xl - MapWidth) do
     3951      inc(xs, G.lx * (xxt * 2));
     3952    ys := (y0 - yw) * yyt - yyt;
     3953    if xs + xl > MapWidth then
     3954      xl := MapWidth - xs;
     3955    if ys + yl > MapHeight then
     3956      yl := MapHeight - ys;
     3957    if (xl <= 0) or (yl <= 0) then
     3958      exit;
     3959    if Options and prPaint <> 0 then
     3960    begin
     3961      if Options and prAutoBounds <> 0 then
     3962        MainMap.SetPaintBounds(xs, ys, xs + xl, ys + yl);
     3963      MainMap.Paint(xs, ys, x0 + G.lx * y0, nx, ny, -1, -1);
     3964    end;
     3965    if Options and prInvalidate <> 0 then
     3966      RectInvalidate(MapOffset + xs, TopBarHeight + ys, MapOffset + xs + xl,
     3967        TopBarHeight + ys + yl)
     3968  end;
     3969
     3970  procedure TMainScreen.PaintLoc(Loc: integer; Radius: integer = 0);
     3971  var
     3972    yLoc, x0: integer;
     3973  begin
     3974    if MapValid then
     3975    begin
     3976      yLoc := (Loc + G.lx * 1024) div G.lx - 1024;
     3977      x0 := (Loc + (yLoc and 1 - 2 * Radius + G.lx * 1024) div 2) mod G.lx;
     3978      offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     3979      ProcessRect(x0, yLoc - 2 * Radius, 4 * Radius + 1, 4 * Radius + 1,
     3980        prPaint or prAutoBounds or prInvalidate);
     3981      Update;
     3982    end
     3983  end;
     3984
     3985  procedure TMainScreen.PaintLocTemp(Loc, Style: integer);
     3986  var
     3987    y0, x0, xMap, yMap: integer;
     3988  begin
     3989    if not MapValid then
     3990      exit;
     3991    Buffer.Canvas.Font.Assign(UniFont[ftSmall]);
     3992    y0 := Loc div G.lx;
     3993    x0 := Loc mod G.lx;
     3994    xMap := (x0 - xw) * (xxt * 2) + y0 and 1 * xxt - G.lx * (xxt * 2);
     3995    // |xMap+xxt-MapWidth/2| -> min
     3996    while abs(2 * (xMap + G.lx * (xxt * 2)) + 2 * xxt - MapWidth) <
     3997      abs(2 * xMap + 2 * xxt - MapWidth) do
     3998      inc(xMap, G.lx * (xxt * 2));
     3999    yMap := (y0 - yw) * yyt - yyt;
     4000    NoMap.SetOutput(Buffer);
     4001    NoMap.SetPaintBounds(0, 0, 2 * xxt, 3 * yyt);
     4002    NoMap.Paint(0, 0, Loc, 1, 1, -1, -1, Style = pltsBlink);
     4003    PaintBufferToScreen(xMap, yMap, 2 * xxt, 3 * yyt);
     4004  end;
     4005
     4006  // paint content of buffer directly to screen instead of offscreen
     4007  // panel protusions are added
     4008  // NoMap must be set to buffer and bounds before
     4009  procedure TMainScreen.PaintBufferToScreen(xMap, yMap, width, height: integer);
     4010  begin
     4011    if xMap + width > MapWidth then
     4012      width := MapWidth - xMap;
     4013    if yMap + height > MapHeight then
     4014      height := MapHeight - yMap;
     4015    if (width <= 0) or (height <= 0) or (width + xMap <= 0) or
     4016      (height + yMap <= 0) then
     4017      exit;
     4018
     4019    NoMap.BitBlt(Panel, -xMap - MapOffset, -yMap + MapHeight - overlap,
     4020      xMidPanel, overlap, 0, 0, SRCCOPY);
     4021    NoMap.BitBlt(Panel, -xMap - MapOffset + xRightPanel,
     4022      -yMap + MapHeight - overlap, Panel.width - xRightPanel, overlap,
     4023      xRightPanel, 0, SRCCOPY);
     4024    if yMap < 0 then
     4025    begin
     4026      if xMap < 0 then
     4027        BitBlt(Canvas.Handle, MapOffset, TopBarHeight, width + xMap,
     4028          height + yMap, Buffer.Canvas.Handle, -xMap, -yMap, SRCCOPY)
     4029      else
     4030        BitBlt(Canvas.Handle, xMap + MapOffset, TopBarHeight, width,
     4031          height + yMap, Buffer.Canvas.Handle, 0, -yMap, SRCCOPY)
     4032    end
     4033    else
     4034    begin
     4035      if xMap < 0 then
     4036        BitBlt(Canvas.Handle, MapOffset, TopBarHeight + yMap, width + xMap,
     4037          height, Buffer.Canvas.Handle, -xMap, 0, SRCCOPY)
     4038      else
     4039        BitBlt(Canvas.Handle, xMap + MapOffset, TopBarHeight + yMap, width,
     4040          height, Buffer.Canvas.Handle, 0, 0, SRCCOPY);
     4041    end
     4042  end;
     4043
     4044  procedure TMainScreen.PaintLoc_BeforeMove(FromLoc: integer);
     4045  var
     4046    yLoc, x0: integer;
     4047  begin
     4048    if MapValid then
     4049    begin
     4050      yLoc := (FromLoc + G.lx * 1024) div G.lx - 1024;
     4051      x0 := (FromLoc + (yLoc and 1 + G.lx * 1024) div 2) mod G.lx;
     4052      offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     4053      ProcessRect(x0, yLoc, 1, 1, prPaint or prAutoBounds);
     4054    end
     4055  end;
     4056
     4057  procedure TMainScreen.PaintDestination;
     4058  var
     4059    Destination: integer;
     4060  begin
     4061    if (UnFocus >= 0) and (MyUn[UnFocus].Status and usGoto <> 0) then
     4062    begin
     4063      Destination := MyUn[UnFocus].Status shr 16;
     4064      if (Destination <> $7FFF) and (Destination <> MyUn[UnFocus].Loc) then
     4065        PaintLocTemp(Destination, pltsBlink);
     4066    end;
     4067  end;
     4068
     4069  procedure TMainScreen.MiniPaint;
     4070  type
     4071    TLine = array [0 .. 99999999, 0 .. 2] of Byte;
     4072  var
     4073    uix, cix, x, y, Loc, i, hw, xm, cm, cmPolOcean, cmPolNone: integer;
     4074    PrevMiniLine, MiniLine: ^TLine;
     4075  begin
     4076    cmPolOcean := GrExt[HGrSystem].Data.Canvas.Pixels[101, 67];
     4077    cmPolNone := GrExt[HGrSystem].Data.Canvas.Pixels[102, 67];
     4078    hw := MapWidth div (xxt * 2);
     4079    with Mini.Canvas do
     4080    begin
     4081      Brush.Color := $000000;
     4082      FillRect(Rect(0, 0, Mini.width, Mini.height));
     4083    end;
     4084    MiniLine := nil;
     4085    for y := 0 to G.ly - 1 do
     4086    begin
     4087      PrevMiniLine := MiniLine;
     4088      MiniLine := Mini.ScanLine[y];
     4089      for x := 0 to G.lx - 1 do
     4090        if MyMap[x + G.lx * y] and fTerrain <> fUNKNOWN then
     4091        begin
     4092          Loc := x + G.lx * y;
     4093          for i := 0 to 1 do
     4094          begin
     4095            xm := ((x - xwMini) * 2 + i + y and 1 - hw + G.lx * 5)
     4096              mod (G.lx * 2);
     4097            cm := MiniColors[MyMap[Loc] and fTerrain, i];
     4098            if ClientMode = cEditMap then
     4099            begin
     4100              if MyMap[Loc] and (fPrefStartPos or fStartPos) <> 0 then
     4101                cm := $FFFFFF;
     4102            end
     4103            else if MyMap[Loc] and fCity <> 0 then
     4104            begin
     4105              cix := MyRO.nCity - 1;
     4106              while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
     4107                dec(cix);
     4108              if cix >= 0 then
     4109                cm := Tribe[me].Color
     4110              else
     4111              begin
     4112                cix := MyRO.nEnemyCity - 1;
     4113                while (cix >= 0) and (MyRO.EnemyCity[cix].Loc <> Loc) do
     4114                  dec(cix);
     4115                if cix >= 0 then
     4116                  cm := Tribe[MyRO.EnemyCity[cix].Owner].Color
     4117              end;
     4118              cm := $808080 or cm shr 1; { increase brightness }
     4119              if PrevMiniLine <> nil then
     4120              begin // 2x2 city dot covers two scanlines
     4121                PrevMiniLine[xm, 0] := cm shr 16;
     4122                PrevMiniLine[xm, 1] := cm shr 8 and $FF;
     4123                PrevMiniLine[xm, 2] := cm and $FF;
     4124              end
     4125            end
     4126            else if (i = 0) and (MyMap[Loc] and fUnit <> 0) then
     4127            begin
     4128              uix := MyRO.nUn - 1;
     4129              while (uix >= 0) and (MyUn[uix].Loc <> Loc) do
     4130                dec(uix);
     4131              if uix >= 0 then
     4132                cm := Tribe[me].Color
     4133              else
     4134              begin
     4135                uix := MyRO.nEnemyUn - 1;
     4136                while (uix >= 0) and (MyRO.EnemyUn[uix].Loc <> Loc) do
     4137                  dec(uix);
     4138                if uix >= 0 then
     4139                  cm := Tribe[MyRO.EnemyUn[uix].Owner].Color
     4140              end;
     4141              cm := $808080 or cm shr 1; { increase brightness }
     4142            end
     4143            else if MapOptionChecked and (1 shl moPolitical) <> 0 then
     4144            begin
     4145              if MyMap[Loc] and fTerrain < fGrass then
     4146                cm := cmPolOcean
     4147              else if MyRO.Territory[Loc] < 0 then
     4148                cm := cmPolNone
     4149              else
     4150                cm := Tribe[MyRO.Territory[Loc]].Color;
     4151            end;
     4152            MiniLine[xm, 0] := cm shr 16;
     4153            MiniLine[xm, 1] := cm shr 8 and $FF;
     4154            MiniLine[xm, 2] := cm and $FF;
     4155          end;
     4156        end
     4157    end;
     4158  end;
     4159
     4160  procedure TMainScreen.MainOffscreenPaint;
     4161  var
     4162    ProcessOptions: integer;
     4163    rec: TRect;
     4164    DoInvalidate: boolean;
     4165  begin
     4166    if me < 0 then
     4167      with offscreen.Canvas do
     4168      begin
     4169        Brush.Color := $000000;
     4170        FillRect(Rect(0, 0, MapWidth, MapHeight));
     4171        Brush.Style := bsClear;
     4172        OffscreenUser := self;
     4173        exit
     4174      end;
     4175
     4176    MainMap.SetPaintBounds(0, 0, MapWidth, MapHeight);
     4177    if OffscreenUser <> self then
     4178    begin
     4179      if OffscreenUser <> nil then
     4180        OffscreenUser.Update;
     4181      // complete working with old owner to prevent rebound
     4182      if MapValid and (xwd = xw) and (ywd = yw) then
     4183        MainMap.SetPaintBounds(0, 0, UsedOffscreenWidth, UsedOffscreenHeight);
     4184      MapValid := false;
     4185      OffscreenUser := self;
     4186    end;
     4187
     4188    if xw - xwd > G.lx div 2 then
     4189      xwd := xwd + G.lx
     4190    else if xwd - xw > G.lx div 2 then
     4191      xwd := xwd - G.lx;
     4192    if not MapValid or (xw - xwd > MapWidth div (xxt * 2)) or
     4193      (xwd - xw > MapWidth div (xxt * 2)) or (yw - ywd > MapHeight div yyt) or
     4194      (ywd - yw > MapHeight div yyt) then
     4195    begin
     4196      offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     4197      ProcessRect(xw, yw, MapWidth div xxt, MapHeight div yyt,
     4198        prPaint or prInvalidate)
     4199    end
     4200    else
     4201    begin
     4202      if (xwd = xw) and (ywd = yw) then
     4203        exit; { map window not moved }
     4204      offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     4205      rec := Rect(0, 0, MapWidth, MapHeight);
     4206      ScrollDC(offscreen.Canvas.Handle, (xwd - xw) * (xxt * 2),
     4207        (ywd - yw) * yyt, rec, rec, 0, nil);
     4208      for DoInvalidate := false to FastScrolling do
     4209      begin
     4210        if DoInvalidate then
     4211        begin
     4212          rec.Bottom := MapHeight - overlap;
     4213          ScrollDC(Canvas.Handle, (xwd - xw) * (xxt * 2), (ywd - yw) * yyt, rec,
     4214            rec, 0, nil);
     4215          ProcessOptions := prInvalidate;
     4216        end
     4217        else
     4218          ProcessOptions := prPaint or prAutoBounds;
     4219        if yw < ywd then
     4220        begin
     4221          ProcessRect(xw, yw, MapWidth div xxt, ywd - yw - 1, ProcessOptions);
     4222          if xw < xwd then
     4223            ProcessRect(xw, ywd, (xwd - xw) * 2 - 1, MapHeight div yyt - ywd +
     4224              yw, ProcessOptions)
     4225          else if xw > xwd then
     4226            ProcessRect((xwd + MapWidth div (xxt * 2)) mod G.lx, ywd,
     4227              (xw - xwd) * 2 + 1, MapHeight div yyt - ywd + yw, ProcessOptions)
     4228        end
     4229        else if yw > ywd then
     4230        begin
     4231          if DoInvalidate then
     4232            RectInvalidate(MapOffset, TopBarHeight + MapHeight - overlap -
     4233              (yw - ywd) * yyt, MapOffset + MapWidth, TopBarHeight + MapHeight
     4234              - overlap)
     4235          else
     4236            ProcessRect(xw, (ywd + MapHeight div (yyt * 2) * 2),
     4237              MapWidth div xxt, yw - ywd + 1, ProcessOptions);
     4238          if xw < xwd then
     4239            ProcessRect(xw, yw, (xwd - xw) * 2 - 1, MapHeight div yyt - yw + ywd
     4240              - 2, ProcessOptions)
     4241          else if xw > xwd then
     4242            ProcessRect((xwd + MapWidth div (xxt * 2)) mod G.lx, yw,
     4243              (xw - xwd) * 2 + 1, MapHeight div yyt - yw + ywd - 2,
     4244              ProcessOptions)
     4245        end
     4246        else if xw < xwd then
     4247          ProcessRect(xw, yw, (xwd - xw) * 2 - 1, MapHeight div yyt,
     4248            ProcessOptions)
     4249        else if xw > xwd then
     4250          ProcessRect((xwd + MapWidth div (xxt * 2)) mod G.lx, yw,
     4251            (xw - xwd) * 2 + 1, MapHeight div yyt, ProcessOptions);
     4252      end;
     4253      if not FastScrolling then
     4254        RectInvalidate(MapOffset, TopBarHeight, MapOffset + MapWidth,
     4255          TopBarHeight + MapHeight - overlap);
     4256      RectInvalidate(xMidPanel, TopBarHeight + MapHeight - overlap, xRightPanel,
     4257        TopBarHeight + MapHeight)
     4258    end;
     4259    // if (xwd<>xw) or (ywd<>yw) then
     4260    // Server(sChangeSuperView,me,yw*G.lx+xw,nil^); // for synchronizing client side viewer, not used currently
     4261    xwd := xw;
     4262    ywd := yw;
     4263    MapValid := true;
     4264  end;
     4265
     4266  procedure TMainScreen.PaintAll;
     4267  begin
     4268    MainOffscreenPaint;
     4269    xwMini := xw;
     4270    ywMini := yw;
     4271    MiniPaint;
     4272    PanelPaint;
     4273  end;
     4274
     4275  procedure TMainScreen.PaintAllMaps;
     4276  begin
     4277    MainOffscreenPaint;
     4278    xwMini := xw;
     4279    ywMini := yw;
     4280    MiniPaint;
     4281    CopyMiniToPanel;
     4282    RectInvalidate(xMini + 2, TopBarHeight + MapHeight - overlap + yMini + 2,
     4283      xMini + 2 + G.lx * 2, TopBarHeight + MapHeight - overlap + yMini +
     4284      2 + G.ly);
     4285  end;
     4286
     4287  procedure TMainScreen.CopyMiniToPanel;
     4288  begin
     4289    BitBlt(Panel.Canvas.Handle, xMini + 2, yMini + 2, G.lx * 2, G.ly,
     4290      Mini.Canvas.Handle, 0, 0, SRCCOPY);
     4291    if MarkCityLoc >= 0 then
     4292      Sprite(Panel, HGrSystem,
     4293        xMini - 2 + (4 * G.lx + 2 * (MarkCityLoc mod G.lx) +
     4294        (G.lx - MapWidth div (xxt * 2)) - 2 * xwd) mod (2 * G.lx) +
     4295        MarkCityLoc div G.lx and 1, yMini - 3 + MarkCityLoc div G.lx, 10,
     4296        10, 77, 47)
     4297    else if ywmax <= 0 then
     4298      Frame(Panel.Canvas, xMini + 2 + G.lx - MapWidth div (xxt * 2), yMini + 2,
     4299        xMini + 1 + G.lx + MapWidth div (xxt * 2), yMini + 2 + G.ly - 1,
     4300        MainTexture.clMark, MainTexture.clMark)
     4301    else
     4302      Frame(Panel.Canvas, xMini + 2 + G.lx - MapWidth div (xxt * 2),
     4303        yMini + 2 + yw, xMini + 1 + G.lx + MapWidth div (xxt * 2),
     4304        yMini + yw + MapHeight div yyt, MainTexture.clMark, MainTexture.clMark);
     4305  end;
     4306
     4307  procedure TMainScreen.PanelPaint;
     4308
     4309    function MovementToString(var Un: TUn): string;
     4310    begin
     4311      result := ScreenTools.MovementToString(Un.Movement);
     4312      if Un.Master >= 0 then
     4313        result := '(' + result + ')'
     4314      else if (MyModel[Un.mix].Domain = dAir) and
     4315        (MyModel[Un.mix].Kind <> mkSpecial_Glider) then
     4316        result := Format('%s(%d)', [result, Un.Fuel]);
     4317    end;
     4318
     4319  var
     4320    i, uix, uixDefender, x, xSrc, ySrc, xSrcBase, ySrcBase, CostFactor, Count,
     4321      mixShow, xTreasurySection, xResearchSection, JobFocus, TrueMoney,
     4322      TrueResearch: integer;
     4323    Tile: Cardinal;
     4324    s: string;
     4325    unx: TUn;
     4326    UnitInfo: TUnitInfo;
     4327    JobProgressData: TJobProgressData;
     4328    Prio: boolean;
     4329  begin
     4330    with Panel.Canvas do
     4331    begin
     4332      Fill(Panel.Canvas, 0, 3, xMidPanel + 7 - 10, PanelHeight - 3,
     4333        wMainTexture - (xMidPanel + 7 - 10), hMainTexture - PanelHeight);
     4334      Fill(Panel.Canvas, xRightPanel + 10 - 7, 3, Panel.width - xRightPanel - 10
     4335        + 7, PanelHeight - 3, -(xRightPanel + 10 - 7),
     4336        hMainTexture - PanelHeight);
     4337      FillLarge(Panel.Canvas, xMidPanel - 2, PanelHeight - MidPanelHeight,
     4338        xRightPanel + 2, PanelHeight, ClientWidth div 2);
     4339
     4340      Brush.Style := bsClear;
     4341      Pen.Color := $000000;
     4342      MoveTo(0, 0);
     4343      LineTo(xMidPanel + 7 - 8, 0);
     4344      LineTo(xMidPanel + 7 - 8, PanelHeight - MidPanelHeight);
     4345      LineTo(xRightPanel, PanelHeight - MidPanelHeight);
     4346      LineTo(xRightPanel, 0);
     4347      LineTo(ClientWidth, 0);
     4348      Pen.Color := MainTexture.clBevelLight;
     4349      MoveTo(xMidPanel + 7 - 9, PanelHeight - MidPanelHeight + 2);
     4350      LineTo(xRightPanel + 10 - 8, PanelHeight - MidPanelHeight + 2);
     4351      Pen.Color := MainTexture.clBevelLight;
     4352      MoveTo(0, 1);
     4353      LineTo(xMidPanel + 7 - 9, 1);
     4354      Pen.Color := MainTexture.clBevelShade;
     4355      LineTo(xMidPanel + 7 - 9, PanelHeight - MidPanelHeight + 1);
     4356      Pen.Color := MainTexture.clBevelLight;
     4357      LineTo(xRightPanel + 10 - 9, PanelHeight - MidPanelHeight + 1);
     4358      Pen.Color := MainTexture.clBevelLight;
     4359      LineTo(xRightPanel + 10 - 9, 1);
     4360      LineTo(ClientWidth, 1);
     4361      MoveTo(ClientWidth, 2);
     4362      LineTo(xRightPanel + 10 - 8, 2);
     4363      LineTo(xRightPanel + 10 - 8, PanelHeight);
     4364      MoveTo(0, 2);
     4365      LineTo(xMidPanel + 7 - 10, 2);
     4366      Pen.Color := MainTexture.clBevelShade;
     4367      LineTo(xMidPanel + 7 - 10, PanelHeight);
     4368      Corner(Panel.Canvas, xMidPanel + 7 - 16, 1, 1, MainTexture);
     4369      Corner(Panel.Canvas, xRightPanel + 10 - 9, 1, 0, MainTexture);
     4370      if ClientMode <> cEditMap then
     4371      begin
     4372        if supervising then
     4373        begin
     4374          Frame(Panel.Canvas, ClientWidth - xPalace - 1, yPalace - 1,
     4375            ClientWidth - xPalace + xSizeBig, yPalace + ySizeBig,
     4376            $B0B0B0, $FFFFFF);
     4377          RFrame(Panel.Canvas, ClientWidth - xPalace - 2, yPalace - 2,
     4378            ClientWidth - xPalace + xSizeBig + 1, yPalace + ySizeBig + 1,
     4379            $FFFFFF, $B0B0B0);
     4380          BitBlt(Panel.Canvas.Handle, ClientWidth - xPalace, yPalace, xSizeBig,
     4381            ySizeBig, GrExt[HGrSystem2].Data.Canvas.Handle, 70, 123, SRCCOPY);
     4382        end
     4383        else if MyRO.NatBuilt[imPalace] > 0 then
     4384          ImpImage(Panel.Canvas, ClientWidth - xPalace, yPalace, imPalace, -1,
     4385            GameMode <> cMovie
     4386            { (GameMode<>cMovie) and (MyRO.Government<>gAnarchy) } )
     4387        else
     4388          ImpImage(Panel.Canvas, ClientWidth - xPalace, yPalace, 21, -1,
     4389            GameMode <> cMovie
     4390            { (GameMode<>cMovie) and (MyRO.Government<>gAnarchy) } );
     4391      end;
     4392
     4393      if GameMode = cMovie then
     4394        Frame(Panel.Canvas, xMini + 1, yMini + 1, xMini + 2 + G.lx * 2,
     4395          yMini + 2 + G.ly, $000000, $000000)
     4396      else
     4397      begin
     4398        Frame(Panel.Canvas, xMini + 1, yMini + 1, xMini + 2 + G.lx * 2,
     4399          yMini + 2 + G.ly, $B0B0B0, $FFFFFF);
     4400        RFrame(Panel.Canvas, xMini, yMini, xMini + 3 + G.lx * 2,
     4401          yMini + 3 + G.ly, $FFFFFF, $B0B0B0);
     4402      end;
     4403      CopyMiniToPanel;
     4404      if ClientMode <> cEditMap then // MapBtn icons
     4405        for i := 0 to 5 do
     4406          if i <> 3 then
     4407            Dump(Panel, HGrSystem, xMini + G.lx - 42 + 16 * i, PanelHeight - 26,
     4408              8, 8, 121 + i * 9, 61);
     4409
     4410      if ClientMode = cEditMap then
     4411      begin
     4412        for i := 0 to TrRow - 1 do
     4413          trix[i] := -1;
     4414        Count := 0;
     4415        for i := 0 to nBrushTypes - 1 do
     4416        begin // display terrain types
     4417          if (Count >= TrRow * sb.si.npos) and (Count < TrRow * (sb.si.npos + 1))
     4418          then
     4419          begin
     4420            trix[Count - TrRow * sb.si.npos] := BrushTypes[i];
     4421            x := (Count - TrRow * sb.si.npos) * TrPitch;
     4422            xSrcBase := -1;
     4423            case BrushTypes[i] of
     4424              0 .. 8:
     4425                begin
     4426                  xSrc := BrushTypes[i];
     4427                  ySrc := 0
     4428                end;
     4429              9 .. 30:
     4430                begin
     4431                  xSrcBase := 2;
     4432                  ySrcBase := 2;
     4433                  xSrc := 0;
     4434                  ySrc := 2 * integer(BrushTypes[i]) - 15
     4435                end;
     4436              fRiver:
     4437                begin
     4438                  xSrc := 7;
     4439                  ySrc := 14
     4440                end;
     4441              fRoad:
     4442                begin
     4443                  xSrc := 0;
     4444                  ySrc := 9
     4445                end;
     4446              fRR:
     4447                begin
     4448                  xSrc := 0;
     4449                  ySrc := 10
     4450                end;
     4451              fCanal:
     4452                begin
     4453                  xSrc := 0;
     4454                  ySrc := 11
     4455                end;
     4456              fPoll:
     4457                begin
     4458                  xSrc := 6;
     4459                  ySrc := 12
     4460                end;
     4461              fDeadLands, fDeadLands or fCobalt, fDeadLands or fUranium,
     4462                fDeadLands or fMercury:
     4463                begin
     4464                  xSrcBase := 6;
     4465                  ySrcBase := 2;
     4466                  xSrc := 8;
     4467                  ySrc := 12 + BrushTypes[i] shr 25;
     4468                end;
     4469              tiIrrigation, tiFarm, tiMine, tiBase:
     4470                begin
     4471                  xSrc := BrushTypes[i] shr 12 - 1;
     4472                  ySrc := 12
     4473                end;
     4474              tiFort:
     4475                begin
     4476                  xSrc := 3;
     4477                  ySrc := 12;
     4478                  xSrcBase := 7;
     4479                  ySrcBase := 12
     4480                end;
     4481              fPrefStartPos:
     4482                begin
     4483                  xSrc := 0;
     4484                  ySrc := 1
     4485                end;
     4486              fStartPos:
     4487                begin
     4488                  xSrc := 0;
     4489                  ySrc := 2
     4490                end;
     4491            end;
     4492            if xSrcBase >= 0 then
     4493              Sprite(Panel, HGrTerrain, xTroop + 2 + x, yTroop + 9 - yyt,
     4494                xxt * 2, yyt * 3, 1 + xSrcBase * (xxt * 2 + 1),
     4495                1 + ySrcBase * (yyt * 3 + 1));
     4496            Sprite(Panel, HGrTerrain, xTroop + 2 + x, yTroop + 9 - yyt, xxt * 2,
     4497              yyt * 3, 1 + xSrc * (xxt * 2 + 1), 1 + ySrc * (yyt * 3 + 1));
     4498            if BrushTypes[i] = BrushType then
     4499            begin
     4500              Frame(Panel.Canvas, xTroop + 2 + x, yTroop + 7 - yyt div 2,
     4501                xTroop + 2 * xxt + x, yTroop + 2 * yyt + 11, $000000, $000000);
     4502              Frame(Panel.Canvas, xTroop + 1 + x, yTroop + 6 - yyt div 2,
     4503                xTroop + 2 * xxt - 1 + x, yTroop + 2 * yyt + 10,
     4504                MainTexture.clMark, MainTexture.clMark);
     4505            end
     4506          end;
     4507          inc(Count)
     4508        end;
     4509        case BrushType of
     4510          fDesert, fPrairie, fTundra, fArctic, fSwamp, fHills, fMountains:
     4511            s := Phrases.Lookup('TERRAIN', BrushType);
     4512          fShore:
     4513            s := Format(Phrases.Lookup('TWOTERRAINS'),
     4514              [Phrases.Lookup('TERRAIN', fOcean), Phrases.Lookup('TERRAIN',
     4515              fShore)]);
     4516          fGrass:
     4517            s := Format(Phrases.Lookup('TWOTERRAINS'),
     4518              [Phrases.Lookup('TERRAIN', fGrass), Phrases.Lookup('TERRAIN',
     4519              fGrass + 12)]);
     4520          fForest:
     4521            s := Format(Phrases.Lookup('TWOTERRAINS'),
     4522              [Phrases.Lookup('TERRAIN', fForest), Phrases.Lookup('TERRAIN',
     4523              fJungle)]);
     4524          fRiver:
     4525            s := Phrases.Lookup('RIVER');
     4526          fDeadLands, fDeadLands or fCobalt, fDeadLands or fUranium,
     4527            fDeadLands or fMercury:
     4528            s := Phrases.Lookup('TERRAIN', 3 * 12 + BrushType shr 25);
     4529          fPrefStartPos:
     4530            s := Phrases.Lookup('MAP_PREFSTART');
     4531          fStartPos:
     4532            s := Phrases.Lookup('MAP_START');
     4533          fPoll:
     4534            s := Phrases.Lookup('POLL');
     4535        else // terrain improvements
     4536          begin
     4537            case BrushType of
     4538              fRoad:
     4539                i := 1;
     4540              fRR:
     4541                i := 2;
     4542              tiIrrigation:
     4543                i := 4;
     4544              tiFarm:
     4545                i := 5;
     4546              tiMine:
     4547                i := 7;
     4548              fCanal:
     4549                i := 8;
     4550              tiFort:
     4551                i := 10;
     4552              tiBase:
     4553                i := 12;
     4554            end;
     4555            s := Phrases.Lookup('JOBRESULT', i);
     4556          end
     4557        end;
     4558        LoweredTextOut(Panel.Canvas, -1, MainTexture, xTroop + 1,
     4559          PanelHeight - 19, s);
     4560      end
     4561      else if TroopLoc >= 0 then
     4562      begin
     4563        Brush.Style := bsClear;
     4564        if UnFocus >= 0 then
     4565          with MyUn[UnFocus], MyModel[mix] do
     4566          begin { display info about selected unit }
     4567            if Job = jCity then
     4568              mixShow := -1 // building site
     4569            else
     4570              mixShow := mix;
     4571            with Tribe[me].ModelPicture[mixShow] do
     4572            begin
     4573              Sprite(Panel, HGr, xMidPanel + 7 + 12, yTroop + 1, 64, 48,
     4574                pix mod 10 * 65 + 1, pix div 10 * 49 + 1);
     4575              if MyUn[UnFocus].Flags and unFortified <> 0 then
     4576                Sprite(Panel, HGrStdUnits, xMidPanel + 7 + 12, yTroop + 1,
     4577                  xxu * 2, yyu * 2, 1 + 6 * (xxu * 2 + 1), 1);
     4578            end;
     4579
     4580            MakeBlue(Panel, xMidPanel + 7 + 12 + 10, yTroop - 13, 44, 12);
     4581            s := MovementToString(MyUn[UnFocus]);
     4582            RisedTextOut(Panel.Canvas, xMidPanel + 7 + 12 + 32 -
     4583              BiColorTextWidth(Panel.Canvas, s) div 2, yTroop - 16, s);
     4584
     4585            s := IntToStr(Health) + '%';
     4586            LightGradient(Panel.Canvas, xMidPanel + 7 + 12 + 7,
     4587              PanelHeight - 22, (Health + 1) div 2,
     4588              (ColorOfHealth(Health) and $FEFEFE shr 2) * 3);
     4589            if Health < 100 then
     4590              LightGradient(Panel.Canvas, xMidPanel + 7 + 12 + 7 + (Health + 1)
     4591                div 2, PanelHeight - 22, 50 - (Health + 1) div 2, $000000);
     4592            RisedTextOut(Panel.Canvas, xMidPanel + 7 + 12 + 32 -
     4593              BiColorTextWidth(Panel.Canvas, s) div 2, PanelHeight - 23, s);
     4594
     4595            FrameImage(Panel.Canvas, GrExt[HGrSystem].Data,
     4596              xMidPanel + 7 + xUnitText, yTroop + 15, 12, 14,
     4597              121 + Exp div ExpCost * 13, 28);
     4598            if Job = jCity then
     4599              s := Tribe[me].ModelName[-1]
     4600            else
     4601              s := Tribe[me].ModelName[mix];
     4602            if Home >= 0 then
     4603            begin
     4604              LoweredTextOut(Panel.Canvas, -1, MainTexture,
     4605                xMidPanel + 7 + xUnitText + 18, yTroop + 5, s);
     4606              LoweredTextOut(Panel.Canvas, -1, MainTexture,
     4607                xMidPanel + 7 + xUnitText + 18, yTroop + 21,
     4608                '(' + CityName(MyCity[Home].ID) + ')');
     4609            end
     4610            else
     4611              LoweredTextOut(Panel.Canvas, -1, MainTexture,
     4612                xMidPanel + 7 + xUnitText + 18, yTroop + 13, s);
     4613          end;
     4614
     4615        if (UnFocus >= 0) and (MyUn[UnFocus].Loc <> TroopLoc) then
     4616        begin // divide panel
     4617          if SmallScreen and not supervising then
     4618            x := xTroop - 8
     4619          else
     4620            x := xTroop - 152;
     4621          Pen.Color := MainTexture.clBevelShade;
     4622          MoveTo(x - 1, PanelHeight - MidPanelHeight + 2);
     4623          LineTo(x - 1, PanelHeight);
     4624          Pen.Color := MainTexture.clBevelLight;
     4625          MoveTo(x, PanelHeight - MidPanelHeight + 2);
     4626          LineTo(x, PanelHeight);
     4627        end;
     4628
     4629        for i := 0 to 23 do
     4630          trix[i] := -1;
     4631        if MyMap[TroopLoc] and fUnit <> 0 then
     4632        begin
     4633          if MyMap[TroopLoc] and fOwned <> 0 then
     4634          begin
     4635            if (TrCnt > 1) or (UnFocus < 0) or (MyUn[UnFocus].Loc <> TroopLoc)
     4636            then
     4637            begin
     4638              LoweredTextOut(Panel.Canvas, -1, MainTexture, xTroop + 10,
     4639                PanelHeight - 24, Phrases.Lookup('PRESENT'));
     4640              Server(sGetDefender, me, TroopLoc, uixDefender);
     4641              Count := 0;
     4642              for Prio := true downto false do
     4643                for uix := 0 to MyRO.nUn - 1 do
     4644                  if (uix = uixDefender) = Prio then
     4645                  begin // display own units
     4646                    unx := MyUn[uix];
     4647                    if unx.Loc = TroopLoc then
     4648                    begin
     4649                      if (Count >= TrRow * sb.si.npos) and
     4650                        (Count < TrRow * (sb.si.npos + 1)) then
     4651                      begin
     4652                        trix[Count - TrRow * sb.si.npos] := uix;
     4653                        MakeUnitInfo(me, unx, UnitInfo);
     4654                        x := (Count - TrRow * sb.si.npos) * TrPitch;
     4655                        if uix = UnFocus then
     4656                        begin
     4657                          Frame(Panel.Canvas, xTroop + 4 + x, yTroop + 3,
     4658                            xTroop + 64 + x, yTroop + 47, $000000, $000000);
     4659                          Frame(Panel.Canvas, xTroop + 3 + x, yTroop + 2,
     4660                            xTroop + 63 + x, yTroop + 46, MainTexture.clMark,
     4661                            MainTexture.clMark);
     4662                        end
     4663                        else if (unx.Master >= 0) and (unx.Master = UnFocus)
     4664                        then
     4665                        begin
     4666                          CFrame(Panel.Canvas, xTroop + 4 + x, yTroop + 3,
     4667                            xTroop + 64 + x, yTroop + 47, 8, $000000);
     4668                          CFrame(Panel.Canvas, xTroop + 3 + x, yTroop + 2,
     4669                            xTroop + 63 + x, yTroop + 46, 8,
     4670                            MainTexture.clMark);
     4671                        end;
     4672                        NoMap.SetOutput(Panel);
     4673                        NoMap.PaintUnit(xTroop + 2 + x, yTroop + 1, UnitInfo,
     4674                          unx.Status);
     4675                        if (ClientMode < scContact) and
     4676                          ((unx.Job > jNone) or
     4677                          (unx.Status and (usStay or usRecover or usGoto) <> 0))
     4678                        then
     4679                          Sprite(Panel, HGrSystem, xTroop + 2 + 60 - 20 + x,
     4680                            yTroop + 35, 20, 20, 81, 25);
     4681
     4682                        if not supervising then
     4683                        begin
     4684                          MakeBlue(Panel, xTroop + 2 + 10 + x,
     4685                            yTroop - 13, 44, 12);
     4686                          s := MovementToString(unx);
     4687                          RisedTextOut(Panel.Canvas,
     4688                            xTroop + x + 34 - BiColorTextWidth(Panel.Canvas, s)
     4689                            div 2, yTroop - 16, s);
     4690                        end
     4691                      end;
     4692                      inc(Count)
     4693                    end;
     4694                  end; // for uix:=0 to MyRO.nUn-1
     4695              assert(Count = TrCnt);
     4696            end
     4697          end
     4698          else
     4699          begin
     4700            LoweredTextOut(Panel.Canvas, -1, MainTexture, xTroop + 8,
     4701              PanelHeight - 24, Phrases.Lookup('PRESENT'));
     4702            Server(sGetUnits, me, TroopLoc, Count);
     4703            for i := 0 to Count - 1 do
     4704              if (i >= TrRow * sb.si.npos) and (i < TrRow * (sb.si.npos + 1))
     4705              then
     4706              begin // display enemy units
     4707                trix[i - TrRow * sb.si.npos] := i;
     4708                x := (i - TrRow * sb.si.npos) * TrPitch;
     4709                NoMap.SetOutput(Panel);
     4710                NoMap.PaintUnit(xTroop + 2 + x, yTroop + 1,
     4711                  MyRO.EnemyUn[MyRO.nEnemyUn + i], 0);
     4712              end;
     4713          end;
     4714        end;
     4715        if not SmallScreen or supervising then
     4716        begin // show terrain and improvements
     4717          PaintZoomedTile(Panel, xTerrain - xxt * 2, 110 - yyt * 3, TroopLoc);
     4718          if (UnFocus >= 0) and (MyUn[UnFocus].Job <> jNone) then
     4719          begin
     4720            JobFocus := MyUn[UnFocus].Job;
     4721            Server(sGetJobProgress, me, MyUn[UnFocus].Loc, JobProgressData);
     4722            MakeBlue(Panel, xTerrain - 72, 148 - 17, 144, 31);
     4723            PaintRelativeProgressBar(Panel.Canvas, 3, xTerrain - 68, 148 + 3,
     4724              63, JobProgressData[JobFocus].Done,
     4725              JobProgressData[JobFocus].NextTurnPlus,
     4726              JobProgressData[JobFocus].Required, true, MainTexture);
     4727            s := Format('%s/%s',
     4728              [ScreenTools.MovementToString(JobProgressData[JobFocus].Done),
     4729              ScreenTools.MovementToString(JobProgressData[JobFocus]
     4730              .Required)]);
     4731            RisedTextOut(Panel.Canvas, xTerrain + 6, 148 - 3, s);
     4732            Tile := MyMap[MyUn[UnFocus].Loc];
     4733            if (JobFocus = jRoad) and (Tile and fRiver <> 0) then
     4734              JobFocus := nJob + 0
     4735            else if (JobFocus = jRR) and (Tile and fRiver <> 0) then
     4736              JobFocus := nJob + 1
     4737            else if JobFocus = jClear then
     4738            begin
     4739              if Tile and fTerrain = fForest then
     4740                JobFocus := nJob + 2
     4741              else if Tile and fTerrain = fDesert then
     4742                JobFocus := nJob + 3
     4743              else
     4744                JobFocus := nJob + 4
     4745            end;
     4746            s := Phrases.Lookup('JOBRESULT', JobFocus);
     4747            RisedTextOut(Panel.Canvas, xTerrain - BiColorTextWidth(Panel.Canvas,
     4748              s) div 2, 148 - 19, s);
     4749          end;
     4750          if MyMap[TroopLoc] and (fTerrain or fSpecial) = fGrass or fSpecial1
     4751          then
     4752            s := Phrases.Lookup('TERRAIN', fGrass + 12)
     4753          else if MyMap[TroopLoc] and fDeadLands <> 0 then
     4754            s := Phrases.Lookup('TERRAIN', 3 * 12)
     4755          else if (MyMap[TroopLoc] and fTerrain = fForest) and
     4756            IsJungle(TroopLoc div G.lx) then
     4757            s := Phrases.Lookup('TERRAIN', fJungle)
     4758          else
     4759            s := Phrases.Lookup('TERRAIN', MyMap[TroopLoc] and fTerrain);
     4760          RisedTextOut(Panel.Canvas, xTerrain - BiColorTextWidth(Panel.Canvas,
     4761            s) div 2, 99, s);
     4762        end;
     4763
     4764        if TerrainBtn.Visible then
     4765          with TerrainBtn do
     4766            RFrame(Panel.Canvas, Left - 1, Top - self.ClientHeight +
     4767              (PanelHeight - 1), Left + width, Top + height - self.ClientHeight
     4768              + PanelHeight, MainTexture.clBevelShade, MainTexture.clBevelLight)
     4769      end { if TroopLoc>=0 }
     4770    end;
     4771
     4772    for i := 0 to ControlCount - 1 do
     4773      if Controls[i] is TButtonB then
     4774        with TButtonB(Controls[i]) do
     4775        begin
     4776          if Visible then
     4777          begin
     4778            Dump(Panel, HGrSystem, Left, Top - self.ClientHeight + PanelHeight,
     4779              25, 25, 169, 243);
     4780            Sprite(Panel, HGrSystem, Left, Top - self.ClientHeight +
     4781              PanelHeight, 25, 25, 1 + 26 * ButtonIndex, 337);
     4782            RFrame(Panel.Canvas, Left - 1, Top - self.ClientHeight +
     4783              (PanelHeight - 1), Left + width, Top + height - self.ClientHeight
     4784              + PanelHeight, MainTexture.clBevelShade,
     4785              MainTexture.clBevelLight);
     4786          end;
     4787        end;
     4788
     4789    if ClientMode <> cEditMap then
     4790    begin
     4791      for i := 0 to ControlCount - 1 do
     4792        if Controls[i] is TButtonC then
     4793          with TButtonC(Controls[i]) do
     4794          begin
     4795            Dump(Panel, HGrSystem, Left, Top - self.ClientHeight + PanelHeight,
     4796              12, 12, 169, 178 + 13 * ButtonIndex);
     4797            RFrame(Panel.Canvas, Left - 1, Top - self.ClientHeight +
     4798              (PanelHeight - 1), Left + width, Top + height - self.ClientHeight
     4799              + PanelHeight, MainTexture.clBevelShade,
     4800              MainTexture.clBevelLight);
     4801          end
     4802    end;
     4803    EOT.SetBack(Panel.Canvas, EOT.Left, EOT.Top - (ClientHeight - PanelHeight));
     4804    SmartRectInvalidate(0, ClientHeight - PanelHeight, ClientWidth,
     4805      ClientHeight);
     4806
     4807    // topbar
     4808    xTreasurySection := ClientWidth div 2 - 172;
     4809    xResearchSection := ClientWidth div 2;
     4810    // ClientWidth div 2+68 = maximum to right
     4811    FillLarge(TopBar.Canvas, 0, 0, ClientWidth, TopBarHeight - 3,
     4812      ClientWidth div 2);
     4813    with TopBar.Canvas do
     4814    begin
     4815      Pen.Color := $000000;
     4816      MoveTo(0, TopBarHeight - 1);
     4817      LineTo(ClientWidth, TopBarHeight - 1);
     4818      Pen.Color := MainTexture.clBevelShade;
     4819      MoveTo(0, TopBarHeight - 2);
     4820      LineTo(ClientWidth, TopBarHeight - 2);
     4821      MoveTo(0, TopBarHeight - 3);
     4822      LineTo(ClientWidth, TopBarHeight - 3);
     4823      Pen.Color := MainTexture.clBevelLight;
     4824      Frame(TopBar.Canvas, 40, -1, xTreasurySection - 1, TopBarHeight - 7,
     4825        MainTexture.clBevelShade, MainTexture.clBevelLight);
     4826      Frame(TopBar.Canvas, xResearchSection + 332, -1, ClientWidth,
     4827        TopBarHeight - 7, MainTexture.clBevelShade, MainTexture.clBevelLight);
     4828    end;
     4829    if GameMode <> cMovie then
     4830      ImageOp_BCC(TopBar, Templates, 2, 1, 145, 38, 36, 36, $BFBF20, $4040DF);
     4831    if MyRO.nCity > 0 then
     4832    begin
     4833      TrueMoney := MyRO.Money;
     4834      TrueResearch := MyRO.Research;
     4835      if supervising then
     4836      begin // normalize values from after-turn state
     4837        dec(TrueMoney, TaxSum);
     4838        if TrueMoney < 0 then
     4839          TrueMoney := 0; // shouldn't happen
     4840        dec(TrueResearch, ScienceSum);
     4841        if TrueResearch < 0 then
     4842          TrueResearch := 0; // shouldn't happen
     4843      end;
     4844
     4845      // treasury section
     4846      ImageOp_BCC(TopBar, Templates, xTreasurySection + 8, 1, 145, 1, 36, 36,
     4847        $40A040, $4030C0);
     4848      s := IntToStr(TrueMoney);
     4849      LoweredTextOut(TopBar.Canvas, -1, MainTexture, xTreasurySection + 48, 0,
     4850        s + '%c');
     4851      if MyRO.Government <> gAnarchy then
     4852      begin
     4853        ImageOp_BCC(TopBar, Templates, xTreasurySection + 48, 22, 124, 1, 14,
     4854          14, $0000C0, $0080C0);
     4855        if TaxSum >= 0 then
     4856          s := Format(Phrases.Lookup('MONEYGAINPOS'), [TaxSum])
     4857        else
     4858          s := Format(Phrases.Lookup('MONEYGAINNEG'), [TaxSum]);
     4859        LoweredTextOut(TopBar.Canvas, -1, MainTexture,
     4860          xTreasurySection + 48 + 15, 18, s);
     4861      end;
     4862
     4863      // research section
     4864      ImageOp_BCC(TopBar, Templates, xResearchSection + 8, 1, 145, 75, 36, 36,
     4865        $FF0000, $00FFE0);
     4866      if MyData.FarTech <> adNexus then
     4867      begin
     4868        if MyRO.ResearchTech < 0 then
     4869          CostFactor := 2
     4870        else if (MyRO.ResearchTech = adMilitary) or
     4871          (MyRO.Tech[MyRO.ResearchTech] = tsSeen) then
     4872          CostFactor := 1
     4873        else if MyRO.ResearchTech in FutureTech then
     4874          if MyRO.Government = gFuture then
     4875            CostFactor := 4
     4876          else
     4877            CostFactor := 8
     4878        else
     4879          CostFactor := 2;
     4880        Server(sGetTechCost, me, 0, i);
     4881        CostFactor := CostFactor * 22; // length of progress bar
     4882        PaintRelativeProgressBar(TopBar.Canvas, 2, xResearchSection + 48 + 1,
     4883          26, CostFactor, TrueResearch, ScienceSum, i, true, MainTexture);
     4884
     4885        if MyRO.ResearchTech < 0 then
     4886          s := Phrases.Lookup('SCIENCE')
     4887        else if MyRO.ResearchTech = adMilitary then
     4888          s := Phrases.Lookup('INITUNIT')
     4889        else
     4890        begin
     4891          s := Phrases.Lookup('ADVANCES', MyRO.ResearchTech);
     4892          if MyRO.ResearchTech in FutureTech then
     4893            if MyRO.Tech[MyRO.ResearchTech] >= 1 then
     4894              s := s + ' ' + IntToStr(MyRO.Tech[MyRO.ResearchTech] + 1)
     4895            else
     4896              s := s + ' 1';
     4897        end;
     4898        if ScienceSum > 0 then
     4899        begin
     4900          { j:=(i-MyRO.Research-1) div ScienceSum +1;
     4901            if j<1 then j:=1;
     4902            if j>1 then
     4903            s:=Format(Phrases.Lookup('TECHWAIT'),[s,j]); }
     4904          LoweredTextOut(TopBar.Canvas, -1, MainTexture,
     4905            xResearchSection + 48, 0, s);
     4906        end
     4907        else
     4908          LoweredTextOut(TopBar.Canvas, -1, MainTexture,
     4909            xResearchSection + 48, 0, s);
     4910      end
     4911      else
     4912        CostFactor := 0;
     4913      if (MyData.FarTech <> adNexus) and (ScienceSum > 0) then
     4914      begin
     4915        ImageOp_BCC(TopBar, Templates, xResearchSection + 48 + CostFactor + 11,
     4916          22, 124, 1, 14, 14, $0000C0, $0080C0);
     4917        s := Format(Phrases.Lookup('TECHGAIN'), [ScienceSum]);
     4918        LoweredTextOut(TopBar.Canvas, -1, MainTexture, xResearchSection + 48 +
     4919          CostFactor + 26, 18, s);
     4920      end
     4921    end;
     4922    if ClientMode <> cEditMap then
     4923    begin
     4924      TopBar.Canvas.Font.Assign(UniFont[ftCaption]);
     4925      s := TurnToString(MyRO.Turn);
     4926      RisedTextOut(TopBar.Canvas,
     4927        40 + (xTreasurySection - 40 - BiColorTextWidth(TopBar.Canvas, s))
     4928        div 2, 6, s);
     4929      TopBar.Canvas.Font.Assign(UniFont[ftNormal]);
     4930    end;
     4931    RectInvalidate(0, 0, ClientWidth, TopBarHeight);
     4932  end; { PanelPaint }
     4933
     4934  procedure TMainScreen.FocusOnLoc(Loc: integer; Options: integer = 0);
     4935  var
     4936    dx: integer;
     4937    Outside, Changed: boolean;
     4938  begin
     4939    dx := G.lx + 1 - (xw - Loc + G.lx * 1024 + 1) mod G.lx;
     4940    Outside := (dx >= (MapWidth + 1) div (xxt * 2) - 2) or (ywmax > 0) and
     4941      ((yw > 0) and (Loc div G.lx <= yw + 1) or (yw < ywmax) and
     4942      (Loc div G.lx >= yw + (MapHeight - 1) div yyt - 2));
     4943    Changed := true;
     4944    if Outside then
     4945    begin
     4946      Centre(Loc);
     4947      PaintAllMaps
     4948    end
     4949    else if not MapValid then
     4950      PaintAllMaps
     4951    else
     4952      Changed := false;
     4953    if Options and flRepaintPanel <> 0 then
     4954      PanelPaint;
     4955    if Changed and (Options and flImmUpdate <> 0) then
     4956      Update;
     4957  end;
     4958
     4959  procedure TMainScreen.NextUnit(NearLoc: integer; AutoTurn: boolean);
     4960  var
     4961    Dist, TestDist: single;
     4962    i, uix, NewFocus: integer;
     4963    GotoOnly: boolean;
     4964  begin
     4965    if ClientMode >= scContact then
     4966      exit;
     4967    DestinationMarkON := false;
     4968    PaintDestination;
     4969    for GotoOnly := GoOnPhase downto false do
     4970    begin
     4971      NewFocus := -1;
     4972      for i := 1 to MyRO.nUn do
     4973      begin
     4974        uix := (UnFocus + i) mod MyRO.nUn;
     4975        if (MyUn[uix].Loc >= 0) and (MyUn[uix].Job = jNone) and
     4976          (MyUn[uix].Status and (usStay or usRecover or usWaiting) = usWaiting)
     4977          and (not GotoOnly or (MyUn[uix].Status and usGoto <> 0)) then
     4978          if NearLoc < 0 then
     4979          begin
     4980            NewFocus := uix;
     4981            Break
     4982          end
     4983          else
     4984          begin
     4985            TestDist := Distance(NearLoc, MyUn[uix].Loc);
     4986            if (NewFocus < 0) or (TestDist < Dist) then
     4987            begin
     4988              NewFocus := uix;
     4989              Dist := TestDist
     4990            end
     4991          end
     4992      end;
     4993      if GotoOnly then
     4994        if NewFocus < 0 then
     4995          GoOnPhase := false
     4996        else
     4997          Break;
     4998    end;
     4999    if NewFocus >= 0 then
     5000    begin
     5001      SetUnFocus(NewFocus);
     5002      SetTroopLoc(MyUn[NewFocus].Loc);
     5003      FocusOnLoc(TroopLoc, flRepaintPanel)
     5004    end
     5005    else if AutoTurn and not mWaitTurn.Checked then
     5006    begin
     5007      TurnComplete := true;
     5008      SetUnFocus(-1);
     5009      SetTroopLoc(-1);
     5010      PostMessage(Handle, WM_EOT, 0, 0)
     5011    end
     5012    else
     5013    begin
     5014      if { (UnFocus>=0) and } not TurnComplete and EOT.Visible then
     5015        Play('TURNEND');
     5016      TurnComplete := true;
     5017      SetUnFocus(-1);
     5018      SetTroopLoc(-1);
     5019      PanelPaint;
     5020    end;
     5021  end; { NextUnit }
     5022
     5023  procedure TMainScreen.Scroll(dx, dy: integer);
     5024  begin
     5025    xw := (xw + G.lx + dx) mod G.lx;
     5026    if ywmax > 0 then
     5027    begin
     5028      yw := yw + 2 * dy;
     5029      if yw < 0 then
     5030        yw := 0
     5031      else if yw > ywmax then
     5032        yw := ywmax;
     5033    end;
     5034    MainOffscreenPaint;
     5035    xwMini := xw;
     5036    ywMini := yw;
     5037    MiniPaint;
     5038    CopyMiniToPanel;
     5039    RectInvalidate(xMini + 2, TopBarHeight + MapHeight - overlap + yMini + 2,
     5040      xMini + 2 + G.lx * 2, TopBarHeight + MapHeight - overlap + yMini +
     5041      2 + G.ly);
     5042    Update;
     5043  end;
     5044
     5045  procedure TMainScreen.Timer1Timer(Sender: TObject);
     5046  var
     5047    dx, dy, speed: integer;
     5048  begin
     5049    if idle and (me >= 0) and (GameMode <> cMovie) then
     5050      if (fsModal in Screen.ActiveForm.FormState) or
     5051        (Screen.ActiveForm is TBufferedDrawDlg) and
     5052        (TBufferedDrawDlg(Screen.ActiveForm).WindowMode <> wmPersistent) then
     5053      begin
     5054        BlinkTime := BlinkOnTime + BlinkOffTime - 1;
     5055        if not BlinkON then
     5056        begin
     5057          BlinkON := true;
     5058          if UnFocus >= 0 then
     5059            PaintLocTemp(MyUn[UnFocus].Loc)
     5060          else if TurnComplete and not supervising then
     5061            EOT.SetButtonIndexFast(eotBlinkOn)
     5062        end
     5063      end
     5064      else
     5065      begin
     5066        if Application.Active and not mScrollOff.Checked then
     5067        begin
     5068          if mScrollFast.Checked then
     5069            speed := 2
     5070          else
     5071            speed := 1;
     5072          dx := 0;
     5073          dy := 0;
     5074          if Mouse.CursorPos.y < Screen.height - PanelHeight then
     5075            if Mouse.CursorPos.x = 0 then
     5076              dx := -speed // scroll left
     5077            else if Mouse.CursorPos.x = Screen.width - 1 then
     5078              dx := speed; // scroll right
     5079          if Mouse.CursorPos.y = 0 then
     5080            dy := -speed // scroll up
     5081          else if (Mouse.CursorPos.y = Screen.height - 1) and
     5082            (Mouse.CursorPos.x >= TerrainBtn.Left + TerrainBtn.width) and
     5083            (Mouse.CursorPos.x < xRightPanel + 10 - 8) then
     5084            dy := speed; // scroll down
     5085          if (dx <> 0) or (dy <> 0) then
     5086          begin
     5087            if (Screen.ActiveForm <> MainScreen) and
     5088              (@Screen.ActiveForm.OnDeactivate <> nil) then
     5089              Screen.ActiveForm.OnDeactivate(nil);
     5090            Scroll(dx, dy);
     5091          end
     5092        end;
     5093
     5094        BlinkTime := (BlinkTime + 1) mod (BlinkOnTime + BlinkOffTime);
     5095        BlinkON := BlinkTime >= BlinkOffTime;
     5096        DestinationMarkON := true;
     5097        if UnFocus >= 0 then
     5098        begin
     5099          if (BlinkTime = 0) or (BlinkTime = BlinkOffTime) then
     5100          begin
     5101            PaintLocTemp(MyUn[UnFocus].Loc, pltsBlink);
     5102            PaintDestination;
     5103            // if MoveHintToLoc>=0 then
     5104            // ShowMoveHint(MoveHintToLoc, true);
    17035105          end
    17045106        end
    1705       else
    1706         begin
    1707         s:=Tribe[me].TPhrase('AGE'+char(48+Age));
    1708         MessgText:=Format(s,[TurnToString(MyRO.Turn)]);
    1709         end;
    1710       IconKind:=mikAge;
    1711       IconIndex:=Age;
    1712       {if age=0 then} Kind:=mkOK
    1713       {else begin Kind:=mkOkHelp; HelpKind:=hkAdv; HelpNo:=AgePreq[age]; end};
    1714       CenterTo:=NewAgeCenterTo;
    1715       OpenSound:='AGE_'+char(48+Age);
    1716       ShowModal;
    1717       MyData.ToldAge:=Age;
    1718       if Age>0 then
    1719         MyData.ToldTech[AgePreq[Age]]:=MyRO.Tech[AgePreq[Age]];
    1720       end;
    1721 
    1722     if MyData.ToldAlive<>MyRO.Alive then
    1723       begin
    1724       for p1:=0 to nPl-1 do
    1725         if (MyData.ToldAlive-MyRO.Alive) and (1 shl p1)<>0 then
    1726           with MessgExDlg do
    1727             begin
    1728             OpenSound:='MSG_EXTINCT';
    1729             s:=Tribe[p1].TPhrase('EXTINCT');
    1730             MessgText:=Format(s,[TurnToString(MyRO.Turn)]);
    1731             if MyRO.Alive=1 shl me then
    1732               MessgText:=MessgText+Phrases.Lookup('EXTINCTALL');
    1733             Kind:=mkOK;
    1734             IconKind:=mikImp;
    1735             IconIndex:=21;
    1736             ShowModal;
     5107        else if TurnComplete and not supervising then
     5108        begin
     5109          if BlinkTime = 0 then
     5110            EOT.SetButtonIndexFast(eotBlinkOff)
     5111          else if BlinkTime = BlinkOffTime then
     5112            EOT.SetButtonIndexFast(eotBlinkOn)
     5113        end
     5114      end
     5115  end;
     5116
     5117  procedure TMainScreen.Centre(Loc: integer);
     5118  begin
     5119    if FastScrolling and MapValid then
     5120      Update;
     5121    // necessary because ScrollDC for form canvas is called after
     5122    xw := (Loc mod G.lx - (MapWidth - xxt * 2 * ((Loc div G.lx) and 1))
     5123      div (xxt * 4) + G.lx) mod G.lx;
     5124    if ywmax <= 0 then
     5125      yw := ywcenter
     5126    else
     5127    begin
     5128      yw := (Loc div G.lx - MapHeight div (yyt * 2) + 1) and not 1;
     5129      if yw < 0 then
     5130        yw := 0
     5131      else if yw > ywmax then
     5132        yw := ywmax;
     5133    end
     5134  end;
     5135
     5136  function TMainScreen.ZoomToCity(Loc: integer;
     5137    NextUnitOnClose: boolean = false; ShowEvent: integer = 0): boolean;
     5138  begin
     5139    result := MyMap[Loc] and (fOwned or fSpiedOut) <> 0;
     5140    if result then
     5141      with CityDlg do
     5142      begin
     5143        if ClientMode >= scContact then
     5144        begin
     5145          CloseAction := None;
     5146          RestoreUnFocus := -1;
     5147        end
     5148        else if NextUnitOnClose then
     5149        begin
     5150          CloseAction := StepFocus;
     5151          RestoreUnFocus := -1;
     5152        end
     5153        else if not Visible then
     5154        begin
     5155          CloseAction := RestoreFocus;
     5156          RestoreUnFocus := UnFocus;
     5157        end;
     5158        SetUnFocus(-1);
     5159        SetTroopLoc(Loc);
     5160        MarkCityLoc := Loc;
     5161        PanelPaint;
     5162        ShowNewContent(wmPersistent, Loc, ShowEvent);
     5163      end
     5164  end;
     5165
     5166  function TMainScreen.LocationOfScreenPixel(x, y: integer): integer;
     5167  var
     5168    qx, qy: integer;
     5169  begin
     5170    qx := (x * (yyt * 2) + y * (xxt * 2) + xxt * yyt * 2)
     5171      div (xxt * yyt * 4) - 1;
     5172    qy := (y * (xxt * 2) - x * (yyt * 2) - xxt * yyt * 2 + 4000 * xxt * yyt)
     5173      div (xxt * yyt * 4) - 999;
     5174    result := (xw + (qx - qy + 2048) div 2 - 1024 + G.lx) mod G.lx + G.lx *
     5175      (yw + qx + qy);
     5176  end;
     5177
     5178  procedure TMainScreen.MapBoxMouseDown(Sender: TObject; Button: TMouseButton;
     5179    Shift: TShiftState; x, y: integer);
     5180  var
     5181    i, uix, emix, p1, dx, dy, MouseLoc: integer;
     5182    EditTileData: TEditTileData;
     5183    m, m2: TMenuItem;
     5184    MoveAdviceData: TMoveAdviceData;
     5185    DoCenter: boolean;
     5186  begin
     5187    if GameMode = cMovie then
     5188      exit;
     5189
     5190    if CityDlg.Visible then
     5191      CityDlg.Close;
     5192    if UnitStatDlg.Visible then
     5193      UnitStatDlg.Close;
     5194    MouseLoc := LocationOfScreenPixel(x, y);
     5195    if (MouseLoc < 0) or (MouseLoc >= G.lx * G.ly) then
     5196      exit;
     5197    if (Button = mbLeft) and not(ssShift in Shift) then
     5198    begin
     5199      DoCenter := true;
     5200      if ClientMode = cEditMap then
     5201      begin
     5202        DoCenter := false;
     5203        EditTileData.Loc := MouseLoc;
     5204        if ssCtrl in Shift then // toggle special resource
     5205          case MyMap[MouseLoc] and fTerrain of
     5206            fOcean:
     5207              EditTileData.NewTile := MyMap[MouseLoc];
     5208            fGrass, fArctic:
     5209              EditTileData.NewTile := MyMap[MouseLoc] and not fSpecial or
     5210                ((MyMap[MouseLoc] shr 5 and 3 + 1) mod 2 shl 5);
     5211          else
     5212            EditTileData.NewTile := MyMap[MouseLoc] and not fSpecial or
     5213              ((MyMap[MouseLoc] shr 5 and 3 + 1) mod 3 shl 5)
     5214          end
     5215        else if BrushType <= fTerrain then
     5216          EditTileData.NewTile := MyMap[MouseLoc] and not fTerrain or
     5217            fSpecial or BrushType
     5218        else if BrushType and fDeadLands <> 0 then
     5219          if MyMap[MouseLoc] and (fDeadLands or fModern) = BrushType and
     5220            (fDeadLands or fModern) then
     5221            EditTileData.NewTile := MyMap[MouseLoc] and
     5222              not(fDeadLands or fModern)
     5223          else
     5224            EditTileData.NewTile := MyMap[MouseLoc] and
     5225              not(fDeadLands or fModern) or BrushType
     5226        else if BrushType and fTerImp <> 0 then
     5227          if MyMap[MouseLoc] and fTerImp = BrushType then
     5228            EditTileData.NewTile := MyMap[MouseLoc] and not fTerImp
     5229          else
     5230            EditTileData.NewTile := MyMap[MouseLoc] and not fTerImp or BrushType
     5231        else if BrushType and (fPrefStartPos or fStartPos) <> 0 then
     5232          if MyMap[MouseLoc] and (fPrefStartPos or fStartPos) = BrushType and
     5233            (fPrefStartPos or fStartPos) then
     5234            EditTileData.NewTile := MyMap[MouseLoc] and
     5235              not(fPrefStartPos or fStartPos)
     5236          else
     5237            EditTileData.NewTile := MyMap[MouseLoc] and
     5238              not(fPrefStartPos or fStartPos) or BrushType
     5239        else
     5240          EditTileData.NewTile := MyMap[MouseLoc] xor BrushType;
     5241        Server(sEditTile, me, 0, EditTileData);
     5242        Edited := true;
     5243        BrushLoc := MouseLoc;
     5244        PaintLoc(MouseLoc, 2);
     5245        MiniPaint;
     5246        BitBlt(Panel.Canvas.Handle, xMini + 2, yMini + 2, G.lx * 2, G.ly,
     5247          Mini.Canvas.Handle, 0, 0, SRCCOPY);
     5248        if ywmax <= 0 then
     5249          Frame(Panel.Canvas, xMini + 2 + G.lx - MapWidth div (2 * xxt),
     5250            yMini + 2, xMini + 1 + G.lx + MapWidth div (2 * xxt),
     5251            yMini + 2 + G.ly - 1, MainTexture.clMark, MainTexture.clMark)
     5252        else
     5253          Frame(Panel.Canvas, xMini + 2 + G.lx - MapWidth div (2 * xxt),
     5254            yMini + 2 + yw, xMini + 2 + G.lx + MapWidth div (2 * xxt) - 1,
     5255            yMini + 2 + yw + MapHeight div yyt - 2, MainTexture.clMark,
     5256            MainTexture.clMark);
     5257        RectInvalidate(xMini + 2, TopBarHeight + MapHeight - overlap + yMini +
     5258          2, xMini + 2 + G.lx * 2, TopBarHeight + MapHeight - overlap + yMini
     5259          + 2 + G.ly)
     5260      end
     5261      else if MyMap[MouseLoc] and fCity <> 0 then { city clicked }
     5262      begin
     5263        if MyMap[MouseLoc] and (fOwned or fSpiedOut) <> 0 then
     5264        begin
     5265          ZoomToCity(MouseLoc);
     5266          DoCenter := false;
     5267        end
     5268        else
     5269        begin
     5270          UnitStatDlg.ShowNewContent_EnemyCity(wmPersistent, MouseLoc);
     5271          DoCenter := false;
     5272        end
     5273      end
     5274      else if MyMap[MouseLoc] and fUnit <> 0 then { unit clicked }
     5275        if MyMap[MouseLoc] and fOwned <> 0 then
     5276        begin
     5277          DoCenter := false;
     5278          if not supervising and (ClientMode < scContact) then
     5279          begin // not in negotiation mode
     5280            if (UnFocus >= 0) and (MyUn[UnFocus].Loc = MouseLoc) then
     5281            begin // rotate
     5282              uix := (UnFocus + 1) mod MyRO.nUn;
     5283              i := MyRO.nUn - 1;
     5284              while i > 0 do
     5285              begin
     5286                if (MyUn[uix].Loc = MouseLoc) and (MyUn[uix].Job = jNone) and
     5287                  (MyUn[uix].Status and (usStay or usRecover or usEnhance or
     5288                  usWaiting) = usWaiting) then
     5289                  Break;
     5290                dec(i);
     5291                uix := (uix + 1) mod MyRO.nUn;
     5292              end;
     5293              if i = 0 then
     5294                uix := UnFocus
     5295            end
     5296            else
     5297              Server(sGetDefender, me, MouseLoc, uix);
     5298            if uix <> UnFocus then
     5299              SetUnFocus(uix);
     5300            TurnComplete := false;
     5301            EOT.ButtonIndex := eotGray;
     5302          end;
     5303          SetTroopLoc(MouseLoc);
     5304          PanelPaint;
     5305        end // own unit
     5306        else if (MyMap[MouseLoc] and fSpiedOut <> 0) and not(ssCtrl in Shift)
     5307        then
     5308        begin
     5309          DoCenter := false;
     5310          SetTroopLoc(MouseLoc);
     5311          PanelPaint;
     5312        end
     5313        else
     5314        begin
     5315          DoCenter := false;
     5316          UnitStatDlg.ShowNewContent_EnemyLoc(wmPersistent, MouseLoc);
     5317        end;
     5318      if DoCenter then
     5319      begin
     5320        Centre(MouseLoc);
     5321        PaintAllMaps
     5322      end
     5323    end
     5324    else if (ClientMode <> cEditMap) and (Button = mbRight) and
     5325      not(ssShift in Shift) then
     5326    begin
     5327      if supervising then
     5328      begin
     5329        EditLoc := MouseLoc;
     5330        Server(sGetModels, me, 0, nil^);
     5331        EmptyMenu(mCreateUnit);
     5332        for p1 := 0 to nPl - 1 do
     5333          if 1 shl p1 and MyRO.Alive <> 0 then
     5334          begin
     5335            m := TMenuItem.Create(mCreateUnit);
     5336            m.Caption := Tribe[p1].TPhrase('SHORTNAME');
     5337            for emix := MyRO.nEnemyModel - 1 downto 0 do
     5338              if (MyRO.EnemyModel[emix].Owner = p1) and
     5339                (Server(sCreateUnit - sExecute + p1 shl 4, me,
     5340                MyRO.EnemyModel[emix].mix, MouseLoc) >= rExecuted) then
     5341              begin
     5342                if Tribe[p1].ModelPicture[MyRO.EnemyModel[emix].mix].HGr = 0
     5343                then
     5344                  InitEnemyModel(emix);
     5345                m2 := TMenuItem.Create(m);
     5346                m2.Caption := Tribe[p1].ModelName[MyRO.EnemyModel[emix].mix];
     5347                m2.Tag := p1 shl 16 + MyRO.EnemyModel[emix].mix;
     5348                m2.OnClick := CreateUnitClick;
     5349                m.Add(m2);
     5350              end;
     5351            m.Visible := m.Count > 0;
     5352            mCreateUnit.Add(m);
     5353          end;
     5354        if FullScreen then
     5355          EditPopup.Popup(Left + x, Top + y)
     5356        else
     5357          EditPopup.Popup(Left + x + 4,
     5358            Top + y + GetSystemMetrics(SM_CYCAPTION) + 4);
     5359      end
     5360      else if (UnFocus >= 0) and (MyUn[UnFocus].Loc <> MouseLoc) then
     5361        with MyUn[UnFocus] do
     5362        begin
     5363          dx := ((MouseLoc mod G.lx * 2 + MouseLoc div G.lx and 1) -
     5364            (Loc mod G.lx * 2 + Loc div G.lx and 1) + 3 * G.lx)
     5365            mod (2 * G.lx) - G.lx;
     5366          dy := MouseLoc div G.lx - Loc div G.lx;
     5367          if abs(dx) + abs(dy) < 3 then
     5368          begin
     5369            DestinationMarkON := false;
     5370            PaintDestination;
     5371            Status := Status and
     5372              ($FFFF - usStay - usRecover - usGoto - usEnhance) or usWaiting;
     5373            MoveUnit(dx, dy, muAutoNext) { simple move }
     5374          end
     5375          else if GetMoveAdvice(UnFocus, MouseLoc, MoveAdviceData) >= rExecuted
     5376          then
     5377          begin
     5378            if MyMap[MouseLoc] and (fUnit or fOwned) = fUnit then
     5379            begin // check for suicide mission before movement
     5380              with MyUn[UnFocus], BattleDlg.Forecast do
     5381              begin
     5382                pAtt := me;
     5383                mixAtt := mix;
     5384                HealthAtt := Health;
     5385                ExpAtt := Exp;
     5386                FlagsAtt := Flags;
     5387              end;
     5388              BattleDlg.Forecast.Movement := MyUn[UnFocus].Movement;
     5389              if (Server(sGetBattleForecastEx, me, MouseLoc, BattleDlg.Forecast)
     5390                >= rExecuted) and (BattleDlg.Forecast.EndHealthAtt <= 0) then
     5391              begin
     5392                BattleDlg.uix := UnFocus;
     5393                BattleDlg.ToLoc := MouseLoc;
     5394                BattleDlg.IsSuicideQuery := true;
     5395                BattleDlg.ShowModal;
     5396                if BattleDlg.ModalResult <> mrOK then
     5397                  exit;
     5398              end
    17375399            end;
    1738       if (ClientMode<>cMovieTurn) and not supervising then
    1739         DiaDlg.ShowNewContent_Charts(wmModal);
    1740       end;
    1741 
    1742     // tell changes of own credibility
    1743     if not supervising then
    1744       begin
    1745       if RoughCredibility(MyRO.Credibility)
    1746         <>RoughCredibility(MyData.ToldOwnCredibility) then
    1747         begin
    1748         if RoughCredibility(MyRO.Credibility)
    1749           >RoughCredibility(MyData.ToldOwnCredibility) then
    1750           s:=Phrases.Lookup('CREDUP')
    1751         else s:=Phrases.Lookup('CREDDOWN');
    1752         TribeMessage(me, Format(s,[Phrases.Lookup('CREDIBILITY',
    1753           RoughCredibility(MyRO.Credibility))]), '');
    1754         end;
    1755       MyData.ToldOwnCredibility:=MyRO.Credibility;
    1756       end;
    1757 
    1758     for i:=0 to 27 do
    1759       begin
    1760       OwnWonder:=false;
    1761       for cix:=0 to MyRO.nCity-1 do
    1762         if (MyCity[cix].Loc>=0) and (MyCity[cix].ID=MyRO.Wonder[i].CityID) then
    1763           OwnWonder:=true;
    1764       if MyRO.Wonder[i].CityID<>MyData.ToldWonders[i].CityID then
    1765         begin
    1766         if MyRO.Wonder[i].CityID=-2 then with MessgExDlg do
    1767           begin {tell about destroyed wonders}
    1768           OpenSound:='WONDER_DESTROYED';
    1769           MessgText:=Format(Phrases.Lookup('WONDERDEST'),
    1770             [Phrases.Lookup('IMPROVEMENTS',i)]);
    1771           Kind:=mkOkHelp;
    1772           HelpKind:=hkImp;
    1773           HelpNo:=i;
    1774           IconKind:=mikImp;
    1775           IconIndex:=i;
    1776           ShowModal;
    1777           end
    1778         else
    1779           begin
    1780           if i=woManhattan then
    1781             if MyRO.Wonder[i].EffectiveOwner>me then
    1782               MyData.ColdWarStart:=MyRO.Turn-1
    1783             else MyData.ColdWarStart:=MyRO.Turn;
    1784           if not OwnWonder then with MessgExDlg do
    1785             begin {tell about newly built wonders}
    1786             if i=woManhattan then
    1787               begin
    1788               OpenSound:='MSG_COLDWAR';
    1789               s:=Tribe[MyRO.Wonder[i].EffectiveOwner].TPhrase('COLDWAR')
    1790               end
    1791             else if MyRO.Wonder[i].EffectiveOwner>=0 then
    1792               begin
    1793               OpenSound:='WONDER_BUILT';
    1794               s:=Tribe[MyRO.Wonder[i].EffectiveOwner].TPhrase('WONDERBUILT')
    1795               end
    1796             else
    1797               begin
    1798               OpenSound:='MSG_DEFAULT';
    1799               s:=Phrases.Lookup('WONDERBUILTEXP'); // already expired when built
    1800               end;
    1801             MessgText:=Format(s, [Phrases.Lookup('IMPROVEMENTS',i),
    1802               CityName(MyRO.Wonder[i].CityID)]);
    1803             Kind:=mkOkHelp;
    1804             HelpKind:=hkImp;
    1805             HelpNo:=i;
    1806             IconKind:=mikImp;
    1807             IconIndex:=i;
    1808             ShowModal;
    1809             end
     5400            DestinationMarkON := false;
     5401            PaintDestination;
     5402            Status := Status and not(usStay or usRecover or usEnhance) or
     5403              usWaiting;
     5404            MoveToLoc(MouseLoc, false); { goto }
    18105405          end
    18115406        end
    1812       else if (MyRO.Wonder[i].EffectiveOwner<>MyData.ToldWonders[i].EffectiveOwner)
    1813         and (MyRO.Wonder[i].CityID>-2) then
    1814         if MyRO.Wonder[i].EffectiveOwner<0 then
     5407    end
     5408    else if (Button = mbMiddle) and (UnFocus >= 0) and
     5409      (MyModel[MyUn[UnFocus].mix].Kind in [mkSettler, mkSlaves]) then
     5410    begin
     5411      DestinationMarkON := false;
     5412      PaintDestination;
     5413      MyUn[UnFocus].Status := MyUn[UnFocus].Status and
     5414        ($FFFF - usStay - usRecover - usGoto) or usEnhance;
     5415      uix := UnFocus;
     5416      if MouseLoc <> MyUn[uix].Loc then
     5417        MoveToLoc(MouseLoc, true); { goto }
     5418      if (UnFocus = uix) and (MyUn[uix].Loc = MouseLoc) then
     5419        MenuClick(mEnhance)
     5420    end
     5421    else if (Button = mbLeft) and (ssShift in Shift) and
     5422      (MyMap[MouseLoc] and fTerrain <> fUNKNOWN) then
     5423      HelpOnTerrain(MouseLoc, wmPersistent)
     5424    else if (ClientMode <= cContinue) and (Button = mbRight) and
     5425      (ssShift in Shift) and (UnFocus >= 0) and
     5426      (MyMap[MouseLoc] and (fUnit or fOwned) = fUnit) then
     5427    begin // battle forecast
     5428      with MyUn[UnFocus], BattleDlg.Forecast do
     5429      begin
     5430        pAtt := me;
     5431        mixAtt := mix;
     5432        HealthAtt := Health;
     5433        ExpAtt := Exp;
     5434        FlagsAtt := Flags;
     5435      end;
     5436      BattleDlg.Forecast.Movement := MyUn[UnFocus].Movement;
     5437      if Server(sGetBattleForecastEx, me, MouseLoc, BattleDlg.Forecast) >= rExecuted
     5438      then
     5439      begin
     5440        BattleDlg.uix := UnFocus;
     5441        BattleDlg.ToLoc := MouseLoc;
     5442        BattleDlg.Left := x - BattleDlg.width div 2;
     5443        if BattleDlg.Left < 0 then
     5444          BattleDlg.Left := 0
     5445        else if BattleDlg.Left + BattleDlg.width > Screen.width then
     5446          BattleDlg.Left := Screen.width - BattleDlg.width;
     5447        BattleDlg.Top := y - BattleDlg.height div 2;
     5448        if BattleDlg.Top < 0 then
     5449          BattleDlg.Top := 0
     5450        else if BattleDlg.Top + BattleDlg.height > Screen.height then
     5451          BattleDlg.Top := Screen.height - BattleDlg.height;
     5452        BattleDlg.IsSuicideQuery := false;
     5453        BattleDlg.Show;
     5454      end
     5455    end
     5456  end;
     5457
     5458  function TMainScreen.MoveUnit(dx, dy: integer; Options: integer): integer;
     5459  // move focused unit to adjacent tile
     5460  var
     5461    i, cix, uix, euix, FromLoc, ToLoc, DirCode, UnFocus0, Defender, Mission, p1,
     5462      NewTiles, cixChanged: integer;
     5463    OldToTile: Cardinal;
     5464    CityCaptured, IsAttack, OldUnrest, NewUnrest, NeedEcoUpdate,
     5465      NeedRepaintPanel, ToTransport, ToShip: boolean;
     5466    PlaneReturnData: TPlaneReturnData;
     5467    QueryItem: string;
     5468  begin
     5469    result := eInvalid;
     5470    UnFocus0 := UnFocus;
     5471    FromLoc := MyUn[UnFocus].Loc;
     5472    ToLoc := dLoc(FromLoc, dx, dy);
     5473    if (ToLoc < 0) or (ToLoc >= G.lx * G.ly) then
     5474    begin
     5475      result := eInvalid;
     5476      exit;
     5477    end;
     5478    if MyMap[ToLoc] and fStealthUnit <> 0 then
     5479    begin
     5480      SoundMessage(Phrases.Lookup('ATTACKSTEALTH'), '');
     5481      exit;
     5482    end;
     5483    if MyMap[ToLoc] and fHiddenUnit <> 0 then
     5484    begin
     5485      SoundMessage(Phrases.Lookup('ATTACKSUB'), '');
     5486      exit;
     5487    end;
     5488
     5489    if MyMap[ToLoc] and (fUnit or fOwned) = fUnit then
     5490    begin // attack -- search enemy unit
     5491      if (MyModel[MyUn[UnFocus].mix].Attack = 0) and
     5492        not((MyModel[MyUn[UnFocus].mix].Cap[mcBombs] > 0) and
     5493        (MyUn[UnFocus].Flags and unBombsLoaded <> 0)) then
     5494      begin
     5495        SoundMessage(Phrases.Lookup('NOATTACKER'), '');
     5496        exit;
     5497      end;
     5498      euix := MyRO.nEnemyUn - 1;
     5499      while (euix >= 0) and (MyRO.EnemyUn[euix].Loc <> ToLoc) do
     5500        dec(euix);
     5501    end;
     5502
     5503    DirCode := dx and 7 shl 4 + dy and 7 shl 7;
     5504    result := Server(sMoveUnit - sExecute + DirCode, me, UnFocus, nil^);
     5505    if (result < rExecuted) and (MyUn[UnFocus].Job > jNone) then
     5506      Server(sStartJob + jNone shl 4, me, UnFocus, nil^);
     5507    if (result < rExecuted) and (result <> eNoTime_Move) then
     5508    begin
     5509      case result of
     5510        eNoTime_Load:
     5511          if MyModel[MyUn[UnFocus].mix].Domain = dAir then
     5512            SoundMessage(Phrases.Lookup('NOTIMELOADAIR'), 'NOMOVE_TIME')
     5513          else
     5514            SoundMessage(Format(Phrases.Lookup('NOTIMELOADGROUND'),
     5515              [MovementToString(MyModel[MyUn[UnFocus].mix].speed)]),
     5516              'NOMOVE_TIME');
     5517        eNoTime_Bombard:
     5518          SoundMessage(Phrases.Lookup('NOTIMEBOMBARD'), 'NOMOVE_TIME');
     5519        eNoTime_Expel:
     5520          SoundMessage(Phrases.Lookup('NOTIMEEXPEL'), 'NOMOVE_TIME');
     5521        eNoRoad:
     5522          SoundMessage(Phrases.Lookup('NOROAD'), 'NOMOVE_DEFAULT');
     5523        eNoNav:
     5524          SoundMessage(Phrases.Lookup('NONAV'), 'NOMOVE_DEFAULT');
     5525        eNoCapturer:
     5526          SoundMessage(Phrases.Lookup('NOCAPTURER'), 'NOMOVE_DEFAULT');
     5527        eNoBombarder:
     5528          SoundMessage(Phrases.Lookup('NOBOMBARDER'), 'NOMOVE_DEFAULT');
     5529        eZOC:
     5530          ContextMessage(Phrases.Lookup('ZOC'), 'NOMOVE_ZOC', hkText,
     5531            HelpDlg.TextIndex('MOVEMENT'));
     5532        eTreaty:
     5533          if MyMap[ToLoc] and (fUnit or fOwned) <> fUnit
     5534          then { no enemy unit -- move }
     5535            SoundMessage(Tribe[MyRO.Territory[ToLoc]].TPhrase('PEACE_NOMOVE'),
     5536              'NOMOVE_TREATY')
     5537          else
     5538            SoundMessage(Tribe[MyRO.EnemyUn[euix].Owner]
     5539              .TPhrase('PEACE_NOATTACK'), 'NOMOVE_TREATY');
     5540        eDomainMismatch:
    18155541          begin
    1816           if i<>woMir then with MessgExDlg do
    1817             begin {tell about expired wonders}
    1818             OpenSound:='WONDER_EXPIRED';
    1819             MessgText:=Format(Phrases.Lookup('WONDEREXP'),
    1820               [Phrases.Lookup('IMPROVEMENTS',i),
    1821               CityName(MyRO.Wonder[i].CityID)]);
    1822             Kind:=mkOkHelp;
    1823             HelpKind:=hkImp;
    1824             HelpNo:=i;
    1825             IconKind:=mikImp;
    1826             IconIndex:=i;
    1827             ShowModal;
     5542            if (MyModel[MyUn[UnFocus].mix].Domain < dSea) and
     5543              (MyMap[ToLoc] and (fUnit or fOwned) = fUnit or fOwned) then
     5544            begin // false load attempt
     5545              ToShip := false;
     5546              ToTransport := false;
     5547              for uix := 0 to MyRO.nUn - 1 do
     5548                if (MyUn[uix].Loc = ToLoc) and
     5549                  (MyModel[MyUn[uix].mix].Domain = dSea) then
     5550                begin
     5551                  ToShip := true;
     5552                  if MyModel[MyUn[uix].mix].Cap[mcSeaTrans] > 0 then
     5553                    ToTransport := true;
     5554                end;
     5555              if ToTransport then
     5556                SoundMessage(Phrases.Lookup('FULLTRANSPORT'), 'NOMOVE_DEFAULT')
     5557              else if ToShip then
     5558                SoundMessage(Phrases.Lookup('NOTRANSPORT'), 'NOMOVE_DEFAULT')
     5559              else
     5560                Play('NOMOVE_DOMAIN');
    18285561            end
     5562            else
     5563              Play('NOMOVE_DOMAIN');
    18295564          end
    1830         else if (MyData.ToldWonders[i].EffectiveOwner>=0) and not OwnWonder then
    1831           with MessgExDlg do
    1832             begin {tell about capture of wonders}
    1833             OpenSound:='WONDER_CAPTURED';
    1834             s:=Tribe[MyRO.Wonder[i].EffectiveOwner].TPhrase('WONDERCAPT');
    1835             MessgText:=Format(s, [Phrases.Lookup('IMPROVEMENTS',i),
    1836               CityName(MyRO.Wonder[i].CityID)]);
    1837             Kind:=mkOkHelp;
    1838             HelpKind:=hkImp;
    1839             HelpNo:=i;
    1840             IconKind:=mikImp;
    1841             IconIndex:=i;
    1842             ShowModal;
     5565      else
     5566        Play('NOMOVE_DEFAULT');
     5567      end;
     5568      exit;
     5569    end;
     5570
     5571    if ((result = eWon) or (result = eLost) or (result = eBloody)) and
     5572      (MyUn[UnFocus].Movement < 100) and
     5573      (MyModel[MyUn[UnFocus].mix].Cap[mcWill] = 0) then
     5574    begin
     5575      if SimpleQuery(mkYesNo, Format(Phrases.Lookup('FASTATTACK'),
     5576        [MyUn[UnFocus].Movement]), 'NOMOVE_TIME') <> mrOK then
     5577      begin
     5578        result := eInvalid;
     5579        exit;
     5580      end;
     5581      Update; // remove message box from screen
     5582    end;
     5583
     5584    OldUnrest := false;
     5585    NewUnrest := false;
     5586    if (result >= rExecuted) and (result and rUnitRemoved = 0) and
     5587      (MyMap[ToLoc] and (fUnit or fOwned) <> fUnit) then
     5588    begin
     5589      OldUnrest := UnrestAtLoc(UnFocus, FromLoc);
     5590      NewUnrest := UnrestAtLoc(UnFocus, ToLoc);
     5591      if NewUnrest > OldUnrest then
     5592      begin
     5593        if MyRO.Government = gDemocracy then
     5594        begin
     5595          QueryItem := 'UNREST_NOTOWN';
     5596          p1 := me;
     5597        end
     5598        else
     5599        begin
     5600          QueryItem := 'UNREST_FOREIGN';
     5601          p1 := MyRO.Territory[ToLoc];
     5602        end;
     5603        with MessgExDlg do
     5604        begin
     5605          MessgText := Format(Tribe[p1].TPhrase(QueryItem),
     5606            [Phrases.Lookup('GOVERNMENT', MyRO.Government)]);
     5607          Kind := mkYesNo;
     5608          IconKind := mikImp;
     5609          IconIndex := imPalace;
     5610          ShowModal;
     5611          if ModalResult <> mrOK then
     5612          begin
     5613            result := eInvalid;
     5614            exit;
     5615          end;
     5616        end;
     5617        Update; // remove message box from screen
     5618      end
     5619    end;
     5620
     5621    if (result >= rExecuted) and (MyModel[MyUn[UnFocus].mix].Domain = dAir) and
     5622      (MyUn[UnFocus].Status and usToldNoReturn = 0) then
     5623    begin // can plane return?
     5624      PlaneReturnData.Fuel := MyUn[UnFocus].Fuel;
     5625      if (MyMap[ToLoc] and (fUnit or fOwned) = fUnit) or
     5626        (MyMap[ToLoc] and (fCity or fOwned) = fCity) then
     5627      begin // attack/expel/bombard -> 100MP
     5628        PlaneReturnData.Loc := FromLoc;
     5629        PlaneReturnData.Movement := MyUn[UnFocus].Movement - 100;
     5630        if PlaneReturnData.Movement < 0 then
     5631          PlaneReturnData.Movement := 0;
     5632      end
     5633      else // move
     5634      begin
     5635        PlaneReturnData.Loc := ToLoc;
     5636        if dx and 1 <> 0 then
     5637          PlaneReturnData.Movement := MyUn[UnFocus].Movement - 100
     5638        else
     5639          PlaneReturnData.Movement := MyUn[UnFocus].Movement - 150;
     5640      end;
     5641      if Server(sGetPlaneReturn, me, UnFocus, PlaneReturnData) = eNoWay then
     5642      begin
     5643        if MyModel[MyUn[UnFocus].mix].Kind = mkSpecial_Glider then
     5644          QueryItem := 'LOWFUEL_GLIDER'
     5645        else
     5646          QueryItem := 'LOWFUEL';
     5647        if SimpleQuery(mkYesNo, Phrases.Lookup(QueryItem), 'WARNING_LOWSUPPORT')
     5648          <> mrOK then
     5649        begin
     5650          result := eInvalid;
     5651          exit;
     5652        end;
     5653        Update; // remove message box from screen
     5654        MyUn[UnFocus].Status := MyUn[UnFocus].Status or usToldNoReturn;
     5655      end
     5656    end;
     5657
     5658    if result = eMissionDone then
     5659    begin
     5660      ModalSelectDlg.ShowNewContent(wmModal, kMission);
     5661      Update; // dialog still on screen
     5662      Mission := ModalSelectDlg.result;
     5663      if Mission < 0 then
     5664        exit;
     5665      Server(sSetSpyMission + Mission shl 4, me, 0, nil^);
     5666    end;
     5667
     5668    CityCaptured := false;
     5669    if result = eNoTime_Move then
     5670      Play('NOMOVE_TIME')
     5671    else
     5672    begin
     5673      NeedEcoUpdate := false;
     5674      DestinationMarkON := false;
     5675      PaintDestination;
     5676      if result and rUnitRemoved <> 0 then
     5677        CityOptimizer_BeforeRemoveUnit(UnFocus);
     5678      IsAttack := (result = eBombarded) or (result <> eMissionDone) and
     5679        (MyMap[ToLoc] and (fUnit or fOwned) = fUnit);
     5680      if not IsAttack then
     5681      begin // move
     5682        cix := MyRO.nCity - 1; { look for own city at dest location }
     5683        while (cix >= 0) and (MyCity[cix].Loc <> ToLoc) do
     5684          dec(cix);
     5685        if (result <> eMissionDone) and (MyMap[ToLoc] and fCity <> 0) and
     5686          (cix < 0) then
     5687          CityCaptured := true;
     5688        result := Server(sMoveUnit + DirCode, me, UnFocus, nil^);
     5689        case result of
     5690          eHiddenUnit:
     5691            begin
     5692              Play('NOMOVE_SUBMARINE');
     5693              PaintLoc(ToLoc)
    18435694            end;
    1844       end;
    1845 
    1846     if MyRO.Turn=MyData.ColdWarStart+ColdWarTurns then
    1847       begin
    1848       SoundMessageEx(Phrases.Lookup('COLDWAREND'),'MSG_DEFAULT');
    1849       MyData.ColdWarStart:=-ColdWarTurns-1
    1850       end;
    1851 
    1852     TellNewModels;
    1853     end; // ClientMode<>cResume
    1854   MyData.ToldAlive:=MyRO.Alive;
    1855   move(MyRO.Wonder,MyData.ToldWonders,SizeOf(MyData.ToldWonders));
    1856 
    1857   NewGovAvailable:=-1;
    1858   if ClientMode<>cResume then
    1859     begin // tell about new techs
    1860     for ad:=0 to nAdv-1 do
    1861       if (MyRO.TestFlags and tfAllTechs=0)
    1862           and ((MyRO.Tech[ad]>=tsApplicable)<>(MyData.ToldTech[ad]>=tsApplicable))
    1863         or (ad in FutureTech ) and (MyRO.Tech[ad]<>MyData.ToldTech[ad]) then
    1864         with MessgExDlg do
    1865           begin
    1866           Item:='RESEARCH_GENERAL';
    1867           if GameMode<>cMovie then
    1868             OpenSound:='NEWADVANCE_'+char(48+Age);
    1869           Item2:=Phrases.Lookup('ADVANCES',ad);
    1870           if ad in FutureTech then Item2:=Item2+' '+IntToStr(MyRO.Tech[ad]);
    1871           MessgText:=Format(Phrases.Lookup(Item),[Item2]);
    1872           Kind:=mkOkHelp;
    1873           HelpKind:=hkAdv;
    1874           HelpNo:=ad;
    1875           IconKind:=mikBook;
    1876           IconIndex:=-1;
    1877           for i:=0 to nAdvBookIcon-1 do if AdvBookIcon[i].Adv=ad then
    1878             IconIndex:=AdvBookIcon[i].Icon;
    1879           ShowModal;
    1880           MyData.ToldTech[ad]:=MyRO.Tech[ad];
    1881           for i:=gMonarchy to nGov-1 do if GovPreq[i]=ad then
    1882             NewGovAvailable:=i;
    1883           end;
    1884     end;
    1885 
    1886   ShowCityList:=false;
    1887   if ClientMode=cTurn then
    1888     begin
    1889     if (MyRO.Happened and phTech<>0) and (MyData.FarTech<>adNexus) then
    1890       ChooseResearch;
    1891 
    1892     UpdatePanel:=false;
    1893     if MyRO.Happened and phChangeGov<>0 then
    1894       begin
    1895       ModalSelectDlg.ShowNewContent(wmModal,kGov);
    1896       Play('NEWGOV');
    1897       Server(sSetGovernment,me,ModalSelectDlg.result,nil^);
    1898       CityOptimizer_BeginOfTurn;
    1899       UpdatePanel:=true;
    1900       end;
    1901     end; // ClientMode=cTurn
    1902 
    1903   if not supervising and ((ClientMode=cTurn) or (ClientMode=cMovieTurn)) then
    1904     for cix:=0 to MyRO.nCity-1 do with MyCity[cix] do
    1905       Status:=Status and not csToldBombard;
    1906 
    1907   if ((ClientMode=cTurn) or (ClientMode=cMovieTurn))
    1908     and (MyRO.Government<>gAnarchy) then
    1909     begin
    1910     // tell what happened in cities
    1911     for WondersOnly:=true downto false do
    1912       for cix:=0 to MyRO.nCity-1 do with MyCity[cix] do
    1913         if (MyRO.Turn>0) and (Loc>=0) and (Flags and chCaptured=0)
    1914           and (WondersOnly=(Flags and chProduction<>0)
    1915             and (Project0 and cpImp<>0) and (Project0 and cpIndex<28)) then
    1916           begin
    1917           if WondersOnly then with MessgExDlg do
    1918             begin {tell about newly built wonder}
    1919             OpenSound:='WONDER_BUILT';
    1920             s:=Tribe[me].TPhrase('WONDERBUILTOWN');
    1921             MessgText:=Format(s, [Phrases.Lookup('IMPROVEMENTS',Project0 and cpIndex),
    1922               CityName(ID)]);
    1923             Kind:=mkOkHelp;
    1924             HelpKind:=hkImp;
    1925             HelpNo:=Project0 and cpIndex;
    1926             IconKind:=mikImp;
    1927             IconIndex:=Project0 and cpIndex;
    1928             ShowModal;
     5695          eStealthUnit:
     5696            begin
     5697              Play('NOMOVE_STEALTH');
     5698              PaintLoc(ToLoc)
    19295699            end;
    1930           if not supervising and (ClientMode=cTurn) then
     5700          eZOC_EnemySpotted:
    19315701            begin
    1932             AllowCityScreen:=true;
    1933             if (Status and 7<>0) and (Project and (cpImp+cpIndex)=cpImp+imTrGoods) then
    1934               if (MyData.ImpOrder[Status and 7-1,0]>=0) then
     5702              Play('NOMOVE_ZOC');
     5703              PaintLoc(ToLoc, 1)
     5704            end;
     5705          rExecuted .. maxint:
     5706            begin
     5707              if result and rUnitRemoved <> 0 then
     5708                UnFocus := -1 // unit died
     5709              else
     5710              begin
     5711                assert(UnFocus >= 0);
     5712                MyUn[UnFocus].Status := MyUn[UnFocus].Status and
     5713                  not(usStay or usRecover);
     5714                for uix := 0 to MyRO.nUn - 1 do
     5715                  if MyUn[uix].Master = UnFocus then
     5716                    MyUn[uix].Status := MyUn[uix].Status and not usWaiting;
     5717                if CityCaptured and
     5718                  (MyRO.Government in [gRepublic, gDemocracy, gFuture]) then
     5719                begin // borders have moved, unrest might have changed in any city
     5720                  CityOptimizer_BeginOfTurn;
     5721                  NeedEcoUpdate := true;
     5722                end
     5723                else
    19355724                begin
    1936                 if AutoBuild(cix,MyData.ImpOrder[Status and 7-1]) then
    1937                   AllowCityScreen:=false
    1938                 else if Flags and chProduction<>0 then
    1939                   Flags:=(Flags and not chProduction) or chAllImpsMade
    1940                 end
    1941               else Flags:=Flags or chTypeDel;
    1942             if (Size>=NeedAqueductSize) and (MyRO.Tech[Imp[imAqueduct].Preq]<tsApplicable)
    1943               or (Size>=NeedSewerSize) and (MyRO.Tech[Imp[imSewer].Preq]<tsApplicable) then
    1944               Flags:=Flags and not chNoGrowthWarning; // don't remind of unknown building
    1945             if Flags and chNoSettlerProd=0 then
    1946               Status:=Status and not csToldDelay
    1947             else if Status and csToldDelay=0 then
    1948               Status:=Status or csToldDelay
    1949             else Flags:=Flags and not chNoSettlerProd;
    1950             if mRepScreens.Checked then
    1951               begin
    1952               if (Flags and CityRepMask<>0) and AllowCityScreen then
    1953                 begin {show what happened in cities}
    1954                 SetTroopLoc(MyCity[cix].Loc);
    1955                 MarkCityLoc:=MyCity[cix].Loc;
    1956                 PanelPaint;
    1957                 CityDlg.CloseAction:=None;
    1958                 CityDlg.ShowNewContent(wmModal, MyCity[cix].Loc, Flags and CityRepMask);
    1959                 UpdatePanel:=true;
    1960                 end
    1961               end
    1962             else {if mRepList.Checked then}
    1963               begin
    1964               if Flags and CityRepMask<>0 then
    1965                 ShowCityList:=true
    1966               end
    1967             end
    1968           end; {city loop}
    1969     end; // ClientMode=cTurn
    1970 
    1971   if ClientMode=cTurn then
    1972     begin
    1973     if NewGovAvailable>=0 then with MessgExDlg do
    1974       begin
    1975       MessgText:=Format(Phrases.Lookup('AUTOREVOLUTION'),
    1976         [Phrases.Lookup('GOVERNMENT',NewGovAvailable)]);
    1977       Kind:=mkYesNo;
    1978       IconKind:=mikPureIcon;
    1979       IconIndex:=6+NewGovAvailable;
    1980       ShowModal;
    1981       if ModalResult=mrOK then
    1982         begin
    1983         Play('REVOLUTION');
    1984         Server(sRevolution,me,0,nil^);
    1985         end
    1986       end;
    1987     end; // ClientMode=cTurn
    1988 
    1989   if (ClientMode=cTurn) or (ClientMode=cMovieTurn) then
    1990     begin
    1991     if MyRO.Happened and phGliderLost<>0 then
    1992       ContextMessage(Phrases.Lookup('GLIDERLOST'), 'MSG_DEFAULT', hkModel, 200);
    1993     if MyRO.Happened and phPlaneLost<>0 then
    1994       ContextMessage(Phrases.Lookup('PLANELOST'), 'MSG_DEFAULT', hkFeature,
    1995         mcFuel);
    1996     if MyRO.Happened and phPeaceEvacuation<>0 then
    1997       for p1:=0 to nPl-1 do if 1 shl p1 and MyData.PeaceEvaHappened<>0 then
    1998         SoundMessageEx(Tribe[p1].TPhrase('WITHDRAW'), 'MSG_DEFAULT');
    1999     if MyRO.Happened and phPeaceViolation<>0 then
    2000       for p1:=0 to nPl-1 do
    2001         if (1 shl p1 and MyRO.Alive<>0) and (MyRO.EvaStart[p1]=MyRO.Turn) then
    2002           SoundMessageEx(Format(Tribe[p1].TPhrase('VIOLATION'),
    2003             [TurnToString(MyRO.Turn+PeaceEvaTurns-1)]), 'MSG_WITHDRAW');
    2004     TellNewContacts;
    2005     end;
    2006 
    2007   if ClientMode=cMovieTurn then Update
    2008   else if ClientMode=cTurn then
    2009     begin
    2010     if UpdatePanel then UpdateViews;
    2011     Application.ProcessMessages;
    2012 
    2013     if not supervising then
    2014       for uix:=0 to MyRO.nUn-1 do with MyUn[uix] do if Loc>=0 then
    2015         begin
    2016         if Flags and unWithdrawn<>0 then Status:=0;
    2017         if Health=100 then
    2018           Status:=Status and not usRecover;
    2019         if (Master>=0) or UnitExhausted(uix) then
    2020           Status:=Status and not usWaiting
    2021         else Status:=Status or usWaiting;
    2022         CheckToldNoReturn(uix);
    2023         if Status and usGoto<>0 then
    2024           begin {continue multi-turn goto}
    2025           SetUnFocus(uix);
    2026           SetTroopLoc(Loc);
    2027           FocusOnLoc(TroopLoc,flRepaintPanel or flImmUpdate);
    2028           if Status shr 16=$7FFF then
    2029             MoveResult:=GetMoveAdvice(UnFocus,maNextCity,MoveAdviceData)
    2030           else MoveResult:=GetMoveAdvice(UnFocus,Status shr 16,MoveAdviceData);
    2031           if MoveResult>=rExecuted then
    2032             begin // !!! Shinkansen
    2033             MoveResult:=eOK;
    2034             ok:=true;
    2035             for i:=0 to MoveAdviceData.nStep-1 do
    2036               begin
    2037               Loc1:=dLoc(Loc,MoveAdviceData.dx[i],MoveAdviceData.dy[i]);
    2038               if (MyMap[Loc1] and (fCity or fOwned)=fCity) // don't capture cities during auto move
    2039                 or (MyMap[Loc1] and (fUnit or fOwned)=fUnit) then // don't attack during auto move
    2040                 begin ok:=false; Break end
    2041               else
    2042                 begin
    2043                 if (Loc1=MoveAdviceData.ToLoc) or (MoveAdviceData.ToLoc=maNextCity)
    2044                   and (MyMap[dLoc(Loc,MoveAdviceData.dx[i],MoveAdviceData.dy[i])] and fCity<>0) then
    2045                   MoveOptions:=muAutoNoWait
    2046                 else MoveOptions:=0;
    2047                 MoveResult:=MoveUnit(MoveAdviceData.dx[i],MoveAdviceData.dy[i],MoveOptions);
    2048                 if (MoveResult<rExecuted) or (MoveResult=eEnemySpotted) then
    2049                   begin ok:=false; Break end;
     5725                  if OldUnrest <> NewUnrest then
     5726                  begin
     5727                    CityOptimizer_CityChange(MyUn[UnFocus].Home);
     5728                    for uix := 0 to MyRO.nUn - 1 do
     5729                      if MyUn[uix].Master = UnFocus then
     5730                        CityOptimizer_CityChange(MyUn[uix].Home);
     5731                    NeedEcoUpdate := true;
     5732                  end;
     5733                  if (MyRO.Government = gDespotism) and
     5734                    (MyModel[MyUn[UnFocus].mix].Kind = mkSpecial_TownGuard) then
     5735                  begin
     5736                    if MyMap[FromLoc] and fCity <> 0 then
     5737                    begin // town guard moved out of city in despotism -- reoptimize!
     5738                      cixChanged := MyRO.nCity - 1;
     5739                      while (cixChanged >= 0) and
     5740                        (MyCity[cixChanged].Loc <> FromLoc) do
     5741                        dec(cixChanged);
     5742                      assert(cixChanged >= 0);
     5743                      if cixChanged >= 0 then
     5744                      begin
     5745                        CityOptimizer_CityChange(cixChanged);
     5746                        NeedEcoUpdate := true;
     5747                      end;
     5748                    end;
     5749                    if (MyMap[ToLoc] and fCity <> 0) and not CityCaptured then
     5750                    begin // town guard moved into city in despotism -- reoptimize!
     5751                      cixChanged := MyRO.nCity - 1;
     5752                      while (cixChanged >= 0) and
     5753                        (MyCity[cixChanged].Loc <> ToLoc) do
     5754                        dec(cixChanged);
     5755                      assert(cixChanged >= 0);
     5756                      if cixChanged >= 0 then
     5757                      begin
     5758                        CityOptimizer_CityChange(cixChanged);
     5759                        NeedEcoUpdate := true;
     5760                      end
     5761                    end
     5762                  end
    20505763                end
    20515764              end;
    2052             Stop:=not ok or (Loc=MoveAdviceData.ToLoc)
    2053               or (MoveAdviceData.ToLoc=maNextCity) and (MyMap[Loc] and fCity<>0)
    2054             end
    2055           else
    2056             begin
    2057             MoveResult:=eOK;
    2058             Stop:=true;
    20595765            end;
    2060 
    2061           if MoveResult<>eDied then
    2062             if Stop then Status:=Status and ($FFFF-usGoto)
    2063             else Status:=Status and not usWaiting;
     5766        else
     5767          assert(false);
     5768        end;
     5769        SetTroopLoc(ToLoc);
     5770      end
     5771      else
     5772      begin { enemy unit -- attack }
     5773        if result = eBombarded then
     5774          Defender := MyRO.Territory[ToLoc]
     5775        else
     5776          Defender := MyRO.EnemyUn[euix].Owner;
     5777        { if MyRO.Treaty[Defender]=trCeaseFire then
     5778          if SimpleQuery(mkYesNo,Phrases.Lookup('FRCANCELQUERY_CEASEFIRE'),
     5779          'MSG_DEFAULT')<>mrOK then
     5780          exit; }
     5781        if (Options and muNoSuicideCheck = 0) and (result and rUnitRemoved <> 0)
     5782          and (result <> eMissionDone) then
     5783        begin // suicide query
     5784          with MyUn[UnFocus], BattleDlg.Forecast do
     5785          begin
     5786            pAtt := me;
     5787            mixAtt := mix;
     5788            HealthAtt := Health;
     5789            ExpAtt := Exp;
     5790            FlagsAtt := Flags;
    20645791          end;
    2065 
    2066         if Status and (usEnhance or usGoto)=usEnhance then
    2067           // continue terrain enhancement
     5792          BattleDlg.Forecast.Movement := MyUn[UnFocus].Movement;
     5793          Server(sGetBattleForecastEx, me, ToLoc, BattleDlg.Forecast);
     5794          BattleDlg.uix := UnFocus;
     5795          BattleDlg.ToLoc := ToLoc;
     5796          BattleDlg.IsSuicideQuery := true;
     5797          BattleDlg.ShowModal;
     5798          if BattleDlg.ModalResult <> mrOK then
     5799            exit;
     5800        end;
     5801
     5802        cixChanged := -1;
     5803        if (result and rUnitRemoved <> 0) and (MyRO.Government = gDespotism) and
     5804          (MyModel[MyUn[UnFocus].mix].Kind = mkSpecial_TownGuard) and
     5805          (MyMap[FromLoc] and fCity <> 0) then
     5806        begin // town guard died in city in despotism -- reoptimize!
     5807          cixChanged := MyRO.nCity - 1;
     5808          while (cixChanged >= 0) and (MyCity[cixChanged].Loc <> FromLoc) do
     5809            dec(cixChanged);
     5810          assert(cixChanged >= 0);
     5811        end;
     5812
     5813        for i := 0 to MyRO.nEnemyModel - 1 do
     5814          LostArmy[i] := MyRO.EnemyModel[i].Lost;
     5815        OldToTile := MyMap[ToLoc];
     5816        result := Server(sMoveUnit + DirCode, me, UnFocus, nil^);
     5817        nLostArmy := 0;
     5818        for i := 0 to MyRO.nEnemyModel - 1 do
     5819        begin
     5820          LostArmy[i] := MyRO.EnemyModel[i].Lost - LostArmy[i];
     5821          inc(nLostArmy, LostArmy[i])
     5822        end;
     5823        if result and rUnitRemoved <> 0 then
     5824        begin
     5825          UnFocus := -1;
     5826          SetTroopLoc(FromLoc);
     5827        end;
     5828        if (OldToTile and not MyMap[ToLoc] and fCity <> 0) and
     5829          (MyRO.Government in [gRepublic, gDemocracy, gFuture]) then
     5830        begin // city was destroyed, borders have moved, unrest might have changed in any city
     5831          CityOptimizer_BeginOfTurn;
     5832          NeedEcoUpdate := true;
     5833        end
     5834        else
     5835        begin
     5836          if cixChanged >= 0 then
    20685837          begin
    2069           MoveResult:=ProcessEnhancement(uix,MyData.EnhancementJobs);
    2070           if MoveResult<>eDied then
    2071             if MoveResult=eJobDone then Status:=Status and not usEnhance
    2072             else Status:=Status and not usWaiting;
    2073           end
    2074         end;
    2075     end; // ClientMode=cTurn
    2076 
    2077   HaveStrategyAdvice:= false;
    2078 //    (GameMode<>cMovie) and not supervising
    2079 //    and AdvisorDlg.HaveStrategyAdvice;
    2080   GoOnPhase:=true;
    2081   if supervising or (GameMode=cMovie) then
    2082     begin SetTroopLoc(-1); PaintAll end {supervisor}
    2083 {  else if (ClientMode=cTurn) and (MyRO.Turn=0) then
    2084     begin
    2085     SetUnFocus(0);
    2086     ZoomToCity(MyCity[0].Loc)
    2087     end}
    2088   else
    2089     begin
    2090     if ClientMode>=scContact then SetUnFocus(-1)
    2091     else NextUnit(-1,false);
    2092     if UnFocus<0 then
    2093       begin
    2094       UnStartLoc:=-1;
    2095       if IsMultiPlayerGame or (ClientMode=cResume) then
    2096         if MyRO.nCity>0 then FocusOnLoc(MyCity[0].Loc)
    2097         else FocusOnLoc(G.lx*G.ly div 2);
    2098       SetTroopLoc(-1);
    2099       PanelPaint
    2100       end;
    2101     if ShowCityList then
    2102       ListDlg.ShowNewContent(wmPersistent,kCityEvents);
    2103     end;
    2104   end;{InitTurn}
    2105 
    2106 var
    2107 i,j,p1,mix,ToLoc,AnimationSpeed,ShowMoveDomain,cix,ecix: integer;
    2108 Color: TColor;
    2109 Name,s: string;
    2110 TribeInfo: TTribeInfo;
    2111 mi: TModelInfo;
    2112 SkipTurn,IsAlpine,IsTreatyDeal: boolean;
    2113 
    2114 begin {>>>client}
    2115 case command of
    2116   cTurn,cResume,cContinue,cMovieTurn,scContact,scDipStart..scDipBreak:
    2117     begin
    2118     supervising:= G.Difficulty[NewPlayer]=0;
    2119     ArrangeMidPanel;
    2120     end
    2121   end; 
    2122 case Command of
    2123   cDebugMessage:
    2124     LogDlg.Add(NewPlayer, G.RO[0].Turn, pchar(@Data));
    2125 
    2126   cShowNego: with TShowNegoData(Data) do
    2127     begin
    2128     s:=Format('P%d to P%d: ',[pSender,pTarget]);
    2129     if (Action=scDipOffer) and (Offer.nDeliver+Offer.nCost>0) then
    2130       begin
    2131       s:=s+'Offer ';
    2132       for i:=0 to Offer.nDeliver+Offer.nCost-1 do
    2133         begin
    2134         if i=Offer.nDeliver then s:=s+' for '
    2135         else if i>0 then s:=s+'+';
    2136         case Offer.Price[i] and opMask of
    2137           opChoose: s:=s+'Price of choice';
    2138           opCivilReport: s:=s+'State report';
    2139           opMilReport: s:=s+'Military report';
    2140           opMap: s:=s+'Map';
    2141           opTreaty: s:=s+'Treaty';
    2142           opShipParts: s:=s+'Ship part';
    2143           opMoney: s:=s+InttoStr(Offer.Price[i] and $FFFFFF)+'o';
    2144           opTribute: s:=s+InttoStr(Offer.Price[i] and $FFFFFF)+'o tribute';
    2145           opTech: s:=s+Phrases.Lookup('ADVANCES', Offer.Price[i] and $FFFFFF);
    2146           opAllTech: s:=s+'All advances';
    2147           opModel: s:=s+Tribe[pSender].ModelName[Offer.Price[i] and $FFFFFF];
    2148           opAllModel: s:=s+'All models';
    2149           end
    2150         end;
    2151       LogDlg.Add(NewPlayer, G.RO[0].Turn, pchar(s));
    2152       end
    2153     else if Action=scDipAccept then
    2154       begin
    2155       s:=s+'--- ACCEPTED! ---';
    2156       LogDlg.Add(NewPlayer, G.RO[0].Turn, pchar(s));
    2157       end
    2158     end;
    2159 
    2160   cInitModule:
    2161     begin
    2162     Server:=TInitModuleData(Data).Server;
    2163     //AdvisorDlg.Init;
    2164     InitModule;
    2165     TInitModuleData(Data).DataSize:=SizeOf(TPersistentData);
    2166     TInitModuleData(Data).Flags:=aiThreaded;
    2167     end;
    2168 
    2169   cReleaseModule:
    2170     begin
    2171     SmallImp.Free;
    2172     UnusedTribeFiles.Free;
    2173     TribeNames.Free;
    2174     MainMap.Free;
    2175     IsoEngine.Done;
    2176     //AdvisorDlg.DeInit;
    2177     end;
    2178 
    2179   cHelpOnly,cStartHelp,cStartCredits:
    2180     begin
    2181     Age:=0;
    2182     if Command=cHelpOnly then
    2183       SetMainTextureByAge(-1);
    2184     Tribes.Init;
    2185     HelpDlg.UserLeft:=(Screen.Width-HelpDlg.Width) div 2;
    2186     HelpDlg.UserTop:=(Screen.Height-HelpDlg.Height) div 2;
    2187     HelpDlg.Difficulty:=0;
    2188     if Command=cStartCredits then
    2189       HelpDlg.ShowNewContent(wmModal, hkMisc, miscCredits)
    2190     else HelpDlg.ShowNewContent(wmModal, hkMisc, miscMain);
    2191     Tribes.Done;
    2192     end;
    2193 
    2194   cNewGame,cLoadGame,cMovie,cNewMap:
    2195     begin
    2196     {if (Command=cNewGame) or (Command=cLoadGame) then
    2197       AdvisorDlg.NewGame(Data);}
    2198     GenerateNames:=mNames.Checked;
    2199     GameOK:=true;
    2200     G:=TNewGameData(Data);
    2201     me:=-1;
    2202     pLogo:=-1;
    2203     ClientMode:=-1;
    2204     SetMapOptions;
    2205     IsoEngine.pDebugMap:=-1;
    2206     idle:=false;
    2207     FillChar(Jump,SizeOf(Jump),0);
    2208     if StartRunning then Jump[0]:=999999;
    2209     GameMode:=Command;
    2210     for i:=0 to nGrExt-1 do
    2211       FillChar(GrExt[i].pixUsed,GrExt[i].Data.Height div 49 *10,0);
    2212     IsoEngine.Reset;
    2213     Tribes.Init;
    2214     GetTribeList;
    2215     for p1:=0 to nPl-1 do if (G.RO[p1]<>nil) and (G.RO[p1].Data<>nil) then
    2216       with TPersistentData(G.RO[p1].Data^) do
    2217         begin
    2218         FarTech:=adNone;
    2219         FillChar(EnhancementJobs,SizeOf(EnhancementJobs),jNone);
    2220         FillChar(ImpOrder,SizeOf(ImpOrder),-1);
    2221         ColdWarStart:=-ColdWarTurns-1;
    2222         ToldAge:=-1;
    2223         ToldModels:=3;
    2224         ToldAlive:=0;
    2225         ToldContact:=0;
    2226         ToldOwnCredibility:=InitialCredibility;
    2227         for i:=0 to nPl-1 do if G.Difficulty[i]>0 then inc(ToldAlive,1 shl i);
    2228         PeaceEvaHappened:=0;
    2229         for i:=0 to 27 do with ToldWonders[i] do
    2230           begin CityID:=-1; EffectiveOwner:=-1 end;
    2231         FillChar(ToldTech,SizeOf(ToldTech),tsNA);
    2232         if G.Difficulty[p1]>0 then
    2233           SoundPreload(sbStart);
    2234         end;
    2235 
    2236     // arrange dialogs
    2237     ListDlg.UserLeft:=8;
    2238     ListDlg.UserTop:=TopBarHeight+8;
    2239     HelpDlg.UserLeft:=Screen.Width-HelpDlg.Width-8;
    2240     HelpDlg.UserTop:=TopBarHeight+8;
    2241     UnitStatDlg.UserLeft:=397;
    2242     UnitStatDlg.UserTop:=TopBarHeight+64;
    2243     DiaDlg.UserLeft:=(Screen.Width-DiaDlg.Width) div 2;
    2244     DiaDlg.UserTop:=(Screen.Height-DiaDlg.Height) div 2;
    2245     NatStatDlg.UserLeft:=Screen.Width-NatStatDlg.Width-8;
    2246     NatStatDlg.UserTop:=Screen.Height-PanelHeight-NatStatDlg.Height-8;
    2247     if NatStatDlg.UserTop<8 then
    2248       NatStatDlg.UserTop:=8;
    2249 
    2250     Age:=0;
    2251     MovieSpeed:=1;
    2252     LogDlg.mSlot.Visible:=true;
    2253     LogDlg.Host:=self;
    2254     HelpDlg.ClearHistory;
    2255     CityDlg.Reset;
    2256 
    2257     Mini.Width:=G.lx*2; Mini.Height:=G.ly;
    2258     for i:=0 to nPl-1 do
    2259       begin Tribe[i]:=nil; TribeOriginal[i]:=false; end;
    2260     ToldSlavery:=-1;
    2261     RepaintOnResize:=false;
    2262     Closable:=false;
    2263     FirstMovieTurn:=true;
    2264 
    2265     MenuArea.Visible:= GameMode<>cMovie;
    2266     TreasuryArea.Visible:= GameMode<cMovie;
    2267     ResearchArea.Visible:= GameMode<cMovie;
    2268     ManagementArea.Visible:= GameMode<cMovie;
    2269     end;
    2270 
    2271   cGetReady,cReplay: if NewPlayer=0 then
    2272     begin
    2273     i:=0;
    2274     for p1:=0 to nPl-1 do
    2275       if (G.Difficulty[p1]>0) and (Tribe[p1]=nil) then inc(i);
    2276     if i>UnusedTribeFiles.Count then
    2277       begin
    2278       GameOK:=false;
    2279       SimpleMessage(Phrases.Lookup('TOOFEWTRIBES'));
    2280       end
    2281     else
    2282       begin
    2283       for p1:=0 to nPl-1 do
    2284         if (G.Difficulty[p1]>0) and (Tribe[p1]=nil) and (G.RO[p1]<>nil) then
    2285           begin // let player select own tribes
    2286           TribeInfo.trix:=p1;
    2287           TribeNames.Clear;
    2288           for j:=0 to UnusedTribeFiles.Count-1 do
    2289             begin
    2290             GetTribeInfo(UnusedTribeFiles[j], Name, Color);
    2291             TribeNames.AddObject(Name,TObject(Color));
    2292             end;
    2293           assert(TribeNames.Count>0);
    2294           ModalSelectDlg.ShowNewContent(wmModal,kTribe);
    2295           Application.ProcessMessages;
    2296           TribeInfo.FileName:=UnusedTribeFiles[ModalSelectDlg.result];
    2297           UnusedTribeFiles.Delete(ModalSelectDlg.result);
    2298 
    2299           if GameMode=cLoadGame then
    2300             CreateTribe(TribeInfo.trix,TribeInfo.FileName,false)
    2301           else Server(cSetTribe+(Length(TribeInfo.FileName)+1+7) div 4,
    2302             0,0,TribeInfo);
     5838            CityOptimizer_CityChange(cixChanged);
     5839            NeedEcoUpdate := true;
    23035840          end;
    2304 
    2305       for p1:=0 to nPl-1 do
    2306         if (G.Difficulty[p1]>0) and (Tribe[p1]=nil) and (G.RO[p1]=nil) then
    2307           begin // autoselect enemy tribes
    2308           j:=ChooseUnusedTribe;
    2309           TribeInfo.FileName:=UnusedTribeFiles[j];
    2310           UnusedTribeFiles.Delete(j);
    2311           TribeInfo.trix:=p1;
    2312           if GameMode=cLoadGame then
    2313             CreateTribe(TribeInfo.trix,TribeInfo.FileName,false)
    2314           else Server(cSetTribe+(Length(TribeInfo.FileName)+1+7) div 4,
    2315             0,0,TribeInfo);
     5841          if (result = eWon) or (result = eBloody) or (result = eExpelled) then
     5842          begin
     5843            CityOptimizer_TileBecomesAvailable(ToLoc);
     5844            NeedEcoUpdate := true;
    23165845          end;
    2317       end;
    2318     if not mNames.Checked then
    2319       for p1:=0 to nPl-1 do if Tribe[p1]<>nil then
    2320         Tribe[p1].NumberName:=p1;
    2321     end;
    2322 
    2323   cBreakGame:
    2324     begin
    2325     SaveSettings;
    2326     CityDlg.CloseAction:=None;
    2327     for i:=0 to Screen.FormCount-1 do
    2328       if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    2329         Screen.Forms[i].Close;
    2330     if LogDlg.Visible then LogDlg.Close;
    2331     LogDlg.List.Clear;
    2332     StartRunning:=not idle and (Jump[0]>0); // AI called Reload
    2333     me:=-1;
    2334     idle:=false;
    2335     ClientMode:=-1;
    2336     UnitInfoBtn.Visible:=false;
    2337     UnitBtn.Visible:=false;
    2338     TerrainBtn.Visible:=false;
    2339     MovieSpeed1Btn.Visible:=false;
    2340     MovieSpeed2Btn.Visible:=false;
    2341     MovieSpeed3Btn.Visible:=false;
    2342     MovieSpeed4Btn.Visible:=false;
    2343     EOT.Visible:=false;
    2344     for i:=0 to ControlCount-1 do if Controls[i] is TButtonC then
    2345       Controls[i].visible:=false;
    2346     InitPVSB(sb,0,1);
    2347     for p1:=0 to nPl-1 do if Tribe[p1]<>nil then Tribe[p1].Free;
    2348     Tribes.Done;
    2349     RepaintOnResize:=false;
    2350     Closable:=true; Close;
    2351     {if (GameMode=cNewGame) or (GameMode=cLoadGame) then
    2352       AdvisorDlg.BreakGame;}
    2353     end;
    2354 
    2355   cShowGame:
    2356     begin
    2357     with Panel.Canvas do
    2358       begin
    2359       Brush.Color:=$000000;
    2360       FillRect(Rect(0,0,Panel.Width,Panel.Height));
    2361       Brush.Style:=bsClear;
    2362       end;
    2363     with TopBar.Canvas do
    2364       begin
    2365       Brush.Color:=$000000;
    2366       FillRect(Rect(0,0,TopBar.Width,TopBar.Height));
    2367       Brush.Style:=bsClear;
    2368       end;
    2369     FormResize(nil); // place mini map correctly according to its size
    2370     Show;
    2371     Update;
    2372     RepaintOnResize:=true;
    2373     xw:=0; yw:=ywcenter;
    2374     if not StayOnTop_Ensured then
    2375       begin
    2376       StayOnTop_Ensured:=true;
    2377       CityDlg.StayOnTop_Workaround;
    2378       CityTypeDlg.StayOnTop_Workaround;
    2379       DiaDlg.StayOnTop_Workaround;
    2380       DraftDlg.StayOnTop_Workaround;
    2381       EnhanceDlg.StayOnTop_Workaround;
    2382       HelpDlg.StayOnTop_Workaround;
    2383       NatStatDlg.StayOnTop_Workaround;
    2384       NegoDlg.StayOnTop_Workaround;
    2385       ModalSelectDlg.StayOnTop_Workaround;
    2386       ListDlg.StayOnTop_Workaround;
    2387       UnitStatDlg.StayOnTop_Workaround;
    2388       WondersDlg.StayOnTop_Workaround;
    2389       RatesDlg.StayOnTop_Workaround;
    2390       end;
    2391     end;
    2392 
    2393   cShowTurnChange:
    2394     begin
    2395     if integer(data)>=0 then
    2396       begin
    2397       pLogo:=integer(data);
    2398       if G.RO[pLogo]=nil then
    2399         begin
    2400         if AILogo[pLogo]<>nil then
    2401           BitBlt(Canvas.Handle, (xRightPanel+10)-(16+64), ClientHeight-PanelHeight, 64,64,
    2402             AILogo[pLogo].Canvas.Handle,0,0,SRCCOPY);
    2403         end
    2404       end 
    2405     end;
    2406 
    2407   cTurn,cResume,cContinue:
    2408     if not GameOK then Server(sResign,NewPlayer,0,nil^)
    2409     else
    2410       begin
    2411       ClientMode:=Command;
    2412       pTurn:=NewPlayer;
    2413       pLogo:=NewPlayer;
    2414 
    2415       if Command=cResume then
    2416         begin // init non-original model pictures (maybe tribes not found)
    2417         for p1:=0 to nPl-1 do if G.RO[p1]<>nil then
     5846        end;
     5847        if nLostArmy > 1 then
     5848        begin
     5849          with MessgExDlg do
    24185850          begin
    2419           ItsMeAgain(p1);
    2420           for mix:=0 to MyRO.nModel-1 do
    2421             if Tribe[me].ModelPicture[mix].HGr=0 then
    2422               InitMyModel(mix, true);
    2423           end;
    2424         me:=-1;
    2425         end;
    2426 
    2427       if Jump[pTurn]>0 then
    2428         Application.ProcessMessages;
    2429       if Jump[pTurn]>0 then
    2430         if G.RO[NewPlayer].Happened and phGameEnd<>0 then Jump[pTurn]:=0
    2431         else dec(Jump[pTurn]);
    2432       SkipTurn:= Jump[pTurn]>0;
    2433       if SkipTurn then
    2434         begin
    2435         ItsMeAgain(NewPlayer);
    2436         MyData:=G.RO[NewPlayer].Data;
    2437         SetTroopLoc(-1);
    2438         MiniPaint;
    2439         InitAllEnemyModels; // necessary for correct replay
    2440         if not EndTurn(true) then SkipTurn:=false;
    2441         end;
    2442       if not SkipTurn then
    2443         begin
    2444         if ((ClientMode<scDipStart) or (ClientMode>scDipBreak))
    2445           and NegoDlg.Visible then
    2446           NegoDlg.Close;
    2447         skipped:=false; // always show my moves during my turn
    2448         idle:=true;
    2449         InitTurn(NewPlayer);
    2450         DipMem[me].pContact:=-1;
    2451   (*      if (me=0) and (MyRO.Alive and (1 shl me)=0)} then
    2452           begin
    2453           if SimpleQuery(Phrases.Lookup('RESIGN'))=mrIgnore then
    2454             Server(sResign,me,0,nil^)
    2455           else Server(sBreak,me,0,nil^)
    2456           end
    2457         else Play('TURNSTART');*)
    2458         end;
    2459       end;
    2460 
    2461   cMovieTurn:
    2462     begin
    2463     ClientMode:=Command;
    2464     pTurn:=NewPlayer;
    2465     pLogo:=-1;
    2466     skipped:=false; // always show my moves during my turn
    2467     idle:=true;
    2468     if FirstMovieTurn then
    2469       begin
    2470       CheckMovieSpeedBtnState;
    2471       FirstMovieTurn:=false;
    2472       end;
    2473     InitTurn(NewPlayer);
    2474     Application.ProcessMessages;
    2475     if MovieSpeed=4 then
    2476       begin
    2477       Sleep(75); // this break will ensure speed of fast forward does not depend on cpu speed
    2478       Application.ProcessMessages;
    2479       end
    2480     end;
    2481 
    2482   cMovieEndTurn:
    2483     begin
    2484     RememberPeaceViolation;
    2485     pTurn:=-1;
    2486     pLogo:=-1;
    2487     MapValid:=false;
    2488     ClientMode:=-1;
    2489     idle:=false;
    2490     skipped:=false;
    2491     end;
    2492 
    2493   cEditMap:
    2494     begin
    2495     ClientMode:=cEditMap;
    2496     SetMapOptions;
    2497     IsoEngine.pDebugMap:=-1;
    2498     ItsMeAgain(0);
    2499     MyData:=nil;
    2500     UnitInfoBtn.Visible:=false;
    2501     UnitBtn.Visible:=false;
    2502     TerrainBtn.Visible:=false;
    2503     MovieSpeed1Btn.Visible:=false;
    2504     MovieSpeed2Btn.Visible:=false;
    2505     MovieSpeed3Btn.Visible:=false;
    2506     MovieSpeed4Btn.Visible:=false;
    2507     EOT.Visible:=false;
    2508     HelpDlg.Difficulty:=0;
    2509     BrushType:=fGrass;
    2510     BrushLoc:=-1;
    2511     Edited:=false;
    2512     UnFocus:=-1;
    2513     MarkCityLoc:=-1;
    2514     Tracking:=false;
    2515     TurnComplete:=false;
    2516     MapValid:=false;
    2517     FormResize(nil); // calculate geometrics and paint all
    2518     SetTroopLoc(-1);
    2519     idle:=true
    2520     end;
    2521 
    2522 (*  cNewContact:
    2523     begin
    2524     end;
    2525 *)
    2526 
    2527   scContact:
    2528     begin
    2529     DipMem[NewPlayer].pContact:=integer(Data);
    2530     if Jump[NewPlayer]>0 then DipCall(scReject)
    2531     else
    2532       begin
    2533       ClientMode:=Command;
    2534       InitTurn(NewPlayer);
    2535       MyData.ToldContact:=MyData.ToldContact or (1 shl integer(Data));
    2536         // don't tell about new nation when already contacted by them
    2537       with MessgExDlg do
    2538         begin
    2539         OpenSound:='CONTACT_'+char(48+MyRO.EnemyReport[integer(Data)].Attitude);
    2540         MessgText:=Tribe[integer(Data)].TPhrase('FRCONTACT');
    2541         Kind:=mkYesNo;
    2542         IconKind:=mikTribe;
    2543         IconIndex:=integer(Data);
    2544         ShowModal;
    2545         if ModalResult=mrOK then
    2546           begin
    2547           NegoDlg.Respond;
    2548           DipMem[me].DeliveredPrices:=[];
    2549           DipMem[me].ReceivedPrices:=[];
    2550           DipCall(scDipStart)
    2551           end
    2552         else
    2553           begin
    2554           DipCall(scReject);
    2555           EndNego
     5851            Kind := mkOk;
     5852            IconKind := mikEnemyArmy;
     5853            MessgText := Tribe[Defender].TString(Phrases.Lookup('ARMYLOST',
     5854              MyRO.EnemyModel[MyRO.EnemyUn[euix].emix].Domain));
     5855            ShowModal;
    25565856          end
    25575857        end
    25585858      end;
    2559     end;
    2560 
    2561   scDipStart..scDipBreak:
    2562     begin
    2563     ClientMode:=Command;
    2564     InitTurn(NewPlayer);
    2565     if Command=scDipStart then
    2566       Play('CONTACT_'+char(48+MyRO.Attitude[DipMem[NewPlayer].pContact]))
    2567     else if Command=scDipCancelTreaty then
    2568       Play('CANCELTREATY')
    2569     else if Command=scDipOffer then
    2570       begin
    2571       ReceivedOffer:=TOffer(Data);
    2572       InitAllEnemyModels;
     5859      if result and rUnitRemoved <> 0 then
     5860      begin
     5861        CityOptimizer_AfterRemoveUnit;
     5862        ListDlg.RemoveUnit;
     5863        NeedEcoUpdate := true;
     5864      end;
     5865      if NeedEcoUpdate then
     5866      begin
     5867        UpdateViews(true);
     5868        Update
    25735869      end
    2574     else if Command=scDipAccept then
    2575       begin // remember delivered and received prices
    2576       for i:=0 to DipMem[me].SentOffer.nDeliver-1 do
    2577         include(DipMem[me].DeliveredPrices,DipMem[me].SentOffer.Price[i] shr 24);
    2578       for i:=0 to DipMem[me].SentOffer.nCost-1 do
    2579         include(DipMem[me].ReceivedPrices,
    2580           DipMem[me].SentOffer.Price[DipMem[me].SentOffer.nDeliver+i] shr 24);
    2581       IsTreatyDeal:=false;
    2582       for i:=0 to ReceivedOffer.nDeliver+ReceivedOffer.nCost-1 do
    2583         if DipMem[me].SentOffer.Price[i] and opMask=opTreaty then
    2584           IsTreatyDeal:=true;
    2585       if IsTreatyDeal then Play('NEWTREATY')
    2586       else Play('ACCEPTOFFER');
     5870    end;
     5871
     5872    if result = eMissionDone then
     5873    begin
     5874      p1 := MyRO.Territory[ToLoc];
     5875      case Mission of
     5876        smStealMap:
     5877          begin
     5878            MapValid := false;
     5879            PaintAllMaps
     5880          end;
     5881        smStealCivilReport:
     5882          TribeMessage(p1, Tribe[p1].TPhrase('DOSSIER_PREPARED'), '');
     5883        smStealMilReport:
     5884          ListDlg.ShowNewContent_MilReport(wmPersistent, p1);
    25875885      end;
    2588     NegoDlg.Start;
    2589     idle:=true
    2590     end;
    2591 
    2592   cShowCancelTreaty:
    2593     if not IsMultiPlayerGame then
    2594       begin
    2595       case G.RO[NewPlayer].Treaty[integer(data)] of
    2596         trPeace: s:=Tribe[integer(data)].TPhrase('FRCANCELBYREJECT_PEACE');
    2597         trFriendlyContact: s:=Tribe[integer(data)].TPhrase('FRCANCELBYREJECT_FRIENDLY');
    2598         trAlliance: s:=Tribe[integer(data)].TPhrase('FRCANCELBYREJECT_ALLIANCE');
    2599         end;
    2600       TribeMessage(integer(data), s, 'CANCELTREATY');
     5886    end;
     5887
     5888    if UnFocus >= 0 then
     5889      CheckToldNoReturn(UnFocus);
     5890
     5891    NeedRepaintPanel := false;
     5892    if result >= rExecuted then
     5893    begin
     5894      if CityCaptured and (MyMap[ToLoc] and fCity = 0) then
     5895      begin // city destroyed
     5896        for i := 0 to 27 do { tell about destroyed wonders }
     5897          if (MyRO.Wonder[i].CityID = -2) and
     5898            (MyData.ToldWonders[i].CityID <> -2) then
     5899            with MessgExDlg do
     5900            begin
     5901              if WondersDlg.Visible then
     5902                WondersDlg.SmartUpdateContent(false);
     5903              OpenSound := 'WONDER_DESTROYED';
     5904              MessgText := Format(Phrases.Lookup('WONDERDEST'),
     5905                [Phrases.Lookup('IMPROVEMENTS', i)]);
     5906              Kind := mkOkHelp;
     5907              HelpKind := hkImp;
     5908              HelpNo := i;
     5909              IconKind := mikImp;
     5910              IconIndex := i;
     5911              ShowModal;
     5912              MyData.ToldWonders[i] := MyRO.Wonder[i];
     5913            end
    26015914      end;
    2602 
    2603   cShowCancelTreatyByAlliance:
    2604     if idle and (NewPlayer=me) then
    2605       TribeMessage(integer(data), Tribe[integer(data)].TPhrase('FRENEMYALLIANCE'),
    2606         'CANCELTREATY');
    2607 
    2608   cShowSupportAllianceAgainst:
    2609     if not IsMultiPlayerGame and (Jump[0]=0) then
    2610       TribeMessage(integer(data) and $F,
    2611         Tribe[integer(data) and $F].TPhrase('FRMYALLIANCE1')
    2612           +' '+Tribe[integer(data) shr 4].TPhrase('FRMYALLIANCE2'),
    2613         'CANCELTREATY');
    2614 
    2615   cShowPeaceViolation:
    2616     if not IsMultiPlayerGame and (Jump[0]=0) then
    2617       TribeMessage(integer(data), Format(Tribe[integer(data)].TPhrase('EVIOLATION'),
    2618         [TurnToString(MyRO.Turn+PeaceEvaTurns-1)]), 'MSG_WITHDRAW');
    2619 
    2620   cShowEndContact: EndNego;
    2621 
    2622   cShowUnitChanged,cShowCityChanged,cShowAfterMove,cShowAfterAttack:
    2623     if (idle and (NewPlayer=me) or not idle and not skipped)
    2624       and not ((GameMode=cMovie) and (MovieSpeed=4)) then
    2625       begin
    2626       assert(NewPlayer=me);
    2627       if not idle or (GameMode=cMovie) then
    2628         Application.ProcessMessages;
    2629       if Command=cShowCityChanged then
    2630         begin
    2631         CurrentMoveInfo.DoShow:=false;
    2632         if idle then
    2633           CurrentMoveInfo.DoShow:=true
    2634         else if CurrentMoveInfo.IsAlly then
    2635           CurrentMoveInfo.DoShow:=not mAlNoMoves.Checked
    2636         else CurrentMoveInfo.DoShow:=not mEnNoMoves.Checked
     5915      if CityCaptured and (MyMap[ToLoc] and fCity <> 0) then
     5916      begin // city captured
     5917        ListDlg.AddCity;
     5918        for i := 0 to 27 do { tell about capture of wonders }
     5919          if MyRO.City[MyRO.nCity - 1].Built[i] > 0 then
     5920            with MessgExDlg do
     5921            begin
     5922              if WondersDlg.Visible then
     5923                WondersDlg.SmartUpdateContent(false);
     5924              OpenSound := 'WONDER_CAPTURED';
     5925              MessgText := Format(Tribe[me].TPhrase('WONDERCAPTOWN'),
     5926                [Phrases.Lookup('IMPROVEMENTS', i)]);
     5927              Kind := mkOkHelp;
     5928              HelpKind := hkImp;
     5929              HelpNo := i;
     5930              IconKind := mikImp;
     5931              IconIndex := i;
     5932              ShowModal;
     5933              MyData.ToldWonders[i] := MyRO.Wonder[i];
     5934            end;
     5935
     5936        if MyRO.Happened and phStealTech <> 0 then
     5937        begin { Temple of Zeus -- choose advance to steal }
     5938          ModalSelectDlg.ShowNewContent(wmModal, kStealTech);
     5939          Server(sStealTech, me, ModalSelectDlg.result, nil^);
     5940        end;
     5941        TellNewModels;
     5942
     5943        cix := MyRO.nCity - 1;
     5944        while (cix >= 0) and (MyCity[cix].Loc <> ToLoc) do
     5945          dec(cix);
     5946        assert(cix >= 0);
     5947        MyCity[cix].Status := MyCity[cix].Status and
     5948          not csResourceWeightsMask or (3 shl 4);
     5949        // captured city, set to maximum growth
     5950        NewTiles := 1 shl 13; { exploit central tile only }
     5951        Server(sSetCityTiles, me, cix, NewTiles);
     5952      end
     5953      else
     5954        NeedRepaintPanel := true;
     5955    end;
     5956    TellNewContacts;
     5957
     5958    if (UnFocus >= 0) and (MyUn[UnFocus].Master >= 0) then
     5959      with MyUn[MyUn[UnFocus].Master] do
     5960        if Status and usStay <> 0 then
     5961        begin
     5962          Status := Status and not usStay;
     5963          if (Movement >= 100) and (Status and (usRecover or usGoto) = 0) then
     5964            Status := Status or usWaiting;
     5965        end;
     5966    if Options and (muAutoNoWait or muAutoNext) <> 0 then
     5967    begin
     5968      if (UnFocus >= 0) and ((result = eNoTime_Move) or UnitExhausted(UnFocus)
     5969        or (MyUn[UnFocus].Master >= 0) or
     5970        (MyModel[MyUn[UnFocus].mix].Domain = dAir) and
     5971        ((MyMap[MyUn[UnFocus].Loc] and fCity <> 0) { aircrafts stop in cities }
     5972        or (MyMap[MyUn[UnFocus].Loc] and fTerImp = tiBase))) then
     5973      begin
     5974        MyUn[UnFocus].Status := MyUn[UnFocus].Status and not usWaiting;
     5975        if Options and muAutoNext <> 0 then
     5976          if CityCaptured and (MyMap[ToLoc] and fCity <> 0) then
     5977          begin
     5978            UnFocus := -1;
     5979            PaintLoc(ToLoc); // don't show unit in city if not selected
     5980          end
     5981          else
     5982            NextUnit(UnStartLoc, true)
     5983      end
     5984      else if (UnFocus < 0) and (Options and muAutoNext <> 0) then
     5985        NextUnit(UnStartLoc, result <> eMissionDone);
     5986    end;
     5987
     5988    if NeedRepaintPanel and (UnFocus = UnFocus0) then
     5989      if IsAttack then
     5990        PanelPaint
     5991      else
     5992      begin
     5993        assert(result <> eMissionDone);
     5994        CheckTerrainBtnVisible;
     5995        FocusOnLoc(ToLoc, flRepaintPanel or flImmUpdate)
     5996      end;
     5997
     5998    if (result >= rExecuted) and CityCaptured and (MyMap[ToLoc] and fCity <> 0)
     5999    then
     6000      ZoomToCity(ToLoc, UnFocus < 0, chCaptured); // show captured city
     6001  end; // moveunit
     6002
     6003  procedure TMainScreen.MoveOnScreen(ShowMove: TShowMove;
     6004    Step0, Step1, nStep: integer; Restore: boolean = true);
     6005  var
     6006    ToLoc, xFromLoc, yFromLoc, xToLoc, yToLoc, xFrom, yFrom, xTo, yTo, xMin,
     6007      yMin, xRange, yRange, xw1, Step, xMoving, yMoving, yl,
     6008      SliceCount: integer;
     6009    UnitInfo: TUnitInfo;
     6010    Ticks0, Ticks: int64;
     6011  begin
     6012    Timer1.Enabled := false;
     6013    QueryPerformanceCounter(Ticks0);
     6014    with ShowMove do
     6015    begin
     6016      UnitInfo.Owner := Owner;
     6017      UnitInfo.mix := mix;
     6018      UnitInfo.Health := Health;
     6019      UnitInfo.Job := jNone;
     6020      UnitInfo.Flags := Flags;
     6021      if Owner <> me then
     6022        UnitInfo.emix := emix;
     6023
     6024      ToLoc := dLoc(FromLoc, dx, dy);
     6025      xToLoc := ToLoc mod G.lx;
     6026      yToLoc := ToLoc div G.lx;
     6027      xFromLoc := FromLoc mod G.lx;
     6028      yFromLoc := FromLoc div G.lx;
     6029      if xToLoc > xFromLoc + 2 then
     6030        xToLoc := xToLoc - G.lx
     6031      else if xToLoc < xFromLoc - 2 then
     6032        xToLoc := xToLoc + G.lx;
     6033
     6034      xw1 := xw + G.lx;
     6035      // ((xFromLoc-xw1)*2+yFromLoc and 1+1)*xxt+dx*xxt/2-MapWidth/2 -> min
     6036      while abs(((xFromLoc - xw1 + G.lx) * 2 + yFromLoc and 1 + 1) * xxt * 2 +
     6037        dx * xxt - MapWidth) < abs(((xFromLoc - xw1) * 2 + yFromLoc and 1 + 1) *
     6038        xxt * 2 + dx * xxt - MapWidth) do
     6039        dec(xw1, G.lx);
     6040
     6041      xTo := (xToLoc - xw1) * (xxt * 2) + yToLoc and 1 * xxt + (xxt - xxu);
     6042      yTo := (yToLoc - yw) * yyt + (yyt - yyu_anchor);
     6043      xFrom := (xFromLoc - xw1) * (xxt * 2) + yFromLoc and 1 * xxt +
     6044        (xxt - xxu);
     6045      yFrom := (yFromLoc - yw) * yyt + (yyt - yyu_anchor);
     6046      if xFrom < xTo then
     6047      begin
     6048        xMin := xFrom;
     6049        xRange := xTo - xFrom
     6050      end
     6051      else
     6052      begin
     6053        xMin := xTo;
     6054        xRange := xFrom - xTo
     6055      end;
     6056      if yFrom < yTo then
     6057      begin
     6058        yMin := yFrom;
     6059        yRange := yTo - yFrom
     6060      end
     6061      else
     6062      begin
     6063        yMin := yTo;
     6064        yRange := yFrom - yTo
     6065      end;
     6066      inc(xRange, xxt * 2);
     6067      inc(yRange, yyt * 3);
     6068
     6069      MainOffscreenPaint;
     6070      NoMap.SetOutput(Buffer);
     6071      NoMap.SetPaintBounds(0, 0, xRange, yRange);
     6072      for Step := 0 to abs(Step1 - Step0) do
     6073      begin
     6074        BitBlt(Buffer.Canvas.Handle, 0, 0, xRange, yRange,
     6075          offscreen.Canvas.Handle, xMin, yMin, SRCCOPY);
     6076        if Step1 <> Step0 then
     6077        begin
     6078          xMoving := xFrom +
     6079            Round((Step0 + Step * (Step1 - Step0) div abs(Step1 - Step0)) *
     6080            (xTo - xFrom) / nStep);
     6081          yMoving := yFrom +
     6082            Round((Step0 + Step * (Step1 - Step0) div abs(Step1 - Step0)) *
     6083            (yTo - yFrom) / nStep);
    26376084        end
    2638       else if Command=cShowUnitChanged then
    2639         begin
    2640         CurrentMoveInfo.DoShow:=false;
    2641         if idle then
    2642           CurrentMoveInfo.DoShow:=not mEffectiveMovesOnly.Checked
    2643         else if CurrentMoveInfo.IsAlly then
    2644           CurrentMoveInfo.DoShow:=not (mAlNoMoves.Checked or mAlEffectiveMovesOnly.Checked)
    2645         else CurrentMoveInfo.DoShow:=not (mEnNoMoves.Checked or mEnAttacks.Checked)
    2646         end;
    2647       // else keep DoShow from cShowMove/cShowAttack
    2648 
    2649       if CurrentMoveInfo.DoShow then
    2650         begin
    2651         if Command=cShowCityChanged then MapValid:=false;
    2652         FocusOnLoc(integer(Data),flImmUpdate);
    2653 //        OldUnFocus:=UnFocus;
    2654 //        UnFocus:=-1;
    2655         if Command=cShowAfterMove then
    2656           PaintLoc(integer(Data),CurrentMoveInfo.AfterMovePaintRadius) // show discovered areas
    2657         else PaintLoc(integer(Data),1);
    2658 //        UnFocus:=OldUnFocus;
    2659         if (Command=cShowAfterAttack) and (CurrentMoveInfo.AfterAttackExpeller>=0) then
     6085        else
     6086        begin
     6087          xMoving := xFrom;
     6088          yMoving := yFrom;
     6089        end;
     6090        NoMap.PaintUnit(xMoving - xMin, yMoving - yMin, UnitInfo, 0);
     6091        PaintBufferToScreen(xMin, yMin, xRange, yRange);
     6092
     6093        SliceCount := 0;
     6094        Ticks := Ticks0;
     6095        repeat
     6096          if (SliceCount = 0) or ((Ticks - Ticks0) * 12000 * (SliceCount + 1)
     6097            div SliceCount < MoveTime * PerfFreq) then
    26606098          begin
    2661           SoundMessageEx(Tribe[CurrentMoveInfo.AfterAttackExpeller].TPhrase('EXPEL'),'');
    2662           CurrentMoveInfo.AfterAttackExpeller:=-1;
    2663           Update; // remove message box from screen
     6099            if not idle or (GameMode = cMovie) then
     6100              Application.ProcessMessages;
     6101            Sleep(1);
     6102            inc(SliceCount)
     6103          end;
     6104          QueryPerformanceCounter(Ticks);
     6105        until (Ticks - Ticks0) * 12000 >= MoveTime * PerfFreq;
     6106        Ticks0 := Ticks
     6107      end;
     6108    end;
     6109    if Restore then
     6110    begin
     6111      BitBlt(Buffer.Canvas.Handle, 0, 0, xRange, yRange,
     6112        offscreen.Canvas.Handle, xMin, yMin, SRCCOPY);
     6113      PaintBufferToScreen(xMin, yMin, xRange, yRange);
     6114    end;
     6115    BlinkTime := -1;
     6116    Timer1.Enabled := true;
     6117  end;
     6118
     6119  procedure TMainScreen.MoveToLoc(Loc: integer; CheckSuicide: boolean);
     6120  // path finder: move focused unit to loc, start multi-turn goto if too far
     6121  var
     6122    uix, i, MoveOptions, NextLoc, MoveResult: integer;
     6123    MoveAdviceData: TMoveAdviceData;
     6124    StopReason: (None, Arrived, Dead, NoTime, EnemySpotted, MoveError);
     6125  begin
     6126    if MyUn[UnFocus].Job > jNone then
     6127      Server(sStartJob + jNone shl 4, me, UnFocus, nil^);
     6128    if GetMoveAdvice(UnFocus, Loc, MoveAdviceData) >= rExecuted then
     6129    begin
     6130      uix := UnFocus;
     6131      StopReason := None;
     6132      repeat
     6133        for i := 0 to MoveAdviceData.nStep - 1 do
     6134        begin
     6135          if i = MoveAdviceData.nStep - 1 then
     6136            MoveOptions := muAutoNext
     6137          else
     6138            MoveOptions := 0;
     6139          NextLoc := dLoc(MyUn[uix].Loc, MoveAdviceData.dx[i],
     6140            MoveAdviceData.dy[i]);
     6141          if (NextLoc = Loc) or (Loc = maNextCity) and
     6142            (MyMap[NextLoc] and fCity <> 0) then
     6143            StopReason := Arrived;
     6144          if not CheckSuicide and (NextLoc = Loc) then
     6145            MoveOptions := MoveOptions or muNoSuicideCheck;
     6146          MoveResult := MoveUnit(MoveAdviceData.dx[i], MoveAdviceData.dy[i],
     6147            MoveOptions);
     6148          if MoveResult < rExecuted then
     6149            StopReason := MoveError
     6150          else if MoveResult and rUnitRemoved <> 0 then
     6151            StopReason := Dead
     6152          else if (StopReason = None) and (MoveResult and rEnemySpotted <> 0)
     6153          then
     6154            StopReason := EnemySpotted;
     6155          if StopReason <> None then
     6156            Break;
     6157        end;
     6158        if (StopReason = None) and
     6159          ((MoveAdviceData.nStep < 25) or
     6160          (MyRO.Wonder[woShinkansen].EffectiveOwner <> me)) then
     6161          StopReason := NoTime;
     6162        if StopReason <> None then
     6163          Break;
     6164        if GetMoveAdvice(UnFocus, Loc, MoveAdviceData) < rExecuted then
     6165        begin
     6166          assert(false);
     6167          Break
     6168        end
     6169        until false;
     6170
     6171        case StopReason of
     6172          None:
     6173            assert(false);
     6174          Arrived:
     6175            MyUn[uix].Status := MyUn[uix].Status and ($FFFF - usGoto);
     6176          Dead:
     6177            if UnFocus < 0 then
     6178              NextUnit(UnStartLoc, false);
     6179        else
     6180          begin // multi-turn goto
     6181            if Loc = maNextCity then
     6182              MyUn[uix].Status := MyUn[uix].Status and
     6183                ($FFFF - usStay - usRecover) or usGoto + $7FFF shl 16
     6184            else
     6185              MyUn[uix].Status := MyUn[uix].Status and
     6186                ($FFFF - usStay - usRecover) or usGoto + Loc shl 16;
     6187            PaintLoc(MyUn[uix].Loc);
     6188            if (StopReason = NoTime) and (UnFocus = uix) then
     6189            begin
     6190              MyUn[uix].Status := MyUn[uix].Status and not usWaiting;
     6191              NextUnit(UnStartLoc, true)
     6192            end;
    26646193          end
    2665         else if not idle then
    2666           if Command=cShowCityChanged then
    2667             Sleep(MoveTime*WaitAfterShowMove div 16)
    2668           else if (Command=cShowUnitChanged)
    2669             and (MyMap[integer(Data)] and fUnit<>0) then
    2670             Sleep(MoveTime*WaitAfterShowMove div 32)
    2671         end // if CurrentMoveInfo.DoShow
    2672       else MapValid:=false;
     6194        end
     6195      end
     6196    end;
     6197
     6198    procedure TMainScreen.PanelBoxMouseDown(Sender: TObject;
     6199      Button: TMouseButton; Shift: TShiftState; x, y: integer);
     6200    var
     6201      i, xMouse, MouseLoc, p1: integer;
     6202    begin
     6203      if GameMode = cMovie then
     6204        exit;
     6205
     6206      if Button = mbLeft then
     6207      begin
     6208        if (x >= xMini + 2) and (y >= yMini + 2) and (x < xMini + 2 + 2 * G.lx)
     6209          and (y < yMini + 2 + G.ly) then
     6210          if ssShift in Shift then
     6211          begin
     6212            xMouse := (xwMini + (x - (xMini + 2) + MapWidth div (xxt * 2) +
     6213              G.lx) div 2) mod G.lx;
     6214            MouseLoc := xMouse + G.lx * (y - (yMini + 2));
     6215            if MyMap[MouseLoc] and fTerrain <> fUNKNOWN then
     6216            begin
     6217              p1 := MyRO.Territory[MouseLoc];
     6218              if (p1 = me) or (p1 >= 0) and (MyRO.Treaty[p1] >= trNone) then
     6219                NatStatDlg.ShowNewContent(wmPersistent, p1);
     6220            end
     6221          end
     6222          else
     6223          begin
     6224            if CityDlg.Visible then
     6225              CityDlg.Close;
     6226            if UnitStatDlg.Visible then
     6227              UnitStatDlg.Close;
     6228            Tracking := true;
     6229            PanelBoxMouseMove(Sender, Shift + [ssLeft], x, y);
     6230          end
     6231        else if (ClientMode <> cEditMap) and (x >= ClientWidth - xPalace) and
     6232          (y >= yPalace) and (x < ClientWidth - xPalace + xSizeBig) and
     6233          (y < yPalace + ySizeBig) then
     6234        begin
     6235          InitPopup(StatPopup);
     6236          if FullScreen then
     6237            StatPopup.Popup(Left + ClientWidth - xPalace + xSizeBig + 2,
     6238              Top + ClientHeight - PanelHeight + yPalace - 1)
     6239          else
     6240            StatPopup.Popup(Left + ClientWidth - xPalace + 6,
     6241              Top + ClientHeight - PanelHeight + yPalace + ySizeBig +
     6242              GetSystemMetrics(SM_CYCAPTION) + 3)
     6243        end
     6244        (* else if (x>=xAdvisor-3) and (y>=yAdvisor-3)
     6245          and (x<xAdvisor+16+3) and (y<yAdvisor+16+3) and HaveStrategyAdvice then
     6246          AdviceBtnClick *)
     6247        else if (x >= xTroop + 1) and (y >= yTroop + 1) and
     6248          (x < xTroop + TrRow * TrPitch) and (y <= yTroop + 55) then
     6249        begin
     6250          i := (x - xTroop - 1) div TrPitch;
     6251          if trix[i] >= 0 then
     6252            if ClientMode = cEditMap then
     6253            begin
     6254              BrushType := trix[i];
     6255              PanelPaint
     6256            end
     6257            else if (TroopLoc >= 0) then
     6258              if MyMap[TroopLoc] and fOwned <> 0 then
     6259              begin
     6260                if ssShift in Shift then
     6261                  UnitStatDlg.ShowNewContent_OwnModel(wmPersistent,
     6262                    MyUn[trix[i]].mix)
     6263                else if not supervising and (ClientMode < scContact) and
     6264                  (x - xTroop - 1 - i * TrPitch >= 60 - 20) and
     6265                  (y >= yTroop + 35) and
     6266                  ((MyUn[trix[i]].Job > jNone) or (MyUn[trix[i]].Status and
     6267                  (usStay or usRecover or usGoto) <> 0)) then
     6268                begin // wake up
     6269                  MyUn[trix[i]].Status := MyUn[trix[i]].Status and
     6270                    ($FFFF - usStay - usRecover - usGoto - usEnhance) or
     6271                    usWaiting;
     6272                  if MyUn[trix[i]].Job > jNone then
     6273                    Server(sStartJob + jNone shl 4, me, trix[i], nil^);
     6274                  if (UnFocus < 0) and not CityDlg.Visible then
     6275                  begin
     6276                    SetUnFocus(trix[i]);
     6277                    SetTroopLoc(MyUn[trix[i]].Loc);
     6278                    FocusOnLoc(TroopLoc, flRepaintPanel)
     6279                  end
     6280                  else
     6281                  begin
     6282                    if CityDlg.Visible and (CityDlg.RestoreUnFocus < 0) then
     6283                      CityDlg.RestoreUnFocus := trix[i];
     6284                    PanelPaint;
     6285                  end
     6286                end
     6287                else if (ClientMode < scContact) then
     6288                begin
     6289                  if supervising then
     6290                    UnitStatDlg.ShowNewContent_OwnUnit(wmPersistent, trix[i])
     6291                  else if CityDlg.Visible then
     6292                  begin
     6293                    CityDlg.CloseAction := None;
     6294                    CityDlg.Close;
     6295                    SumCities(TaxSum, ScienceSum);
     6296                    SetUnFocus(trix[i]);
     6297                  end
     6298                  else
     6299                  begin
     6300                    DestinationMarkON := false;
     6301                    PaintDestination;
     6302                    UnFocus := trix[i];
     6303                    UnStartLoc := TroopLoc;
     6304                    BlinkTime := 0;
     6305                    BlinkON := false;
     6306                    PaintLoc(TroopLoc);
     6307                  end;
     6308                  if UnFocus >= 0 then
     6309                  begin
     6310                    UnitInfoBtn.Visible := true;
     6311                    UnitBtn.Visible := true;
     6312                    TurnComplete := false;
     6313                    EOT.ButtonIndex := eotGray;
     6314                  end;
     6315                  CheckTerrainBtnVisible;
     6316                  PanelPaint;
     6317                end
     6318              end
     6319              else if Server(sGetUnits, me, TroopLoc, TrCnt) >= rExecuted then
     6320                if ssShift in Shift then
     6321                  UnitStatDlg.ShowNewContent_EnemyModel(wmPersistent,
     6322                    MyRO.EnemyUn[MyRO.nEnemyUn + trix[i]].emix) // model info
     6323                else
     6324                  UnitStatDlg.ShowNewContent_EnemyUnit(wmPersistent,
     6325                    MyRO.nEnemyUn + trix[i]); // unit info
     6326        end
     6327      end
     6328    end;
     6329
     6330    procedure TMainScreen.SetTroopLoc(Loc: integer);
     6331    var
     6332      trixFocus, uix, uixDefender: integer;
     6333      Prio: boolean;
     6334    begin
     6335      TroopLoc := Loc;
     6336      TrRow := (xRightPanel + 10 - xTroop - GetSystemMetrics(SM_CXVSCROLL) - 19)
     6337        div TrPitch;
     6338      TrCnt := 0;
     6339      trixFocus := -1;
     6340      if ClientMode = cEditMap then
     6341        TrCnt := nBrushTypes
     6342      else if (Loc >= 0) and (MyMap[Loc] and fUnit <> 0) then
     6343        if MyMap[Loc] and fOwned <> 0 then
     6344        begin // count own units here
     6345          Server(sGetDefender, me, TroopLoc, uixDefender);
     6346          for Prio := true downto false do
     6347            for uix := 0 to MyRO.nUn - 1 do
     6348              if ((uix = uixDefender) = Prio) and (MyUn[uix].Loc = Loc) then
     6349              begin
     6350                if uix = UnFocus then
     6351                  trixFocus := TrCnt;
     6352                inc(TrCnt);
     6353              end
     6354        end
     6355        else // count enemy units here
     6356          Server(sGetUnits, me, Loc, TrCnt);
     6357      if TrCnt = 0 then
     6358        InitPVSB(sb, 0, 1)
     6359      else
     6360      begin
     6361        InitPVSB(sb, (TrCnt + TrRow - 1) div TrRow - 1, 1);
     6362        with sb.si do
     6363          if (nMax >= integer(nPage)) and (trixFocus >= 0) then
     6364          begin
     6365            sb.si.npos := trixFocus div TrRow;
     6366            sb.si.FMask := SIF_POS;
     6367            SetScrollInfo(sb.h, SB_CTL, sb.si, true);
     6368          end
     6369      end
     6370    end;
     6371
     6372    (* procedure TMainScreen.ShowMoveHint(ToLoc: integer; Force: boolean = false);
     6373      var
     6374      Step,Loc,x0,y0,xs,ys: integer;
     6375      Info: string;
     6376      InfoSize: TSize;
     6377      MoveAdvice: TMoveAdviceData;
     6378      begin
     6379      if (ToLoc<0) or (ToLoc>=G.lx*G.ly)
     6380      or (UnFocus<0) or (MyUn[UnFocus].Loc=ToLoc) then
     6381      ToLoc:=-1
     6382      else
     6383      begin
     6384      MoveAdvice.ToLoc:=ToLoc;
     6385      MoveAdvice.MoreTurns:=0;
     6386      MoveAdvice.MaxHostile_MovementLeft:=MyUn[UnFocus].Health-50;
     6387      if Server(sGetMoveAdvice,me,UnFocus,MoveAdvice)<rExecuted then
     6388      ToLoc:=-1
    26736389      end;
    2674 
    2675   cShowMoving,cShowCapturing:
    2676     if (idle and (NewPlayer=me)
    2677       or not idle and not skipped and (TShowMove(Data).emix<>$FFFF))
    2678       and not ((GameMode=cMovie) and (MovieSpeed=4)) then
    2679       begin
    2680       assert(NewPlayer=me);
    2681       if not idle or (GameMode=cMovie) then
    2682         Application.ProcessMessages;
    2683       with TShowMove(Data) do
    2684         begin
    2685         CurrentMoveInfo.DoShow:=false;
    2686         if not idle and (Tribe[Owner].ModelPicture[mix].HGr=0) then
    2687           InitEnemyModel(emix);
    2688 
    2689         ToLoc:=dLoc(FromLoc,dx,dy);
    2690         if idle then
    2691           begin // own unit -- make discovered land visible
    2692           assert(Owner=me); // no foreign moves during my turn!
    2693           CurrentMoveInfo.DoShow:=not mEffectiveMovesOnly.Checked
    2694             or (Command=cShowCapturing);
    2695           if CurrentMoveInfo.DoShow then
     6390      if (ToLoc=MoveHintToLoc) and not Force then exit;
     6391      if (ToLoc<>MoveHintToLoc) and (MoveHintToLoc>=0) then
     6392      begin invalidate; update end; // clear old hint from screen
     6393      MoveHintToLoc:=ToLoc;
     6394      if ToLoc<0 then exit;
     6395
     6396      with canvas do
     6397      begin
     6398      Pen.Color:=$80C0FF;
     6399      Pen.Width:=3;
     6400      Loc:=MyUn[UnFocus].Loc;
     6401      for Step:=0 to MoveAdvice.nStep do
     6402      begin
     6403      y0:=(Loc+G.lx*1024) div G.lx -1024;
     6404      x0:=(Loc+(y0 and 1+G.lx*1024) div 2) mod G.lx;
     6405      xs:=(x0-xw)*66+y0 and 1*33-G.lx*66;
     6406      while abs(2*(xs+G.lx*66)-MapWidth)<abs(2*xs-MapWidth) do
     6407      inc(xs,G.lx*66);
     6408      ys:=(y0-yw)*16;
     6409      if Step=0 then moveto(xs+33,ys+16)
     6410      else lineto(xs+33,ys+16);
     6411      if Step<MoveAdvice.nStep then
     6412      Loc:=dLoc(Loc,MoveAdvice.dx[Step],MoveAdvice.dy[Step]);
     6413      end;
     6414      Brush.Color:=$80C0FF;
     6415      Info:=' '+inttostr(88)+' ';
     6416      InfoSize:=TextExtent(Info);
     6417      TextOut(xs+33-InfoSize.cx div 2, ys+16-InfoSize.cy div 2, Info);
     6418      Brush.Style:=bsClear;
     6419      end
     6420      end; *)
     6421
     6422    procedure TMainScreen.SetDebugMap(p: integer);
     6423    begin
     6424      IsoEngine.pDebugMap := p;
     6425      IsoEngine.Options := IsoEngine.Options and not(1 shl moLocCodes);
     6426      mLocCodes.Checked := false;
     6427      MapValid := false;
     6428      MainOffscreenPaint;
     6429    end;
     6430
     6431    procedure TMainScreen.SetViewpoint(p: integer);
     6432    var
     6433      i: integer;
     6434    begin
     6435      if supervising and (G.RO[0].Turn > 0) and
     6436        ((p = 0) or (1 shl p and G.RO[0].Alive <> 0)) then
     6437      begin
     6438        for i := 0 to Screen.FormCount - 1 do
     6439          if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg)
     6440          then
     6441            Screen.Forms[i].Close; // close windows
     6442        ItsMeAgain(p);
     6443        SumCities(TaxSum, ScienceSum);
     6444        for i := 0 to MyRO.nModel - 1 do
     6445          if Tribe[me].ModelPicture[i].HGr = 0 then
     6446            InitMyModel(i, true);
     6447
     6448        SetTroopLoc(-1);
     6449        PanelPaint;
     6450        MapValid := false;
     6451        PaintAllMaps;
     6452      end
     6453    end;
     6454
     6455    procedure TMainScreen.FormKeyDown(Sender: TObject; var Key: word;
     6456      Shift: TShiftState);
     6457
     6458      procedure MenuClick_Check(Popup: TPopupMenu; Item: TMenuItem);
     6459      begin
     6460        InitPopup(Popup);
     6461        if Item.Visible and Item.Enabled then
     6462          MenuClick(Item);
     6463      end;
     6464
     6465    var
     6466      dx, dy: integer;
     6467      time0, time1: int64;
     6468    begin
     6469      if GameMode = cMovie then
     6470      begin
     6471        case Key of
     6472          VK_F4:
     6473            MenuClick_Check(StatPopup, mScienceStat);
     6474          VK_F6:
     6475            MenuClick_Check(StatPopup, mDiagram);
     6476          VK_F7:
     6477            MenuClick_Check(StatPopup, mWonders);
     6478          VK_F8:
     6479            MenuClick_Check(StatPopup, mShips);
     6480        end;
     6481        exit;
     6482      end;
     6483
     6484      if not idle then
     6485        exit;
     6486
     6487      if ClientMode = cEditMap then
     6488      begin
     6489        if Shift = [ssCtrl] then
     6490          case char(Key) of
     6491            (* 'A':
     6492              begin // auto symmetry
     6493              Server($7F0,me,0,nil^);
     6494              MapValid:=false;
     6495              PaintAll;
     6496              end;
     6497              'B':
     6498              begin // land mass
     6499              dy:=0;
     6500              for dx:=G.lx to G.lx*(G.ly-1)-1 do
     6501              if MyMap[dx] and fTerrain>=fGrass then inc(dy);
     6502              dy:=dy
     6503              end; *)
     6504            'Q':
     6505              MenuClick(mResign);
     6506            'R':
     6507              MenuClick(mRandomMap);
     6508          end
     6509        else if Shift = [] then
     6510          case char(Key) of
     6511            char(VK_F1):
     6512              MenuClick(mHelp);
     6513          end;
     6514        exit;
     6515      end;
     6516
     6517      if Shift = [ssAlt] then
     6518        case char(Key) of
     6519          '0':
     6520            SetDebugMap(-1);
     6521          '1' .. '9':
     6522            SetDebugMap(ord(Key) - 48);
     6523        end
     6524      else if Shift = [ssCtrl] then
     6525        case char(Key) of
     6526          'J':
     6527            MenuClick(mJump);
     6528          'K':
     6529            mShowClick(mDebugMap);
     6530          'L':
     6531            mShowClick(mLocCodes);
     6532          'M':
     6533            if LogDlg.Visible then
     6534              LogDlg.Close
     6535            else
     6536              LogDlg.Show;
     6537          'N':
     6538            mNamesClick(mNames);
     6539          'Q':
     6540            MenuClick_Check(GamePopup, mResign);
     6541          'R':
     6542            MenuClick(mRun);
     6543          '0' .. '9':
    26966544            begin
    2697             if GameMode=cMovie then
     6545              if ord(Key) - 48 = me then
     6546                SetViewpoint(0)
     6547              else
     6548                SetViewpoint(ord(Key) - 48);
     6549            end;
     6550          ' ':
     6551            begin // test map repaint time
     6552              QueryPerformanceCounter(time0);
     6553              MapValid := false;
     6554              MainOffscreenPaint;
     6555              QueryPerformanceCounter(time1);
     6556              SimpleMessage(Format('Map repaint time: %.3f ms',
     6557                [{$IFDEF VER100}(time1.LowPart - time0.LowPart)
     6558{$ELSE}(time1 - time0){$ENDIF} * 1000.0 / PerfFreq]));
     6559            end
     6560        end
     6561      else if Shift = [] then
     6562        case char(Key) of
     6563          char(VK_F1):
     6564            MenuClick(mHelp);
     6565          char(VK_F2):
     6566            MenuClick_Check(StatPopup, mUnitStat);
     6567          char(VK_F3):
     6568            MenuClick_Check(StatPopup, mCityStat);
     6569          char(VK_F4):
     6570            MenuClick_Check(StatPopup, mScienceStat);
     6571          char(VK_F5):
     6572            MenuClick_Check(StatPopup, mEUnitStat);
     6573          char(VK_F6):
     6574            MenuClick_Check(StatPopup, mDiagram);
     6575          char(VK_F7):
     6576            MenuClick_Check(StatPopup, mWonders);
     6577          char(VK_F8):
     6578            MenuClick_Check(StatPopup, mShips);
     6579          char(VK_F9):
     6580            MenuClick_Check(StatPopup, mNations);
     6581          char(VK_F10):
     6582            MenuClick_Check(StatPopup, mEmpire);
     6583          char(VK_ADD):
     6584            EndTurn;
     6585          '1':
     6586            MapBtnClick(MapBtn0);
     6587          '2':
     6588            MapBtnClick(MapBtn1);
     6589          '3':
     6590            MapBtnClick(MapBtn4);
     6591          '4':
     6592            MapBtnClick(MapBtn5);
     6593          '5':
     6594            MapBtnClick(MapBtn6);
     6595          'T':
     6596            MenuClick(mTechTree);
     6597          'W':
     6598            MenuClick(mWait);
     6599        end;
     6600
     6601      if UnFocus >= 0 then
     6602        if Shift = [ssCtrl] then
     6603          case char(Key) of
     6604            'C':
     6605              MenuClick_Check(UnitPopup, mCancel);
     6606            'D':
     6607              MenuClick(mDisband);
     6608            'P':
     6609              MenuClick_Check(UnitPopup, mPillage);
     6610            'T':
     6611              MenuClick_Check(UnitPopup, mSelectTransport);
     6612          end
     6613        else if Shift = [] then
     6614          case char(Key) of
     6615            ' ':
     6616              MenuClick(mNoOrders);
     6617            'A':
     6618              MenuClick_Check(TerrainPopup, mAirBase);
     6619            'B':
     6620              MenuClick_Check(UnitPopup, mCity);
     6621            'C':
     6622              MenuClick(mCentre);
     6623            'E':
    26986624              begin
    2699               if MovieSpeed=3 then AnimationSpeed:=4
    2700               else if MovieSpeed=2 then AnimationSpeed:=8
    2701               else AnimationSpeed:=16;
     6625                InitPopup(TerrainPopup);
     6626                if mEnhance.Visible and mEnhance.Enabled then
     6627                  MenuClick(mEnhance)
     6628                else
     6629                  MenuClick(mEnhanceDef)
     6630              end;
     6631            'F':
     6632              MenuClick_Check(TerrainPopup, mFort);
     6633            'G':
     6634              MenuClick_Check(UnitPopup, mGoOn);
     6635            'H':
     6636              MenuClick_Check(UnitPopup, mHome);
     6637            'I':
     6638              if JobTest(UnFocus, jFarm, [eTreaty]) then
     6639                MenuClick(mFarm)
     6640              else if JobTest(UnFocus, jClear, [eTreaty]) then
     6641                MenuClick(mClear)
     6642              else
     6643                MenuClick_Check(TerrainPopup, mIrrigation);
     6644            'L':
     6645              MenuClick_Check(UnitPopup, mLoad);
     6646            'M':
     6647              if JobTest(UnFocus, jAfforest, [eTreaty]) then
     6648                MenuClick(mAfforest)
     6649              else
     6650                MenuClick_Check(TerrainPopup, mMine);
     6651            'N':
     6652              MenuClick_Check(TerrainPopup, mCanal);
     6653            'O':
     6654              MenuClick_Check(TerrainPopup, MTrans);
     6655            'P':
     6656              MenuClick_Check(TerrainPopup, mPollution);
     6657            'R':
     6658              if JobTest(UnFocus, jRR, [eTreaty]) then
     6659                MenuClick(mRR)
     6660              else
     6661                MenuClick_Check(TerrainPopup, mRoad);
     6662            'S':
     6663              MenuClick(mStay);
     6664            'U':
     6665              MenuClick_Check(UnitPopup, mUnload);
     6666            'V':
     6667              MenuClick_Check(UnitPopup, mRecover);
     6668            'Z':
     6669              MenuClick_Check(UnitPopup, mUtilize);
     6670            #33 .. #40, #97 .. #100, #102 .. #105:
     6671              begin { arrow keys }
     6672                DestinationMarkON := false;
     6673                PaintDestination;
     6674                MyUn[UnFocus].Status := MyUn[UnFocus].Status and
     6675                  ($FFFF - usStay - usRecover - usGoto - usEnhance) or
     6676                  usWaiting;
     6677                case Key of
     6678                  VK_NUMPAD1, VK_END:
     6679                    begin
     6680                      dx := -1;
     6681                      dy := 1
     6682                    end;
     6683                  VK_NUMPAD2, VK_DOWN:
     6684                    begin
     6685                      dx := 0;
     6686                      dy := 2
     6687                    end;
     6688                  VK_NUMPAD3, VK_NEXT:
     6689                    begin
     6690                      dx := 1;
     6691                      dy := 1
     6692                    end;
     6693                  VK_NUMPAD4, VK_LEFT:
     6694                    begin
     6695                      dx := -2;
     6696                      dy := 0
     6697                    end;
     6698                  VK_NUMPAD6, VK_RIGHT:
     6699                    begin
     6700                      dx := 2;
     6701                      dy := 0
     6702                    end;
     6703                  VK_NUMPAD7, VK_HOME:
     6704                    begin
     6705                      dx := -1;
     6706                      dy := -1
     6707                    end;
     6708                  VK_NUMPAD8, VK_UP:
     6709                    begin
     6710                      dx := 0;
     6711                      dy := -2
     6712                    end;
     6713                  VK_NUMPAD9, VK_PRIOR:
     6714                    begin
     6715                      dx := 1;
     6716                      dy := -1
     6717                    end;
     6718                end;
     6719                MoveUnit(dx, dy, muAutoNext)
     6720              end;
     6721          end
     6722    end;
     6723
     6724    procedure TMainScreen.MenuClick(Sender: TObject);
     6725
     6726      function DoJob(j0: integer): integer;
     6727      var
     6728        Loc0, Movement0: integer;
     6729      begin
     6730        with MyUn[UnFocus] do
     6731        begin
     6732          DestinationMarkON := false;
     6733          PaintDestination;
     6734          Loc0 := Loc;
     6735          Movement0 := Movement;
     6736          if j0 < 0 then
     6737            result := ProcessEnhancement(UnFocus, MyData.EnhancementJobs)
     6738            // terrain enhancement
     6739          else
     6740            result := Server(sStartJob + j0 shl 4, me, UnFocus, nil^);
     6741          if result >= rExecuted then
     6742          begin
     6743            if result = eDied then
     6744              UnFocus := -1;
     6745            PaintLoc(Loc0);
     6746            if UnFocus >= 0 then
     6747            begin
     6748              if (j0 < 0) and (result <> eJobDone) then
     6749              // multi-turn terrain enhancement
     6750                Status := Status and ($FFFF - usStay - usRecover - usGoto) or
     6751                  usEnhance
     6752              else
     6753                Status := Status and
     6754                  ($FFFF - usStay - usRecover - usGoto - usEnhance);
     6755              if (Job <> jNone) or (Movement0 < 100) then
     6756              begin
     6757                Status := Status and not usWaiting;
     6758                NextUnit(UnStartLoc, true);
    27026759              end
     6760              else
     6761                PanelPaint
     6762            end
    27036763            else
     6764              NextUnit(UnStartLoc, true);
     6765          end
     6766        end;
     6767        case result of
     6768          eNoBridgeBuilding:
     6769            SoundMessage(Phrases.Lookup('NOBB'), 'INVALID');
     6770          eNoCityTerrain:
     6771            SoundMessage(Phrases.Lookup('NOCITY'), 'INVALID');
     6772          eTreaty:
     6773            SoundMessage(Tribe[MyRO.Territory[Loc0]].TPhrase('PEACE_NOWORK'),
     6774              'NOMOVE_TREATY');
     6775        else
     6776          if result < rExecuted then
     6777            Play('INVALID')
     6778        end
     6779      end;
     6780
     6781    var
     6782      i, uix, NewFocus, Loc0, OldMaster, Destination, cix, cixOldHome,
     6783        ServerResult: integer;
     6784      AltGovs, Changed: boolean;
     6785      QueryText: string;
     6786
     6787    begin
     6788      if Sender = mResign then
     6789        if ClientMode = cEditMap then
     6790        begin
     6791          if Edited then
     6792          begin
     6793            QueryText := Phrases.Lookup('MAP_CLOSE');
     6794            case SimpleQuery(mkYesNoCancel, QueryText, '') of
     6795              mrIgnore:
     6796                Server(sAbandonMap, me, 0, nil^);
     6797              mrOK:
     6798                Server(sSaveMap, me, 0, nil^);
     6799            end
     6800          end
     6801          else
     6802            Server(sAbandonMap, me, 0, nil^)
     6803        end
     6804        else
     6805        begin
     6806          if Server(sGetGameChanged, 0, 0, nil^) = eOK then
     6807          begin
     6808            QueryText := Phrases.Lookup('RESIGN');
     6809            case SimpleQuery(mkYesNoCancel, QueryText, '') of
     6810              mrIgnore:
     6811                Server(sResign, 0, 0, nil^);
     6812              mrOK:
     6813                Server(sBreak, 0, 0, nil^)
     6814            end
     6815          end
     6816          else
     6817            Server(sResign, 0, 0, nil^)
     6818        end
     6819      else if Sender = mEmpire then
     6820        RatesDlg.ShowNewContent(wmPersistent)
     6821      else if Sender = mRevolution then
     6822      begin
     6823        AltGovs := false;
     6824        for i := 2 to nGov - 1 do
     6825          if (GovPreq[i] <> preNA) and
     6826            ((GovPreq[i] = preNone) or (MyRO.Tech[GovPreq[i]] >= tsApplicable))
     6827          then
     6828            AltGovs := true;
     6829
     6830        if not AltGovs then
     6831          SoundMessage(Phrases.Lookup('NOALTGOVS'), 'MSG_DEFAULT')
     6832        else
     6833        begin
     6834          Changed := false;
     6835          if MyRO.Happened and phChangeGov <> 0 then
     6836          begin
     6837            ModalSelectDlg.ShowNewContent(wmModal, kGov);
     6838            if ModalSelectDlg.result >= 0 then
     6839            begin
     6840              Play('NEWGOV');
     6841              Server(sSetGovernment, me, ModalSelectDlg.result, nil^);
     6842              CityOptimizer_BeginOfTurn;
     6843              Changed := true;
     6844            end
     6845          end
     6846          else
     6847            with MessgExDlg do
     6848            begin // revolution!
     6849              MessgText := Tribe[me].TPhrase('REVOLUTION');
     6850              Kind := mkYesNo;
     6851              IconKind := mikPureIcon;
     6852              IconIndex := 72; // anarchy palace
     6853              ShowModal;
     6854              if ModalResult = mrOK then
    27046855              begin
    2705               if mVeryFastMoves.Checked then AnimationSpeed:=4
    2706               else if mFastMoves.Checked then AnimationSpeed:=8
    2707               else AnimationSpeed:=16;
    2708               end;
    2709             with MyModel[mix] do
     6856                Play('REVOLUTION');
     6857                Server(sRevolution, me, 0, nil^);
     6858                Changed := true;
     6859                if NatStatDlg.Visible then
     6860                  NatStatDlg.Close;
     6861                if CityDlg.Visible then
     6862                  CityDlg.Close;
     6863              end
     6864            end;
     6865          if Changed then
     6866            UpdateViews(true);
     6867        end
     6868      end
     6869      else if Sender = mWebsite then
     6870        ShellExecute(Handle, 'open', 'http://c-evo.org', '', '', SW_SHOWNORMAL)
     6871      else if Sender = mRandomMap then
     6872      begin
     6873        if not Edited or (SimpleQuery(mkYesNo, Phrases.Lookup('MAP_RANDOM'), '')
     6874          = mrOK) then
     6875        begin
     6876          Server(sRandomMap, me, 0, nil^);
     6877          Edited := true;
     6878          MapValid := false;
     6879          PaintAllMaps;
     6880        end
     6881      end
     6882      else if Sender = mJump then
     6883      begin
     6884        if supervising then
     6885          Jump[0] := 20
     6886        else
     6887          Jump[me] := 20;
     6888        EndTurn(true);
     6889      end
     6890      else if Sender = mRun then
     6891      begin
     6892        if supervising then
     6893          Jump[0] := 999999
     6894        else
     6895          Jump[me] := 999999;
     6896        EndTurn(true);
     6897      end
     6898      else if Sender = mEnhanceDef then
     6899      begin
     6900        if UnFocus >= 0 then
     6901          EnhanceDlg.ShowNewContent(wmPersistent,
     6902            MyMap[MyUn[UnFocus].Loc] and fTerrain)
     6903        else
     6904          EnhanceDlg.ShowNewContent(wmPersistent)
     6905      end
     6906      else if Sender = mCityTypes then
     6907        CityTypeDlg.ShowNewContent(wmModal)
     6908        // must be modal because types are not saved before closing
     6909      else if Sender = mUnitStat then
     6910      begin
     6911        if G.Difficulty[me] > 0 then
     6912          ListDlg.ShowNewContent_MilReport(wmPersistent, me)
     6913        else
     6914        begin
     6915          i := 1;
     6916          while (i < nPl) and (1 shl i and MyRO.Alive = 0) do
     6917            inc(i);
     6918          if i < nPl then
     6919            ListDlg.ShowNewContent_MilReport(wmPersistent, i);
     6920        end;
     6921      end
     6922      else if Sender = mEUnitStat then
     6923      begin
     6924        if MyRO.nEnemyModel > 0 then
     6925          ListDlg.ShowNewContent(wmPersistent, kAllEModels);
     6926      end
     6927      else if Sender = mCityStat then
     6928        ListDlg.ShowNewContent(wmPersistent, kCities)
     6929      else if Sender = mScienceStat then
     6930        ListDlg.ShowNewContent(wmPersistent, kScience)
     6931      else if Sender = mNations then
     6932        NatStatDlg.ShowNewContent(wmPersistent)
     6933      else if Sender = mHelp then
     6934        if ClientMode = cEditMap then
     6935          HelpDlg.ShowNewContent(wmPersistent, hkText,
     6936            HelpDlg.TextIndex('MAPEDIT'))
     6937        else
     6938          HelpDlg.ShowNewContent(wmPersistent, hkMisc, miscMain)
     6939      else if Sender = mTechTree then
     6940        TechTreeDlg.ShowModal
     6941      else if Sender = mWonders then
     6942        WondersDlg.ShowNewContent(wmPersistent)
     6943      else if Sender = mDiagram then
     6944        DiaDlg.ShowNewContent_Charts(wmPersistent)
     6945      else if Sender = mShips then
     6946        DiaDlg.ShowNewContent_Ship(wmPersistent)
     6947      else if Sender = mWait then
     6948      begin
     6949        if UnFocus >= 0 then
     6950        begin
     6951          DestinationMarkON := false;
     6952          PaintDestination;
     6953          MyUn[UnFocus].Status := MyUn[UnFocus].Status and
     6954            ($FFFF - usStay - usRecover - usGoto - usEnhance) or usWaiting;
     6955        end;
     6956        NextUnit(-1, false);
     6957      end
     6958      else if UnFocus >= 0 then
     6959        with MyUn[UnFocus] do
     6960          if Sender = mGoOn then
     6961          begin
     6962            if Status shr 16 = $7FFF then
     6963              Destination := maNextCity
     6964            else
     6965              Destination := Status shr 16;
     6966            Status := Status and not(usStay or usRecover) or usWaiting;
     6967            MoveToLoc(Destination, true);
     6968          end
     6969          else if Sender = mHome then
     6970            if MyMap[Loc] and fCity <> 0 then
     6971            begin
     6972              cixOldHome := Home;
     6973              if Server(sSetUnitHome, me, UnFocus, nil^) >= rExecuted then
    27106974              begin
    2711               if (Kind=mkDiplomat) or (Domain=dAir)
    2712                 or (Cap[mcRadar]+Cap[mcCarrier]+Cap[mcAcademy]>0)
    2713                 or (MyMap[ToLoc] and fTerrain=fMountains)
    2714                 or (MyMap[ToLoc] and fTerImp=tiFort)
    2715                 or (MyMap[ToLoc] and fTerImp=tiBase) then
    2716                 CurrentMoveInfo.AfterMovePaintRadius:=2
    2717               else CurrentMoveInfo.AfterMovePaintRadius:=1;
    2718               if (MyRO.Wonder[woShinkansen].EffectiveOwner=me)
    2719                 and (Domain=dGround)
    2720                 and (MyMap[FromLoc] and (fRR or fCity)<>0)
    2721                 and (MyMap[ToLoc] and (fRR or fCity)<>0)
    2722                 and (Flags and umPlaneUnloading=0) then
    2723                 AnimationSpeed:=4;
    2724               ShowMoveDomain:=Domain;
    2725               IsAlpine:= Cap[mcAlpine]>0;
     6975                CityOptimizer_CityChange(cixOldHome);
     6976                CityOptimizer_CityChange(Home);
     6977                UpdateViews(true);
     6978              end
     6979              else
     6980                Play('INVALID');
     6981            end
     6982            else
     6983            begin
     6984              Status := Status and not(usStay or usRecover or usEnhance);
     6985              MoveToLoc(maNextCity, true)
     6986            end
     6987          else if Sender = mCentre then
     6988          begin
     6989            Centre(Loc);
     6990            PaintAllMaps
     6991          end
     6992          else if Sender = mCity then
     6993          begin
     6994            Loc0 := Loc;
     6995            if MyMap[Loc] and fCity = 0 then
     6996            begin // build city
     6997              if DoJob(jCity) = eCity then
     6998              begin
     6999                MapValid := false;
     7000                PaintAll;
     7001                ZoomToCity(Loc0, true, chFounded);
    27267002              end
    27277003            end
     7004            else
     7005            begin
     7006              CityOptimizer_BeforeRemoveUnit(UnFocus);
     7007              ServerResult := Server(sAddToCity, me, UnFocus, nil^);
     7008              if ServerResult >= rExecuted then
     7009              begin
     7010                cix := MyRO.nCity - 1;
     7011                while (cix >= 0) and (MyCity[cix].Loc <> Loc0) do
     7012                  dec(cix);
     7013                assert(cix >= 0);
     7014                CityOptimizer_CityChange(cix);
     7015                CityOptimizer_AfterRemoveUnit; // does nothing here
     7016                SetTroopLoc(Loc0);
     7017                UpdateViews(true);
     7018                DestinationMarkON := false;
     7019                PaintDestination;
     7020                UnFocus := -1;
     7021                PaintLoc(Loc0);
     7022                NextUnit(UnStartLoc, true);
     7023              end
     7024              else if ServerResult = eMaxSize then
     7025                SimpleMessage(Phrases.Lookup('ADDTOMAXSIZE'));
     7026            end
    27287027          end
     7028          else if Sender = mRoad then
     7029            DoJob(jRoad)
     7030          else if Sender = mRR then
     7031            DoJob(jRR)
     7032          else if Sender = mClear then
     7033            DoJob(jClear)
     7034          else if Sender = mIrrigation then
     7035            DoJob(jIrr)
     7036          else if Sender = mFarm then
     7037            DoJob(jFarm)
     7038          else if Sender = mAfforest then
     7039            DoJob(jAfforest)
     7040          else if Sender = mMine then
     7041            DoJob(jMine)
     7042          else if Sender = mCanal then
     7043            DoJob(jCanal)
     7044          else if Sender = MTrans then
     7045            DoJob(jTrans)
     7046          else if Sender = mFort then
     7047            DoJob(jFort)
     7048          else if Sender = mAirBase then
     7049            DoJob(jBase)
     7050          else if Sender = mPollution then
     7051            DoJob(jPoll)
     7052          else if Sender = mPillage then
     7053            DoJob(jPillage)
     7054          else if Sender = mEnhance then
     7055            DoJob(-1)
     7056          else if Sender = mStay then
     7057          begin
     7058            DestinationMarkON := false;
     7059            PaintDestination;
     7060            Status := Status and ($FFFF - usRecover - usGoto - usEnhance)
     7061              or usStay;
     7062            if Job > jNone then
     7063              Server(sStartJob + jNone shl 4, me, UnFocus, nil^);
     7064            NextUnit(UnStartLoc, true)
     7065          end
     7066          else if Sender = mRecover then
     7067          begin
     7068            DestinationMarkON := false;
     7069            PaintDestination;
     7070            Status := Status and ($FFFF - usStay - usGoto - usEnhance) or
     7071              usRecover;
     7072            if Job > jNone then
     7073              Server(sStartJob + jNone shl 4, me, UnFocus, nil^);
     7074            NextUnit(UnStartLoc, true)
     7075          end
     7076          else if Sender = mNoOrders then
     7077          begin
     7078            Status := Status and not usWaiting;
     7079            NextUnit(UnStartLoc, true)
     7080          end
     7081          else if Sender = mCancel then
     7082          begin
     7083            DestinationMarkON := false;
     7084            PaintDestination;
     7085            Status := Status and ($FFFF - usRecover - usGoto - usEnhance);
     7086            if Job > jNone then
     7087              Server(sStartJob + jNone shl 4, me, UnFocus, nil^);
     7088          end
     7089          else if (Sender = mDisband) or (Sender = mUtilize) then
     7090          begin
     7091            if (Sender = mUtilize) and
     7092              not(Server(sRemoveUnit - sExecute, me, UnFocus, nil^) = eUtilized)
     7093            then
     7094            begin
     7095              SimpleMessage(Phrases2.Lookup('SHIP_UTILIZE'));
     7096              // freight for colony ship is the only case in which the command is
     7097              // available to player though not valid
     7098              exit
     7099            end;
     7100            if (Sender = mUtilize) and (Health < 100) then
     7101              if SimpleQuery(mkYesNo, Phrases.Lookup('DAMAGED_UTILIZE'), '') <> mrOK
     7102              then
     7103                exit;
     7104            Loc0 := Loc;
     7105            CityOptimizer_BeforeRemoveUnit(UnFocus);
     7106            if Server(sRemoveUnit, me, UnFocus, nil^) = eUtilized then
     7107              Play('CITY_UTILIZE')
     7108            else
     7109              Play('DISBAND');
     7110            CityOptimizer_AfterRemoveUnit;
     7111            SetTroopLoc(Loc0);
     7112            UpdateViews(true);
     7113            DestinationMarkON := false;
     7114            PaintDestination;
     7115            UnFocus := -1;
     7116            PaintLoc(Loc0);
     7117            NextUnit(UnStartLoc, true);
     7118          end
     7119          else if Sender = mLoad then
     7120          begin
     7121            i := Server(sLoadUnit, me, UnFocus, nil^);
     7122            if i >= rExecuted then
     7123            begin
     7124              if MyModel[mix].Domain = dAir then
     7125                Play('MOVE_PLANELANDING')
     7126              else
     7127                Play('MOVE_LOAD');
     7128              DestinationMarkON := false;
     7129              PaintDestination;
     7130              Status := Status and
     7131                ($FFFF - usWaiting - usStay - usRecover - usGoto - usEnhance);
     7132              NextUnit(UnStartLoc, true);
     7133            end
     7134            else if i = eNoTime_Load then
     7135              if MyModel[mix].Domain = dAir then
     7136                SoundMessage(Phrases.Lookup('NOTIMELOADAIR'), 'NOMOVE_TIME')
     7137              else
     7138                SoundMessage(Format(Phrases.Lookup('NOTIMELOADGROUND'),
     7139                  [MovementToString(MyModel[mix].speed)]), 'NOMOVE_TIME');
     7140          end
     7141          else if Sender = mUnload then
     7142            if Master >= 0 then
     7143            begin
     7144              OldMaster := Master;
     7145              i := Server(sUnloadUnit, me, UnFocus, nil^);
     7146              if i >= rExecuted then
     7147              begin
     7148                if MyModel[mix].Domain = dAir then
     7149                  Play('MOVE_PLANESTART')
     7150                else if (MyModel[MyUn[OldMaster].mix].Domain = dAir) and
     7151                  (MyMap[Loc] and fCity = 0) and
     7152                  (MyMap[Loc] and fTerImp <> tiBase) then
     7153                  Play('MOVE_PARACHUTE')
     7154                else
     7155                  Play('MOVE_UNLOAD');
     7156                Status := Status and not usWaiting;
     7157                if MyModel[mix].Domain <> dAir then
     7158                  NextUnit(Loc, true)
     7159                else
     7160                  PanelPaint
     7161              end
     7162              else if i = eNoTime_Load then
     7163                if MyModel[mix].Domain = dAir then
     7164                  SoundMessage(Phrases.Lookup('NOTIMELOADAIR'), 'NOMOVE_TIME')
     7165                else
     7166                  SoundMessage(Format(Phrases.Lookup('NOTIMELOADGROUND'),
     7167                    [MovementToString(MyModel[mix].speed)]), 'NOMOVE_TIME');
     7168            end
     7169            else
     7170            begin
     7171              NewFocus := -1;
     7172              uix := UnFocus;
     7173              for i := 1 to MyRO.nUn - 1 do
     7174              begin
     7175                uix := (uix + MyRO.nUn - 1) mod MyRO.nUn;
     7176                if (MyUn[uix].Master = UnFocus) and
     7177                  (MyUn[uix].Movement = integer(MyModel[MyUn[uix].mix].speed))
     7178                then
     7179                begin
     7180                  MyUn[uix].Status := MyUn[uix].Status or usWaiting;
     7181                  NewFocus := uix
     7182                end;
     7183              end;
     7184              if NewFocus >= 0 then
     7185              begin
     7186                SetUnFocus(NewFocus);
     7187                SetTroopLoc(Loc);
     7188                PanelPaint
     7189              end
     7190            end
     7191          else if Sender = mSelectTransport then
     7192            Server(sSelectTransport, me, UnFocus, nil^)
     7193    end;
     7194
     7195    procedure TMainScreen.InitPopup(Popup: TPopupMenu);
     7196    var
     7197      i, p1, Tile, Test: integer;
     7198      NoSuper, extended, Multi, NeedSep, HaveCities: boolean;
     7199      LastSep, m: TMenuItem;
     7200      mox: ^TModel;
     7201    begin
     7202      NoSuper := not supervising and (1 shl me and MyRO.Alive <> 0);
     7203      HaveCities := false;
     7204      for i := 0 to MyRO.nCity - 1 do
     7205        if MyCity[i].Loc >= 0 then
     7206        begin
     7207          HaveCities := true;
     7208          Break
     7209        end;
     7210      if Popup = GamePopup then
     7211      begin
     7212        mTechTree.Visible := ClientMode <> cEditMap;
     7213        mResign.Enabled := supervising or (me = 0) and (ClientMode < scContact);
     7214        mRandomMap.Visible := (ClientMode = cEditMap) and
     7215          (Server(sMapGeneratorRequest, me, 0, nil^) = eOK);
     7216        mOptions.Visible := ClientMode <> cEditMap;
     7217        mManip.Visible := ClientMode <> cEditMap;
     7218        if ClientMode <> cEditMap then
     7219        begin
     7220          mWaitTurn.Visible := NoSuper;
     7221          mRep.Visible := NoSuper;
     7222          mRepList.Visible := NoSuper;
     7223          mRepScreens.Visible := NoSuper;
     7224          N10.Visible := NoSuper;
     7225          mOwnMovement.Visible := NoSuper;
     7226          mAllyMovement.Visible := NoSuper;
     7227          case SoundMode of
     7228            smOff:
     7229              mSoundOff.Checked := true;
     7230            smOn:
     7231              mSoundOn.Checked := true;
     7232            smOnAlt:
     7233              mSoundOnAlt.Checked := true;
     7234          end;
     7235
     7236          for i := 0 to nTestFlags - 1 do
     7237            mManip[i].Checked := MyRO.TestFlags and (1 shl i) <> 0;
     7238          mManip.Enabled := supervising or (me = 0);
     7239
     7240          Multi := false;
     7241          for p1 := 1 to nPl - 1 do
     7242            if G.RO[p1] <> nil then
     7243              Multi := true;
     7244          mEnemyMovement.Visible := not Multi;
     7245        end;
     7246        mMacro.Visible := NoSuper and (ClientMode < scContact);
     7247        if NoSuper and (ClientMode < scContact) then
     7248        begin
     7249          mCityTypes.Enabled := false;
     7250          // check if city types already usefull:
     7251          if MyRO.nCity > 0 then
     7252            for i := 28 to nImp - 1 do
     7253              if (i <> imTrGoods) and (Imp[i].Kind = ikCommon) and
     7254                (Imp[i].Preq <> preNA) and
     7255                ((Imp[i].Preq = preNone) or
     7256                (MyRO.Tech[Imp[i].Preq] >= tsApplicable)) then
     7257              begin
     7258                mCityTypes.Enabled := true;
     7259                Break
     7260              end;
     7261        end;
     7262        mViewpoint.Visible := (ClientMode <> cEditMap) and supervising;
     7263        mViewpoint.Enabled := G.RO[0].Turn > 0;
     7264        if supervising then
     7265        begin
     7266          EmptyMenu(mViewpoint);
     7267          for p1 := 0 to nPl - 1 do
     7268            if (p1 = 0) or (1 shl p1 and G.RO[0].Alive <> 0) then
     7269            begin
     7270              m := TMenuItem.Create(mViewpoint);
     7271              if p1 = 0 then
     7272                m.Caption := Phrases.Lookup('SUPER')
     7273              else
     7274                m.Caption := Tribe[p1].TString(Phrases2.Lookup('BELONG'));
     7275              m.Tag := p1;
     7276              m.OnClick := ViewpointClick;
     7277              if p1 < 10 then
     7278                m.ShortCut := ShortCut(48 + p1, [ssCtrl]);
     7279              m.RadioItem := true;
     7280              if p1 = me then
     7281                m.Checked := true;
     7282              mViewpoint.Add(m);
     7283            end
     7284        end;
     7285        mDebugMap.Visible := (ClientMode <> cEditMap) and supervising;
     7286        if supervising then
     7287        begin
     7288          EmptyMenu(mDebugMap);
     7289          for p1 := 0 to nPl - 1 do
     7290            if (p1 = 0) or (1 shl p1 and G.RO[0].Alive <> 0) then
     7291            begin
     7292              m := TMenuItem.Create(mDebugMap);
     7293              if p1 = 0 then
     7294                m.Caption := Phrases2.Lookup('MENU_DEBUGMAPOFF')
     7295              else
     7296                m.Caption := Tribe[p1].TString(Phrases2.Lookup('BELONG'));
     7297              if p1 = 0 then
     7298                m.Tag := -1
     7299              else
     7300                m.Tag := p1;
     7301              m.OnClick := DebugMapClick;
     7302              if p1 < 10 then
     7303                m.ShortCut := ShortCut(48 + p1, [ssAlt]);
     7304              m.RadioItem := true;
     7305              if m.Tag = IsoEngine.pDebugMap then
     7306                m.Checked := true;
     7307              mDebugMap.Add(m);
     7308            end
     7309        end;
     7310        mSmallTiles.Checked := xxt = 33;
     7311        mNormalTiles.Checked := xxt = 48;
     7312      end
     7313      else if Popup = StatPopup then
     7314      begin
     7315        mEmpire.Visible := NoSuper;
     7316        mEmpire.Enabled := MyRO.Government <> gAnarchy;
     7317        mRevolution.Visible := NoSuper;
     7318        mRevolution.Enabled := (MyRO.Government <> gAnarchy) and
     7319          (ClientMode < scContact);
     7320        mUnitStat.Enabled := NoSuper or (MyRO.Turn > 0);
     7321        mCityStat.Visible := 1 shl me and MyRO.Alive <> 0;
     7322        mCityStat.Enabled := HaveCities;
     7323        mScienceStat.Visible := true;
     7324        mScienceStat.Enabled := not NoSuper or (MyRO.ResearchTech >= 0) or
     7325          (MyRO.Happened and phTech <> 0) or (MyRO.Happened and phGameEnd <> 0)
     7326        // no researchtech in case just completed
     7327          or (MyRO.TestFlags and (tfAllTechs or tfUncover or
     7328          tfAllContact) <> 0);
     7329        mEUnitStat.Enabled := MyRO.nEnemyModel > 0;
     7330        { mWonders.Enabled:= false;
     7331          for i:=0 to 27 do if MyRO.Wonder[i].CityID<>-1 then
     7332          mWonders.Enabled:=true; }
     7333        mDiagram.Enabled := MyRO.Turn >= 2;
     7334        mShips.Enabled := false;
     7335        for p1 := 0 to nPl - 1 do
     7336          if MyRO.Ship[p1].Parts[spComp] + MyRO.Ship[p1].Parts[spPow] +
     7337            MyRO.Ship[p1].Parts[spHab] > 0 then
     7338            mShips.Enabled := true;
     7339      end
     7340      else if Popup = UnitPopup then
     7341      begin
     7342        mox := @MyModel[MyUn[UnFocus].mix];
     7343        Tile := MyMap[MyUn[UnFocus].Loc];
     7344        extended := Tile and fCity = 0;
     7345        if extended then
     7346        begin
     7347          mCity.Caption := Phrases.Lookup('BTN_FOUND');
     7348          mHome.Caption := Phrases.Lookup('BTN_MOVEHOME')
     7349        end
    27297350        else
     7351        begin
     7352          mCity.Caption := Phrases.Lookup('BTN_ADD');
     7353          mHome.Caption := Phrases.Lookup('BTN_SETHOME')
     7354        end;
     7355
     7356        extended := extended and
     7357          ((mox.Kind = mkSettler) or (mox.Kind = mkSlaves) and
     7358          (MyRO.Wonder[woPyramids].EffectiveOwner >= 0)) and
     7359          (MyUn[UnFocus].Master < 0) and (Tile and fDeadLands = 0);
     7360        if (mox.Kind = mkFreight) and (Tile and fCity <> 0) and
     7361          not Phrases2FallenBackToEnglish or
     7362          (Server(sRemoveUnit - sExecute, me, UnFocus, nil^) = eUtilized) then
     7363        begin
     7364          mDisband.Visible := false;
     7365          mUtilize.Visible := true;
     7366          if mox.Kind = mkFreight then
     7367            mUtilize.Caption := Phrases.Lookup('UTILIZE')
     7368          else
     7369            mUtilize.Caption := Phrases.Lookup('INTEGRATE')
     7370        end
     7371        else
     7372        begin
     7373          mDisband.Visible := true;
     7374          mUtilize.Visible := false
     7375        end;
     7376        mGoOn.Visible := MyUn[UnFocus].Status and (usGoto or usWaiting)
     7377          = usGoto or usWaiting;
     7378        mHome.Visible := HaveCities;
     7379        mRecover.Visible := (MyUn[UnFocus].Health < 100) and
     7380          (Tile and fTerrain >= fGrass) and
     7381          ((MyRO.Wonder[woGardens].EffectiveOwner = me) or
     7382          (Tile and fTerrain <> fArctic) and (Tile and fTerrain <> fDesert)) and
     7383          not((mox.Domain = dAir) and (Tile and fCity = 0) and
     7384          (Tile and fTerImp <> tiBase));
     7385        mStay.Visible := not((mox.Domain = dAir) and (Tile and fCity = 0) and
     7386          (Tile and fTerImp <> tiBase));
     7387        mCity.Visible := extended and (mox.Kind = mkSettler) or
     7388          (Tile and fCity <> 0) and ((mox.Kind in [mkSettler, mkSlaves]) or
     7389          (MyUn[UnFocus].Flags and unConscripts <> 0));
     7390        mPillage.Visible := (Tile and (fRoad or fRR or fCanal or fTerImp) <> 0)
     7391          and (MyUn[UnFocus].Master < 0) and (mox.Domain = dGround);
     7392        mCancel.Visible := (MyUn[UnFocus].Job > jNone) or
     7393          (MyUn[UnFocus].Status and (usRecover or usGoto) <> 0);
     7394
     7395        Test := Server(sLoadUnit - sExecute, me, UnFocus, nil^);
     7396        mLoad.Visible := (Test >= rExecuted) or (Test = eNoTime_Load);
     7397        mUnload.Visible := (MyUn[UnFocus].Master >= 0) or
     7398          (MyUn[UnFocus].TroopLoad + MyUn[UnFocus].AirLoad > 0);
     7399        mSelectTransport.Visible := Server(sSelectTransport - sExecute, me,
     7400          UnFocus, nil^) >= rExecuted;
     7401      end
     7402      else { if Popup=TerrainPopup then }
     7403      begin
     7404        mox := @MyModel[MyUn[UnFocus].mix];
     7405        Tile := MyMap[MyUn[UnFocus].Loc];
     7406        extended := Tile and fCity = 0;
     7407
     7408        if (Tile and fRiver <> 0) and
     7409          (MyRO.Tech[adBridgeBuilding] >= tsApplicable) then
     7410        begin
     7411          mRoad.Caption := Phrases.Lookup('BTN_BUILDBRIDGE');
     7412          mRR.Caption := Phrases.Lookup('BTN_BUILDRRBRIDGE');
     7413        end
     7414        else
     7415        begin
     7416          mRoad.Caption := Phrases.Lookup('BTN_BUILDROAD');
     7417          mRR.Caption := Phrases.Lookup('BTN_BUILDRR');
     7418        end;
     7419        if Tile and fTerrain = fForest then
     7420          mClear.Caption := Phrases.Lookup('BTN_CLEAR')
     7421        else if Tile and fTerrain = fDesert then
     7422          mClear.Caption := Phrases.Lookup('BTN_UNDESERT')
     7423        else
     7424          mClear.Caption := Phrases.Lookup('BTN_DRAIN');
     7425
     7426        extended := extended and
     7427          ((mox.Kind = mkSettler) or (mox.Kind = mkSlaves) and
     7428          (MyRO.Wonder[woPyramids].EffectiveOwner >= 0)) and
     7429          (MyUn[UnFocus].Master < 0);
     7430        if extended then
     7431        begin
     7432          mRoad.Visible := JobTest(UnFocus, jRoad,
     7433            [eNoBridgeBuilding, eTreaty]);
     7434          mRR.Visible := JobTest(UnFocus, jRR, [eNoBridgeBuilding, eTreaty]);
     7435          mClear.Visible := JobTest(UnFocus, jClear, [eTreaty]);
     7436          mIrrigation.Visible := JobTest(UnFocus, jIrr, [eTreaty]);
     7437          mFarm.Visible := JobTest(UnFocus, jFarm, [eTreaty]);
     7438          mAfforest.Visible := JobTest(UnFocus, jAfforest, [eTreaty]);
     7439          mMine.Visible := JobTest(UnFocus, jMine, [eTreaty]);
     7440          MTrans.Visible := JobTest(UnFocus, jTrans, [eTreaty]);
     7441          mCanal.Visible := JobTest(UnFocus, jCanal, [eTreaty]);
     7442          mFort.Visible := JobTest(UnFocus, jFort, [eTreaty]);
     7443          mAirBase.Visible := JobTest(UnFocus, jBase, [eTreaty]);
     7444          mPollution.Visible := JobTest(UnFocus, jPoll, [eTreaty]);
     7445          mEnhance.Visible := (Tile and fDeadLands = 0) and
     7446            (MyData.EnhancementJobs[MyMap[MyUn[UnFocus].Loc] and fTerrain, 0]
     7447            <> jNone);
     7448        end
     7449        else
     7450        begin
     7451          for i := 0 to Popup.Items.Count - 1 do
     7452            Popup.Items[i].Visible := false;
     7453        end;
     7454      end;
     7455
     7456      // set menu seperators
     7457      LastSep := nil;
     7458      NeedSep := false;
     7459      for i := 0 to Popup.Items.Count - 1 do
     7460        if Popup.Items[i].Caption = '-' then
     7461        begin
     7462          Popup.Items[i].Visible := NeedSep;
     7463          if NeedSep then
     7464            LastSep := Popup.Items[i];
     7465          NeedSep := false
     7466        end
     7467        else if Popup.Items[i].Visible then
     7468          NeedSep := true;
     7469      if (LastSep <> nil) and not NeedSep then
     7470        LastSep.Visible := false
     7471    end;
     7472
     7473    procedure TMainScreen.PanelBtnClick(Sender: TObject);
     7474    var
     7475      Popup: TPopupMenu;
     7476    begin
     7477      if Sender = UnitBtn then
     7478        Popup := UnitPopup
     7479      else { if Sender=TerrainBtn then }
     7480        Popup := TerrainPopup;
     7481      InitPopup(Popup);
     7482      if FullScreen then
     7483        Popup.Popup(Left + TControl(Sender).Left, Top + TControl(Sender).Top)
     7484      else
     7485        Popup.Popup(Left + TControl(Sender).Left + 4, Top + TControl(Sender).Top
     7486          + GetSystemMetrics(SM_CYCAPTION) + 4);
     7487    end;
     7488
     7489    procedure TMainScreen.CityClosed(Activateuix: integer; StepFocus: boolean;
     7490      SelectFocus: boolean);
     7491    begin
     7492      if supervising then
     7493      begin
     7494        SetTroopLoc(-1);
     7495        PanelPaint
     7496      end
     7497      else
     7498      begin
     7499        if Activateuix >= 0 then
     7500        begin
     7501          SetUnFocus(Activateuix);
     7502          SetTroopLoc(MyUn[Activateuix].Loc);
     7503          if SelectFocus then
     7504            FocusOnLoc(TroopLoc, flRepaintPanel)
     7505          else
     7506            PanelPaint
     7507        end
     7508        else if StepFocus then
     7509          NextUnit(TroopLoc, true)
     7510        else
     7511        begin
     7512          SetTroopLoc(-1);
     7513          PanelPaint
     7514        end
     7515      end
     7516    end;
     7517
     7518    procedure TMainScreen.Toggle(Sender: TObject);
     7519    begin
     7520      TMenuItem(Sender).Checked := not TMenuItem(Sender).Checked
     7521    end;
     7522
     7523    procedure TMainScreen.PanelBoxMouseMove(Sender: TObject; Shift: TShiftState;
     7524      x, y: integer);
     7525    var
     7526      xCentre, yCentre: integer;
     7527    begin
     7528      if Tracking and (ssLeft in Shift) then
     7529      begin
     7530        if (x >= xMini + 2) and (y >= yMini + 2) and (x < xMini + 2 + 2 * G.lx)
     7531          and (y < yMini + 2 + G.ly) then
     7532        begin
     7533          xCentre := (xwMini + (x - xMini - 2) div 2 + G.lx div 2 +
     7534            MapWidth div (xxt * 4)) mod G.lx;
     7535          yCentre := (y - yMini - 2);
     7536          xw := (xCentre - MapWidth div (xxt * 4) + G.lx) mod G.lx;
     7537          if ywmax <= 0 then
     7538            yw := ywcenter
     7539          else
    27307540          begin
    2731           CurrentMoveInfo.IsAlly:= MyRO.Treaty[Owner]=trAlliance;
    2732           if GameMode=cMovie then
    2733             CurrentMoveInfo.DoShow:=true
    2734           else if CurrentMoveInfo.IsAlly then
    2735             CurrentMoveInfo.DoShow:=not mAlNoMoves.Checked
    2736               and not(mAlEffectiveMovesOnly.Checked and (Command<>cShowCapturing))
    2737           else CurrentMoveInfo.DoShow:=not mEnNoMoves.Checked
    2738             and not(mEnAttacks.Checked and (Command<>cShowCapturing));
    2739           if CurrentMoveInfo.DoShow then
    2740             begin
    2741             if Command=cShowCapturing then
    2742               begin // show capture message
    2743               if MyMap[ToLoc] and fOwned<>0 then
    2744                 begin // own city, search
    2745                 cix:=MyRO.nCity-1;
    2746                 while (cix>=0) and (MyCity[cix].Loc<>ToLoc) do
    2747                   dec(cix);
    2748                 s:=CityName(MyCity[cix].ID);
    2749                 end
    2750               else
    2751                 begin // foreign city, search
    2752                 ecix:=MyRO.nEnemyCity-1;
    2753                 while (ecix>=0) and (MyRO.EnemyCity[ecix].Loc<>ToLoc) do
    2754                   dec(ecix);
    2755                 s:=CityName(MyRO.EnemyCity[ecix].ID);
    2756                 end;
    2757               TribeMessage(Owner, Format(Tribe[Owner].TPhrase('CAPTURE'),[s]), '');
    2758               Update; // remove message box from screen
    2759               end;
    2760 
    2761             if CurrentMoveInfo.IsAlly then
    2762               begin // allied unit -- make discovered land visible
    2763               if mAlFastMoves.Checked then AnimationSpeed:=8
    2764               else AnimationSpeed:=16;
    2765               with MyRO.EnemyModel[emix] do
    2766                 if (Kind=mkDiplomat) or (Domain=dAir) or (ATrans_Fuel>0)
    2767                   or (Cap and (1 shl (mcRadar-mcFirstNonCap) or 1 shl (mcAcademy-mcFirstNonCap))<>0)
    2768                   or (MyMap[ToLoc] and fTerrain=fMountains)
    2769                   or (MyMap[ToLoc] and fTerImp=tiFort)
    2770                   or (MyMap[ToLoc] and fTerImp=tiBase) then
    2771                   CurrentMoveInfo.AfterMovePaintRadius:=2
    2772                 else CurrentMoveInfo.AfterMovePaintRadius:=1
    2773               end
     7541            yw := (yCentre - MapHeight div (yyt * 2) + 1) and not 1;
     7542            if yw < 0 then
     7543              yw := 0
     7544            else if yw > ywmax then
     7545              yw := ywmax;
     7546          end;
     7547          BitBlt(Buffer.Canvas.Handle, 0, 0, G.lx * 2, G.ly, Mini.Canvas.Handle,
     7548            0, 0, SRCCOPY);
     7549          if ywmax <= 0 then
     7550            Frame(Buffer.Canvas, x - xMini - 2 - MapWidth div (xxt * 2), 0,
     7551              x - xMini - 2 + MapWidth div (xxt * 2) - 1, G.ly - 1,
     7552              MainTexture.clMark, MainTexture.clMark)
     7553          else
     7554            Frame(Buffer.Canvas, x - xMini - 2 - MapWidth div (xxt * 2), yw,
     7555              x - xMini - 2 + MapWidth div (xxt * 2) - 1,
     7556              yw + MapHeight div yyt - 2, MainTexture.clMark,
     7557              MainTexture.clMark);
     7558          BitBlt(Panel.Canvas.Handle, xMini + 2, yMini + 2, G.lx * 2, G.ly,
     7559            Buffer.Canvas.Handle, 0, 0, SRCCOPY);
     7560          MainOffscreenPaint;
     7561          RectInvalidate(xMini + 2, TopBarHeight + MapHeight - overlap + yMini +
     7562            2, xMini + 2 + G.lx * 2, TopBarHeight + MapHeight - overlap + yMini
     7563            + 2 + G.ly);
     7564          Update;
     7565        end
     7566      end
     7567      else
     7568        Tracking := false
     7569    end;
     7570
     7571    procedure TMainScreen.PanelBoxMouseUp(Sender: TObject; Button: TMouseButton;
     7572      Shift: TShiftState; x, y: integer);
     7573    begin
     7574      if Tracking then
     7575      begin
     7576        Tracking := false;
     7577        xwMini := xw;
     7578        ywMini := yw;
     7579        MiniPaint;
     7580        PanelPaint;
     7581      end
     7582    end;
     7583
     7584    procedure TMainScreen.MapBoxMouseMove(Sender: TObject; Shift: TShiftState;
     7585      x, y: integer);
     7586    var
     7587      MouseLoc: integer;
     7588    begin
     7589      xMouse := x;
     7590      yMouse := y;
     7591      if (ClientMode = cEditMap) and (ssLeft in Shift) and not Tracking then
     7592      begin
     7593        MouseLoc := LocationOfScreenPixel(x, y);
     7594        if MouseLoc <> BrushLoc then
     7595          MapBoxMouseDown(nil, mbLeft, Shift, x, y);
     7596      end
     7597      (* else if idle and (UnFocus>=0) then
     7598        begin
     7599        qx:=(xMouse*32+yMouse*66+16*66) div(32*66)-1;
     7600        qy:=(yMouse*66-xMouse*32-16*66+2000*33*32) div(32*66)-999;
     7601        MouseLoc:=(xw+(qx-qy+2048) div 2-1024+G.lx) mod G.lx+G.lx*(yw+qx+qy);
     7602        ShowMoveHint(MouseLoc);
     7603        end *)
     7604    end;
     7605
     7606    procedure TMainScreen.mShowClick(Sender: TObject);
     7607    begin
     7608      TMenuItem(Sender).Checked := not TMenuItem(Sender).Checked;
     7609      SetMapOptions;
     7610      MapValid := false;
     7611      PaintAllMaps;
     7612    end;
     7613
     7614    procedure TMainScreen.mNamesClick(Sender: TObject);
     7615    var
     7616      p1: integer;
     7617    begin
     7618      mNames.Checked := not mNames.Checked;
     7619      GenerateNames := mNames.Checked;
     7620      for p1 := 0 to nPl - 1 do
     7621        if Tribe[p1] <> nil then
     7622          if GenerateNames then
     7623            Tribe[p1].NumberName := -1
     7624          else
     7625            Tribe[p1].NumberName := p1;
     7626      MapValid := false;
     7627      PaintAll;
     7628    end;
     7629
     7630    function TMainScreen.IsPanelPixel(x, y: integer): boolean;
     7631    begin
     7632      result := (y >= TopBarHeight + MapHeight) or
     7633        (y >= ClientHeight - PanelHeight) and
     7634        ((x < xMidPanel) or (x >= xRightPanel))
     7635    end;
     7636
     7637    procedure TMainScreen.FormMouseDown(Sender: TObject; Button: TMouseButton;
     7638      Shift: TShiftState; x, y: integer);
     7639    begin
     7640      if idle then
     7641        if (x < 40) and (y < 40) then
     7642        begin
     7643          if GameMode <> cMovie then
     7644          begin
     7645            InitPopup(GamePopup);
     7646            if FullScreen then
     7647              GamePopup.Popup(Left, Top + TopBarHeight - 1)
    27747648            else
    2775               begin
    2776               if mEnFastMoves.Checked then AnimationSpeed:=8
    2777               else AnimationSpeed:=16;
    2778               CurrentMoveInfo.AfterMovePaintRadius:=0; // enemy unit, nothing discovered
    2779               end;
    2780             if GameMode=cMovie then
    2781               begin
    2782               if MovieSpeed=3 then AnimationSpeed:=4
    2783               else if MovieSpeed=2 then AnimationSpeed:=8
    2784               else AnimationSpeed:=16;
    2785               end;
    2786             ShowMoveDomain:=MyRO.EnemyModel[emix].Domain;
    2787             IsAlpine:= MyRO.EnemyModel[emix].Cap and (1 shl (mcAlpine-mcFirstNonCap))<>0;
    2788             end
    2789           end;
    2790 
    2791         if CurrentMoveInfo.DoShow then
    2792           begin
    2793           if Command=cShowCapturing then Play('MOVE_CAPTURE')
    2794           else if EndHealth<=0 then Play('MOVE_DIE')
    2795           else if Flags and umSpyMission<>0 then Play('MOVE_COVERT')
    2796           else if Flags and umShipLoading<>0 then
    2797             if ShowMoveDomain=dAir then Play('MOVE_PLANELANDING')
    2798             else Play('MOVE_LOAD')
    2799           else if Flags and umPlaneLoading<>0 then Play('MOVE_LOAD')
    2800           else if Flags and umShipUnloading<>0 then
    2801             if ShowMoveDomain=dAir then Play('MOVE_PLANESTART')
    2802             else Play('MOVE_UNLOAD')
    2803           else if Flags and umPlaneUnloading<>0 then
    2804             if (MyMap[FromLoc] and fCity=0)
    2805               and (MyMap[FromLoc] and fTerImp<>tiBase) then
    2806               Play('MOVE_PARACHUTE')
    2807             else Play('MOVE_UNLOAD')
    2808           else if (ShowMoveDomain=dGround) and not IsAlpine
    2809             and (MyMap[ToLoc] and fTerrain=fMountains)
    2810             and ((MyMap[FromLoc] and (fRoad or fRR or fCity)=0)
    2811               or (MyMap[ToLoc] and (fRoad or fRR or fCity)=0)) then
    2812             Play('MOVE_MOUNTAIN');
    2813 
    2814           FocusOnLoc(FromLoc,flImmUpdate);
    2815           PaintLoc_BeforeMove(FromLoc);
    2816           if Command=cShowCapturing then
    2817             MoveOnScreen(TShowMove(Data),1,32,32)
    2818           else MoveOnScreen(TShowMove(Data),1,AnimationSpeed,AnimationSpeed)
    2819           end // if CurrentMoveInfo.DoShow
    2820         else MapValid:=false;
    2821         end
    2822       end;
    2823 
    2824   cShowAttacking:
    2825     if (idle and (NewPlayer=me)
    2826       or not idle and not skipped and (TShowMove(Data).emix<>$FFFF))
    2827       and not ((GameMode=cMovie) and (MovieSpeed=4)) then
    2828       begin
    2829       assert(NewPlayer=me);
    2830       if not idle or (GameMode=cMovie) then
    2831         Application.ProcessMessages;
    2832       with TShowMove(Data) do
    2833         begin
    2834         CurrentMoveInfo.AfterAttackExpeller:=-1;
    2835         CurrentMoveInfo.DoShow:=false;
    2836         if idle then
    2837           CurrentMoveInfo.DoShow:=true // own unit -- always show attacks
    2838         else
    2839           begin
    2840           CurrentMoveInfo.IsAlly:= MyRO.Treaty[Owner]=trAlliance;
    2841           if CurrentMoveInfo.IsAlly then
    2842             CurrentMoveInfo.DoShow:=not mAlNoMoves.Checked
    2843           else CurrentMoveInfo.DoShow:=not mEnNoMoves.Checked;
    2844           end;
    2845         if CurrentMoveInfo.DoShow then
    2846           begin
    2847           ToLoc:=dLoc(FromLoc,dx,dy);
    2848           if Tribe[Owner].ModelPicture[mix].HGr=0 then
    2849             InitEnemyModel(emix);
    2850 
    2851           if (MyMap[ToLoc] and (fCity or fUnit or fOwned) = fCity or fOwned) then
    2852             begin // tell about bombardment
    2853             cix:=MyRO.nCity-1;
    2854             while (cix>=0) and (MyCity[cix].Loc<>ToLoc) do
    2855               dec(cix);
    2856             if MyCity[cix].Status and csToldBombard=0 then
    2857               begin
    2858               if not supervising then
    2859                 MyCity[cix].Status:=MyCity[cix].Status or csToldBombard;
    2860               s:=CityName(MyCity[cix].ID);
    2861               SoundMessageEx(Format(Tribe[Owner].TPhrase('BOMBARD'),[s]),'');
    2862               Update; // remove message box from screen
    2863               end;
    2864             end
    2865           else if Flags and umExpelling<>0 then
    2866             CurrentMoveInfo.AfterAttackExpeller:=Owner;
    2867 
    2868           if Flags and umExpelling<>0 then Play('MOVE_EXPEL')
    2869           else if Owner=me then
    2870             begin
    2871             MakeModelInfo(me,mix,MyModel[mix],mi);
    2872             Play(AttackSound(ModelCode(mi)));
    2873             end
    2874           else Play(AttackSound(ModelCode(MyRO.EnemyModel[emix])));
    2875 
    2876           FocusOnLoc(FromLoc,flImmUpdate);
    2877 
    2878           // before combat
    2879           MainMap.AttackBegin(TShowMove(Data));
    2880           if MyMap[ToLoc] and fCity<>0 then PaintLoc(ToLoc);
    2881           PaintLoc(FromLoc);
    2882           MoveOnScreen(TShowMove(Data),1,9,16);
    2883           MoveOnScreen(TShowMove(Data),17,12,32);
    2884           MoveOnScreen(TShowMove(Data),7,11,16);
    2885 
    2886           // after combat
    2887           MainMap.AttackEffect(TShowMove(Data));
    2888           PaintLoc(ToLoc);
    2889           if EndHealth>0 then
    2890             begin
    2891             Health:=EndHealth;
    2892             MoveOnScreen(TShowMove(Data),10,0,16);
    2893             end
    2894           else if not idle then
    2895             Sleep(MoveTime div 2);
    2896           MainMap.AttackEnd;
    2897           end // if CurrentMoveInfo.DoShow
    2898         else MapValid:=false;
    2899         end
    2900       end;
    2901 
    2902   cShowMissionResult:
    2903     if Cardinal(Data)=0 then
    2904       SoundMessageEx(Phrases.Lookup('NOFOREIGNINFO'),'')
    2905     else
    2906       begin
    2907       s:=Phrases.Lookup('FOREIGNINFO');
    2908       for p1:=0 to nPl-1 do if 3 shl (p1*2) and Cardinal(Data)<>0 then
    2909         s:=s+'\'+Tribe[p1].TPhrase('SHORTNAME');
    2910       SoundMessageEx(s,'')
    2911       end;
    2912 
    2913   cShowShipChange:
    2914     if not IsMultiPlayerGame and (Jump[0]=0) then
    2915       ShowEnemyShipChange(TShowShipChange(Data));
    2916 
    2917   cShowGreatLibTech:
    2918     if not IsMultiPlayerGame and (Jump[0]=0) then with MessgExDlg do
    2919       begin
    2920       MessgText:=Format(Phrases.Lookup('GRLIB_GENERAL'),
    2921         [Phrases.Lookup('ADVANCES',integer(Data))]);
    2922       OpenSound:='NEWADVANCE_GRLIB';
    2923       Kind:=mkOK;
    2924       IconKind:=mikImp;
    2925       IconIndex:=woGrLibrary;
    2926       ShowModal;
    2927       end;
    2928 
    2929   cRefreshDebugMap:
    2930     begin
    2931     if integer(data)=IsoEngine.pDebugMap then
    2932       begin
    2933       MapValid:=false;
    2934       MainOffscreenPaint;
    2935       Update;
    2936       end
    2937     end;
    2938 
    2939   else if Command>=cClientEx then case Command and $FFF0 of
    2940 
    2941     cSetTribe: with TTribeInfo(Data) do
    2942       begin
    2943       i:=UnusedTribeFiles.Count-1;
    2944       while (i>=0) and (AnsiCompareFileName(UnusedTribeFiles[i],FileName)<>0) do
    2945         dec(i);
    2946       if i>=0 then UnusedTribeFiles.Delete(i);
    2947       CreateTribe(trix,FileName,true);
    2948       end;
    2949 
    2950     cSetNewModelPicture, cSetModelPicture:
    2951       if TribeOriginal[TModelPictureInfo(Data).trix] then
    2952         Tribe[TModelPictureInfo(Data).trix].SetModelPicture(
    2953           TModelPictureInfo(Data),Command and $FFF0=cSetNewModelPicture);
    2954 
    2955     cSetSlaveIndex and $FFF0:
    2956       Tribe[integer(data) shr 16].mixSlaves:=integer(data) and $FFFF;
    2957 
    2958     cSetCityName: with TCityNameInfo(Data) do
    2959       if TribeOriginal[ID shr 12] then
    2960         Tribe[ID shr 12].SetCityName(ID and $FFF,NewName);
    2961 
    2962     cSetModelName: with TModelNameInfo(Data) do
    2963       if TribeOriginal[NewPlayer] then
    2964         Tribe[NewPlayer].ModelName[mix]:=NewName;
    2965     end
    2966   end
    2967 end;{<<<client}
    2968 
    2969 {*** main part ***}
    2970 
    2971 procedure TMainScreen.CreateParams (var p: TCreateParams);
    2972 var
    2973 DefaultOptionChecked: integer;
    2974 Reg: TRegistry;
    2975 doinit: boolean;
    2976 begin
    2977 inherited;
    2978 
    2979 // define which menu settings to save
    2980 SaveOption[0]:=mAlEffectiveMovesOnly.Tag;
    2981 SaveOption[1]:=mEnMoves.Tag;
    2982 SaveOption[2]:=mEnAttacks.Tag;
    2983 SaveOption[3]:=mEnNoMoves.Tag;
    2984 SaveOption[4]:=mWaitTurn.Tag;
    2985 SaveOption[5]:=mEffectiveMovesOnly.Tag;
    2986 SaveOption[6]:=mEnFastMoves.Tag;
    2987 SaveOption[7]:=mSlowMoves.Tag;
    2988 SaveOption[8]:=mFastMoves.Tag;
    2989 SaveOption[9]:=mVeryFastMoves.Tag;
    2990 SaveOption[10]:=mNames.Tag;
    2991 SaveOption[11]:=mRepList.Tag;
    2992 SaveOption[12]:=mRepScreens.Tag;
    2993 SaveOption[13]:=mSoundOff.Tag;
    2994 SaveOption[14]:=mSoundOn.Tag;
    2995 SaveOption[15]:=mSoundOnAlt.Tag;
    2996 SaveOption[16]:=mScrollSlow.Tag;
    2997 SaveOption[17]:=mScrollFast.Tag;
    2998 SaveOption[18]:=mScrollOff.Tag;
    2999 SaveOption[19]:=mAlSlowMoves.Tag;
    3000 SaveOption[20]:=mAlFastMoves.Tag;
    3001 SaveOption[21]:=mAlNoMoves.Tag;
    3002 DefaultOptionChecked:= 1 shl 1 + 1 shl 7 + 1 shl 10 + 1 shl 12 + 1 shl 14 + 1 shl 18 + 1 shl 19;
    3003 
    3004 Reg:=TRegistry.Create;
    3005 doinit:=true;
    3006 if Reg.KeyExists('SOFTWARE\cevo\RegVer9') then
    3007   begin
    3008   doinit:=false;
    3009   Reg.OpenKey('SOFTWARE\cevo\RegVer9',false);
    3010   try
    3011     xxt:=Reg.ReadInteger('TileWidth') div 2;
    3012     yyt:=Reg.ReadInteger('TileHeight') div 2;
    3013     OptionChecked:=Reg.ReadInteger('OptionChecked');
    3014     MapOptionChecked:=Reg.ReadInteger('MapOptionChecked');
    3015     CityRepMask:=cardinal(Reg.ReadInteger('CityReport'));
    3016   except
    3017     doinit:=true;
    3018     end;
    3019   Reg.closekey;
    3020   if OptionChecked and (7 shl 16)=0 then
    3021     OptionChecked:=OptionChecked or (1 shl 16); // old regver with no scrolling
    3022   end;
    3023 Reg.Free;
    3024 if doinit then
    3025   begin
    3026   xxt:=48;
    3027   yyt:=24;
    3028   OptionChecked:=DefaultOptionChecked;
    3029   MapOptionChecked:=1 shl moCityNames;
    3030   CityRepMask:=cardinal(not chPopIncrease and not chNoGrowthWarning and not chCaptured);
    3031   end;
    3032 
    3033 if FullScreen then
    3034   begin
    3035   p.Style:=$87000000;
    3036   BorderStyle:=bsNone;
    3037   BorderIcons:=[];
    3038   end;
    3039 
    3040 if 1 shl 13 and OptionChecked<>0 then SoundMode:=smOff
    3041 else if 1 shl 15 and OptionChecked<>0 then SoundMode:=smOnAlt
    3042 else SoundMode:=smOn
    3043 end;
    3044 
    3045 procedure TMainScreen.FormCreate(Sender:TObject);
    3046 var
    3047 i,j: integer;
    3048 begin
    3049 Screen.Cursors[crImpDrag]:=LoadCursor(HInstance,'DRAG');
    3050 Screen.Cursors[crFlatHand]:=LoadCursor(HInstance,'FLATHAND');
    3051 
    3052 // tag-controlled language
    3053 for i:=0 to ComponentCount-1 do
    3054   if Components[i].Tag and $FF<>0 then
    3055     if Components[i] is TMenuItem then
    3056       begin
    3057       TMenuItem(Components[i]).Caption:=
    3058         Phrases.Lookup('CONTROLS',-1+Components[i].Tag and $FF);
    3059       for j:=0 to nSaveOption-1 do
    3060         if Components[i].Tag and $FF=SaveOption[j] then
    3061           TMenuItem(Components[i]).Checked:= 1 shl j and OptionChecked<>0;
    3062       end
    3063     else if Components[i] is TButtonBase then
    3064       begin
    3065       TButtonBase(Components[i]).Hint:=
    3066         Phrases.Lookup('CONTROLS',-1+Components[i].Tag and $FF);
    3067       if (Components[i] is TButtonC) and (TButtonC(Components[i]).ButtonIndex<>1) then
    3068         TButtonC(Components[i]).ButtonIndex:=
    3069           MapOptionChecked shr (Components[i].Tag shr 8) and 1 +2
    3070       end;
    3071 
    3072 // non-tag-controlled language
    3073 mTechTree.Caption:=Phrases2.Lookup('MENU_ADVTREE');
    3074 mViewpoint.Caption:=Phrases2.Lookup('MENU_VIEWPOINT');
    3075 if not Phrases2FallenBackToEnglish then
    3076   begin
    3077   MenuArea.Hint:=Phrases2.Lookup('BTN_MENU');
    3078   TreasuryArea.Hint:=Phrases2.Lookup('TIP_TREASURY');
    3079   ResearchArea.Hint:=Phrases.Lookup('SCIENCE');
    3080   ManagementArea.Hint:=Phrases2.Lookup('BTN_MANAGE');
    3081   end;
    3082 for i:=0 to mRep.Count-1 do
    3083   begin
    3084   j:=mRep[i].Tag shr 8;
    3085   mRep[i].Caption:=CityEventName(j);
    3086   mRep[i].Checked:= CityRepMask and (1 shl j)<>0;
    3087   end;
    3088 
    3089 Mini:=TBitmap.Create;
    3090 Mini.PixelFormat:=pf24bit;
    3091 Panel:=TBitmap.Create;
    3092 Panel.PixelFormat:=pf24bit;
    3093 Panel.Canvas.Font.Assign(UniFont[ftSmall]);
    3094 Panel.Canvas.Brush.Style:=bsClear;
    3095 TopBar:=TBitmap.Create;
    3096 TopBar.PixelFormat:=pf24bit;
    3097 TopBar.Canvas.Font.Assign(UniFont[ftNormal]);
    3098 TopBar.Canvas.Brush.Style:=bsClear;
    3099 Buffer:=TBitmap.Create;
    3100 Buffer.PixelFormat:=pf24bit;
    3101 if 2*lxmax>3*xSizeBig then
    3102   Buffer.Width:=2*lxmax
    3103 else Buffer.Width:=3*xSizeBig;
    3104 if lymax>3*ySizeBig then
    3105   Buffer.Height:=lymax
    3106 else Buffer.Height:=3*ySizeBig;
    3107 Buffer.Canvas.Font.Assign(UniFont[ftSmall]);
    3108 for i:=0 to nPl-1 do AILogo[i]:=nil;
    3109 Canvas.Font.Assign(UniFont[ftSmall]);
    3110 InitButtons();
    3111 EOT.Template:=Templates;
    3112 end;
    3113 
    3114 procedure TMainScreen.FormDestroy(Sender:TObject);
    3115 var
    3116 i: integer;
    3117 begin
    3118 Mini.Free;Buffer.Free;Panel.Free;
    3119 for i:=0 to nPl-1 do if AILogo[i]<>nil then
    3120   AILogo[i].Free;
    3121 end;
    3122 
    3123 procedure TMainScreen.FormResize(Sender:TObject);
    3124 var
    3125 MiniFrame,MaxMapWidth: integer;
    3126 begin
    3127 SmallScreen:= ClientWidth<1024;
    3128 MaxMapWidth:=(G.lx*2-3)*xxt; // avoide the same tile being visible left and right
    3129 if ClientWidth<=MaxMapWidth then
    3130   begin
    3131   MapWidth:=ClientWidth;
    3132   MapOffset:=0;
    3133   end
    3134 else
    3135   begin
    3136   MapWidth:=MaxMapWidth;
    3137   MapOffset:=(ClientWidth-MapWidth) div 2;
    3138   end;
    3139 MapHeight:=ClientHeight-TopBarHeight-PanelHeight+overlap;
    3140 Panel.Width:=ClientWidth; Panel.Height:=PanelHeight;
    3141 TopBar.Width:=ClientWidth; TopBar.Height:=TopBarHeight;
    3142 MiniFrame:=(lxmax_xxx-G.ly) div 2;
    3143 xMidPanel:=(G.lx+MiniFrame)*2+1;
    3144 xRightPanel:=ClientWidth-LeftPanelWidth-10;
    3145 if ClientMode=cEditMap then
    3146   TrPitch:=2*xxt
    3147 else TrPitch:=66;
    3148 xMini:=MiniFrame-5; yMini:=(PanelHeight-26-lxmax_xxx) div 2+MiniFrame;
    3149 ywmax:=(G.ly-MapHeight div yyt+1) and not 1;
    3150 ywcenter:=-((MapHeight-yyt*(G.ly-1)) div (4*yyt))*2; // only for ywmax<=0
    3151 if ywmax<=0 then yw:=ywcenter
    3152 else if yw<0 then yw:=0
    3153 else if yw>ywmax then yw:=ywmax;
    3154 UnitInfoBtn.Top:=ClientHeight-29;
    3155 UnitInfoBtn.Left:=xMidPanel+7+99;
    3156 UnitBtn.Top:=ClientHeight-29;
    3157 UnitBtn.Left:=xMidPanel+7+99+31;
    3158 TerrainBtn.Top:=ClientHeight-29;
    3159 TerrainBtn.Left:=xMidPanel+7+99+62;
    3160 MovieSpeed1Btn.Top:=ClientHeight-91;
    3161 MovieSpeed1Btn.Left:=ClientWidth div 2-62;
    3162 MovieSpeed2Btn.Top:=ClientHeight-91;
    3163 MovieSpeed2Btn.Left:=ClientWidth div 2-62+29;
    3164 MovieSpeed3Btn.Top:=ClientHeight-91;
    3165 MovieSpeed3Btn.Left:=ClientWidth div 2-62+2*29;
    3166 MovieSpeed4Btn.Top:=ClientHeight-91;
    3167 MovieSpeed4Btn.Left:=ClientWidth div 2-62+3*29+12;
    3168 EOT.Top:=ClientHeight-64;
    3169 EOT.Left:=ClientWidth-62;
    3170 SetWindowPos(sb.h,0,xRightPanel+10-14-GetSystemMetrics(SM_CXVSCROLL),
    3171   ClientHeight-MidPanelHeight+8,0,0,SWP_NOSIZE or SWP_NOZORDER);
    3172 MapBtn0.Left:=xMini+G.lx-44;
    3173 MapBtn0.Top:=ClientHeight-15;
    3174 MapBtn1.Left:=xMini+G.lx-28;
    3175 MapBtn1.Top:=ClientHeight-15;
    3176 {MapBtn2.Left:=xMini+G.lx-20;
    3177 MapBtn2.Top:=ClientHeight-15;
    3178 MapBtn3.Left:=xMini+G.lx-4;
    3179 MapBtn3.Top:=ClientHeight-15;}
    3180 MapBtn5.Left:=xMini+G.lx-12;
    3181 MapBtn5.Top:=ClientHeight-15;
    3182 MapBtn4.Left:=xMini+G.lx+20;
    3183 MapBtn4.Top:=ClientHeight-15;
    3184 MapBtn6.Left:=xMini+G.lx+36;
    3185 MapBtn6.Top:=ClientHeight-15;
    3186 TreasuryArea.Left:=ClientWidth div 2-172;
    3187 ResearchArea.Left:=ClientWidth div 2;
    3188 ManagementArea.Left:=ClientWidth-xPalace;
    3189 ManagementArea.Top:=TopBarHeight+MapHeight-overlap+yPalace;
    3190 ArrangeMidPanel;
    3191 if RepaintOnResize then
    3192   begin
    3193   RectInvalidate(0,TopBarHeight,ClientWidth,TopBarHeight+MapHeight);
    3194   MapValid:=false;
    3195   PaintAll
    3196   end
    3197 end;
    3198 
    3199 procedure TMainScreen.FormCloseQuery(Sender: TObject; var CanClose: boolean);
    3200 begin
    3201 CanClose:=Closable;
    3202 if not Closable and idle and (me=0) and (ClientMode<scContact) then
    3203   MenuClick(mResign)
    3204 end;
    3205 
    3206 procedure TMainScreen.OnScroll(var m:TMessage);
    3207 begin
    3208 if ProcessPVSB(sb,m) then begin PanelPaint; Update end
    3209 end;
    3210 
    3211 procedure TMainScreen.OnEOT(var Msg:TMessage);
    3212 begin
    3213 EndTurn
    3214 end;
    3215 
    3216 procedure TMainScreen.EOTClick(Sender:TObject);
    3217 begin
    3218 if GameMode=cMovie then
    3219   begin
    3220   MessgExDlg.CancelMovie;
    3221   Server(sBreak,me,0,nil^)
    3222   end
    3223 else if ClientMode<0 then
    3224   skipped:=true
    3225 else if ClientMode>=scContact then
    3226   NegoDlg.ShowNewContent(wmPersistent)
    3227 else if Jump[pTurn]>0 then
    3228   begin Jump[pTurn]:=0; StartRunning:=false end
    3229 else EndTurn
    3230 end;
    3231 
    3232 // set xTerrain, xTroop, and TrRow
    3233 procedure TMainScreen.ArrangeMidPanel;
    3234 begin
    3235 if ClientMode=cEditMap then
    3236   xTroop:=xMidPanel+15
    3237 else
    3238   begin
    3239   if supervising then
    3240     xTerrain:=xMidPanel+2*xxt+14
    3241   else if ClientWidth<1280 then
    3242     xTerrain:=ClientWidth div 2+(1280-ClientWidth) div 3
    3243   else xTerrain:=ClientWidth div 2;
    3244   xTroop:=xTerrain+2*xxt+12;
    3245   if SmallScreen and not supervising then
    3246     xTroop:=xRightPanel+10-3*66-GetSystemMetrics(SM_CXVSCROLL)-19-4;
    3247     // not perfect but we assume almost no one is still playing on a 800x600 screen
    3248   end;
    3249 TrRow:=(xRightPanel+10-xTroop-GetSystemMetrics(SM_CXVSCROLL)-19) div TrPitch;
    3250 end;
    3251 
    3252 function TMainScreen.EndTurn(WasSkipped: boolean): boolean;
    3253 
    3254   function IsResourceUnused(cix, NeedFood, NeedProd: integer): boolean;
    3255   var
    3256   dx,dy,fix: integer;
    3257   CityAreaInfo: TCityAreaInfo;
    3258   TileInfo: TTileInfo;
    3259   begin
    3260   Server(sGetCityAreaInfo,me,cix,CityAreaInfo);
    3261   for dy:=-3 to 3 do for dx:=-3 to 3 do
    3262     if ((dx+dy) and 1=0) and (dx*dx*dy*dy<81) then
    3263       begin
    3264       fix:=(dy+3) shl 2+(dx+3) shr 1;
    3265       if (MyCity[cix].Tiles and (1 shl fix)=0) // not used yet
    3266         and (CityAreaInfo.Available[fix]=faAvailable) then // usable
    3267         begin
    3268         TileInfo.ExplCity:=cix;
    3269         Server(sGetHypoCityTileInfo, me, dLoc(MyCity[cix].Loc,dx,dy), TileInfo);
    3270         if (TileInfo.Food>=NeedFood) and (TileInfo.Prod>=NeedProd) then
    3271           begin result:=true; exit end;
    3272         end
    3273       end;
    3274   result:=false;
    3275   end;
    3276 
    3277 var
    3278 i,p1,uix,cix,CenterLoc: integer;
    3279 MsgItem: string;
    3280 CityReport: TCityReport;
    3281 PlaneReturnData: TPlaneReturnData;
    3282 Zoom: boolean;
    3283 begin
    3284 result:=false;
    3285 if ClientMode>=scDipOffer then exit;
    3286 
    3287 if supervising and (me<>0) then
    3288   begin
    3289   for i:=0 to Screen.FormCount-1 do
    3290     if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    3291       Screen.Forms[i].Close; // close windows
    3292   ItsMeAgain(0);
    3293   end;
    3294 
    3295 CityOptimizer_EndOfTurn;
    3296 
    3297 if not WasSkipped then // check warnings
    3298   begin
    3299   // need to move planes home?
    3300   for uix:=0 to MyRO.nUn-1 do with MyUn[uix] do
    3301     if (Loc>=0) and (MyModel[mix].Domain=dAir)
    3302       and (Status and usToldNoReturn=0) and (Master<0)
    3303       and (MyMap[Loc] and fCity=0) and (MyMap[Loc] and fTerImp<>tiBase) then
    3304       begin
    3305       PlaneReturnData.Fuel:=Fuel;
    3306       PlaneReturnData.Loc:=Loc;
    3307       PlaneReturnData.Movement:=0; // end turn without further movement?
    3308       if Server(sGetPlaneReturn, me, uix, PlaneReturnData)=eNoWay then
    3309         begin
    3310         CenterLoc:=Loc+G.lx*6; // centering the unit itself would make it covered by the query dialog
    3311         while CenterLoc>=G.lx*G.ly do
    3312           dec(CenterLoc, G.lx*2);
    3313         Centre(CenterLoc);
    3314         SetTroopLoc(-1);
    3315         PaintAll;
    3316 
    3317         if MyModel[mix].Kind=mkSpecial_Glider then
    3318           MsgItem:='LOWFUEL_GLIDER'
    3319         else MsgItem:='LOWFUEL';
    3320         if SimpleQuery(mkYesNo,Phrases.Lookup(MsgItem),'WARNING_LOWSUPPORT')<>mrOk then
    3321           begin
    3322           SetUnFocus(uix);
    3323           SetTroopLoc(Loc);
    3324           PanelPaint;
    3325           exit;
    3326           end;
    3327         MyUn[uix].Status:=MyUn[uix].Status or usToldNoReturn;
    3328         end
    3329       end;
    3330 
    3331   if not supervising and (MyRO.TestFlags and tfImmImprove=0)
    3332     and (MyRO.Government<>gAnarchy)
    3333     and (MyRO.Money+TaxSum<0) and (MyRO.TaxRate<100) then // low funds!
    3334     with MessgExDlg do
    3335       begin
    3336       OpenSound:='WARNING_LOWFUNDS';
    3337       MessgText:=Phrases.Lookup('LOWFUNDS');
    3338       Kind:=mkYesNo;
    3339       IconKind:=mikImp;
    3340       IconIndex:=imTrGoods;
    3341       ShowModal;
    3342       if ModalResult<>mrOK then exit
    3343       end;
    3344 
    3345   if MyRO.Government<>gAnarchy then
    3346     for cix:=0 to MyRO.nCity-1 do with MyCity[cix] do
    3347       if (Loc>=0) and (Flags and chCaptured=0) then
    3348         begin
    3349         Zoom:=false;
    3350         CityReport.HypoTiles:=-1;
    3351         CityReport.HypoTax:=-1;
    3352         CityReport.HypoLux:=-1;
    3353         Server(sGetCityReport,me,cix,CityReport);
    3354 
    3355         if (CityReport.Working-CityReport.Happy>Size shr 1)
    3356           and (Flags and chCaptured<=$10000) then
    3357           with MessgExDlg do
    3358             begin
    3359             OpenSound:='WARNING_DISORDER';
    3360             if Status and csResourceWeightsMask=0 then
    3361               MsgItem:='DISORDER'
    3362             else MsgItem:='DISORDER_UNREST';
    3363             MessgText:=Format(Phrases.Lookup(MsgItem),[CityName(ID)]);
    3364             Kind:=mkYesNo;
    3365       //      BigIcon:=29;
    3366             ShowModal;
    3367             Zoom:= ModalResult<>mrOK;
    3368             end;
    3369         if not Zoom and (Food+CityReport.FoodRep-CityReport.Eaten<0) then
    3370           with MessgExDlg do
    3371             begin
    3372             OpenSound:='WARNING_FAMINE';
    3373             if Status and csResourceWeightsMask=0 then
    3374               MsgItem:='FAMINE'
    3375             else if (CityReport.Deployed<>0) and IsResourceUnused(cix,1,0) then
    3376               MsgItem:='FAMINE_UNREST'
    3377             else MsgItem:='FAMINE_TILES';
    3378             MessgText:=Format(Phrases.Lookup(MsgItem),[CityName(ID)]);
    3379             Kind:=mkYesNo;
    3380             IconKind:=mikImp;
    3381             IconIndex:=22;
    3382             ShowModal;
    3383             Zoom:= ModalResult<>mrOK;
    3384             end;
    3385         if not Zoom and (CityReport.ProdRep<CityReport.Support) then
    3386           with MessgExDlg do
    3387             begin
    3388             OpenSound:='WARNING_LOWSUPPORT';
    3389             if Status and csResourceWeightsMask=0 then
    3390               MsgItem:='LOWSUPPORT'
    3391             else if (CityReport.Deployed<>0) and IsResourceUnused(cix,0,1) then
    3392               MsgItem:='LOWSUPPORT_UNREST'
    3393             else MsgItem:='LOWSUPPORT_TILES';
    3394             MessgText:=Format(Phrases.Lookup(MsgItem),[CityName(ID)]);
    3395             Kind:=mkYesNo;
    3396             IconKind:=mikImp;
    3397             IconIndex:=29;
    3398             ShowModal;
    3399             Zoom:= ModalResult<>mrOK;
    3400             end;
    3401         if Zoom then
    3402           begin // zoom to city
    3403           ZoomToCity(Loc);
    3404           exit
    3405           end
    3406         end;
    3407 
    3408   if (MyRO.Happened and phTech<>0) and (MyRO.ResearchTech<0)
    3409     and (MyData.FarTech<>adNexus) then
    3410     if not ChooseResearch then
    3411       exit;
    3412   end;
    3413 
    3414 RememberPeaceViolation;
    3415 
    3416 SetUnFocus(-1);
    3417 for uix:=0 to MyRO.nUn-1 do
    3418   MyUn[uix].Status:=MyUn[uix].Status and usPersistent;
    3419 
    3420 CityDlg.CloseAction:=None;
    3421 if IsMultiPlayerGame then
    3422   begin // close windows for next player
    3423   for i:=0 to Screen.FormCount-1 do
    3424     if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    3425       Screen.Forms[i].Close;
    3426   end
    3427 else
    3428   begin
    3429   if CityDlg.Visible then CityDlg.Close;
    3430   if UnitStatDlg.Visible then UnitStatDlg.Close;
    3431   end;
    3432 for i:=0 to Screen.FormCount-1 do
    3433   if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    3434     Screen.Forms[i].Enabled:=false;
    3435 
    3436 if Server(sTurn,pTurn,0,nil^)>=rExecuted then
    3437   begin
    3438   if Jump[pTurn]>0 then EOT.Hint:=Phrases.Lookup('BTN_STOP')
    3439   else EOT.Hint:=Phrases.Lookup('BTN_SKIP');
    3440   result:=true;
    3441   SetTroopLoc(-1);
    3442   pTurn:=-1;
    3443   pLogo:=-1;
    3444   UnitInfoBtn.Visible:=false;
    3445   UnitBtn.Visible:=false;
    3446   TerrainBtn.Visible:=false;
    3447   EOT.ButtonIndex:=eotCancel;
    3448   EOT.Visible:=true;
    3449   MapValid:=false;
    3450   PanelPaint;
    3451   Update;
    3452   ClientMode:=-1;
    3453   idle:=false;
    3454   skipped:=WasSkipped;
    3455   for p1:=1 to nPl-1 do
    3456     if G.RO[p1]<>nil then skipped:=true; // don't show enemy moves in hotseat mode
    3457   end
    3458 else PanelPaint
    3459 end; // EndTurn
    3460 
    3461 procedure TMainScreen.EndNego;
    3462 begin
    3463 if NegoDlg.Visible then NegoDlg.Close;
    3464 HaveStrategyAdvice:=false;
    3465   // AdvisorDlg.HaveStrategyAdvice;
    3466   // negotiation might have changed advices
    3467 EOT.ButtonIndex:=eotCancel;
    3468 EOT.Visible:=true;
    3469 PanelPaint;
    3470 Update;
    3471 ClientMode:=-1;
    3472 idle:=false;
    3473 end;
    3474 
    3475 procedure TMainScreen.ProcessRect(x0,y0,nx,ny,Options: integer);
    3476 var
    3477 xs,ys,xl,yl: integer;
    3478 begin
    3479 xl:=nx*xxt+xxt;
    3480 yl:=ny*yyt+yyt*2;
    3481 xs:=(x0-xw)*(xxt*2)+y0 and 1*xxt-G.lx*(xxt*2);
    3482 // |xs+xl/2-MapWidth/2| -> min
    3483 while abs(2*(xs+G.lx*(xxt*2))+xl-MapWidth)<abs(2*xs+xl-MapWidth) do
    3484   inc(xs,G.lx*(xxt*2));
    3485 ys:=(y0-yw)*yyt-yyt;
    3486 if xs+xl>MapWidth then xl:=MapWidth-xs;
    3487 if ys+yl>MapHeight then yl:=MapHeight-ys;
    3488 if (xl<=0) or (yl<=0) then exit;
    3489 if Options and prPaint<>0 then
    3490   begin
    3491   if Options and prAutoBounds<>0 then
    3492     MainMap.SetPaintBounds(xs,ys,xs+xl,ys+yl);
    3493   MainMap.Paint(xs,ys,x0+G.lx*y0,nx,ny,-1,-1);
    3494   end;
    3495 if Options and prInvalidate<>0 then
    3496   RectInvalidate(MapOffset+xs,TopBarHeight+ys,MapOffset+xs+xl,TopBarHeight+ys+yl)
    3497 end;
    3498 
    3499 procedure TMainScreen.PaintLoc(Loc: integer; Radius: integer = 0);
    3500 var
    3501 yLoc,x0: integer;
    3502 begin
    3503 if MapValid then
    3504   begin
    3505   yLoc:=(Loc+G.lx*1024) div G.lx -1024;
    3506   x0:=(Loc+(yLoc and 1-2*Radius+G.lx*1024) div 2) mod G.lx;
    3507   offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    3508   ProcessRect(x0,yLoc-2*Radius,4*Radius+1,4*Radius+1,
    3509     prPaint or prAutoBounds or prInvalidate);
    3510   Update;
    3511   end
    3512 end;
    3513 
    3514 procedure TMainScreen.PaintLocTemp(Loc, Style: integer);
    3515 var
    3516 y0,x0,xMap,yMap: integer;
    3517 begin
    3518 if not MapValid then exit;
    3519 Buffer.Canvas.Font.Assign(UniFont[ftSmall]);
    3520 y0:=Loc div G.lx;
    3521 x0:=Loc mod G.lx;
    3522 xMap:=(x0-xw)*(xxt*2)+y0 and 1*xxt-G.lx*(xxt*2);
    3523 // |xMap+xxt-MapWidth/2| -> min
    3524 while abs(2*(xMap+G.lx*(xxt*2))+2*xxt-MapWidth)<abs(2*xMap+2*xxt-MapWidth) do
    3525   inc(xMap,G.lx*(xxt*2));
    3526 yMap:=(y0-yw)*yyt-yyt;
    3527 NoMap.SetOutput(Buffer);
    3528 NoMap.SetPaintBounds(0,0,2*xxt,3*yyt);
    3529 NoMap.Paint(0,0,Loc,1,1,-1,-1,Style=pltsBlink);
    3530 PaintBufferToScreen(xMap,yMap,2*xxt,3*yyt);
    3531 end;
    3532 
    3533 // paint content of buffer directly to screen instead of offscreen
    3534 // panel protusions are added
    3535 // NoMap must be set to buffer and bounds before
    3536 procedure TMainScreen.PaintBufferToScreen(xMap,yMap,width,height: integer);
    3537 begin
    3538 if xMap+width>MapWidth then
    3539   width:=MapWidth-xMap;
    3540 if yMap+height>MapHeight then
    3541   height:=MapHeight-yMap;
    3542 if (width<=0) or (height<=0) or (width+xMap<=0) or (height+yMap<=0) then
    3543   exit;
    3544 
    3545 NoMap.BitBlt(Panel,-xMap-MapOffset,-yMap+MapHeight-overlap,xMidPanel,overlap,
    3546   0,0,SRCCOPY);
    3547 NoMap.BitBlt(Panel,-xMap-MapOffset+xRightPanel,-yMap+MapHeight-overlap,
    3548   Panel.Width-xRightPanel,overlap,xRightPanel,0,SRCCOPY);
    3549 if yMap<0 then
    3550   begin
    3551   if xMap<0 then
    3552     BitBlt(Canvas.Handle,MapOffset,TopBarHeight,width+xMap,height+yMap,
    3553       Buffer.Canvas.Handle,-xMap,-yMap,SRCCOPY)
    3554   else BitBlt(Canvas.Handle,xMap+MapOffset,TopBarHeight,width,height+yMap,
    3555     Buffer.Canvas.Handle,0,-yMap,SRCCOPY)
    3556   end
    3557 else
    3558   begin
    3559   if xMap<0 then
    3560     BitBlt(Canvas.Handle,MapOffset,TopBarHeight+yMap,width+xMap,height,
    3561       Buffer.Canvas.Handle,-xMap,0,SRCCOPY)
    3562   else BitBlt(Canvas.Handle,xMap+MapOffset,TopBarHeight+yMap,width,height,
    3563     Buffer.Canvas.Handle,0,0,SRCCOPY);
    3564   end
    3565 end;
    3566 
    3567 procedure TMainScreen.PaintLoc_BeforeMove(FromLoc: integer);
    3568 var
    3569 yLoc,x0: integer;
    3570 begin
    3571 if MapValid then
    3572   begin
    3573   yLoc:=(FromLoc+G.lx*1024) div G.lx -1024;
    3574   x0:=(FromLoc+(yLoc and 1+G.lx*1024) div 2) mod G.lx;
    3575   offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    3576   ProcessRect(x0,yLoc,1,1,prPaint or prAutoBounds);
    3577   end
    3578 end;
    3579 
    3580 procedure TMainScreen.PaintDestination;
    3581 var
    3582 Destination: integer;
    3583 begin
    3584 if (UnFocus>=0) and (MyUn[UnFocus].Status and usGoto<>0) then
    3585   begin
    3586   Destination:=MyUn[UnFocus].Status shr 16;
    3587   if (Destination<>$7FFF) and (Destination<>MyUn[UnFocus].Loc) then
    3588     PaintLocTemp(Destination,pltsBlink);
    3589   end;
    3590 end;
    3591 
    3592 procedure TMainScreen.MiniPaint;
    3593 type
    3594 TLine=array[0..99999999,0..2] of Byte;
    3595 var
    3596 uix,cix,x,y,Loc,i,hw,xm,cm,cmPolOcean,cmPolNone:integer;
    3597 PrevMiniLine,MiniLine:^TLine;
    3598 begin
    3599 cmPolOcean:=GrExt[HGrSystem].Data.Canvas.Pixels[101,67];
    3600 cmPolNone:=GrExt[HGrSystem].Data.Canvas.Pixels[102,67];
    3601 hw:=MapWidth div (xxt*2);
    3602 with Mini.Canvas do
    3603   begin
    3604   Brush.Color:=$000000;
    3605   FillRect(Rect(0,0,Mini.Width,Mini.Height));
    3606   end;
    3607 MiniLine:=nil;
    3608 for y:=0 to G.ly-1 do
    3609   begin
    3610   PrevMiniLine:=MiniLine;
    3611   MiniLine:=Mini.ScanLine[y];
    3612   for x:=0 to G.lx-1 do if MyMap[x+G.lx*y] and fTerrain<>fUNKNOWN then
    3613     begin
    3614     Loc:=x+G.lx*y;
    3615     for i:=0 to 1 do
    3616       begin
    3617       xm:=((x-xwMini)*2+i+y and 1-hw+G.lx*5) mod (G.lx*2);
    3618       cm:=MiniColors[MyMap[Loc] and fTerrain,i];
    3619       if ClientMode=cEditMap then
    3620         begin
    3621         if MyMap[Loc] and (fPrefStartPos or fStartPos)<>0 then
    3622           cm:=$FFFFFF;
    3623         end
    3624       else if MyMap[Loc] and fCity<>0 then
    3625         begin
    3626         cix:=MyRO.nCity-1;
    3627         while (cix>=0) and (MyCity[cix].Loc<>Loc) do dec(cix);
    3628         if cix>=0 then cm:=Tribe[me].Color
    3629         else
    3630           begin
    3631           cix:=MyRO.nEnemyCity-1;
    3632           while (cix>=0) and (MyRO.EnemyCity[cix].Loc<>Loc) do dec(cix);
    3633           if cix>=0 then cm:=Tribe[MyRO.EnemyCity[cix].Owner].Color
    3634           end;
    3635         cm:=$808080 or cm shr 1; {increase brightness}
    3636         if PrevMiniLine<>nil then
    3637           begin // 2x2 city dot covers two scanlines
    3638           PrevMiniLine[xm,0]:=cm shr 16;
    3639           PrevMiniLine[xm,1]:=cm shr 8 and $FF;
    3640           PrevMiniLine[xm,2]:=cm and $FF;
     7649              GamePopup.Popup(Left + 4, Top + GetSystemMetrics(SM_CYCAPTION) + 4
     7650                + TopBarHeight - 1);
    36417651          end
    36427652        end
    3643       else if (i=0) and (MyMap[Loc] and fUnit<>0) then
    3644         begin
    3645         uix:=MyRO.nUn-1;
    3646         while (uix>=0) and (MyUn[uix].Loc<>Loc) do dec(uix);
    3647         if uix>=0 then cm:=Tribe[me].Color
     7653        else if IsPanelPixel(x, y) then
     7654          PanelBoxMouseDown(Sender, Button, Shift, x,
     7655            y - (ClientHeight - PanelHeight))
     7656        else if (y >= TopBarHeight) and (x >= MapOffset) and
     7657          (x < MapOffset + MapWidth) then
     7658          MapBoxMouseDown(Sender, Button, Shift, x - MapOffset,
     7659            y - TopBarHeight)
     7660    end;
     7661
     7662    procedure TMainScreen.FormMouseMove(Sender: TObject; Shift: TShiftState;
     7663      x, y: integer);
     7664    begin
     7665      if idle then
     7666        if IsPanelPixel(x, y) then
     7667          PanelBoxMouseMove(Sender, Shift, x, y - (ClientHeight - PanelHeight))
     7668        else if (y >= TopBarHeight) and (x >= MapOffset) and
     7669          (x < MapOffset + MapWidth) then
     7670          MapBoxMouseMove(Sender, Shift, x - MapOffset, y - TopBarHeight);
     7671    end;
     7672
     7673    procedure TMainScreen.FormMouseUp(Sender: TObject; Button: TMouseButton;
     7674      Shift: TShiftState; x, y: integer);
     7675    begin
     7676      if idle then
     7677        PanelBoxMouseUp(Sender, Button, Shift, x,
     7678          y - (ClientHeight - PanelHeight));
     7679    end;
     7680
     7681    procedure TMainScreen.FormPaint(Sender: TObject);
     7682    begin
     7683      MainOffscreenPaint;
     7684      if (MapOffset > 0) or (MapOffset + MapWidth < ClientWidth) then
     7685        with Canvas do
     7686        begin // pillarbox, make left and right border black
     7687          if me < 0 then
     7688            Brush.Color := $000000
     7689          else
     7690            Brush.Color := EmptySpaceColor;
     7691          if xMidPanel > MapOffset then
     7692            FillRect(Rect(0, TopBarHeight, MapOffset, TopBarHeight + MapHeight
     7693              - overlap))
     7694          else
     7695          begin
     7696            FillRect(Rect(0, TopBarHeight, xMidPanel, TopBarHeight + MapHeight -
     7697              overlap));
     7698            FillRect(Rect(xMidPanel, TopBarHeight, MapOffset,
     7699              TopBarHeight + MapHeight));
     7700          end;
     7701          if xRightPanel < MapOffset + MapWidth then
     7702            FillRect(Rect(MapOffset + MapWidth, TopBarHeight, ClientWidth,
     7703              TopBarHeight + MapHeight - overlap))
     7704          else
     7705          begin
     7706            FillRect(Rect(MapOffset + MapWidth, TopBarHeight, xRightPanel,
     7707              TopBarHeight + MapHeight));
     7708            FillRect(Rect(xRightPanel, TopBarHeight, ClientWidth,
     7709              TopBarHeight + MapHeight - overlap));
     7710          end;
     7711          Brush.Style := bsClear;
     7712        end;
     7713      BitBlt(Canvas.Handle, MapOffset, TopBarHeight, MapWidth,
     7714        MapHeight - overlap, offscreen.Canvas.Handle, 0, 0, SRCCOPY);
     7715      BitBlt(Canvas.Handle, 0, 0, ClientWidth, TopBarHeight,
     7716        TopBar.Canvas.Handle, 0, 0, SRCCOPY);
     7717      if xMidPanel > MapOffset then
     7718        BitBlt(Canvas.Handle, xMidPanel, TopBarHeight + MapHeight - overlap,
     7719          ClientWidth div 2 - xMidPanel, overlap, offscreen.Canvas.Handle,
     7720          xMidPanel - MapOffset, MapHeight - overlap, SRCCOPY)
     7721      else
     7722        BitBlt(Canvas.Handle, MapOffset, TopBarHeight + MapHeight - overlap,
     7723          ClientWidth div 2 - MapOffset, overlap, offscreen.Canvas.Handle, 0,
     7724          MapHeight - overlap, SRCCOPY);
     7725      if xRightPanel < MapOffset + MapWidth then
     7726        BitBlt(Canvas.Handle, ClientWidth div 2, TopBarHeight + MapHeight -
     7727          overlap, xRightPanel - ClientWidth div 2, overlap,
     7728          offscreen.Canvas.Handle, ClientWidth div 2 - MapOffset,
     7729          MapHeight - overlap, SRCCOPY)
     7730      else
     7731        BitBlt(Canvas.Handle, ClientWidth div 2, TopBarHeight + MapHeight -
     7732          overlap, MapOffset + MapWidth - ClientWidth div 2, overlap,
     7733          offscreen.Canvas.Handle, ClientWidth div 2 - MapOffset,
     7734          MapHeight - overlap, SRCCOPY);
     7735      BitBlt(Canvas.Handle, 0, TopBarHeight + MapHeight - overlap, xMidPanel,
     7736        overlap, Panel.Canvas.Handle, 0, 0, SRCCOPY);
     7737      BitBlt(Canvas.Handle, xRightPanel, TopBarHeight + MapHeight - overlap,
     7738        Panel.width - xRightPanel, overlap, Panel.Canvas.Handle, xRightPanel,
     7739        0, SRCCOPY);
     7740      BitBlt(Canvas.Handle, 0, TopBarHeight + MapHeight, Panel.width,
     7741        PanelHeight - overlap, Panel.Canvas.Handle, 0, overlap, SRCCOPY);
     7742      if (pLogo >= 0) and (G.RO[pLogo] = nil) and (AILogo[pLogo] <> nil) then
     7743        BitBlt(Canvas.Handle, xRightPanel + 10 - (16 + 64),
     7744          ClientHeight - PanelHeight, 64, 64, AILogo[pLogo].Canvas.Handle, 0, 0,
     7745          SRCCOPY);
     7746    end;
     7747
     7748    procedure TMainScreen.RectInvalidate(Left, Top, Rigth, Bottom: integer);
     7749    var
     7750      r0: HRgn;
     7751    begin
     7752      r0 := CreateRectRgn(Left, Top, Rigth, Bottom);
     7753      InvalidateRgn(Handle, r0, false);
     7754      DeleteObject(r0);
     7755    end;
     7756
     7757    procedure TMainScreen.SmartRectInvalidate(Left, Top, Rigth,
     7758      Bottom: integer);
     7759    var
     7760      i: integer;
     7761      r0, r1: HRgn;
     7762    begin
     7763      r0 := CreateRectRgn(Left, Top, Rigth, Bottom);
     7764      for i := 0 to ControlCount - 1 do
     7765        if not(Controls[i] is TArea) and Controls[i].Visible then
     7766        begin
     7767          with Controls[i].BoundsRect do
     7768            r1 := CreateRectRgn(Left, Top, Right, Bottom);
     7769          CombineRgn(r0, r0, r1, RGN_DIFF);
     7770          DeleteObject(r1);
     7771        end;
     7772      InvalidateRgn(Handle, r0, false);
     7773      DeleteObject(r0);
     7774    end;
     7775
     7776    procedure TMainScreen.mRepClicked(Sender: TObject);
     7777    begin
     7778      with TMenuItem(Sender) do
     7779      begin
     7780        Checked := not Checked;
     7781        if Checked then
     7782          CityRepMask := CityRepMask or (1 shl (Tag shr 8))
    36487783        else
     7784          CityRepMask := CityRepMask and not(1 shl (Tag shr 8))
     7785      end
     7786    end;
     7787
     7788    procedure TMainScreen.mLogClick(Sender: TObject);
     7789    begin
     7790      LogDlg.Show
     7791    end;
     7792
     7793    procedure TMainScreen.FormShow(Sender: TObject);
     7794    begin
     7795      Timer1.Enabled := true
     7796    end;
     7797
     7798    procedure TMainScreen.FormClose(Sender: TObject; var Action: TCloseAction);
     7799    begin
     7800      Timer1.Enabled := false
     7801    end;
     7802
     7803    procedure TMainScreen.Radio(Sender: TObject);
     7804    begin
     7805      TMenuItem(Sender).Checked := true
     7806    end;
     7807
     7808    procedure TMainScreen.mManipClick(Sender: TObject);
     7809    var
     7810      Flag: integer;
     7811    begin
     7812      with TMenuItem(Sender) do
     7813      begin
     7814        Flag := 1 shl (Tag shr 8);
     7815        if Checked then
     7816          Server(sClearTestFlag, 0, Flag, nil^)
     7817        else
     7818        begin
     7819          Server(sSetTestFlag, 0, Flag, nil^);
     7820          Play('CHEAT');
     7821        end;
     7822        if not supervising then
     7823        begin
     7824          if Flag = tfUncover then
    36497825          begin
    3650           uix:=MyRO.nEnemyUn-1;
    3651           while (uix>=0) and (MyRO.EnemyUn[uix].Loc<>Loc) do dec(uix);
    3652           if uix>=0 then cm:=Tribe[MyRO.EnemyUn[uix].Owner].Color
    3653           end;
    3654         cm:=$808080 or cm shr 1; {increase brightness}
    3655         end
    3656       else if MapOptionChecked and (1 shl moPolitical)<>0 then
    3657         begin
    3658         if MyMap[Loc] and fTerrain<fGrass then cm:=cmPolOcean
    3659         else if MyRO.Territory[Loc]<0 then cm:=cmPolNone
    3660         else cm:=Tribe[MyRO.Territory[Loc]].Color;
    3661         end;
    3662       MiniLine[xm,0]:=cm shr 16;
    3663       MiniLine[xm,1]:=cm shr 8 and $FF;
    3664       MiniLine[xm,2]:=cm and $FF;
    3665       end;
    3666     end
    3667   end;
    3668 end;
    3669 
    3670 procedure TMainScreen.MainOffscreenPaint;
    3671 var
    3672 ProcessOptions: integer;
    3673 rec:TRect;
    3674 DoInvalidate: boolean;
    3675 begin
    3676 if me<0 then
    3677   with offscreen.Canvas do
    3678     begin
    3679     Brush.Color:=$000000;
    3680     FillRect(Rect(0,0,MapWidth,MapHeight));
    3681     Brush.Style:=bsClear;
    3682     OffscreenUser:=self;
    3683     exit
    3684     end;
    3685 
    3686 MainMap.SetPaintBounds(0,0,MapWidth,MapHeight);
    3687 if OffscreenUser<>self then
    3688   begin
    3689   if OffscreenUser<>nil then OffscreenUser.Update;
    3690     // complete working with old owner to prevent rebound
    3691   if MapValid and (xwd=xw) and (ywd=yw) then
    3692     MainMap.SetPaintBounds(0,0,UsedOffscreenWidth,UsedOffscreenHeight);
    3693   MapValid:=false;
    3694   OffscreenUser:=self;
    3695   end;
    3696 
    3697 if xw-xwd>G.lx div 2 then xwd:=xwd+G.lx
    3698 else if xwd-xw>G.lx div 2 then xwd:=xwd-G.lx;
    3699 if not MapValid or (xw-xwd>MapWidth div (xxt*2)) or (xwd-xw>MapWidth div (xxt*2))
    3700   or (yw-ywd>MapHeight div yyt) or (ywd-yw>MapHeight div yyt) then
    3701   begin
    3702   offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    3703   ProcessRect(xw,yw,MapWidth div xxt,MapHeight div yyt,prPaint or prInvalidate)
    3704   end
    3705 else
    3706   begin
    3707   if (xwd=xw) and (ywd=yw) then exit; {map window not moved}
    3708   offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    3709   rec:=Rect(0,0,MapWidth,MapHeight);
    3710   ScrollDC(offscreen.Canvas.Handle,(xwd-xw)*(xxt*2),(ywd-yw)*yyt,rec,rec,0,nil);
    3711   for DoInvalidate:=false to FastScrolling do
    3712     begin
    3713     if DoInvalidate then
    3714       begin
    3715       rec.bottom:=MapHeight-overlap;
    3716       ScrollDC(Canvas.Handle,(xwd-xw)*(xxt*2),(ywd-yw)*yyt,rec,rec,0,nil);
    3717       ProcessOptions:=prInvalidate;
    3718       end
    3719     else ProcessOptions:=prPaint or prAutoBounds;
    3720     if yw<ywd then
    3721       begin
    3722       ProcessRect(xw,yw,MapWidth div xxt,ywd-yw-1,ProcessOptions);
    3723       if xw<xwd then
    3724         ProcessRect(xw,ywd,(xwd-xw)*2-1,MapHeight div yyt-ywd+yw,ProcessOptions)
    3725       else if xw>xwd then
    3726         ProcessRect((xwd+MapWidth div (xxt*2)) mod G.lx,ywd,(xw-xwd)*2+1,
    3727           MapHeight div yyt-ywd+yw,ProcessOptions)
    3728       end
    3729     else if yw>ywd then
    3730       begin
    3731       if DoInvalidate then
    3732         RectInvalidate(MapOffset,TopBarHeight+MapHeight-overlap-(yw-ywd)*yyt,MapOffset+MapWidth,
    3733           TopBarHeight+MapHeight-overlap)
    3734       else ProcessRect(xw,(ywd+MapHeight div (yyt*2) *2),MapWidth div xxt,
    3735         yw-ywd+1,ProcessOptions);
    3736       if xw<xwd then
    3737         ProcessRect(xw,yw,(xwd-xw)*2-1,MapHeight div yyt-yw+ywd-2,
    3738           ProcessOptions)
    3739       else if xw>xwd then
    3740         ProcessRect((xwd+MapWidth div (xxt*2)) mod G.lx,yw,(xw-xwd)*2+1,
    3741           MapHeight div yyt-yw+ywd-2,ProcessOptions)
    3742       end
    3743     else
    3744       if xw<xwd then
    3745         ProcessRect(xw,yw,(xwd-xw)*2-1,MapHeight div yyt,ProcessOptions)
    3746       else if xw>xwd then
    3747         ProcessRect((xwd+MapWidth div (xxt*2)) mod G.lx,yw,(xw-xwd)*2+1,
    3748           MapHeight div yyt,ProcessOptions);
    3749     end;
    3750   if not FastScrolling then
    3751     RectInvalidate(MapOffset,TopBarHeight,MapOffset+MapWidth,TopBarHeight+MapHeight-overlap);
    3752   RectInvalidate(xMidPanel,TopBarHeight+MapHeight-overlap,xRightPanel,TopBarHeight+MapHeight)
    3753   end;
    3754 //if (xwd<>xw) or (ywd<>yw) then
    3755 //  Server(sChangeSuperView,me,yw*G.lx+xw,nil^); // for synchronizing client side viewer, not used currently
    3756 xwd:=xw;ywd:=yw;
    3757 MapValid:=true;
    3758 end;
    3759 
    3760 procedure TMainScreen.PaintAll;
    3761 begin
    3762 MainOffscreenPaint;
    3763 xwMini:=xw; ywMini:=yw;
    3764 MiniPaint;
    3765 PanelPaint;
    3766 end;
    3767 
    3768 procedure TMainScreen.PaintAllMaps;
    3769 begin
    3770 MainOffscreenPaint;
    3771 xwMini:=xw; ywMini:=yw;
    3772 MiniPaint;
    3773 CopyMiniToPanel;
    3774 RectInvalidate(xMini+2,TopBarHeight+MapHeight-overlap+yMini+2,xMini+2+G.lx*2,
    3775   TopBarHeight+MapHeight-overlap+yMini+2+G.ly);
    3776 end;
    3777 
    3778 procedure TMainScreen.CopyMiniToPanel;
    3779 begin
    3780 BitBlt(Panel.Canvas.Handle,xMini+2,yMini+2,G.lx*2,G.ly,Mini.Canvas.Handle,0,0,SRCCOPY);
    3781 if MarkCityLoc>=0 then
    3782   Sprite(Panel, HGrSystem, xMini-2+(4*G.lx+2*(MarkCityLoc mod G.lx)
    3783     +(G.lx-MapWidth div (xxt*2))-2*xwd) mod (2*G.lx) +MarkCityLoc div G.lx and 1,
    3784     yMini-3+MarkCityLoc div G.lx,10,10,77,47)
    3785 else if ywmax<=0 then
    3786   Frame(Panel.Canvas,xMini+2+G.lx-MapWidth div (xxt*2),yMini+2,
    3787     xMini+1+G.lx+MapWidth div (xxt*2),
    3788     yMini+2+G.ly-1,MainTexture.clMark,MainTexture.clMark)
    3789 else Frame(Panel.Canvas,xMini+2+G.lx-MapWidth div (xxt*2),yMini+2+yw,
    3790   xMini+1+G.lx+MapWidth div (xxt*2),
    3791   yMini+yw+MapHeight div yyt,MainTexture.clMark,MainTexture.clMark);
    3792 end;
    3793 
    3794 procedure TMainScreen.PanelPaint;
    3795 
    3796   function MovementToString(var Un: TUn): string;
    3797   begin
    3798   result:=ScreenTools.MovementToString(Un.Movement);
    3799   if Un.Master>=0 then
    3800     result:='('+result+')'
    3801   else if (MyModel[Un.mix].Domain=dAir)
    3802     and (MyModel[Un.mix].Kind<>mkSpecial_Glider) then
    3803     result:=Format('%s(%d)',[result,Un.Fuel]);
    3804   end;
    3805 
    3806 var
    3807 i,uix,uixDefender,x,xSrc,ySrc,xSrcBase,ySrcBase,CostFactor,Count,mixShow,
    3808   xTreasurySection,xResearchSection,JobFocus,TrueMoney,
    3809   TrueResearch: integer;
    3810 Tile: cardinal;
    3811 s: string;
    3812 unx:TUn;
    3813 UnitInfo: TUnitInfo;
    3814 JobProgressData: TJobProgressData;
    3815 Prio: boolean;
    3816 begin
    3817 with Panel.Canvas do
    3818   begin
    3819   Fill(Panel.Canvas,0,3,xMidPanel+7-10,PanelHeight-3,
    3820     wMainTexture-(xMidPanel+7-10),hMainTexture-PanelHeight);
    3821   Fill(Panel.Canvas,xRightPanel+10-7,3,Panel.Width-xRightPanel-10+7,PanelHeight-3,
    3822     -(xRightPanel+10-7),hMainTexture-PanelHeight);
    3823   FillLarge(Panel.Canvas,xMidPanel-2,PanelHeight-MidPanelHeight,xRightPanel+2,PanelHeight,
    3824     ClientWidth div 2);
    3825 
    3826   Brush.Style:=bsClear;
    3827   Pen.Color:=$000000;
    3828   MoveTo(0,0);LineTo(xMidPanel+7-8,0); LineTo(xMidPanel+7-8,PanelHeight-MidPanelHeight);
    3829   LineTo(xRightPanel,PanelHeight-MidPanelHeight); LineTo(xRightPanel,0);
    3830   LineTo(ClientWidth,0);
    3831   Pen.Color:=MainTexture.clBevelLight;
    3832   MoveTo(xMidPanel+7-9,PanelHeight-MidPanelHeight+2);
    3833   LineTo(xRightPanel+10-8,PanelHeight-MidPanelHeight+2);
    3834   Pen.Color:=MainTexture.clBevelLight;
    3835   MoveTo(0,1);LineTo(xMidPanel+7-9,1); Pen.Color:=MainTexture.clBevelShade;
    3836   LineTo(xMidPanel+7-9,PanelHeight-MidPanelHeight+1); Pen.Color:=MainTexture.clBevelLight;
    3837   LineTo(xRightPanel+10-9,PanelHeight-MidPanelHeight+1); Pen.Color:=MainTexture.clBevelLight;
    3838   LineTo(xRightPanel+10-9,1); LineTo(ClientWidth,1);
    3839   MoveTo(ClientWidth,2); LineTo(xRightPanel+10-8,2); LineTo(xRightPanel+10-8,PanelHeight);
    3840   MoveTo(0,2);LineTo(xMidPanel+7-10,2); Pen.Color:=MainTexture.clBevelShade;
    3841   LineTo(xMidPanel+7-10,PanelHeight);
    3842   Corner(Panel.Canvas,xMidPanel+7-16,1,1,MainTexture);
    3843   Corner(Panel.Canvas,xRightPanel+10-9,1,0,MainTexture);
    3844   if ClientMode<>cEditMap then
    3845     begin
    3846     if supervising then
    3847       begin
    3848       Frame(Panel.Canvas, ClientWidth-xPalace-1, yPalace-1,
    3849         ClientWidth-xPalace+xSizeBig, yPalace+ySizeBig, $B0B0B0, $FFFFFF);
    3850       RFrame(Panel.Canvas, ClientWidth-xPalace-2, yPalace-2,
    3851         ClientWidth-xPalace+xSizeBig+1, yPalace+ySizeBig+1, $FFFFFF, $B0B0B0);
    3852       BitBlt(Panel.Canvas.Handle, ClientWidth-xPalace, yPalace, xSizeBig,
    3853         ySizeBig, GrExt[HGrSystem2].Data.Canvas.Handle, 70, 123, SRCCOPY);
    3854       end
    3855     else if MyRO.NatBuilt[imPalace]>0 then
    3856       ImpImage(Panel.Canvas, ClientWidth-xPalace, yPalace, imPalace, -1, GameMode<>cMovie
    3857        {(GameMode<>cMovie) and (MyRO.Government<>gAnarchy)})
    3858     else ImpImage(Panel.Canvas, ClientWidth-xPalace, yPalace, 21, -1, GameMode<>cMovie
    3859       {(GameMode<>cMovie) and (MyRO.Government<>gAnarchy)});
    3860     end;
    3861 
    3862   if GameMode=cMovie then
    3863     Frame(Panel.Canvas,xMini+1,yMini+1,xMini+2+G.lx*2,yMini+2+G.ly,$000000,$000000)
    3864   else
    3865     begin
    3866     Frame(Panel.Canvas,xMini+1,yMini+1,xMini+2+G.lx*2,yMini+2+G.ly,$B0B0B0,$FFFFFF);
    3867     RFrame(Panel.Canvas,xMini,yMini,xMini+3+G.lx*2,yMini+3+G.ly,$FFFFFF,$B0B0B0);
    3868     end;
    3869   CopyMiniToPanel;
    3870   if ClientMode<>cEditMap then // MapBtn icons
    3871     for i:=0 to 5 do if i<>3 then
    3872       Dump(Panel,HGrSystem,xMini+G.lx-42+16*i,PanelHeight-26,8,8,121+i*9,61);
    3873 
    3874   if ClientMode=cEditMap then
    3875     begin
    3876     for i:=0 to TrRow-1 do trix[i]:=-1;
    3877     Count:=0;
    3878     for i:=0 to nBrushTypes-1 do
    3879       begin // display terrain types
    3880       if (Count>=TrRow*sb.si.npos) and (Count<TrRow*(sb.si.npos+1)) then
    3881         begin
    3882         trix[Count-TrRow*sb.si.npos]:=BrushTypes[i];
    3883         x:=(Count-TrRow*sb.si.npos)*TrPitch;
    3884         xSrcBase:=-1;
    3885         case BrushTypes[i] of
    3886           0..8: begin xSrc:=BrushTypes[i]; ySrc:=0 end;
    3887           9..30:
    3888             begin
    3889             xSrcBase:=2; ySrcBase:=2;
    3890             xSrc:=0; ySrc:=2*integer(BrushTypes[i])-15
    3891             end;
    3892           fRiver: begin xSrc:=7; ySrc:=14 end;
    3893           fRoad: begin xSrc:=0; ySrc:=9 end;
    3894           fRR: begin xSrc:=0; ySrc:=10 end;
    3895           fCanal: begin xSrc:=0; ySrc:=11 end;
    3896           fPoll: begin xSrc:=6; ySrc:=12 end;
    3897           fDeadLands,fDeadLands or fCobalt,fDeadLands or fUranium,
    3898             fDeadLands or fMercury:
    3899             begin
    3900             xSrcBase:=6; ySrcBase:=2;
    3901             xSrc:=8; ySrc:=12+BrushTypes[i] shr 25;
    3902             end;
    3903           tiIrrigation, tiFarm, tiMine, tiBase:
    3904             begin xSrc:=BrushTypes[i] shr 12-1; ySrc:=12 end;
    3905           tiFort:
    3906             begin xSrc:=3; ySrc:=12; xSrcBase:=7; ySrcBase:=12 end;
    3907           fPrefStartPos: begin xSrc:=0; ySrc:=1 end;
    3908           fStartPos: begin xSrc:=0; ySrc:=2 end;
    3909           end;
    3910         if xSrcBase>=0 then
    3911           Sprite(Panel,HGrTerrain,xTroop+2+x,yTroop+9-yyt,xxt*2,yyt*3,
    3912             1+xSrcBase*(xxt*2+1),1+ySrcBase*(yyt*3+1));
    3913         Sprite(Panel,HGrTerrain,xTroop+2+x,yTroop+9-yyt,xxt*2,yyt*3,
    3914           1+xSrc*(xxt*2+1),1+ySrc*(yyt*3+1));
    3915         if BrushTypes[i]=BrushType then
    3916           begin
    3917           Frame(Panel.Canvas,xTroop+2+x,yTroop+7-yyt div 2,xTroop+2*xxt+x,
    3918             yTroop+2*yyt+11,$000000,$000000);
    3919           Frame(Panel.Canvas,xTroop+1+x,yTroop+6-yyt div 2,xTroop+2*xxt-1+x,
    3920             yTroop+2*yyt+10,MainTexture.clMark,MainTexture.clMark);
     7826            MapValid := false;
     7827            PaintAllMaps;
    39217828          end
    3922         end;
    3923       inc(Count)
    3924       end;
    3925     case BrushType of
    3926       fDesert, fPrairie, fTundra, fArctic, fSwamp, fHills, fMountains:
    3927         s:=Phrases.Lookup('TERRAIN',BrushType);
    3928       fShore: s:=Format(Phrases.Lookup('TWOTERRAINS'),
    3929         [Phrases.Lookup('TERRAIN',fOcean),Phrases.Lookup('TERRAIN',fShore)]);
    3930       fGrass: s:=Format(Phrases.Lookup('TWOTERRAINS'),
    3931         [Phrases.Lookup('TERRAIN',fGrass),Phrases.Lookup('TERRAIN',fGrass+12)]);
    3932       fForest: s:=Format(Phrases.Lookup('TWOTERRAINS'),
    3933         [Phrases.Lookup('TERRAIN',fForest),Phrases.Lookup('TERRAIN',fJungle)]);
    3934       fRiver: s:=Phrases.Lookup('RIVER');
    3935       fDeadLands,fDeadLands or fCobalt,fDeadLands or fUranium,
    3936         fDeadLands or fMercury:
    3937         s:=Phrases.Lookup('TERRAIN',3*12+BrushType shr 25);
    3938       fPrefStartPos: s:=Phrases.Lookup('MAP_PREFSTART');
    3939       fStartPos: s:=Phrases.Lookup('MAP_START');
    3940       fPoll: s:=Phrases.Lookup('POLL');
    3941       else // terrain improvements
    3942         begin
    3943         case BrushType of
    3944           fRoad: i:=1;
    3945           fRR: i:=2;
    3946           tiIrrigation: i:=4;
    3947           tiFarm: i:=5;
    3948           tiMine: i:=7;
    3949           fCanal: i:=8;
    3950           tiFort: i:=10;
    3951           tiBase: i:=12;
    3952           end;
    3953         s:=Phrases.Lookup('JOBRESULT',i);
    3954         end
    3955       end;
    3956     LoweredTextOut(Panel.Canvas,-1,MainTexture,xTroop+1,PanelHeight-19,s);
    3957     end
    3958   else if TroopLoc>=0 then
    3959     begin
    3960     Brush.Style:=bsClear;
    3961     if UnFocus>=0 then with MyUn[UnFocus],MyModel[mix] do
    3962       begin {display info about selected unit}
    3963       if Job=jCity then
    3964         mixShow:=-1 // building site
    3965       else mixShow:=mix;
    3966       with Tribe[me].ModelPicture[mixShow] do
    3967         begin
    3968         Sprite(Panel,HGr,xMidPanel+7+12,yTroop+1,64,48,
    3969           pix mod 10 *65+1,pix div 10 *49+1);
    3970         if MyUn[UnFocus].Flags and unFortified<>0 then
    3971           Sprite(Panel,HGrStdUnits,xMidPanel+7+12,yTroop+1,xxu*2,yyu*2,1+6*(xxu*2+1),1);
    3972         end;
    3973 
    3974       MakeBlue(Panel,xMidPanel+7+12+10,yTroop-13,44,12);
    3975       s:=MovementToString(MyUn[UnFocus]);
    3976       RisedTextOut(Panel.Canvas,xMidPanel+7+12+32-BiColorTextWidth(Panel.Canvas,s) div 2,
    3977         yTroop-16,s);
    3978 
    3979       s:=IntToStr(Health)+'%';
    3980       LightGradient(Panel.Canvas,xMidPanel+7+12+7,PanelHeight-22,(Health+1) div 2,
    3981         (ColorOfHealth(Health) and $FEFEFE shr 2)*3);
    3982       if Health<100 then
    3983         LightGradient(Panel.Canvas,xMidPanel+7+12+7+(Health+1) div 2,
    3984           PanelHeight-22,50-(Health+1) div 2,$000000);
    3985       RisedTextOut(Panel.Canvas,xMidPanel+7+12+32-BiColorTextWidth(Panel.Canvas,s) div 2,
    3986         PanelHeight-23,s);
    3987 
    3988       FrameImage(Panel.Canvas,GrExt[HGrSystem].Data,xMidPanel+7+xUnitText,yTroop+15,12,14,
    3989         121+Exp div ExpCost *13,28);
    3990       if Job=jCity then s:=Tribe[me].ModelName[-1]
    3991       else s:=Tribe[me].ModelName[mix];
    3992       if Home>=0 then
    3993         begin
    3994         LoweredTextOut(Panel.Canvas,-1,MainTexture,xMidPanel+7+xUnitText+18,yTroop+5,s);
    3995         LoweredTextOut(Panel.Canvas,-1,MainTexture,xMidPanel+7+xUnitText+18,yTroop+21,
    3996           '('+CityName(MyCity[Home].ID)+')');
    3997         end
    3998       else LoweredTextOut(Panel.Canvas,-1,MainTexture,xMidPanel+7+xUnitText+18,yTroop+13,s);
    3999       end;
    4000 
    4001     if (UnFocus>=0) and (MyUn[UnFocus].Loc<>TroopLoc) then
    4002       begin // divide panel
    4003       if SmallScreen and not supervising then
    4004         x:=xTroop-8
    4005       else x:=xTroop-152;
    4006       Pen.Color:=MainTexture.clBevelShade;
    4007       MoveTo(x-1,PanelHeight-MidPanelHeight+2);
    4008       LineTo(x-1,PanelHeight);
    4009       Pen.Color:=MainTexture.clBevelLight;
    4010       MoveTo(x,PanelHeight-MidPanelHeight+2);
    4011       LineTo(x,PanelHeight);
    4012       end;
    4013 
    4014     for i:=0 to 23 do trix[i]:=-1;
    4015     if MyMap[TroopLoc] and fUnit<>0 then
    4016       begin
    4017       if MyMap[TroopLoc] and fOwned<>0 then
    4018         begin
    4019         if (TrCnt>1) or (UnFocus<0) or (MyUn[UnFocus].Loc<>TroopLoc) then
    4020           begin
    4021           LoweredTextOut(Panel.Canvas, -1, MainTexture, xTroop+10, PanelHeight-24,
    4022             Phrases.Lookup('PRESENT'));
    4023           Server(sGetDefender,me,TroopLoc,uixDefender);
    4024           Count:=0;
    4025           for Prio:=true downto false do
    4026             for uix:=0 to MyRO.nUn-1 do if (uix=uixDefender)=Prio then
    4027               begin // display own units
    4028               unx:=MyUn[uix];
    4029               if unx.Loc=TroopLoc then
    4030                 begin
    4031                 if (Count>=TrRow*sb.si.npos) and (Count<TrRow*(sb.si.npos+1)) then
    4032                   begin
    4033                   trix[Count-TrRow*sb.si.npos]:=uix;
    4034                   MakeUnitInfo(me,unx,UnitInfo);
    4035                   x:=(Count-TrRow*sb.si.npos)*TrPitch;
    4036                   if uix=UnFocus then
    4037                     begin
    4038                     Frame(Panel.Canvas,xTroop+4+x,yTroop+3,xTroop+64+x,
    4039                       yTroop+47,$000000,$000000);
    4040                     Frame(Panel.Canvas,xTroop+3+x,yTroop+2,xTroop+63+x,
    4041                       yTroop+46,MainTexture.clMark,MainTexture.clMark);
    4042                     end
    4043                   else if (unx.Master>=0) and (unx.Master=UnFocus) then
    4044                     begin
    4045                     CFrame(Panel.Canvas,xTroop+4+x,yTroop+3,xTroop+64+x,
    4046                       yTroop+47,8,$000000);
    4047                     CFrame(Panel.Canvas,xTroop+3+x,yTroop+2,xTroop+63+x,
    4048                       yTroop+46,8,MainTexture.clMark);
    4049                     end;
    4050                   NoMap.SetOutput(Panel);
    4051                   NoMap.PaintUnit(xTroop+2+x,yTroop+1,UnitInfo,unx.Status);
    4052                   if (ClientMode<scContact)
    4053                     and ((unx.Job>jNone)
    4054                       or (unx.Status and (usStay or usRecover or usGoto)<>0)) then
    4055                     Sprite(Panel, HGrSystem, xTroop+2+60-20+x, yTroop+35,
    4056                       20, 20, 81, 25);
    4057 
    4058                   if not supervising then
    4059                     begin
    4060                     MakeBlue(Panel,xTroop+2+10+x,yTroop-13,44,12);
    4061                     s:=MovementToString(unx);
    4062                     RisedTextOut(Panel.Canvas,xTroop+x+34-BiColorTextWidth(Panel.Canvas,s) div 2,
    4063                       yTroop-16,s);
    4064                     end
    4065                   end;
    4066                 inc(Count)
    4067                 end;
    4068               end; // for uix:=0 to MyRO.nUn-1
    4069           assert(Count=TrCnt);
    4070           end
    4071         end
    4072       else
    4073         begin
    4074         LoweredTextOut(Panel.Canvas, -1, MainTexture, xTroop+8, PanelHeight-24,
    4075           Phrases.Lookup('PRESENT'));
    4076         Server(sGetUnits,me,TroopLoc,Count);
    4077         for i:=0 to Count-1 do
    4078           if (i>=TrRow*sb.si.npos) and (i<TrRow*(sb.si.npos+1)) then
    4079             begin // display enemy units
    4080             trix[i-TrRow*sb.si.npos]:=i;
    4081             x:=(i-TrRow*sb.si.npos)*TrPitch;
    4082             NoMap.SetOutput(Panel);
    4083             NoMap.PaintUnit(xTroop+2+x,yTroop+1,MyRO.EnemyUn[MyRO.nEnemyUn+i],0);
    4084             end;
    4085         end;
    4086       end;
    4087     if not SmallScreen or supervising then
    4088       begin // show terrain and improvements
    4089       PaintZoomedTile(Panel, xTerrain-xxt*2, 110-yyt*3, TroopLoc);
    4090       if (UnFocus>=0) and (MyUn[UnFocus].Job<>jNone) then
    4091         begin
    4092         JobFocus:=MyUn[UnFocus].Job;
    4093         Server(sGetJobProgress, me, MyUn[UnFocus].Loc, JobProgressData);
    4094         MakeBlue(Panel,xTerrain-72,148-17,144,31);
    4095         PaintRelativeProgressBar(Panel.Canvas,3,xTerrain-68,148+3,63,
    4096           JobProgressData[JobFocus].Done,
    4097           JobProgressData[JobFocus].NextTurnPlus,
    4098           JobProgressData[JobFocus].Required,true,MainTexture);
    4099         s:=Format('%s/%s',[ScreenTools.MovementToString(JobProgressData[JobFocus].Done),
    4100           ScreenTools.MovementToString(JobProgressData[JobFocus].Required)]);
    4101         RisedTextOut(Panel.Canvas,xTerrain+6,148-3,s);
    4102         Tile:=MyMap[MyUn[UnFocus].Loc];
    4103         if (JobFocus=jRoad) and (Tile and fRiver<>0) then
    4104           JobFocus:=nJob+0
    4105         else if (JobFocus=jRR) and (Tile and fRiver<>0) then
    4106           JobFocus:=nJob+1
    4107         else if JobFocus=jClear then
    4108           begin
    4109           if Tile and fTerrain=fForest then
    4110             JobFocus:=nJob+2
    4111           else if Tile and fTerrain=fDesert then
    4112             JobFocus:=nJob+3
    4113           else JobFocus:=nJob+4
    4114           end;
    4115         s:=Phrases.Lookup('JOBRESULT', JobFocus);
    4116         RisedTextOut(Panel.Canvas,xTerrain-BiColorTextWidth(Panel.Canvas,s) div 2,
    4117           148-19,s);
    4118         end;
    4119       if MyMap[TroopLoc] and (fTerrain or fSpecial)=fGrass or fSpecial1 then
    4120         s:=Phrases.Lookup('TERRAIN',fGrass+12)
    4121       else if MyMap[TroopLoc] and fDeadlands<>0 then
    4122         s:=Phrases.Lookup('TERRAIN',3*12)
    4123       else if (MyMap[TroopLoc] and fTerrain=fForest)
    4124         and IsJungle(TroopLoc div G.lx) then
    4125         s:=Phrases.Lookup('TERRAIN',fJungle)
    4126       else s:=Phrases.Lookup('TERRAIN',MyMap[TroopLoc] and fTerrain);
    4127       RisedTextOut(Panel.Canvas,xTerrain-BiColorTextWidth(Panel.Canvas,s) div 2,
    4128         99,s);
    4129       end;
    4130 
    4131     if TerrainBtn.Visible then with TerrainBtn do
    4132       RFrame(Panel.Canvas,Left-1,Top-self.ClientHeight+(PanelHeight-1),
    4133         Left+Width,Top+Height-self.ClientHeight+PanelHeight,
    4134         MainTexture.clBevelShade,MainTexture.clBevelLight)
    4135     end {if TroopLoc>=0}
    4136   end;
    4137 
    4138 for i:=0 to ControlCount-1 do
    4139   if Controls[i] is TButtonB then with TButtonB(Controls[i]) do
    4140     begin
    4141     if Visible then
    4142       begin
    4143       Dump(Panel,HGrSystem,Left,Top-self.ClientHeight+PanelHeight,25,25,169,243);
    4144       Sprite(Panel,HGrSystem,Left,Top-self.ClientHeight+PanelHeight,25,25,
    4145         1+26*ButtonIndex,337);
    4146       RFrame(Panel.Canvas,Left-1,Top-self.ClientHeight+(PanelHeight-1),
    4147         Left+Width,Top+Height-self.ClientHeight+PanelHeight,
    4148         MainTexture.clBevelShade,MainTexture.clBevelLight);
    4149       end;
    4150     end;
    4151 
    4152 if ClientMode<>cEditMap then
    4153   begin
    4154   for i:=0 to ControlCount-1 do
    4155     if Controls[i] is TButtonC then with TButtonC(Controls[i]) do
    4156       begin
    4157       Dump(Panel,HGrSystem,Left,Top-self.ClientHeight+PanelHeight,12,12,
    4158         169,178+13*ButtonIndex);
    4159       RFrame(Panel.Canvas,Left-1,Top-self.ClientHeight+(PanelHeight-1),
    4160         Left+Width,Top+Height-self.ClientHeight+PanelHeight,
    4161         MainTexture.clBevelShade,MainTexture.clBevelLight);
    4162       end
    4163   end;
    4164 EOT.SetBack(Panel.Canvas,EOT.Left,EOT.Top-(ClientHeight-PanelHeight));
    4165 SmartRectInvalidate(0,ClientHeight-PanelHeight,ClientWidth,ClientHeight);
    4166 
    4167 // topbar
    4168 xTreasurySection:=ClientWidth div 2-172;
    4169 xResearchSection:=ClientWidth div 2; //ClientWidth div 2+68 = maximum to right
    4170 FillLarge(TopBar.Canvas,0,0,ClientWidth,TopBarHeight-3,ClientWidth div 2);
    4171 with TopBar.Canvas do
    4172   begin
    4173   Pen.Color:=$000000;
    4174   MoveTo(0,TopBarHeight-1); LineTo(ClientWidth, TopBarHeight-1);
    4175   Pen.Color:=MainTexture.clBevelShade;
    4176   MoveTo(0,TopBarHeight-2); LineTo(ClientWidth, TopBarHeight-2);
    4177   MoveTo(0,TopBarHeight-3); LineTo(ClientWidth, TopBarHeight-3);
    4178   Pen.Color:=MainTexture.clBevelLight;
    4179   frame(TopBar.Canvas,40,-1,xTreasurySection-1,TopBarHeight-7,
    4180     MainTexture.clBevelShade,MainTexture.clBevelLight);
    4181   frame(TopBar.Canvas,xResearchSection+332,-1,ClientWidth,TopBarHeight-7,
    4182     MainTexture.clBevelShade,MainTexture.clBevelLight);
    4183   end;
    4184 if GameMode<>cMovie then
    4185   ImageOp_BCC(TopBar,Templates,2,1,145,38,36,36,$BFBF20,$4040DF);
    4186 if MyRO.nCity>0 then
    4187   begin
    4188   TrueMoney:=MyRO.Money;
    4189   TrueResearch:=MyRO.Research;
    4190   if supervising then
    4191     begin // normalize values from after-turn state
    4192     dec(TrueMoney,TaxSum);
    4193     if TrueMoney<0 then
    4194       TrueMoney:=0; // shouldn't happen
    4195     dec(TrueResearch,ScienceSum);
    4196     if TrueResearch<0 then
    4197       TrueResearch:=0; // shouldn't happen
    4198     end;
    4199 
    4200   // treasury section
    4201   ImageOp_BCC(TopBar,Templates,xTreasurySection+8,1,145,1,36,36,$40A040,$4030C0);
    4202   s:=IntToStr(TrueMoney);
    4203   LoweredTextOut(TopBar.Canvas,-1,MainTexture,xTreasurySection+48,0,s+'%c');
    4204   if MyRO.Government<>gAnarchy then
    4205     begin
    4206     ImageOp_BCC(TopBar,Templates,xTreasurySection+48,22,124,1,14,14,$0000C0, $0080C0);
    4207     if TaxSum>=0 then
    4208       s:=Format(Phrases.Lookup('MONEYGAINPOS'),[TaxSum])
    4209     else s:=Format(Phrases.Lookup('MONEYGAINNEG'),[TaxSum]);
    4210     LoweredTextOut(TopBar.Canvas,-1,MainTexture,xTreasurySection+48+15,18,s);
    4211     end;
    4212 
    4213   // research section
    4214   ImageOp_BCC(TopBar,Templates,xResearchSection+8,1,145,75,36,36,$FF0000,$00FFE0);
    4215   if MyData.FarTech<>adNexus then
    4216     begin
    4217     if MyRO.ResearchTech<0 then
    4218       CostFactor:=2
    4219     else if (MyRO.ResearchTech=adMilitary) or (MyRO.Tech[MyRO.ResearchTech]=tsSeen) then
    4220       CostFactor:=1
    4221     else if MyRO.ResearchTech in FutureTech then
    4222       if MyRO.Government=gFuture then
    4223         CostFactor:=4
    4224       else CostFactor:=8
    4225     else CostFactor:=2;
    4226     Server(sGetTechCost,me,0,i);
    4227     CostFactor:=CostFactor*22; // length of progress bar
    4228     PaintRelativeProgressBar(TopBar.Canvas,2,xResearchSection+48+1,26,
    4229       CostFactor,TrueResearch,ScienceSum,i,true,MainTexture);
    4230 
    4231     if MyRO.ResearchTech<0 then
    4232       s:=Phrases.Lookup('SCIENCE')
    4233     else if MyRO.ResearchTech=adMilitary then
    4234       s:=Phrases.Lookup('INITUNIT')
    4235     else
    4236       begin
    4237       s:=Phrases.Lookup('ADVANCES', MyRO.ResearchTech);
    4238       if MyRO.ResearchTech in FutureTech then
    4239         if MyRO.Tech[MyRO.ResearchTech]>=1 then
    4240           s:=s+' '+IntToStr(MyRO.Tech[MyRO.ResearchTech]+1)
    4241         else s:=s+' 1';
    4242       end;
    4243     if ScienceSum>0 then
    4244       begin
    4245 {      j:=(i-MyRO.Research-1) div ScienceSum +1;
    4246       if j<1 then j:=1;
    4247       if j>1 then
    4248         s:=Format(Phrases.Lookup('TECHWAIT'),[s,j]);}
    4249       LoweredTextOut(TopBar.Canvas,-1,MainTexture,xResearchSection+48,0,s);
    4250       end
    4251     else LoweredTextOut(TopBar.Canvas,-1,MainTexture,xResearchSection+48,0,s);
    4252     end
    4253   else CostFactor:=0;
    4254   if (MyData.FarTech<>adNexus) and (ScienceSum>0) then
    4255     begin
    4256     ImageOp_BCC(TopBar,Templates,xResearchSection+48+CostFactor+11,22,124,1,14,14,$0000C0, $0080C0);
    4257     s:=Format(Phrases.Lookup('TECHGAIN'),[ScienceSum]);
    4258     LoweredTextOut(TopBar.Canvas,-1,MainTexture,xResearchSection+48+CostFactor+26,18,s);
    4259     end
    4260   end;
    4261 if ClientMode<>cEditMap then
    4262   begin
    4263   TopBar.Canvas.Font.Assign(UniFont[ftCaption]);
    4264   s:=TurnToString(MyRO.Turn);
    4265   RisedTextOut(TopBar.Canvas,40+(xTreasurySection-40-BiColorTextWidth(TopBar.Canvas,s)) div 2,6,s);
    4266   TopBar.Canvas.Font.Assign(UniFont[ftNormal]);
    4267   end;
    4268 RectInvalidate(0,0,ClientWidth,TopBarHeight);
    4269 end;{PanelPaint}
    4270 
    4271 procedure TMainScreen.FocusOnLoc(Loc:integer; Options: integer = 0);
    4272 var
    4273 dx: integer;
    4274 Outside, Changed: boolean;
    4275 begin
    4276 dx:=G.lx+1-(xw-Loc+G.lx*1024+1) mod G.lx;
    4277 Outside:=(dx>=(MapWidth+1) div (xxt*2)-2)
    4278   or (ywmax>0) and ((yw>0) and (Loc div G.lx<=yw+1)
    4279     or (yw<ywmax) and (Loc div G.lx>=yw+(MapHeight-1) div yyt-2));
    4280 Changed:=true;
    4281 if Outside then
    4282   begin Centre(Loc); PaintAllMaps end
    4283 else if not MapValid then
    4284   PaintAllMaps
    4285 else Changed:=false;
    4286 if Options and flRepaintPanel<>0 then
    4287   PanelPaint;
    4288 if Changed and (Options and flImmUpdate<>0) then Update;
    4289 end;
    4290 
    4291 procedure TMainScreen.NextUnit(NearLoc:integer;AutoTurn:boolean);
    4292 var
    4293 Dist,TestDist:single;
    4294 i,uix,NewFocus:integer;
    4295 GotoOnly: boolean;
    4296 begin
    4297 if ClientMode>=scContact then exit;
    4298 DestinationMarkON:=false;
    4299 PaintDestination;
    4300 for GotoOnly:=GoOnPhase downto false do
    4301   begin
    4302   NewFocus:=-1;
    4303   for i:=1 to MyRO.nUn do
    4304     begin
    4305     uix:=(UnFocus+i) mod MyRO.nUn;
    4306     if (MyUn[uix].Loc>=0) and (MyUn[uix].Job=jNone)
    4307       and (MyUn[uix].Status and (usStay or usRecover or usWaiting)=usWaiting)
    4308       and (not GotoOnly or (MyUn[uix].Status and usGoto<>0)) then
    4309       if NearLoc<0 then begin NewFocus:=uix; Break end
    4310       else
    4311         begin
    4312         TestDist:=Distance(NearLoc,MyUn[uix].Loc);
    4313         if (NewFocus<0) or (TestDist<Dist) then
    4314           begin NewFocus:=uix; Dist:=TestDist end
    4315         end
    4316     end;
    4317   if GotoOnly then
    4318     if NewFocus<0 then GoOnPhase:=false
    4319     else break;
    4320   end;
    4321 if NewFocus>=0 then
    4322   begin
    4323   SetUnFocus(NewFocus);
    4324   SetTroopLoc(MyUn[NewFocus].Loc);
    4325   FocusOnLoc(TroopLoc,flRepaintPanel)
    4326   end
    4327 else if AutoTurn and not mWaitTurn.Checked then
    4328   begin
    4329   TurnComplete:=true;
    4330   SetUnFocus(-1);
    4331   SetTroopLoc(-1);
    4332   PostMessage(Handle,WM_EOT,0,0)
    4333   end
    4334 else
    4335   begin
    4336   if {(UnFocus>=0) and} not TurnComplete and EOT.Visible then Play('TURNEND');
    4337   TurnComplete:=true;
    4338   SetUnFocus(-1);
    4339   SetTroopLoc(-1);
    4340   PanelPaint;
    4341   end;
    4342 end;{NextUnit}
    4343 
    4344 procedure TMainScreen.Scroll(dx,dy: integer);
    4345 begin
    4346 xw:=(xw+G.lx+dx) mod G.lx;
    4347 if ywmax>0 then
    4348   begin
    4349   yw:=yw+2*dy;
    4350   if yw<0 then yw:=0
    4351   else if yw>ywmax then yw:=ywmax;
    4352   end;
    4353 MainOffscreenPaint;
    4354 xwMini:=xw; ywMini:=yw;
    4355 MiniPaint;
    4356 CopyMiniToPanel;
    4357 RectInvalidate(xMini+2,TopBarHeight+MapHeight-overlap+yMini+2,xMini+2+G.lx*2,
    4358   TopBarHeight+MapHeight-overlap+yMini+2+G.ly);
    4359 Update;
    4360 end;
    4361 
    4362 procedure TMainScreen.Timer1Timer(Sender:TObject);
    4363 var
    4364 dx, dy, speed: integer;
    4365 begin
    4366 if idle and (me>=0) and (GameMode<>cMovie) then
    4367   if (fsModal in Screen.ActiveForm.FormState)
    4368     or (Screen.ActiveForm is TBufferedDrawDlg)
    4369     and (TBufferedDrawDlg(Screen.ActiveForm).WindowMode<>wmPersistent) then
    4370     begin
    4371     BlinkTime:=BlinkOnTime+BlinkOffTime-1;
    4372     if not BlinkON then
    4373       begin
    4374       BlinkON:=true;
    4375       if UnFocus>=0 then
    4376         PaintLocTemp(MyUn[UnFocus].Loc)
    4377       else if TurnComplete and not supervising then
    4378         EOT.SetButtonIndexFast(eotBlinkOn)
    4379       end
    4380     end
    4381   else
    4382     begin
    4383     if Application.Active and not mScrollOff.Checked then
    4384       begin
    4385       if mScrollFast.Checked then Speed:=2
    4386       else Speed:=1;
    4387       dx:=0;
    4388       dy:=0;
    4389       if Mouse.CursorPos.y<Screen.Height-PanelHeight then
    4390         if Mouse.CursorPos.x=0 then dx:=-Speed // scroll left
    4391         else if Mouse.CursorPos.x=Screen.Width-1 then dx:=Speed; // scroll right
    4392       if Mouse.CursorPos.y=0 then dy:=-Speed // scroll up
    4393       else if (Mouse.CursorPos.y=Screen.Height-1)
    4394         and (Mouse.CursorPos.x>=TerrainBtn.Left+TerrainBtn.Width)
    4395         and (Mouse.CursorPos.x<xRightPanel+10-8) then dy:=Speed; // scroll down
    4396       if (dx<>0) or (dy<>0) then
    4397         begin
    4398         if (Screen.ActiveForm<>MainScreen)
    4399           and (@Screen.ActiveForm.OnDeactivate<>nil) then
    4400           Screen.ActiveForm.OnDeactivate(nil);
    4401         Scroll(dx,dy);
    4402         end
    4403       end;
    4404 
    4405     BlinkTime:=(BlinkTime+1) mod (BlinkOnTime+BlinkOffTime);
    4406     BlinkON:= BlinkTime>=BlinkOffTime;
    4407     DestinationMarkON:=true;
    4408     if UnFocus>=0 then
    4409       begin
    4410       if (BlinkTime=0) or (BlinkTime=BlinkOffTime) then
    4411         begin
    4412         PaintLocTemp(MyUn[UnFocus].Loc,pltsBlink);
    4413         PaintDestination;
    4414   //      if MoveHintToLoc>=0 then
    4415   //        ShowMoveHint(MoveHintToLoc, true);
     7829          else if Flag = tfAllTechs then
     7830            TellNewModels
    44167831        end
    44177832      end
    4418     else if TurnComplete and not supervising then
    4419       begin
    4420       if BlinkTime=0 then EOT.SetButtonIndexFast(eotBlinkOff)
    4421       else if BlinkTime=BlinkOffTime then EOT.SetButtonIndexFast(eotBlinkOn)
     7833    end;
     7834
     7835    procedure TMainScreen.MapBtnClick(Sender: TObject);
     7836    begin
     7837      with TButtonC(Sender) do
     7838      begin
     7839        MapOptionChecked := MapOptionChecked xor (1 shl (Tag shr 8));
     7840        SetMapOptions;
     7841        ButtonIndex := MapOptionChecked shr (Tag shr 8) and 1 + 2
     7842      end;
     7843      if Sender = MapBtn0 then
     7844      begin
     7845        MiniPaint;
     7846        PanelPaint
     7847      end // update mini map only
     7848      else
     7849      begin
     7850        MapValid := false;
     7851        PaintAllMaps;
     7852      end; // update main map
     7853    end;
     7854
     7855    procedure TMainScreen.GrWallBtnDownChanged(Sender: TObject);
     7856    begin
     7857      if TButtonBase(Sender).Down then
     7858      begin
     7859        MapOptionChecked := MapOptionChecked or (1 shl moGreatWall);
     7860        TButtonBase(Sender).Hint := '';
    44227861      end
    4423     end
    4424 end;
    4425 
    4426 procedure TMainScreen.Centre(Loc:integer);
    4427 begin
    4428 if FastScrolling and MapValid then update;
    4429   // necessary because ScrollDC for form canvas is called after
    4430 xw:=(Loc mod G.lx-(MapWidth-xxt*2*((Loc div G.lx) and 1)) div (xxt*4)+G.lx) mod G.lx;
    4431 if ywmax<=0 then yw:=ywcenter
    4432 else
    4433   begin
    4434   yw:=(Loc div G.lx-MapHeight div (yyt*2)+1) and not 1;
    4435   if yw<0 then yw:=0
    4436   else if yw>ywmax then yw:=ywmax;
    4437   end
    4438 end;
    4439 
    4440 function TMainScreen.ZoomToCity(Loc: integer; NextUnitOnClose: boolean = false;
    4441   ShowEvent: integer = 0): boolean;
    4442 begin
    4443 result:= MyMap[Loc] and (fOwned or fSpiedOut)<>0;
    4444 if result then with CityDlg do
    4445   begin
    4446   if ClientMode>=scContact then
    4447     begin
    4448     CloseAction:=None;
    4449     RestoreUnFocus:=-1;
    4450     end
    4451   else if NextUnitOnClose then
    4452     begin
    4453     CloseAction:=StepFocus;
    4454     RestoreUnFocus:=-1;
    4455     end
    4456   else if not Visible then
    4457     begin
    4458     CloseAction:=RestoreFocus;
    4459     RestoreUnFocus:=UnFocus;
    4460     end;
    4461   SetUnFocus(-1);
    4462   SetTroopLoc(Loc);
    4463   MarkCityLoc:=Loc;
    4464   PanelPaint;
    4465   ShowNewContent(wmPersistent, Loc, ShowEvent);
    4466   end
    4467 end;
    4468 
    4469 function TMainScreen.LocationOfScreenPixel(x,y: integer): integer;
    4470 var
    4471 qx,qy: integer;
    4472 begin
    4473 qx:=(x*(yyt*2)+y*(xxt*2)+xxt*yyt*2) div (xxt*yyt*4)-1;
    4474 qy:=(y*(xxt*2)-x*(yyt*2)-xxt*yyt*2+4000*xxt*yyt) div (xxt*yyt*4)-999;
    4475 result:=(xw+(qx-qy+2048) div 2-1024+G.lx) mod G.lx+G.lx*(yw+qx+qy);
    4476 end;
    4477 
    4478 procedure TMainScreen.MapBoxMouseDown(Sender:TObject;
    4479   Button:TMouseButton;Shift:TShiftState;x,y:integer);
    4480 var
    4481 i,uix,emix,p1,dx,dy,MouseLoc:integer;
    4482 EditTileData: TEditTileData;
    4483 m,m2: TMenuItem;
    4484 MoveAdviceData: TMoveAdviceData;
    4485 DoCenter: boolean;
    4486 begin
    4487 if GameMode=cMovie then
    4488   exit;
    4489 
    4490 if CityDlg.Visible then CityDlg.Close;
    4491 if UnitStatDlg.Visible then UnitStatDlg.Close;
    4492 MouseLoc:=LocationOfScreenPixel(x,y);
    4493 if (MouseLoc<0) or (MouseLoc>=G.lx*G.ly) then exit;
    4494 if (Button=mbLeft) and not(ssShift in Shift) then
    4495   begin
    4496   DoCenter:=true;
    4497   if ClientMode=cEditMap then
    4498     begin
    4499     DoCenter:=false;
    4500     EditTileData.Loc:=MouseLoc;
    4501     if ssCtrl in Shift then // toggle special resource
    4502       case MyMap[MouseLoc] and fTerrain of
    4503         fOcean: EditTileData.NewTile:=MyMap[MouseLoc];
    4504         fGrass, fArctic: EditTileData.NewTile:=MyMap[MouseLoc] and not fSpecial
    4505           or ((MyMap[MouseLoc] shr 5 and 3+1) mod 2 shl 5);
    4506         else EditTileData.NewTile:=MyMap[MouseLoc] and not fSpecial
    4507           or ((MyMap[MouseLoc] shr 5 and 3+1) mod 3 shl 5)
    4508         end
    4509     else if BrushType<=fTerrain then
    4510       EditTileData.NewTile:=MyMap[MouseLoc] and not fTerrain or fSpecial or BrushType
    4511     else if BrushType and fDeadLands<>0 then
    4512       if MyMap[MouseLoc] and (fDeadLands or fModern)
    4513         =BrushType and (fDeadLands or fModern) then
    4514         EditTileData.NewTile:=MyMap[MouseLoc] and not (fDeadLands or fModern)
    4515       else EditTileData.NewTile:=MyMap[MouseLoc] and not (fDeadLands or fModern)
    4516         or BrushType
    4517     else if BrushType and fTerImp<>0 then
    4518       if MyMap[MouseLoc] and fTerImp=BrushType then
    4519         EditTileData.NewTile:=MyMap[MouseLoc] and not fTerImp
    4520       else EditTileData.NewTile:=MyMap[MouseLoc] and not fTerImp or BrushType
    4521     else if BrushType and (fPrefStartPos or fStartPos)<>0 then
    4522       if MyMap[MouseLoc] and (fPrefStartPos or fStartPos)
    4523         =BrushType and (fPrefStartPos or fStartPos) then
    4524         EditTileData.NewTile:=MyMap[MouseLoc] and not (fPrefStartPos or fStartPos)
    4525       else EditTileData.NewTile:=MyMap[MouseLoc]
    4526         and not (fPrefStartPos or fStartPos) or BrushType
    4527     else EditTileData.NewTile:=MyMap[MouseLoc] xor BrushType;
    4528     Server(sEditTile,me,0,EditTileData);
    4529     Edited:=true;
    4530     BrushLoc:=MouseLoc;
    4531     PaintLoc(MouseLoc,2);
    4532     MiniPaint;
    4533     BitBlt(Panel.Canvas.Handle,xMini+2,yMini+2,G.lx*2,G.ly,Mini.Canvas.Handle,
    4534       0,0,SRCCOPY);
    4535     if ywmax<=0 then
    4536       Frame(Panel.Canvas,xMini+2+G.lx-MapWidth div (2*xxt),yMini+2,
    4537         xMini+1+G.lx+MapWidth div (2*xxt),
    4538         yMini+2+G.ly-1,MainTexture.clMark,MainTexture.clMark)
    4539     else Frame(Panel.Canvas,xMini+2+G.lx-MapWidth div (2*xxt),yMini+2+yw,
    4540       xMini+2+G.lx+MapWidth div (2*xxt)-1,
    4541       yMini+2+yw+MapHeight div yyt-2,MainTexture.clMark,MainTexture.clMark);
    4542     RectInvalidate(xMini+2,TopBarHeight+MapHeight-overlap+yMini+2,xMini+2+G.lx*2,
    4543       TopBarHeight+MapHeight-overlap+yMini+2+G.ly)
    4544     end
    4545   else if MyMap[MouseLoc] and fCity<>0 then {city clicked}
    4546     begin
    4547     if MyMap[MouseLoc] and (fOwned or fSpiedOut)<>0 then
    4548       begin
    4549       ZoomToCity(MouseLoc);
    4550       DoCenter:=false;
     7862      else
     7863      begin
     7864        MapOptionChecked := MapOptionChecked and not(1 shl moGreatWall);
     7865        TButtonBase(Sender).Hint := Phrases.Lookup('CONTROLS',
     7866          -1 + TButtonBase(Sender).Tag and $FF);
     7867      end;
     7868      SetMapOptions;
     7869      MapValid := false;
     7870      PaintAllMaps;
     7871    end;
     7872
     7873    procedure TMainScreen.BareBtnDownChanged(Sender: TObject);
     7874    begin
     7875      if TButtonBase(Sender).Down then
     7876      begin
     7877        MapOptionChecked := MapOptionChecked or (1 shl moBareTerrain);
     7878        TButtonBase(Sender).Hint := '';
    45517879      end
    4552     else
    4553       begin
    4554       UnitStatDlg.ShowNewContent_EnemyCity(wmPersistent, MouseLoc);
    4555       DoCenter:=false;
    4556       end
    4557     end
    4558   else if MyMap[MouseLoc] and fUnit<>0 then {unit clicked}
    4559     if MyMap[MouseLoc] and fOwned<>0 then
    4560       begin
    4561       DoCenter:=false;
    4562       if not supervising and (ClientMode<scContact) then
    4563         begin // not in negotiation mode
    4564         if (UnFocus>=0) and (MyUn[UnFocus].Loc=MouseLoc) then
    4565           begin // rotate
    4566           uix:=(UnFocus+1) mod MyRO.nUn;
    4567           i:=MyRO.nUn-1;
    4568           while i>0 do
    4569             begin
    4570             if (MyUn[uix].Loc=MouseLoc) and (MyUn[uix].Job=jNone)
    4571               and (MyUn[uix].Status and (usStay or usRecover or usEnhance or usWaiting)=usWaiting) then
    4572               break;
    4573             dec(i);
    4574             uix:=(uix+1) mod MyRO.nUn;
    4575             end;
    4576           if i=0 then uix:=UnFocus
    4577           end
    4578         else Server(sGetDefender,me,MouseLoc,uix);
    4579         if uix<>UnFocus then
    4580           SetUnFocus(uix);
    4581         TurnComplete:=false;
    4582         EOT.ButtonIndex:=eotGray;
    4583         end;
    4584       SetTroopLoc(MouseLoc);
    4585       PanelPaint;
    4586       end // own unit
    4587     else if (MyMap[MouseLoc] and fSpiedOut<>0) and not(ssCtrl in Shift) then
    4588       begin
    4589       DoCenter:=false;
    4590       SetTroopLoc(MouseLoc);
    4591       PanelPaint;
    4592       end
    4593     else
    4594       begin
    4595       DoCenter:=false;
    4596       UnitStatDlg.ShowNewContent_EnemyLoc(wmPersistent, MouseLoc);
     7880      else
     7881      begin
     7882        MapOptionChecked := MapOptionChecked and not(1 shl moBareTerrain);
     7883        TButtonBase(Sender).Hint := Phrases.Lookup('CONTROLS',
     7884          -1 + TButtonBase(Sender).Tag and $FF);
    45977885      end;
    4598   if DoCenter then begin Centre(MouseLoc); PaintAllMaps end
    4599   end
    4600 else if (ClientMode<>cEditMap) and (Button=mbRight) and not(ssShift in Shift) then
    4601   begin
    4602   if supervising then
    4603     begin
    4604     EditLoc:=MouseLoc;
    4605     Server(sGetModels,me,0,nil^);
    4606     EmptyMenu(mCreateUnit);
    4607     for p1:=0 to nPl-1 do if 1 shl p1 and MyRO.Alive<>0 then
    4608       begin
    4609       m:=TMenuItem.Create(mCreateUnit);
    4610       m.Caption:=Tribe[p1].TPhrase('SHORTNAME');
    4611       for emix:=MyRO.nEnemyModel-1 downto 0 do
    4612         if (MyRO.EnemyModel[emix].Owner=p1) and
    4613           (Server(sCreateUnit-sExecute+p1 shl 4,me,MyRO.EnemyModel[emix].mix,MouseLoc)>=rExecuted) then
    4614           begin
    4615           if Tribe[p1].ModelPicture[MyRO.EnemyModel[emix].mix].HGr=0 then
    4616             InitEnemyModel(emix);
    4617           m2:=TMenuItem.Create(m);
    4618           m2.Caption:=Tribe[p1].ModelName[MyRO.EnemyModel[emix].mix];
    4619           m2.Tag:=p1 shl 16 + MyRO.EnemyModel[emix].mix;
    4620           m2.OnClick:=CreateUnitClick;
    4621           m.Add(m2);
    4622           end;
    4623       m.Visible:= m.Count>0;
    4624       mCreateUnit.Add(m);
    4625       end;
    4626     if FullScreen then EditPopup.Popup(Left+x, Top+y)
    4627     else EditPopup.Popup(Left+x+4, Top+y+GetSystemMetrics(SM_CYCAPTION)+4);
    4628     end
    4629   else if (UnFocus>=0) and (MyUn[UnFocus].Loc<>MouseLoc) then with MyUn[UnFocus] do
    4630     begin
    4631     dx:=((MouseLoc mod G.lx *2 +MouseLoc div G.lx and 1)
    4632       -(Loc mod G.lx *2 +Loc div G.lx and 1)+3*G.lx) mod (2*G.lx) -G.lx;
    4633     dy:=MouseLoc div G.lx-Loc div G.lx;
    4634     if abs(dx)+abs(dy)<3 then
     7886      SetMapOptions;
     7887      MapValid := false;
     7888      PaintAllMaps;
     7889    end;
     7890
     7891    procedure TMainScreen.FormKeyUp(Sender: TObject; var Key: word;
     7892      Shift: TShiftState);
     7893    begin
     7894      if idle and (Key = VK_APPS) then
     7895      begin
     7896        InitPopup(GamePopup);
     7897        if FullScreen then
     7898          GamePopup.Popup(Left, Top + TopBarHeight - 1)
     7899        else
     7900          GamePopup.Popup(Left + 4, Top + GetSystemMetrics(SM_CYCAPTION) + 4 +
     7901            TopBarHeight - 1);
     7902        exit
     7903      end // windows menu button calls game menu
     7904    end;
     7905
     7906    procedure TMainScreen.CreateUnitClick(Sender: TObject);
     7907    var
     7908      p1, mix: integer;
     7909    begin
     7910      p1 := TComponent(Sender).Tag shr 16;
     7911      mix := TComponent(Sender).Tag and $FFFF;
     7912      if Server(sCreateUnit + p1 shl 4, me, mix, EditLoc) >= rExecuted then
     7913        PaintLoc(EditLoc);
     7914    end;
     7915
     7916    procedure TMainScreen.mSoundOffClick(Sender: TObject);
     7917    begin
     7918      SoundMode := smOff;
     7919    end;
     7920
     7921    procedure TMainScreen.mSoundOnClick(Sender: TObject);
     7922    begin
     7923      SoundMode := smOn;
     7924    end;
     7925
     7926    procedure TMainScreen.mSoundOnAltClick(Sender: TObject);
     7927    begin
     7928      SoundMode := smOnAlt;
     7929    end;
     7930
     7931    { procedure TMainScreen.AdviceBtnClick;
     7932      var
     7933      OldAdviceLoc: integer;
    46357934      begin
    46367935      DestinationMarkON:=false;
    46377936      PaintDestination;
    4638       Status:=Status and ($FFFF-usStay-usRecover-usGoto-usEnhance) or usWaiting;
    4639       MoveUnit(dx,dy,muAutoNext) {simple move}
    4640       end
    4641     else if GetMoveAdvice(UnFocus,MouseLoc,MoveAdviceData)>=rExecuted then
    4642       begin
    4643       if MyMap[MouseLoc] and (fUnit or fOwned)=fUnit then
    4644         begin // check for suicide mission before movement
    4645         with MyUn[UnFocus],BattleDlg.Forecast do
    4646           begin
    4647           pAtt:=me;
    4648           mixAtt:=mix;
    4649           HealthAtt:=Health;
    4650           ExpAtt:=Exp;
    4651           FlagsAtt:=Flags;
    4652           end;
    4653         BattleDlg.Forecast.Movement:=MyUn[UnFocus].Movement;
    4654         if (Server(sGetBattleForecastEx,me,MouseLoc,BattleDlg.Forecast)>=rExecuted)
    4655           and (BattleDlg.Forecast.EndHealthAtt<=0) then
    4656           begin
    4657           BattleDlg.uix:=UnFocus;
    4658           BattleDlg.ToLoc:=MouseLoc;
    4659           BattleDlg.IsSuicideQuery:=true;
    4660           BattleDlg.ShowModal;
    4661           if BattleDlg.ModalResult<>mrOK then
    4662             exit;
    4663           end
    4664         end;
    4665       DestinationMarkON:=false;
    4666       PaintDestination;
    4667       Status:=Status and not (usStay or usRecover or usEnhance) or usWaiting;
    4668       MoveToLoc(MouseLoc,false); {goto}
    4669       end
    4670     end
    4671   end
    4672 else if (Button=mbMiddle) and (UnFocus>=0)
    4673   and (MyModel[MyUn[UnFocus].mix].Kind in [mkSettler,mkSlaves]) then
    4674   begin
    4675   DestinationMarkON:=false;
    4676   PaintDestination;
    4677   MyUn[UnFocus].Status:=MyUn[UnFocus].Status and ($FFFF-usStay-usRecover-usGoto) or usEnhance;
    4678   uix:=UnFocus;
    4679   if MouseLoc<>MyUn[uix].Loc then MoveToLoc(MouseLoc,true); {goto}
    4680   if (UnFocus=uix) and (MyUn[uix].Loc=MouseLoc) then MenuClick(mEnhance)
    4681   end
    4682 else if (Button=mbLeft) and (ssShift in Shift)
    4683   and (MyMap[MouseLoc] and fTerrain<>fUNKNOWN) then
    4684   HelpOnTerrain(MouseLoc, wmPersistent)
    4685 else if (ClientMode<=cContinue) and (Button=mbRight) and (ssShift in Shift)
    4686   and (UnFocus>=0) and (MyMap[MouseLoc] and (fUnit or fOwned)=fUnit) then
    4687   begin // battle forecast
    4688   with MyUn[UnFocus],BattleDlg.Forecast do
    4689     begin
    4690     pAtt:=me;
    4691     mixAtt:=mix;
    4692     HealthAtt:=Health;
    4693     ExpAtt:=Exp;
    4694     FlagsAtt:=Flags;
    4695     end;
    4696   BattleDlg.Forecast.Movement:=MyUn[UnFocus].Movement;
    4697   if Server(sGetBattleForecastEx,me,MouseLoc,BattleDlg.Forecast)>=rExecuted then
    4698     begin
    4699     BattleDlg.uix:=UnFocus;
    4700     BattleDlg.ToLoc:=MouseLoc;
    4701     BattleDlg.Left:=x-BattleDlg.Width div 2;
    4702     if BattleDlg.Left<0 then
    4703       BattleDlg.Left:=0
    4704     else if BattleDlg.Left+BattleDlg.Width>Screen.Width then
    4705       BattleDlg.Left:=Screen.Width-BattleDlg.Width;
    4706     BattleDlg.Top:=y-BattleDlg.Height div 2;
    4707     if BattleDlg.Top<0 then
    4708       BattleDlg.Top:=0
    4709     else if BattleDlg.Top+BattleDlg.Height>Screen.Height then
    4710       BattleDlg.Top:=Screen.Height-BattleDlg.Height;
    4711     BattleDlg.IsSuicideQuery:=false;
    4712     BattleDlg.Show;
    4713     end
    4714   end
    4715 end;
    4716 
    4717 function TMainScreen.MoveUnit(dx,dy:integer; Options: integer): integer;
    4718 // move focused unit to adjacent tile
    4719 var
    4720 i,cix,uix,euix,FromLoc,ToLoc,DirCode,UnFocus0,Defender,Mission,p1,
    4721   NewTiles,cixChanged: integer;
    4722 OldToTile: cardinal;
    4723 CityCaptured, IsAttack, OldUnrest, NewUnrest, NeedEcoUpdate, NeedRepaintPanel,
    4724   ToTransport, ToShip: boolean;
    4725 PlaneReturnData: TPlaneReturnData;
    4726 QueryItem: string;
    4727 begin
    4728 result:=eInvalid;
    4729 UnFocus0:=UnFocus;
    4730 FromLoc:=MyUn[UnFocus].Loc;
    4731 ToLoc:=dLoc(FromLoc,dx,dy);
    4732 if (ToLoc<0) or (ToLoc>=G.lx*G.ly) then begin result:=eInvalid; exit; end;
    4733 if MyMap[ToLoc] and fStealthUnit<>0 then
    4734   begin
    4735   SoundMessage(Phrases.LookUp('ATTACKSTEALTH'),'');
    4736   exit;
    4737   end;
    4738 if MyMap[ToLoc] and fHiddenUnit<>0 then
    4739   begin
    4740   SoundMessage(Phrases.LookUp('ATTACKSUB'),'');
    4741   exit;
    4742   end;
    4743 
    4744 if MyMap[ToLoc] and (fUnit or fOwned)=fUnit then
    4745   begin // attack -- search enemy unit
    4746   if (MyModel[MyUn[UnFocus].mix].Attack=0)
    4747     and not ((MyModel[MyUn[UnFocus].mix].Cap[mcBombs]>0)
    4748       and (MyUn[UnFocus].Flags and unBombsLoaded<>0)) then
    4749     begin
    4750     SoundMessage(Phrases.LookUp('NOATTACKER'),'');
    4751     exit;
    4752     end;
    4753   euix:=MyRO.nEnemyUn-1;
    4754   while (euix>=0) and (MyRO.EnemyUn[euix].Loc<>ToLoc) do dec(euix);
    4755   end;
    4756 
    4757 DirCode:=dx and 7 shl 4+dy and 7 shl 7;
    4758 result:=Server(sMoveUnit-sExecute+DirCode,me,UnFocus,nil^);
    4759 if (result<rExecuted) and (MyUn[UnFocus].Job>jNone) then
    4760   Server(sStartJob+jNone shl 4,me,UnFocus,nil^);
    4761 if (result<rExecuted) and (result<>eNoTime_Move) then
    4762   begin
    4763   case result of
    4764     eNoTime_Load:
    4765       if MyModel[MyUn[UnFocus].mix].Domain=dAir then
    4766         SoundMessage(Phrases.Lookup('NOTIMELOADAIR'),'NOMOVE_TIME')
    4767       else
    4768         SoundMessage(Format(Phrases.Lookup('NOTIMELOADGROUND'),
    4769           [MovementToString(MyModel[MyUn[UnFocus].mix].Speed)]),'NOMOVE_TIME');
    4770     eNoTime_Bombard: SoundMessage(Phrases.Lookup('NOTIMEBOMBARD'),'NOMOVE_TIME');
    4771     eNoTime_Expel: SoundMessage(Phrases.Lookup('NOTIMEEXPEL'),'NOMOVE_TIME');
    4772     eNoRoad: SoundMessage(Phrases.Lookup('NOROAD'),'NOMOVE_DEFAULT');
    4773     eNoNav: SoundMessage(Phrases.Lookup('NONAV'),'NOMOVE_DEFAULT');
    4774     eNoCapturer: SoundMessage(Phrases.Lookup('NOCAPTURER'),'NOMOVE_DEFAULT');
    4775     eNoBombarder: SoundMessage(Phrases.Lookup('NOBOMBARDER'),'NOMOVE_DEFAULT');
    4776     eZOC: ContextMessage(Phrases.Lookup('ZOC'), 'NOMOVE_ZOC', hkText, HelpDlg.TextIndex('MOVEMENT'));
    4777     eTreaty:
    4778       if MyMap[ToLoc] and (fUnit or fOwned)<>fUnit then {no enemy unit -- move}
    4779         SoundMessage(Tribe[MyRO.Territory[ToLoc]].TPhrase('PEACE_NOMOVE'),
    4780           'NOMOVE_TREATY')
    4781       else SoundMessage(Tribe[MyRO.EnemyUn[euix].Owner].TPhrase
    4782         ('PEACE_NOATTACK'),'NOMOVE_TREATY');
    4783     eDomainMismatch:
    4784       begin
    4785       if (MyModel[MyUn[UnFocus].mix].Domain<dSea)
    4786         and (MyMap[ToLoc] and (fUnit or fOwned)=fUnit or fOwned) then
    4787         begin // false load attempt
    4788         ToShip:=false;
    4789         ToTransport:=false;
    4790         for uix:=0 to MyRo.nUn-1 do
    4791           if (MyUn[uix].Loc=ToLoc) and (MyModel[MyUn[uix].mix].Domain=dSea) then
    4792             begin
    4793             ToShip:=true;
    4794             if MyModel[MyUn[uix].mix].Cap[mcSeaTrans]>0 then
    4795               ToTransport:=true;
    4796             end;
    4797         if ToTransport then
    4798           SoundMessage(Phrases.Lookup('FULLTRANSPORT'),'NOMOVE_DEFAULT')
    4799         else if ToShip then
    4800           SoundMessage(Phrases.Lookup('NOTRANSPORT'),'NOMOVE_DEFAULT')
    4801         else Play('NOMOVE_DOMAIN');
    4802         end
    4803       else Play('NOMOVE_DOMAIN');
    4804       end
    4805     else Play('NOMOVE_DEFAULT');
    4806     end;
    4807   exit;
    4808   end;
    4809 
    4810 if ((result=eWon) or (result=eLost) or (result=eBloody))
    4811   and (MyUn[UnFocus].Movement<100)
    4812   and (MyModel[MyUn[UnFocus].mix].Cap[mcWill]=0) then
    4813   begin
    4814   if SimpleQuery(mkYesNo,Format(Phrases.Lookup('FASTATTACK'),
    4815     [MyUn[UnFocus].Movement]),'NOMOVE_TIME')<>mrOk then
    4816     begin result:=eInvalid; exit; end;
    4817   Update; // remove message box from screen
    4818   end;
    4819 
    4820 OldUnrest:=false;
    4821 NewUnrest:=false;
    4822 if (result>=rExecuted) and (result and rUnitRemoved=0)
    4823   and (MyMap[ToLoc] and (fUnit or fOwned)<>fUnit) then
    4824   begin
    4825   OldUnrest:=UnrestAtLoc(UnFocus,FromLoc);
    4826   NewUnrest:=UnrestAtLoc(UnFocus,ToLoc);
    4827   if NewUnrest>OldUnrest then
    4828     begin
    4829     if MyRO.Government=gDemocracy then
    4830       begin
    4831       QueryItem:='UNREST_NOTOWN';
    4832       p1:=me;
    4833       end
    4834     else
    4835       begin
    4836       QueryItem:='UNREST_FOREIGN';
    4837       p1:=MyRO.Territory[ToLoc];
     7937      AdvisorDlg.GiveStrategyAdvice;
     7938      OldAdviceLoc:=MainMap.AdviceLoc;
     7939      MainMap.AdviceLoc:=-1;
     7940      PaintLoc(OldAdviceLoc);
     7941      end; }
     7942
     7943    { procedure TMainScreen.SetAdviceLoc(Loc: integer; AvoidRect: TRect);
     7944      var
     7945      OldAdviceLoc,x,y: integer;
     7946      begin
     7947      if Loc<>MainMap.AdviceLoc then
     7948      begin
     7949      if Loc>=0 then
     7950      begin // center
     7951      y:=Loc div G.lx;
     7952      x:=(Loc+G.lx - AvoidRect.Right div (2*66)) mod G.lx;
     7953      Centre(y*G.lx+x);
     7954      PaintAllMaps;
    48387955      end;
    4839     with MessgExDlg do
    4840       begin
    4841       MessgText:=Format(Tribe[p1].TPhrase(QueryItem),[Phrases.Lookup('GOVERNMENT',MyRO.Government)]);
    4842       Kind:=mkYesNo;
    4843       IconKind:=mikImp;
    4844       IconIndex:=imPalace;
    4845       ShowModal;
    4846       if ModalResult<>mrOk then
    4847         begin result:=eInvalid; exit; end;
     7956      OldAdviceLoc:=MainMap.AdviceLoc;
     7957      MainMap.AdviceLoc:=Loc;
     7958      PaintLoc(OldAdviceLoc);
     7959      PaintLoc(MainMap.AdviceLoc);
    48487960      end;
    4849     Update; // remove message box from screen
    4850     end
    4851   end;
    4852 
    4853 if (result>=rExecuted)
    4854   and (MyModel[MyUn[UnFocus].mix].Domain=dAir)
    4855   and (MyUn[UnFocus].Status and usToldNoReturn=0) then
    4856   begin // can plane return?
    4857   PlaneReturnData.Fuel:=MyUn[UnFocus].Fuel;
    4858   if (MyMap[ToLoc] and (fUnit or fOwned)=fUnit)
    4859     or (MyMap[ToLoc] and (fCity or fOwned)=fCity) then
    4860     begin // attack/expel/bombard -> 100MP
    4861     PlaneReturnData.Loc:=FromLoc;
    4862     PlaneReturnData.Movement:=MyUn[UnFocus].Movement-100;
    4863     if PlaneReturnData.Movement<0 then PlaneReturnData.Movement:=0;
    4864     end
    4865   else // move
    4866     begin
    4867     PlaneReturnData.Loc:=ToLoc;
    4868     if dx and 1<>0 then PlaneReturnData.Movement:=MyUn[UnFocus].Movement-100
    4869     else PlaneReturnData.Movement:=MyUn[UnFocus].Movement-150;
    4870     end;
    4871   if Server(sGetPlaneReturn, me, UnFocus, PlaneReturnData)=eNoWay then
    4872     begin
    4873     if MyModel[MyUn[UnFocus].mix].Kind=mkSpecial_Glider then
    4874       QueryItem:='LOWFUEL_GLIDER'
    4875     else QueryItem:='LOWFUEL';
    4876     if SimpleQuery(mkYesNo,Phrases.Lookup(QueryItem),'WARNING_LOWSUPPORT')<>mrOk then
    4877       begin result:=eInvalid; exit; end;
    4878     Update; // remove message box from screen
    4879     MyUn[UnFocus].Status:=MyUn[UnFocus].Status or usToldNoReturn;
    4880     end
    4881   end;
    4882 
    4883 if result=eMissionDone then
    4884   begin
    4885   ModalSelectDlg.ShowNewContent(wmModal,kMission);
    4886   Update; // dialog still on screen
    4887   Mission:=ModalSelectDlg.result;
    4888   if Mission<0 then exit;
    4889   Server(sSetSpyMission+Mission shl 4, me, 0, nil^);
    4890   end;
    4891 
    4892 CityCaptured:=false;
    4893 if result=eNoTime_Move then Play('NOMOVE_TIME')
    4894 else
    4895   begin
    4896   NeedEcoUpdate:=false;
    4897   DestinationMarkON:=false;
    4898   PaintDestination;
    4899   if result and rUnitRemoved<>0 then
    4900     CityOptimizer_BeforeRemoveUnit(UnFocus);
    4901   IsAttack:= (result=eBombarded)
    4902     or (result<>eMissionDone) and (MyMap[ToLoc] and (fUnit or fOwned)=fUnit);
    4903   if not IsAttack then
    4904     begin // move
    4905     cix:=MyRO.nCity-1; {look for own city at dest location}
    4906     while (cix>=0) and (MyCity[cix].Loc<>ToLoc) do dec(cix);
    4907     if (result<>eMissionDone) and (MyMap[ToLoc] and fCity<>0) and (cix<0) then
    4908       CityCaptured:=true;
    4909     result:=Server(sMoveUnit+DirCode,me,UnFocus,nil^);
    4910     case result of
    4911       eHiddenUnit:
    4912         begin Play('NOMOVE_SUBMARINE'); PaintLoc(ToLoc) end;
    4913       eStealthUnit:
    4914         begin Play('NOMOVE_STEALTH'); PaintLoc(ToLoc) end;
    4915       eZOC_EnemySpotted:
    4916         begin Play('NOMOVE_ZOC'); PaintLoc(ToLoc,1) end;
    4917       rExecuted..maxint:
    4918         begin
    4919         if result and rUnitRemoved<>0 then UnFocus:=-1 // unit died
    4920         else
    4921           begin
    4922           assert(UnFocus>=0);
    4923           MyUn[UnFocus].Status:=MyUn[UnFocus].Status and not (usStay or usRecover);
    4924           for uix:=0 to MyRO.nUn-1 do if MyUn[uix].Master=UnFocus then
    4925             MyUn[uix].Status:=MyUn[uix].Status and not usWaiting;
    4926           if CityCaptured
    4927             and (MyRO.Government in [gRepublic,gDemocracy,gFuture]) then
    4928             begin // borders have moved, unrest might have changed in any city
    4929             CityOptimizer_BeginOfTurn;
    4930             NeedEcoUpdate:=true;
    4931             end
    4932           else
    4933             begin
    4934             if OldUnrest<>NewUnrest then
    4935               begin
    4936               CityOptimizer_CityChange(MyUn[UnFocus].Home);
    4937               for uix:=0 to MyRO.nUn-1 do if MyUn[uix].Master=UnFocus then
    4938                 CityOptimizer_CityChange(MyUn[uix].Home);
    4939               NeedEcoUpdate:=true;
    4940               end;
    4941             if (MyRO.Government=gDespotism)
    4942               and (MyModel[MyUn[UnFocus].mix].Kind=mkSpecial_TownGuard) then
    4943               begin
    4944               if MyMap[FromLoc] and fCity<>0 then
    4945                 begin // town guard moved out of city in despotism -- reoptimize!
    4946                 cixChanged:=MyRO.nCity-1;
    4947                 while (cixChanged>=0) and (MyCity[cixChanged].Loc<>FromLoc) do
    4948                   dec(cixChanged);
    4949                 assert(cixChanged>=0);
    4950                 if cixChanged>=0 then
    4951                   begin
    4952                   CityOptimizer_CityChange(cixChanged);
    4953                   NeedEcoUpdate:=true;
    4954                   end;
    4955                 end;
    4956               if (MyMap[ToLoc] and fCity<>0) and not CityCaptured then
    4957                 begin // town guard moved into city in despotism -- reoptimize!
    4958                 cixChanged:=MyRO.nCity-1;
    4959                 while (cixChanged>=0) and (MyCity[cixChanged].Loc<>ToLoc) do
    4960                   dec(cixChanged);
    4961                 assert(cixChanged>=0);
    4962                 if cixChanged>=0 then
    4963                   begin
    4964                   CityOptimizer_CityChange(cixChanged);
    4965                   NeedEcoUpdate:=true;
    4966                   end
    4967                 end
    4968               end
    4969             end 
    4970           end;
    4971         end;
    4972       else
    4973         assert(false);
    4974       end;
    4975     SetTroopLoc(ToLoc);
    4976     end
    4977   else
    4978     begin {enemy unit -- attack}
    4979     if result=eBombarded then Defender:=MyRO.Territory[ToLoc]
    4980     else Defender:=MyRO.EnemyUn[euix].Owner;
    4981     {if MyRO.Treaty[Defender]=trCeaseFire then
    4982       if SimpleQuery(mkYesNo,Phrases.Lookup('FRCANCELQUERY_CEASEFIRE'),
    4983         'MSG_DEFAULT')<>mrOK then
    4984         exit;}
    4985     if (Options and muNoSuicideCheck=0)
    4986       and (result and rUnitRemoved<>0) and (result<>eMissionDone) then
    4987       begin // suicide query
    4988       with MyUn[UnFocus],BattleDlg.Forecast do
    4989         begin
    4990         pAtt:=me;
    4991         mixAtt:=mix;
    4992         HealthAtt:=Health;
    4993         ExpAtt:=Exp;
    4994         FlagsAtt:=Flags;
    4995         end;
    4996       BattleDlg.Forecast.Movement:=MyUn[UnFocus].Movement;
    4997       Server(sGetBattleForecastEx,me,ToLoc,BattleDlg.Forecast);
    4998       BattleDlg.uix:=UnFocus;
    4999       BattleDlg.ToLoc:=ToLoc;
    5000       BattleDlg.IsSuicideQuery:=true;
    5001       BattleDlg.ShowModal;
    5002       if BattleDlg.ModalResult<>mrOK then
    5003         exit;
    5004       end;
    5005 
    5006     cixChanged:=-1;
    5007     if (result and rUnitRemoved<>0) and (MyRO.Government=gDespotism)
    5008       and (MyModel[MyUn[UnFocus].mix].Kind=mkSpecial_TownGuard)
    5009       and (MyMap[FromLoc] and fCity<>0) then
    5010       begin // town guard died in city in despotism -- reoptimize!
    5011       cixChanged:=MyRO.nCity-1;
    5012       while (cixChanged>=0) and (MyCity[cixChanged].Loc<>FromLoc) do
    5013         dec(cixChanged);
    5014       assert(cixChanged>=0);
    5015       end;
    5016 
    5017     for i:=0 to MyRO.nEnemyModel-1 do
    5018       LostArmy[i]:=MyRO.EnemyModel[i].Lost;
    5019     OldToTile:=MyMap[ToLoc];
    5020     result:=Server(sMoveUnit+DirCode,me,UnFocus,nil^);
    5021     nLostArmy:=0;
    5022     for i:=0 to MyRO.nEnemyModel-1 do
    5023       begin
    5024       LostArmy[i]:=MyRO.EnemyModel[i].Lost-LostArmy[i];
    5025       inc(nLostArmy,LostArmy[i])
    5026       end;
    5027     if result and rUnitRemoved<>0 then
    5028       begin
    5029       UnFocus:=-1;
    5030       SetTroopLoc(FromLoc);
    5031       end;
    5032     if (OldToTile and not MyMap[ToLoc] and fCity<>0)
    5033       and (MyRO.Government in [gRepublic,gDemocracy,gFuture]) then
    5034       begin // city was destroyed, borders have moved, unrest might have changed in any city
    5035       CityOptimizer_BeginOfTurn;
    5036       NeedEcoUpdate:=true;
    5037       end
    5038     else
    5039       begin
    5040       if cixChanged>=0 then
    5041         begin
    5042         CityOptimizer_CityChange(cixChanged);
    5043         NeedEcoUpdate:=true;
    5044         end;
    5045       if (result=eWon) or (result=eBloody) or (result=eExpelled) then
    5046         begin
    5047         CityOptimizer_TileBecomesAvailable(ToLoc);
    5048         NeedEcoUpdate:=true;
    5049         end;
    5050       end;
    5051     if nLostArmy>1 then
    5052       begin
    5053       with MessgExDlg do
    5054         begin
    5055         Kind:=mkOk;
    5056         IconKind:=mikEnemyArmy;
    5057         MessgText:=Tribe[Defender].TString(Phrases.Lookup('ARMYLOST',
    5058           MyRO.EnemyModel[MyRO.EnemyUn[euix].emix].Domain));
    5059         ShowModal;
    5060         end
    5061       end
    5062     end;
    5063   if result and rUnitRemoved<>0 then
    5064     begin
    5065     CityOptimizer_AfterRemoveUnit;
    5066     ListDlg.RemoveUnit;
    5067     NeedEcoUpdate:=true;
    5068     end;
    5069   if NeedEcoUpdate then
    5070     begin
    5071     UpdateViews(true);
    5072     Update
    5073     end
    5074   end;
    5075 
    5076 if result=eMissionDone then
    5077   begin
    5078   p1:=MyRO.Territory[ToLoc];
    5079   case Mission of
    5080     smStealMap:
    5081       begin MapValid:=false; PaintAllMaps end;
    5082     smStealCivilReport:
    5083       TribeMessage(p1,Tribe[p1].TPhrase('DOSSIER_PREPARED'),'');
    5084     smStealMilReport:
    5085       ListDlg.ShowNewContent_MilReport(wmPersistent,p1);
    5086     end;
    5087   end;
    5088 
    5089 if UnFocus>=0 then
    5090   CheckToldNoReturn(UnFocus);
    5091 
    5092 NeedRepaintPanel:=false;
    5093 if result>=rExecuted then
    5094   begin
    5095   if CityCaptured and (MyMap[ToLoc] and fCity=0) then
    5096     begin // city destroyed
    5097     for i:=0 to 27 do {tell about destroyed wonders}
    5098       if (MyRO.Wonder[i].CityID=-2) and (MyData.ToldWonders[i].CityID<>-2) then
    5099         with MessgExDlg do
    5100           begin
    5101           if WondersDlg.Visible then
    5102             WondersDlg.SmartUpdateContent(false);
    5103           OpenSound:='WONDER_DESTROYED';
    5104           MessgText:=Format(Phrases.Lookup('WONDERDEST'),
    5105             [Phrases.Lookup('IMPROVEMENTS',i)]);
    5106           Kind:=mkOkHelp;
    5107           HelpKind:=hkImp;
    5108           HelpNo:=i;
    5109           IconKind:=mikImp;
    5110           IconIndex:=i;
    5111           ShowModal;
    5112           MyData.ToldWonders[i]:=MyRO.Wonder[i];
    5113           end
    5114     end;
    5115   if CityCaptured and (MyMap[ToLoc] and fCity<>0) then
    5116     begin // city captured
    5117     ListDlg.AddCity;
    5118     for i:=0 to 27 do {tell about capture of wonders}
    5119       if MyRO.City[MyRO.nCity-1].Built[i]>0 then with MessgExDlg do
    5120         begin
    5121         if WondersDlg.Visible then
    5122           WondersDlg.SmartUpdateContent(false);
    5123         OpenSound:='WONDER_CAPTURED';
    5124         MessgText:=Format(Tribe[me].TPhrase('WONDERCAPTOWN'),
    5125           [Phrases.Lookup('IMPROVEMENTS',i)]);
    5126         Kind:=mkOkHelp;
    5127         HelpKind:=hkImp;
    5128         HelpNo:=i;
    5129         IconKind:=mikImp;
    5130         IconIndex:=i;
    5131         ShowModal;
    5132         MyData.ToldWonders[i]:=MyRO.Wonder[i];
    5133         end;
    5134 
    5135     if MyRO.Happened and phStealTech<>0 then
    5136       begin {Temple of Zeus -- choose advance to steal}
    5137       ModalSelectDlg.ShowNewContent(wmModal,kStealTech);
    5138       Server(sStealTech,me,ModalSelectDlg.result,nil^);
    5139       end;
    5140     TellNewModels;
    5141 
    5142     cix:=MyRO.nCity-1;
    5143     while (cix>=0) and (MyCity[cix].Loc<>ToLoc) do
    5144       dec(cix);
    5145     assert(cix>=0);
    5146     MyCity[cix].Status:=MyCity[cix].Status
    5147       and not csResourceWeightsMask or (3 shl 4); // captured city, set to maximum growth
    5148     NewTiles:=1 shl 13; {exploit central tile only}
    5149     Server(sSetCityTiles,me,cix,NewTiles);
    5150     end
    5151   else NeedRepaintPanel:=true;
    5152   end;
    5153 TellNewContacts;
    5154 
    5155 if (UnFocus>=0) and (MyUn[UnFocus].Master>=0) then
    5156   with MyUn[MyUn[UnFocus].Master] do
    5157     if Status and usStay<>0 then
    5158       begin
    5159       Status:=Status and not usStay;
    5160       if (Movement>=100) and (Status and (usRecover or usGoto)=0) then
    5161         Status:=Status or usWaiting;
    5162       end;
    5163 if Options and (muAutoNoWait or muAutoNext)<>0 then
    5164   begin
    5165   if (UnFocus>=0) and ((result=eNoTime_Move) or UnitExhausted(UnFocus)
    5166     or (MyUn[UnFocus].Master>=0)
    5167     or (MyModel[MyUn[UnFocus].mix].Domain=dAir)
    5168       and ((MyMap[MyUn[UnFocus].Loc] and fCity<>0) {aircrafts stop in cities}
    5169         or (MyMap[MyUn[UnFocus].Loc] and fTerImp=tiBase))) then
    5170     begin
    5171     MyUn[UnFocus].Status:=MyUn[UnFocus].Status and not usWaiting;
    5172     if Options and muAutoNext<>0 then
    5173       if CityCaptured and (MyMap[ToLoc] and fCity<>0) then
    5174         begin
    5175         UnFocus:=-1;
    5176         PaintLoc(ToLoc); // don't show unit in city if not selected
    5177         end
    5178       else NextUnit(UnStartLoc,true)
    5179     end
    5180   else if (UnFocus<0) and (Options and muAutoNext<>0) then
    5181     NextUnit(UnStartLoc,result<>eMissionDone);
    5182   end;
    5183 
    5184 if NeedRepaintPanel and (UnFocus=UnFocus0) then
    5185   if IsAttack then PanelPaint
    5186   else
    5187     begin
    5188     assert(result<>eMissionDone);
    5189     CheckTerrainBtnVisible;
    5190     FocusOnLoc(ToLoc,flRepaintPanel or flImmUpdate)
    5191     end;
    5192 
    5193 if (result>=rExecuted) and CityCaptured and (MyMap[ToLoc] and fCity<>0) then
    5194   ZoomToCity(ToLoc,UnFocus<0,chCaptured); // show captured city
    5195 end; // moveunit
    5196 
    5197 procedure TMainScreen.MoveOnScreen(ShowMove: TShowMove; Step0,Step1,nStep: integer;
    5198   Restore: boolean = true);
    5199 var
    5200 ToLoc,xFromLoc,yFromLoc,xToLoc,yToLoc,xFrom,yFrom,xTo,yTo,xMin,yMin,xRange,yRange,
    5201   xw1,Step,xMoving,yMoving,yl,SliceCount:integer;
    5202 UnitInfo: TUnitInfo;
    5203 Ticks0,Ticks: int64;
    5204 begin
    5205 Timer1.Enabled:=false;
    5206 QueryPerformanceCounter(Ticks0);
    5207 with ShowMove do
    5208   begin
    5209   UnitInfo.Owner:=Owner;
    5210   UnitInfo.mix:=mix;
    5211   UnitInfo.Health:=Health;
    5212   UnitInfo.Job:=jNone;
    5213   UnitInfo.Flags:=Flags;
    5214   if Owner<>me then
    5215     UnitInfo.emix:=emix;
    5216 
    5217   ToLoc:=dLoc(FromLoc,dx,dy);
    5218   xToLoc:=ToLoc mod G.lx; yToLoc:=ToLoc div G.lx;
    5219   xFromLoc:=FromLoc mod G.lx; yFromLoc:=FromLoc div G.lx;
    5220   if xToLoc>xFromLoc+2 then xToLoc:=xToLoc-G.lx
    5221   else if xToLoc<xFromLoc-2 then xToLoc:=xToLoc+G.lx;
    5222 
    5223   xw1:=xw+G.lx;
    5224   // ((xFromLoc-xw1)*2+yFromLoc and 1+1)*xxt+dx*xxt/2-MapWidth/2 -> min
    5225   while abs(((xFromLoc-xw1+G.lx)*2+yFromLoc and 1+1)*xxt*2+dx*xxt-MapWidth)
    5226     <abs(((xFromLoc-xw1)*2+yFromLoc and 1+1)*xxt*2+dx*xxt-MapWidth) do
    5227     dec(xw1,G.lx);
    5228 
    5229   xTo:=(xToLoc-xw1)*(xxt*2) + yToLoc and 1 *xxt +(xxt-xxu);
    5230   yTo:=(yToLoc-yw)*yyt +(yyt-yyu_anchor);
    5231   xFrom:=(xFromLoc-xw1)*(xxt*2) + yFromLoc and 1 *xxt +(xxt-xxu);
    5232   yFrom:=(yFromLoc-yw)*yyt +(yyt-yyu_anchor);
    5233   if xFrom<xTo then begin xMin:=xFrom;xRange:=xTo-xFrom end
    5234   else begin xMin:=xTo;xRange:=xFrom-xTo end;
    5235   if yFrom<yTo then begin yMin:=yFrom;yRange:=yTo-yFrom end
    5236   else begin yMin:=yTo;yRange:=yFrom-yTo end;
    5237   inc(xRange,xxt*2);
    5238   inc(yRange,yyt*3);
    5239 
    5240   MainOffscreenPaint;
    5241   NoMap.SetOutput(Buffer);
    5242   NoMap.SetPaintBounds(0,0,xRange,yRange);
    5243   for Step:=0 to abs(Step1-Step0) do
    5244     begin
    5245     BitBlt(Buffer.Canvas.Handle,0,0,xRange,yRange,
    5246       offscreen.Canvas.Handle,xMin,yMin,SRCCOPY);
    5247     if Step1<>Step0 then
    5248       begin
    5249       xMoving:=xFrom+Round((Step0+Step*(Step1-Step0) div abs(Step1-Step0))
    5250         *(xTo-xFrom)/nStep);
    5251       yMoving:=yFrom+Round((Step0+Step*(Step1-Step0) div abs(Step1-Step0))
    5252         *(yTo-yFrom)/nStep);
    5253       end
    5254     else begin xMoving:=xFrom; yMoving:=yFrom; end;
    5255     NoMap.PaintUnit(xMoving-xMin,yMoving-yMin,UnitInfo,0);
    5256     PaintBufferToScreen(xMin,yMin,xRange,yRange);
    5257 
    5258     SliceCount:=0;
    5259     Ticks:=Ticks0;
    5260     repeat
    5261       if (SliceCount=0) or ((Ticks-Ticks0)*12000 *(SliceCount+1)
    5262         div SliceCount<MoveTime*PerfFreq)  then
    5263         begin
    5264         if not idle or (GameMode=cMovie) then
    5265           Application.ProcessMessages;
    5266         Sleep(1);
    5267         inc(SliceCount)
    5268         end;
    5269       QueryPerformanceCounter(Ticks);
    5270     until (Ticks-Ticks0)*12000>=MoveTime*PerfFreq;
    5271     Ticks0:=Ticks
    5272     end;
    5273   end;
    5274 if Restore then
    5275   begin
    5276   BitBlt(Buffer.Canvas.Handle,0,0,xRange,yRange,offscreen.Canvas.Handle,xMin,
    5277     yMin,SRCCOPY);
    5278   PaintBufferToScreen(xMin,yMin,xRange,yRange);
    5279   end;
    5280 BlinkTime:=-1;
    5281 Timer1.Enabled:=true;
    5282 end;
    5283 
    5284 procedure TMainScreen.MoveToLoc(Loc: integer; CheckSuicide: boolean);
    5285 // path finder: move focused unit to loc, start multi-turn goto if too far
    5286 var
    5287 uix,i,MoveOptions,NextLoc,MoveResult: integer;
    5288 MoveAdviceData: TMoveAdviceData;
    5289 StopReason: (None, Arrived, Dead, NoTime, EnemySpotted, MoveError);
    5290 begin
    5291 if MyUn[UnFocus].Job>jNone then
    5292   Server(sStartJob+jNone shl 4,me,UnFocus,nil^);
    5293 if GetMoveAdvice(UnFocus,Loc,MoveAdviceData)>=rExecuted then
    5294   begin
    5295   uix:=UnFocus;
    5296   StopReason:=None;
    5297   repeat
    5298     for i:=0 to MoveAdviceData.nStep-1 do
    5299       begin
    5300       if i=MoveAdviceData.nStep-1 then MoveOptions:=muAutoNext
    5301       else MoveOptions:=0;
    5302       NextLoc:=dLoc(MyUn[uix].Loc,MoveAdviceData.dx[i],MoveAdviceData.dy[i]);
    5303       if (NextLoc=Loc)
    5304         or (Loc=maNextCity) and (MyMap[NextLoc] and fCity<>0) then
    5305         StopReason:=Arrived;
    5306       if not CheckSuicide and (NextLoc=Loc) then
    5307         MoveOptions:=MoveOptions or muNoSuicideCheck;
    5308       MoveResult:=MoveUnit(MoveAdviceData.dx[i],MoveAdviceData.dy[i],MoveOptions);
    5309       if MoveResult<rExecuted then StopReason:=MoveError
    5310       else if MoveResult and rUnitRemoved<>0 then StopReason:=Dead
    5311       else if (StopReason=None) and (MoveResult and rEnemySpotted<>0) then
    5312         StopReason:=EnemySpotted;
    5313       if StopReason<>None then break;
    5314       end;
    5315     if (StopReason=None) and ((MoveAdviceData.nStep<25)
    5316       or (MyRO.Wonder[woShinkansen].EffectiveOwner<>me)) then
    5317       StopReason:=NoTime;
    5318     if StopReason<>None then break;
    5319     if GetMoveAdvice(UnFocus,Loc,MoveAdviceData)<rExecuted then
    5320       begin assert(false); break end
    5321   until false;
    5322 
    5323   case StopReason of
    5324     None: assert(false);
    5325     Arrived: MyUn[uix].Status:=MyUn[uix].Status and ($FFFF-usGoto);
    5326     Dead: if UnFocus<0 then NextUnit(UnStartLoc,false);
    5327     else
    5328       begin // multi-turn goto
    5329       if Loc=maNextCity then
    5330         MyUn[uix].Status:=MyUn[uix].Status and ($FFFF-usStay-usRecover) or usGoto +$7FFF shl 16
    5331       else MyUn[uix].Status:=MyUn[uix].Status and ($FFFF-usStay-usRecover) or usGoto +Loc shl 16;
    5332       PaintLoc(MyUn[uix].Loc);
    5333       if (StopReason=NoTime) and (UnFocus=uix) then
    5334         begin
    5335         MyUn[uix].Status:=MyUn[uix].Status and not usWaiting;
    5336         NextUnit(UnStartLoc,true)
    5337         end;
    5338       end
    5339     end
    5340   end
    5341 end;
    5342 
    5343 procedure TMainScreen.PanelBoxMouseDown(Sender:TObject;
    5344   Button:TMouseButton;Shift:TShiftState;x,y:integer);
    5345 var
    5346 i,xMouse,MouseLoc,p1: integer;
    5347 begin
    5348 if GameMode=cMovie then
    5349   exit;
    5350 
    5351 if Button=mbLeft then
    5352   begin
    5353   if (x>=xMini+2) and (y>=yMini+2) and (x<xMini+2+2*G.lx) and (y<yMini+2+G.ly) then
    5354     if ssShift in Shift then
    5355       begin
    5356       xMouse:=(xwMini+(x-(xMini+2)+MapWidth div (xxt*2)+G.lx) div 2) mod G.lx;
    5357       MouseLoc:=xMouse+G.lx*(y-(yMini+2));
    5358       if MyMap[MouseLoc] and fTerrain<>fUNKNOWN then
    5359         begin
    5360         p1:=MyRO.Territory[MouseLoc];
    5361         if (p1=me) or (p1>=0) and (MyRO.Treaty[p1]>=trNone) then
    5362         NatStatDlg.ShowNewContent(wmPersistent, p1);
    5363         end
    5364       end
    5365     else
    5366       begin
    5367       if CityDlg.Visible then CityDlg.Close;
    5368       if UnitStatDlg.Visible then UnitStatDlg.Close;
    5369       Tracking:=true;
    5370       PanelBoxMouseMove(Sender,Shift+[ssLeft],x,y);
    5371       end
    5372   else if (ClientMode<>cEditMap) and (x>=ClientWidth-xPalace) and (y>=yPalace)
    5373     and (x<ClientWidth-xPalace+xSizeBig) and (y<yPalace+ySizeBig) then
    5374     begin
    5375     InitPopup(StatPopup);       
    5376     if FullScreen then
    5377       StatPopup.Popup(Left+ClientWidth-xPalace+xSizeBig+2,
    5378         Top+ClientHeight-PanelHeight+yPalace-1)
    5379     else StatPopup.Popup(Left+ClientWidth-xPalace+6,
    5380       Top+ClientHeight-PanelHeight+yPalace+ySizeBig+GetSystemMetrics(SM_CYCAPTION)+3)
    5381     end
    5382 (*  else if (x>=xAdvisor-3) and (y>=yAdvisor-3)
    5383     and (x<xAdvisor+16+3) and (y<yAdvisor+16+3) and HaveStrategyAdvice then
    5384     AdviceBtnClick*)
    5385   else if (x>=xTroop+1) and (y>=yTroop+1)
    5386     and (x<xTroop+TrRow*TrPitch) and (y<=yTroop+55) then
    5387     begin
    5388     i:=(x-xTroop-1) div TrPitch;
    5389     if trix[i]>=0 then
    5390       if ClientMode=cEditMap then begin BrushType:=trix[i]; PanelPaint end
    5391       else if (TroopLoc>=0) then
    5392         if MyMap[TroopLoc] and fOwned<>0 then
    5393           begin
    5394           if ssShift in Shift then
    5395             UnitStatDlg.ShowNewContent_OwnModel(wmPersistent, MyUn[trix[i]].mix)
    5396           else if not supervising and (ClientMode<scContact)
    5397             and (x-xTroop-1-i*TrPitch>=60-20) and (y>=yTroop+35)
    5398             and ((MyUn[trix[i]].Job>jNone)
    5399               or (MyUn[trix[i]].Status and (usStay or usRecover or usGoto)<>0)) then
    5400             begin // wake up
    5401             MyUn[trix[i]].Status:=MyUn[trix[i]].Status
    5402               and ($FFFF-usStay-usRecover-usGoto-usEnhance) or usWaiting;
    5403             if MyUn[trix[i]].Job>jNone then
    5404               Server(sStartJob+jNone shl 4,me,trix[i],nil^);
    5405             if (UnFocus<0) and not CityDlg.Visible then
    5406               begin
    5407               SetUnFocus(trix[i]);
    5408               SetTroopLoc(MyUn[trix[i]].Loc);
    5409               FocusOnLoc(TroopLoc,flRepaintPanel)
    5410               end
    5411             else
    5412               begin
    5413               if CityDlg.Visible and (CityDlg.RestoreUnFocus<0) then
    5414                 CityDlg.RestoreUnFocus:=trix[i];
    5415               PanelPaint;
    5416               end
    5417             end
    5418           else if (ClientMode<scContact) then
    5419             begin
    5420             if supervising then
    5421               UnitStatDlg.ShowNewContent_OwnUnit(wmPersistent, trix[i])
    5422             else if CityDlg.Visible then
    5423               begin
    5424               CityDlg.CloseAction:=None;
    5425               CityDlg.Close;
    5426               SumCities(TaxSum,ScienceSum);
    5427               SetUnFocus(trix[i]);
    5428               end
    5429             else
    5430               begin
    5431               DestinationMarkON:=false;
    5432               PaintDestination;
    5433               UnFocus:=trix[i];
    5434               UnStartLoc:=TroopLoc;
    5435               BlinkTime:=0;
    5436               BlinkOn:=false;
    5437               PaintLoc(TroopLoc);
    5438               end;
    5439             if UnFocus>=0 then
    5440               begin
    5441               UnitInfoBtn.Visible:=true;
    5442               UnitBtn.Visible:=true;
    5443               TurnComplete:=false;
    5444               EOT.ButtonIndex:=eotGray;
    5445               end;
    5446             CheckTerrainBtnVisible;
    5447             PanelPaint;
    5448             end
    5449           end
    5450         else if Server(sGetUnits,me,TroopLoc,TrCnt)>=rExecuted then
    5451           if ssShift in Shift then
    5452             UnitStatDlg.ShowNewContent_EnemyModel(wmPersistent,
    5453               MyRO.EnemyUn[MyRO.nEnemyUn+trix[i]].emix) // model info
    5454           else UnitStatDlg.ShowNewContent_EnemyUnit(wmPersistent,
    5455             MyRO.nEnemyUn+trix[i]); // unit info
    5456     end
    5457   end
    5458 end;
    5459 
    5460 procedure TMainScreen.SetTroopLoc(Loc:integer);
    5461 var
    5462 trixFocus,uix,uixDefender: integer;
    5463 Prio: boolean;
    5464 begin
    5465 TroopLoc:=Loc;
    5466 TrRow:=(xRightPanel+10-xTroop-GetSystemMetrics(SM_CXVSCROLL)-19) div TrPitch;
    5467 TrCnt:=0;
    5468 trixFocus:=-1;
    5469 if ClientMode=cEditMap then TrCnt:=nBrushTypes
    5470 else if (Loc>=0) and (MyMap[Loc] and fUnit<>0) then
    5471   if MyMap[Loc] and fOwned<>0 then
    5472     begin // count own units here
    5473     Server(sGetDefender,me,TroopLoc,uixDefender);
    5474     for Prio:=true downto false do
    5475       for uix:=0 to MyRO.nUn-1 do
    5476         if ((uix=uixDefender)=Prio) and (MyUn[uix].Loc=Loc) then
    5477           begin
    5478           if uix=UnFocus then trixFocus:=TrCnt;
    5479           inc(TrCnt);
    5480           end
    5481     end
    5482   else // count enemy units here
    5483     Server(sGetUnits,me,Loc,TrCnt);
    5484 if TrCnt=0 then InitPVSB(sb,0,1)
    5485 else
    5486   begin
    5487   InitPVSB(sb,(TrCnt+TrRow-1) div TrRow-1,1);
    5488   with sb.si do if (nMax>=integer(nPage)) and (trixFocus>=0) then
    5489     begin
    5490     sb.si.npos:=trixFocus div TrRow;
    5491     sb.si.FMask:=SIF_POS;
    5492     SetScrollInfo(sb.h,SB_CTL,sb.si,true);
    5493     end
    5494   end
    5495 end;
    5496 
    5497 (*procedure TMainScreen.ShowMoveHint(ToLoc: integer; Force: boolean = false);
    5498 var
    5499 Step,Loc,x0,y0,xs,ys: integer;
    5500 Info: string;
    5501 InfoSize: TSize;
    5502 MoveAdvice: TMoveAdviceData;
    5503 begin
    5504 if (ToLoc<0) or (ToLoc>=G.lx*G.ly)
    5505   or (UnFocus<0) or (MyUn[UnFocus].Loc=ToLoc) then
    5506   ToLoc:=-1
    5507 else
    5508   begin
    5509   MoveAdvice.ToLoc:=ToLoc;
    5510   MoveAdvice.MoreTurns:=0;
    5511   MoveAdvice.MaxHostile_MovementLeft:=MyUn[UnFocus].Health-50;
    5512   if Server(sGetMoveAdvice,me,UnFocus,MoveAdvice)<rExecuted then
    5513     ToLoc:=-1
    5514   end;
    5515 if (ToLoc=MoveHintToLoc) and not Force then exit;
    5516 if (ToLoc<>MoveHintToLoc) and (MoveHintToLoc>=0) then
    5517   begin invalidate; update end; // clear old hint from screen
    5518 MoveHintToLoc:=ToLoc;
    5519 if ToLoc<0 then exit;
    5520 
    5521 with canvas do
    5522   begin
    5523   Pen.Color:=$80C0FF;
    5524   Pen.Width:=3;
    5525   Loc:=MyUn[UnFocus].Loc;
    5526   for Step:=0 to MoveAdvice.nStep do
    5527     begin
    5528     y0:=(Loc+G.lx*1024) div G.lx -1024;
    5529     x0:=(Loc+(y0 and 1+G.lx*1024) div 2) mod G.lx;
    5530     xs:=(x0-xw)*66+y0 and 1*33-G.lx*66;
    5531     while abs(2*(xs+G.lx*66)-MapWidth)<abs(2*xs-MapWidth) do
    5532       inc(xs,G.lx*66);
    5533     ys:=(y0-yw)*16;
    5534     if Step=0 then moveto(xs+33,ys+16)
    5535     else lineto(xs+33,ys+16);
    5536     if Step<MoveAdvice.nStep then
    5537       Loc:=dLoc(Loc,MoveAdvice.dx[Step],MoveAdvice.dy[Step]);
    5538     end;
    5539   Brush.Color:=$80C0FF;
    5540   Info:=' '+inttostr(88)+' ';
    5541   InfoSize:=TextExtent(Info);
    5542   TextOut(xs+33-InfoSize.cx div 2, ys+16-InfoSize.cy div 2, Info);
    5543   Brush.Style:=bsClear;
    5544   end
    5545 end;*)
    5546 
    5547 procedure TMainScreen.SetDebugMap(p: integer);
    5548 begin
    5549 IsoEngine.pDebugMap:=p;
    5550 IsoEngine.Options:=IsoEngine.Options and not (1 shl moLocCodes);
    5551 mLocCodes.Checked:=false;
    5552 MapValid:=false;
    5553 MainOffscreenPaint;
    5554 end;
    5555 
    5556 procedure TMainScreen.SetViewpoint(p: integer);
    5557 var
    5558 i: integer;
    5559 begin
    5560 if supervising and (G.RO[0].Turn>0)
    5561   and ((p=0) or (1 shl p and G.RO[0].Alive<>0)) then
    5562   begin
    5563   for i:=0 to Screen.FormCount-1 do
    5564     if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    5565       Screen.Forms[i].Close; // close windows
    5566   ItsMeAgain(p);
    5567   SumCities(TaxSum,ScienceSum);
    5568   for i:=0 to MyRO.nModel-1 do
    5569     if Tribe[me].ModelPicture[i].HGr=0 then
    5570       InitMyModel(i,true);
    5571 
    5572   SetTroopLoc(-1);
    5573   PanelPaint;
    5574   MapValid:=false;
    5575   PaintAllMaps;
    5576   end
    5577 end;
    5578 
    5579 procedure TMainScreen.FormKeyDown(Sender:TObject;var Key:word;
    5580   Shift:TShiftState);
    5581 
    5582   procedure MenuClick_Check(Popup: TPopupMenu; Item: TMenuItem);
    5583   begin
    5584   InitPopup(Popup);
    5585   if Item.Visible and Item.Enabled then MenuClick(Item);
    5586   end;
    5587 
    5588 var
    5589 dx,dy: integer;
    5590 time0,time1: int64;
    5591 begin
    5592 if GameMode=cMovie then
    5593   begin
    5594   case Key of
    5595     VK_F4: MenuClick_Check(StatPopup,mScienceStat);
    5596     VK_F6: MenuClick_Check(StatPopup,mDiagram);
    5597     VK_F7: MenuClick_Check(StatPopup,mWonders);
    5598     VK_F8: MenuClick_Check(StatPopup,mShips);
    5599     end;
    5600   exit;
    5601   end;
    5602 
    5603 if not idle then exit;
    5604 
    5605 if ClientMode=cEditMap then
    5606   begin
    5607   if Shift=[ssCtrl] then
    5608     case char(Key) of
    5609 (*      'A':
    5610         begin // auto symmetry
    5611         Server($7F0,me,0,nil^);
    5612         MapValid:=false;
    5613         PaintAll;
    5614         end;
    5615       'B':
    5616         begin // land mass
    5617         dy:=0;
    5618         for dx:=G.lx to G.lx*(G.ly-1)-1 do
    5619           if MyMap[dx] and fTerrain>=fGrass then inc(dy);
    5620         dy:=dy
    5621         end;*)
    5622       'Q':MenuClick(mResign);
    5623       'R':MenuClick(mRandomMap);
    5624       end
    5625   else if Shift=[] then
    5626     case char(Key) of
    5627       char(VK_F1): MenuClick(mHelp);
    5628       end;
    5629   exit;
    5630   end;
    5631 
    5632 if Shift=[ssAlt] then
    5633   case char(Key) of
    5634     '0': SetDebugMap(-1);
    5635     '1'..'9': SetDebugMap(ord(Key)-48);
    5636     end
    5637 else if Shift=[ssCtrl] then
    5638   case char(Key) of
    5639     'J':MenuClick(mJump);
    5640     'K':mShowClick(mDebugMap);
    5641     'L':mShowClick(mLocCodes);
    5642     'M':if LogDlg.Visible then LogDlg.Close else LogDlg.Show;
    5643     'N':mNamesClick(mNames);
    5644     'Q':MenuClick_Check(GamePopup,mResign);
    5645     'R':MenuClick(mRun);
    5646     '0'..'9':
    5647        begin
    5648        if ord(Key)-48=me then
    5649          SetViewpoint(0)
    5650        else SetViewpoint(ord(Key)-48);
    5651        end;
    5652     ' ':
    5653       begin // test map repaint time
    5654       QueryPerformanceCounter(time0);
    5655       MapValid:=false;
    5656       MainOffscreenPaint;
    5657       QueryPerformanceCounter(time1);
    5658       SimpleMessage(Format('Map repaint time: %.3f ms',[{$IFDEF VER100}(time1.LowPart-time0.LowPart)
    5659         {$ELSE}(time1-time0){$ENDIF}*1000.0/PerfFreq]));
    5660       end
    5661     end
    5662 else if Shift=[] then
    5663   case char(Key) of
    5664     char(VK_F1): MenuClick(mHelp);
    5665     char(VK_F2):MenuClick_Check(StatPopup,mUnitStat);
    5666     char(VK_F3):MenuClick_Check(StatPopup,mCityStat);
    5667     char(VK_F4):MenuClick_Check(StatPopup,mScienceStat);
    5668     char(VK_F5):MenuClick_Check(StatPopup,mEUnitStat);
    5669     char(VK_F6):MenuClick_Check(StatPopup,mDiagram);
    5670     char(VK_F7):MenuClick_Check(StatPopup,mWonders);
    5671     char(VK_F8):MenuClick_Check(StatPopup,mShips);
    5672     char(VK_F9):MenuClick_Check(StatPopup,mNations);
    5673     char(VK_F10):MenuClick_Check(StatPopup,mEmpire);
    5674     char(VK_ADD): EndTurn;
    5675     '1':MapBtnClick(MapBtn0);
    5676     '2':MapBtnClick(MapBtn1);
    5677     '3':MapBtnClick(MapBtn4);
    5678     '4':MapBtnClick(MapBtn5);
    5679     '5':MapBtnClick(MapBtn6);
    5680     'T':MenuClick(mTechTree);
    5681     'W':MenuClick(mWait);
    5682     end;
    5683 
    5684 if UnFocus>=0 then
    5685   if Shift=[ssCtrl] then
    5686     case char(Key) of
    5687       'C':MenuClick_Check(UnitPopup,mCancel);
    5688       'D':MenuClick(mDisband);
    5689       'P':MenuClick_Check(UnitPopup,mPillage);
    5690       'T':MenuClick_Check(UnitPopup,mSelectTransport);
    5691       end
    5692   else if Shift=[] then
    5693     case char(Key) of
    5694       ' ':MenuClick(mNoOrders);
    5695       'A':MenuClick_Check(TerrainPopup,mAirBase);
    5696       'B':MenuClick_Check(UnitPopup,mCity);
    5697       'C':MenuClick(mCentre);
    5698       'E':
    5699         begin
    5700         InitPopup(TerrainPopup);
    5701         if mEnhance.Visible and mEnhance.Enabled then MenuClick(mEnhance)
    5702         else MenuClick(mEnhanceDef)
    5703         end;
    5704       'F':MenuClick_Check(TerrainPopup,mFort);
    5705       'G':MenuClick_Check(UnitPopup,mGoOn);
    5706       'H':MenuClick_Check(UnitPopup,mHome);
    5707       'I':
    5708         if JobTest(UnFocus,jFarm,[eTreaty]) then MenuClick(mFarm)
    5709         else if JobTest(UnFocus,jClear,[eTreaty]) then MenuClick(mClear)
    5710         else MenuClick_Check(TerrainPopup,mIrrigation);
    5711       'L':MenuClick_Check(UnitPopup,mLoad);
    5712       'M':
    5713         if JobTest(UnFocus,jAfforest,[eTreaty]) then MenuClick(mAfforest)
    5714         else MenuClick_Check(TerrainPopup,mMine);
    5715       'N':MenuClick_Check(TerrainPopup,mCanal);
    5716       'O':MenuClick_Check(TerrainPopup,mTrans);
    5717       'P':MenuClick_Check(TerrainPopup,mPollution);
    5718       'R':
    5719         if JobTest(UnFocus,jRR,[eTreaty]) then MenuClick(mRR)
    5720         else MenuClick_Check(TerrainPopup,mRoad);
    5721       'S':MenuClick(mStay);
    5722       'U':MenuClick_Check(UnitPopup,mUnload);
    5723       'V':MenuClick_Check(UnitPopup,mRecover);
    5724       'Z':MenuClick_Check(UnitPopup,mUtilize);
    5725       #33..#40,#97..#100,#102..#105:
    5726         begin {arrow keys}
    5727         DestinationMarkON:=false;
    5728         PaintDestination;
    5729         MyUn[UnFocus].Status:=MyUn[UnFocus].Status
    5730           and ($FFFF-usStay-usRecover-usGoto-usEnhance) or usWaiting;
    5731         case Key of
    5732           VK_NUMPAD1,VK_END: begin dx:=-1; dy:=1 end;
    5733           VK_NUMPAD2,VK_DOWN: begin dx:=0; dy:=2 end;
    5734           VK_NUMPAD3,VK_NEXT: begin dx:=1; dy:=1 end;
    5735           VK_NUMPAD4,VK_LEFT: begin dx:=-2; dy:=0 end;
    5736           VK_NUMPAD6,VK_RIGHT: begin dx:=2; dy:=0 end;
    5737           VK_NUMPAD7,VK_HOME: begin dx:=-1; dy:=-1 end;
    5738           VK_NUMPAD8,VK_UP: begin dx:=0; dy:=-2 end;
    5739           VK_NUMPAD9,VK_PRIOR: begin dx:=1; dy:=-1 end;
    5740           end;
    5741         MoveUnit(dx,dy,muAutoNext)
    5742         end;
    5743       end
    5744 end;
    5745 
    5746 procedure TMainScreen.MenuClick(Sender:TObject);
    5747 
    5748   function DoJob(j0:integer): integer;
    5749   var
    5750   Loc0, Movement0: integer;
    5751   begin
    5752   with MyUn[UnFocus] do
    5753     begin
    5754     DestinationMarkON:=false;
    5755     PaintDestination;
    5756     Loc0:=Loc;
    5757     Movement0:=Movement;
    5758     if j0<0 then result:=ProcessEnhancement(UnFocus,MyData.EnhancementJobs) // terrain enhancement
    5759     else result:=Server(sStartJob+j0 shl 4,me,UnFocus,nil^);
    5760     if result>=rExecuted then
    5761       begin
    5762       if result=eDied then UnFocus:=-1;
    5763       PaintLoc(Loc0);
    5764       if UnFocus>=0 then
    5765         begin
    5766         if (j0<0) and (result<>eJobDone) then // multi-turn terrain enhancement
    5767           Status:=Status and ($FFFF-usStay-usRecover-usGoto) or usEnhance
    5768         else Status:=Status and ($FFFF-usStay-usRecover-usGoto-usEnhance);
    5769         if (Job<>jNone) or (Movement0<100) then
    5770           begin
    5771           Status:=Status and not usWaiting;
    5772           NextUnit(UnStartLoc,true);
    5773           end
    5774         else PanelPaint
    5775         end
    5776       else NextUnit(UnStartLoc,true);
    5777       end
    5778     end;
    5779   case result of
    5780     eNoBridgeBuilding: SoundMessage(Phrases.Lookup('NOBB'),'INVALID');
    5781     eNoCityTerrain: SoundMessage(Phrases.Lookup('NOCITY'),'INVALID');
    5782     eTreaty: SoundMessage(Tribe[MyRO.Territory[Loc0]].TPhrase('PEACE_NOWORK'),'NOMOVE_TREATY');
    5783     else if result<rExecuted then Play('INVALID')
    5784     end
    5785   end;
    5786 
    5787 var
    5788 i,uix,NewFocus,Loc0,OldMaster,Destination,cix,cixOldHome,ServerResult: integer;
    5789 AltGovs,changed: boolean;
    5790 QueryText: string;
    5791 
    5792 begin
    5793 if Sender=mResign then
    5794   if ClientMode=cEditMap then
    5795     begin
    5796     if Edited then
    5797       begin
    5798       QueryText:=Phrases.Lookup('MAP_CLOSE');
    5799       case SimpleQuery(mkYesNoCancel,QueryText,'') of
    5800         mrIgnore:
    5801           Server(sAbandonMap,me,0,nil^);
    5802         mrOK:
    5803           Server(sSaveMap,me,0,nil^);
    5804         end
    5805       end
    5806     else Server(sAbandonMap,me,0,nil^)
    5807     end
    5808   else
    5809     begin
    5810     if Server(sGetGameChanged,0,0,nil^)=eOK then
    5811       begin
    5812       QueryText:=Phrases.Lookup('RESIGN');
    5813       case SimpleQuery(mkYesNoCancel,QueryText,'') of
    5814         mrIgnore:
    5815           Server(sResign,0,0,nil^);
    5816         mrOK:
    5817           Server(sBreak,0,0,nil^)
    5818         end
    5819       end
    5820     else Server(sResign,0,0,nil^)
    5821     end
    5822 else if Sender=mEmpire then
    5823   RatesDlg.ShowNewContent(wmPersistent)
    5824 else if Sender=mRevolution then
    5825   begin
    5826   AltGovs:=false;
    5827   for i:=2 to nGov-1 do
    5828     if (GovPreq[i]<>preNA) and ((GovPreq[i]=preNone)
    5829       or (MyRO.Tech[GovPreq[i]]>=tsApplicable)) then
    5830       AltGovs:=true;
    5831 
    5832   if not AltGovs then
    5833     SoundMessage(Phrases.Lookup('NOALTGOVS'),'MSG_DEFAULT')
    5834   else
    5835     begin
    5836     changed:=false;
    5837     if MyRO.Happened and phChangeGov<>0 then
    5838       begin
    5839       ModalSelectDlg.ShowNewContent(wmModal,kGov);
    5840       if ModalSelectDlg.result>=0 then
    5841         begin
    5842         Play('NEWGOV');
    5843         Server(sSetGovernment,me,ModalSelectDlg.result,nil^);
    5844         CityOptimizer_BeginOfTurn;
    5845         changed:=true;
    5846         end
    5847       end
    5848     else with MessgExDlg do
    5849       begin // revolution!
    5850       MessgText:=Tribe[me].TPhrase('REVOLUTION');
    5851       Kind:=mkYesNo;
    5852       IconKind:=mikPureIcon;
    5853       IconIndex:=72; // anarchy palace
    5854       ShowModal;
    5855       if ModalResult=mrOK then
    5856         begin
    5857         Play('REVOLUTION');
    5858         Server(sRevolution,me,0,nil^);
    5859         changed:=true;
    5860         if NatStatDlg.Visible then NatStatDlg.Close;
    5861         if CityDlg.Visible then CityDlg.Close;
    5862         end
    5863       end;
    5864     if changed then
    5865       UpdateViews(true);
    5866     end
    5867   end
    5868 else if Sender=mWebsite then
    5869   ShellExecute(Handle,'open','http://c-evo.org','','',SW_SHOWNORMAL)
    5870 else if Sender=mRandomMap then
    5871   begin
    5872   if not Edited or (SimpleQuery(mkYesNo,Phrases.Lookup('MAP_RANDOM'),'')=mrOK) then
    5873     begin
    5874     Server(sRandomMap,me,0,nil^);
    5875     Edited:=true;
    5876     MapValid:=false;
    5877     PaintAllMaps;
    5878     end
    5879   end
    5880 else if Sender=mJump then
    5881   begin
    5882   if supervising then
    5883     Jump[0]:=20
    5884   else Jump[me]:=20;
    5885   EndTurn(true);
    5886   end
    5887 else if Sender=mRun then
    5888   begin
    5889   if supervising then
    5890     Jump[0]:=999999
    5891   else Jump[me]:=999999;
    5892   EndTurn(true);
    5893   end
    5894 else if Sender=mEnhanceDef then
    5895   begin
    5896   if UnFocus>=0 then
    5897     EnhanceDlg.ShowNewContent(wmPersistent, MyMap[MyUn[UnFocus].Loc] and fTerrain)
    5898   else EnhanceDlg.ShowNewContent(wmPersistent)
    5899   end
    5900 else if Sender=mCityTypes then
    5901   CityTypeDlg.ShowNewContent(wmModal) // must be modal because types are not saved before closing
    5902 else if Sender=mUnitStat then
    5903   begin
    5904   if G.Difficulty[me]>0 then
    5905     ListDlg.ShowNewContent_MilReport(wmPersistent,me)
    5906   else
    5907     begin
    5908     i:=1;
    5909     while (i<nPl) and (1 shl i and MyRO.Alive=0) do inc(i);
    5910     if i<nPl then
    5911       ListDlg.ShowNewContent_MilReport(wmPersistent,i);
    5912     end;
    5913   end
    5914 else if Sender=mEUnitStat then
    5915   begin
    5916   if MyRO.nEnemyModel>0 then
    5917     ListDlg.ShowNewContent(wmPersistent,kAllEModels);
    5918   end
    5919 else if Sender=mCityStat then
    5920   ListDlg.ShowNewContent(wmPersistent,kCities)
    5921 else if Sender=mScienceStat then
    5922   ListDlg.ShowNewContent(wmPersistent,kScience)
    5923 else if Sender=mNations then
    5924   NatStatDlg.ShowNewContent(wmPersistent)
    5925 else if Sender=mHelp then
    5926   if ClientMode=cEditMap then
    5927     HelpDlg.ShowNewContent(wmPersistent, hkText, HelpDlg.TextIndex('MAPEDIT'))
    5928   else HelpDlg.ShowNewContent(wmPersistent, hkMisc, miscMain)
    5929 else if Sender=mTechTree then
    5930   TechTreeDlg.ShowModal
    5931 else if Sender=mWonders then
    5932   WondersDlg.ShowNewContent(wmPersistent)
    5933 else if Sender=mDiagram then
    5934   DiaDlg.ShowNewContent_Charts(wmPersistent)
    5935 else if Sender=mShips then
    5936   DiaDlg.ShowNewContent_Ship(wmPersistent)
    5937 else if Sender=mWait then
    5938   begin
    5939   if UnFocus>=0 then
    5940     begin
    5941     DestinationMarkON:=false;
    5942     PaintDestination;
    5943     MyUn[UnFocus].Status:=MyUn[UnFocus].Status and ($FFFF-usStay-usRecover-usGoto-usEnhance) or usWaiting;
    5944     end;
    5945   NextUnit(-1,false);
    5946   end
    5947 else if UnFocus>=0 then with MyUn[UnFocus] do
    5948   if Sender=mGoOn then
    5949     begin
    5950     if Status shr 16=$7FFF then Destination:=maNextCity
    5951     else Destination:=Status shr 16;
    5952     Status:=Status and not (usStay or usRecover) or usWaiting;
    5953     MoveToLoc(Destination,true);
    5954     end
    5955   else if Sender=mHome then
    5956     if MyMap[Loc] and fCity<>0 then
    5957       begin
    5958       cixOldHome:=Home;
    5959       if Server(sSetUnitHome,me,UnFocus,nil^)>=rExecuted then
    5960         begin
    5961         CityOptimizer_CityChange(cixOldHome);
    5962         CityOptimizer_CityChange(Home);
    5963         UpdateViews(true);
    5964         end
    5965       else Play('INVALID');
    5966       end
    5967     else
    5968       begin
    5969       Status:=Status and not (usStay or usRecover or usEnhance);
    5970       MoveToLoc(maNextCity,true)
    5971       end
    5972   else if Sender=mCentre then begin Centre(Loc); PaintAllMaps end
    5973   else if Sender=mCity then
    5974     begin
    5975     Loc0:=Loc;
    5976     if MyMap[Loc] and fCity=0 then
    5977       begin // build city
    5978       if DoJob(jCity)=eCity then
    5979         begin
    5980         MapValid:=false;
    5981         PaintAll;
    5982         ZoomToCity(Loc0,true,chFounded);
    5983         end
    5984       end
    5985     else
    5986       begin
    5987       CityOptimizer_BeforeRemoveUnit(UnFocus);
    5988       ServerResult:=Server(sAddToCity,me,UnFocus,nil^);
    5989       if ServerResult>=rExecuted then
    5990         begin
    5991         cix:=MyRO.nCity-1;
    5992         while (cix>=0) and (MyCity[cix].Loc<>Loc0) do
    5993           dec(cix);
    5994         assert(cix>=0);
    5995         CityOptimizer_CityChange(cix);
    5996         CityOptimizer_AfterRemoveUnit; // does nothing here
    5997         SetTroopLoc(Loc0);
    5998         UpdateViews(true);
    5999         DestinationMarkON:=false;
    6000         PaintDestination;
    6001         UnFocus:=-1;
    6002         PaintLoc(Loc0);
    6003         NextUnit(UnStartLoc,true);
    6004         end
    6005       else if ServerResult=eMaxSize then
    6006         SimpleMessage(Phrases.Lookup('ADDTOMAXSIZE'));
    6007       end
    6008     end
    6009   else if Sender=mRoad then DoJob(jRoad)
    6010   else if Sender=mRR then DoJob(jRR)
    6011   else if Sender=mClear then DoJob(jClear)
    6012   else if Sender=mIrrigation then DoJob(jIrr)
    6013   else if Sender=mFarm then DoJob(jFarm)
    6014   else if Sender=mAfforest then DoJob(jAfforest)
    6015   else if Sender=mMine then DoJob(jMine)
    6016   else if Sender=mCanal then DoJob(jCanal)
    6017   else if Sender=MTrans then DoJob(jTrans)
    6018   else if Sender=mFort then DoJob(jFort)
    6019   else if Sender=mAirBase then DoJob(jBase)
    6020   else if Sender=mPollution then DoJob(jPoll)
    6021   else if Sender=mPillage then DoJob(jPillage)
    6022   else if Sender=mEnhance then DoJob(-1)
    6023   else if Sender=mStay then
    6024     begin
    6025     DestinationMarkON:=false;
    6026     PaintDestination;
    6027     Status:=Status and ($FFFF-usRecover-usGoto-usEnhance) or usStay;
    6028     if Job>jNone then
    6029       Server(sStartJob+jNone shl 4,me,UnFocus,nil^);
    6030     NextUnit(UnStartLoc,true)
    6031     end
    6032   else if Sender=mRecover then
    6033     begin
    6034     DestinationMarkON:=false;
    6035     PaintDestination;
    6036     Status:=Status and ($FFFF-usStay-usGoto-usEnhance) or usRecover;
    6037     if Job>jNone then
    6038       Server(sStartJob+jNone shl 4,me,UnFocus,nil^);
    6039     NextUnit(UnStartLoc,true)
    6040     end
    6041   else if Sender=mNoOrders then
    6042     begin
    6043     Status:=Status and not usWaiting;
    6044     NextUnit(UnStartLoc,true)
    6045     end
    6046   else if Sender=mCancel then
    6047     begin
    6048     DestinationMarkON:=false;
    6049     PaintDestination;
    6050     Status:=Status and ($FFFF-usRecover-usGoto-usEnhance);
    6051     if Job>jNone then
    6052       Server(sStartJob+jNone shl 4,me,UnFocus,nil^);
    6053     end
    6054   else if (Sender=mDisband) or (Sender=mUtilize) then
    6055     begin
    6056     if (Sender=mUtilize)
    6057       and not (Server(sRemoveUnit-sExecute,me,UnFocus,nil^)=eUtilized) then
    6058       begin
    6059       SimpleMessage(Phrases2.Lookup('SHIP_UTILIZE'));
    6060         // freight for colony ship is the only case in which the command is
    6061         // available to player though not valid
    6062       exit
    6063       end;
    6064     if (Sender=mUtilize) and (Health<100) then
    6065       if SimpleQuery(mkYesNo,Phrases.Lookup('DAMAGED_UTILIZE'),'')<>mrOK then
    6066         exit;
    6067     Loc0:=Loc;
    6068     CityOptimizer_BeforeRemoveUnit(UnFocus);
    6069     if Server(sRemoveUnit,me,UnFocus,nil^)=eUtilized then Play('CITY_UTILIZE')
    6070     else Play('DISBAND');
    6071     CityOptimizer_AfterRemoveUnit;
    6072     SetTroopLoc(Loc0);
    6073     UpdateViews(true);
    6074     DestinationMarkON:=false;
    6075     PaintDestination;
    6076     UnFocus:=-1;
    6077     PaintLoc(Loc0);
    6078     NextUnit(UnStartLoc,true);
    6079     end
    6080   else if Sender=mLoad then
    6081     begin
    6082     i:=Server(sLoadUnit,me,UnFocus,nil^);
    6083     if i>=rExecuted then
    6084       begin
    6085       if MyModel[mix].Domain=dAir then Play('MOVE_PLANELANDING')
    6086       else Play('MOVE_LOAD');
    6087       DestinationMarkON:=false;
    6088       PaintDestination;
    6089       Status:=Status and ($FFFF-usWaiting-usStay-usRecover-usGoto-usEnhance);
    6090       NextUnit(UnStartLoc,true);
    6091       end
    6092     else if i=eNoTime_Load then
    6093       if MyModel[mix].Domain=dAir then
    6094         SoundMessage(Phrases.Lookup('NOTIMELOADAIR'),'NOMOVE_TIME')
    6095       else
    6096         SoundMessage(Format(Phrases.Lookup('NOTIMELOADGROUND'),
    6097           [MovementToString(MyModel[mix].Speed)]),'NOMOVE_TIME');
    6098     end
    6099   else if Sender=mUnload then
    6100     if Master>=0 then
    6101       begin
    6102       OldMaster:=Master;
    6103       i:=Server(sUnloadUnit,me,UnFocus,nil^);
    6104       if i>=rExecuted then
    6105         begin
    6106         if MyModel[mix].Domain=dAir then Play('MOVE_PLANESTART')
    6107         else if (MyModel[MyUn[OldMaster].mix].Domain=dAir)
    6108           and (MyMap[Loc] and fCity=0) and (MyMap[Loc] and fTerImp<>tiBase) then
    6109           Play('MOVE_PARACHUTE')
    6110         else Play('MOVE_UNLOAD');
    6111         Status:=Status and not usWaiting;
    6112         if MyModel[mix].Domain<>dAir then NextUnit(Loc,true)
    6113         else PanelPaint
    6114         end
    6115       else if i=eNoTime_Load then
    6116         if MyModel[mix].Domain=dAir then
    6117           SoundMessage(Phrases.Lookup('NOTIMELOADAIR'),'NOMOVE_TIME')
    6118         else
    6119           SoundMessage(Format(Phrases.Lookup('NOTIMELOADGROUND'),
    6120             [MovementToString(MyModel[mix].Speed)]),'NOMOVE_TIME');
    6121       end
    6122     else
    6123       begin
    6124       NewFocus:=-1;
    6125       uix:=UnFocus;
    6126       for i:=1 to MyRo.nUn-1 do
    6127         begin
    6128         uix:=(uix+MyRO.nUn-1) mod MyRO.nUn;
    6129         if (MyUn[uix].Master=UnFocus)
    6130           and (MyUn[uix].Movement=integer(MyModel[MyUn[uix].mix].Speed)) then
    6131           begin
    6132           MyUn[uix].Status:=MyUn[uix].Status or usWaiting;
    6133           NewFocus:=uix
    6134           end;
    6135         end;
    6136       if NewFocus>=0 then
    6137         begin
    6138         SetUnFocus(NewFocus);
    6139         SetTroopLoc(Loc);
    6140         PanelPaint
    6141         end
    6142       end
    6143   else if Sender=mSelectTransport then
    6144     Server(sSelectTransport,me,UnFocus,nil^)
    6145 end;
    6146 
    6147 procedure TMainScreen.InitPopup(Popup: TPopupMenu);
    6148 var
    6149 i,p1,Tile,Test: integer;
    6150 NoSuper,extended,Multi,NeedSep,HaveCities: boolean;
    6151 LastSep,m: TMenuItem;
    6152 mox: ^TModel;
    6153 begin
    6154 NoSuper:=not supervising and (1 shl me and MyRO.Alive<>0);
    6155 HaveCities:=false;
    6156 for i:=0 to MyRO.nCity-1 do if MyCity[i].Loc>=0 then
    6157   begin HaveCities:=true; Break end;
    6158 if Popup=GamePopup then
    6159   begin
    6160   mTechTree.Visible:= ClientMode<>cEditMap;
    6161   mResign.Enabled:= supervising or (me=0) and (ClientMode<scContact);
    6162   mRandomMap.Visible:= (ClientMode=cEditMap)
    6163     and (Server(sMapGeneratorRequest,me,0,nil^)=eOk);
    6164   mOptions.Visible:= ClientMode<>cEditMap;
    6165   mManip.Visible:= ClientMode<>cEditMap;
    6166   if ClientMode<>cEditMap then
    6167     begin
    6168     mWaitTurn.Visible:= NoSuper;
    6169     mRep.Visible:= NoSuper;
    6170     mRepList.Visible:= NoSuper;
    6171     mRepScreens.Visible:= NoSuper;
    6172     N10.Visible:= NoSuper;
    6173     mOwnMovement.Visible:= NoSuper;
    6174     mAllyMovement.Visible:= NoSuper;
    6175     case SoundMode of
    6176       smOff: mSoundOff.Checked:=true;
    6177       smOn: mSoundOn.Checked:=true;
    6178       smOnAlt: mSoundOnAlt.Checked:=true;
    6179       end;
    6180 
    6181     for i:=0 to nTestFlags-1 do
    6182       mManip[i].Checked:= MyRO.TestFlags and (1 shl i)<>0;
    6183     mManip.Enabled:= supervising or (me=0);
    6184 
    6185     Multi:=false;
    6186     for p1:=1 to nPl-1 do
    6187       if G.RO[p1]<>nil then Multi:=true;
    6188     mEnemyMovement.Visible:=not Multi;
    6189     end;
    6190   mMacro.Visible:= NoSuper and (ClientMode<scContact);
    6191   if NoSuper and (ClientMode<scContact) then
    6192     begin
    6193     mCityTypes.Enabled:=false;
    6194     // check if city types already usefull:
    6195     if MyRO.nCity>0 then
    6196       for i:=28 to nImp-1 do
    6197         if (i<>imTrGoods) and (Imp[i].Kind=ikCommon) and (Imp[i].Preq<>preNA)
    6198           and ((Imp[i].Preq=preNone) or (MyRO.Tech[Imp[i].Preq]>=tsApplicable)) then
    6199           begin mCityTypes.Enabled:=true; Break end;
    6200     end;
    6201   mViewpoint.visible:=(ClientMode<>cEditMap) and supervising;
    6202   mViewpoint.enabled:= G.RO[0].Turn>0;
    6203   if supervising then
    6204     begin
    6205     EmptyMenu(mViewpoint);
    6206     for p1:=0 to nPl-1 do
    6207       if (p1=0) or (1 shl p1 and G.RO[0].Alive<>0) then
    6208         begin
    6209         m:=TMenuItem.Create(mViewpoint);
    6210         if p1=0 then
    6211           m.Caption:=Phrases.Lookup('SUPER')
    6212         else m.Caption:=Tribe[p1].TString(Phrases2.Lookup('BELONG'));
    6213         m.Tag:=p1;
    6214         m.OnClick:=ViewPointClick;
    6215         if p1<10 then
    6216           m.ShortCut:=Shortcut(48+p1, [ssCtrl]);
    6217         m.RadioItem:=true;
    6218         if p1=me then
    6219           m.Checked:=true;
    6220         mViewPoint.Add(m);
    6221         end
    6222     end;
    6223   mDebugMap.visible:=(ClientMode<>cEditMap) and supervising;
    6224   if supervising then
    6225     begin
    6226     EmptyMenu(mDebugMap);
    6227     for p1:=0 to nPl-1 do
    6228       if (p1=0) or (1 shl p1 and G.RO[0].Alive<>0) then
    6229         begin
    6230         m:=TMenuItem.Create(mDebugMap);
    6231         if p1=0 then
    6232           m.Caption:=Phrases2.Lookup('MENU_DEBUGMAPOFF')
    6233         else m.Caption:=Tribe[p1].TString(Phrases2.Lookup('BELONG'));
    6234         if p1=0 then
    6235           m.Tag:=-1
    6236         else m.Tag:=p1;
    6237         m.OnClick:=DebugMapClick;
    6238         if p1<10 then
    6239           m.ShortCut:=Shortcut(48+p1, [ssAlt]);
    6240         m.RadioItem:=true;
    6241         if m.Tag=IsoEngine.pDebugMap then
    6242           m.Checked:=true;
    6243         mDebugMap.Add(m);
    6244         end
    6245     end;
    6246   mSmallTiles.Checked:= xxt=33;
    6247   mNormalTiles.Checked:= xxt=48;
    6248   end
    6249 else if Popup=StatPopup then
    6250   begin
    6251   mEmpire.Visible:= NoSuper;
    6252   mEmpire.Enabled:= MyRO.Government<>gAnarchy;
    6253   mRevolution.Visible:= NoSuper;
    6254   mRevolution.Enabled:= (MyRO.Government<>gAnarchy) and (ClientMode<scContact);
    6255   mUnitStat.Enabled:= NoSuper or (MyRO.Turn>0);
    6256   mCityStat.Visible:= 1 shl me and MyRO.Alive<>0;
    6257   mCityStat.Enabled:= HaveCities;
    6258   mScienceStat.Visible:= true;
    6259   mScienceStat.Enabled:= not NoSuper or (MyRO.ResearchTech>=0)
    6260     or (MyRO.Happened and phTech<>0)
    6261     or (MyRO.Happened and phGameEnd<>0) // no researchtech in case just completed
    6262     or (MyRO.TestFlags and (tfAllTechs or tfUncover or tfAllContact)<>0);
    6263   mEUnitStat.Enabled:= MyRO.nEnemyModel>0;
    6264 {  mWonders.Enabled:= false;
    6265   for i:=0 to 27 do if MyRO.Wonder[i].CityID<>-1 then
    6266     mWonders.Enabled:=true;}
    6267   mDiagram.Enabled:= MyRO.Turn>=2;
    6268   mShips.Enabled:=false;
    6269   for p1:=0 to nPl-1 do
    6270     if MyRO.Ship[p1].Parts[spComp]+MyRO.Ship[p1].Parts[spPow]
    6271       +MyRO.Ship[p1].Parts[spHab]>0 then
    6272       mShips.Enabled:=true;
    6273   end
    6274 else if Popup=UnitPopup then
    6275   begin
    6276   mox:=@MyModel[MyUn[UnFocus].mix];
    6277   Tile:=MyMap[MyUn[UnFocus].Loc];
    6278   extended:=Tile and fCity=0;
    6279   if extended then
    6280     begin
    6281     mCity.Caption:=Phrases.Lookup('BTN_FOUND');
    6282     mHome.Caption:=Phrases.Lookup('BTN_MOVEHOME')
    6283     end
    6284   else
    6285     begin
    6286     mCity.Caption:=Phrases.Lookup('BTN_ADD');
    6287     mHome.Caption:=Phrases.Lookup('BTN_SETHOME')
    6288     end;
    6289 
    6290   extended:=extended and ((mox.Kind=mkSettler) or (mox.Kind=mkSlaves)
    6291       and (MyRO.Wonder[woPyramids].EffectiveOwner>=0))
    6292     and (MyUn[UnFocus].Master<0) and (Tile and fDeadLands=0);
    6293   if (mox.Kind=mkFreight) and (Tile and fCity<>0) and not Phrases2FallenBackToEnglish
    6294     or (Server(sRemoveUnit-sExecute,me,UnFocus,nil^)=eUtilized) then
    6295     begin
    6296     mDisband.Visible:=false;
    6297     mUtilize.Visible:=true;
    6298     if mox.Kind=mkFreight then
    6299       mUtilize.Caption:=Phrases.Lookup('UTILIZE')
    6300     else mUtilize.Caption:=Phrases.Lookup('INTEGRATE')
    6301     end
    6302   else begin mDisband.Visible:=true; mUtilize.Visible:=false end;
    6303   mGoOn.Visible:= MyUn[UnFocus].Status and (usGoto or usWaiting)=usGoto or usWaiting;
    6304   mHome.Visible:=HaveCities;
    6305   mRecover.Visible:= (MyUn[UnFocus].Health<100) and (Tile and fTerrain>=fGrass)
    6306     and ((MyRO.Wonder[woGardens].EffectiveOwner=me)
    6307       or (Tile and fTerrain<>fArctic) and (Tile and fTerrain<>fDesert))
    6308     and not ((mox.Domain=dAir) and (Tile and fCity=0) and (Tile and fTerImp<>tiBase));
    6309   mStay.Visible:= not ((mox.Domain=dAir) and (Tile and fCity=0) and (Tile and fTerImp<>tiBase));
    6310   mCity.Visible:=extended and (mox.Kind=mkSettler) or (Tile and fCity<>0)
    6311     and ((mox.Kind in [mkSettler,mkSlaves]) or (MyUn[UnFocus].Flags and unConscripts<>0));
    6312   mPillage.Visible:=(Tile and (fRoad or fRR or fCanal or fTerImp)<>0)
    6313     and (MyUn[UnFocus].Master<0) and (mox.Domain=dGround);
    6314   mCancel.Visible:=(MyUn[UnFocus].Job>jNone) or (MyUn[UnFocus].Status and (usRecover or usGoto)<>0);
    6315 
    6316   Test:=Server(sLoadUnit-sExecute,me,UnFocus,nil^);
    6317   mLoad.Visible:= (Test>=rExecuted) or (Test=eNoTime_Load);
    6318   mUnload.Visible:= (MyUn[UnFocus].Master>=0)
    6319     or (MyUn[UnFocus].TroopLoad+MyUn[UnFocus].AirLoad>0);
    6320   mSelectTransport.Visible:=
    6321     Server(sSelectTransport-sExecute,me,UnFocus,nil^)>=rExecuted;
    6322   end
    6323 else {if Popup=TerrainPopup then}
    6324   begin
    6325   mox:=@MyModel[MyUn[UnFocus].mix];
    6326   Tile:=MyMap[MyUn[UnFocus].Loc];
    6327   extended:=Tile and fCity=0;
    6328 
    6329   if (Tile and fRiver<>0) and (MyRO.Tech[adBridgeBuilding]>=tsApplicable) then
    6330     begin
    6331     mRoad.Caption:=Phrases.Lookup('BTN_BUILDBRIDGE');
    6332     mRR.Caption:=Phrases.Lookup('BTN_BUILDRRBRIDGE');
    6333     end
    6334   else
    6335     begin
    6336     mRoad.Caption:=Phrases.Lookup('BTN_BUILDROAD');
    6337     mRR.Caption:=Phrases.Lookup('BTN_BUILDRR');
    6338     end;
    6339   if Tile and fTerrain=fForest then
    6340     mClear.Caption:=Phrases.Lookup('BTN_CLEAR')
    6341   else if Tile and fTerrain=fDesert then
    6342     mClear.Caption:=Phrases.Lookup('BTN_UNDESERT')
    6343   else mClear.Caption:=Phrases.Lookup('BTN_DRAIN');
    6344 
    6345   extended:=extended and ((mox.Kind=mkSettler) or (mox.Kind=mkSlaves)
    6346       and (MyRO.Wonder[woPyramids].EffectiveOwner>=0))
    6347     and (MyUn[UnFocus].Master<0);
    6348   if extended then
    6349     begin
    6350     mRoad.Visible:= JobTest(UnFocus,jRoad,[eNoBridgeBuilding,eTreaty]);
    6351     mRR.Visible:= JobTest(UnFocus,jRR,[eNoBridgeBuilding,eTreaty]);
    6352     mClear.Visible:= JobTest(UnFocus,jClear,[eTreaty]);
    6353     mIrrigation.Visible:= JobTest(UnFocus,jIrr,[eTreaty]);
    6354     mFarm.Visible:= JobTest(UnFocus,jFarm,[eTreaty]);
    6355     mAfforest.Visible:= JobTest(UnFocus,jAfforest,[eTreaty]);
    6356     mMine.Visible:= JobTest(UnFocus,jMine,[eTreaty]);
    6357     MTrans.Visible:= JobTest(UnFocus,jTrans,[eTreaty]);
    6358     mCanal.Visible:= JobTest(UnFocus,jCanal,[eTreaty]);
    6359     mFort.Visible:= JobTest(UnFocus,jFort,[eTreaty]);
    6360     mAirBase.Visible:= JobTest(UnFocus,jBase,[eTreaty]);
    6361     mPollution.Visible:=JobTest(UnFocus,jPoll,[eTreaty]);
    6362     mEnhance.Visible:= (Tile and fDeadLands=0)
    6363       and (MyData.EnhancementJobs[MyMap[MyUn[UnFocus].Loc] and fTerrain,0]<>jNone);
    6364     end
    6365   else
    6366     begin
    6367     for i:=0 to Popup.Items.Count-1 do Popup.Items[i].Visible:=false;
    6368     end;
    6369   end;
    6370 
    6371 // set menu seperators
    6372 LastSep:=nil;
    6373 needsep:=false;
    6374 for i:=0 to Popup.Items.Count-1 do
    6375   if Popup.Items[i].Caption='-' then
    6376     begin
    6377     Popup.Items[i].Visible:=needsep;
    6378     if needsep then LastSep:=Popup.Items[i];
    6379     needsep:=false
    6380     end
    6381   else if Popup.Items[i].Visible then needsep:=true;
    6382 if (LastSep<>nil) and not NeedSep then LastSep.Visible:=false   
    6383 end;
    6384 
    6385 procedure TMainScreen.PanelBtnClick(Sender: TObject);
    6386 var
    6387 Popup: TPopupMenu;
    6388 begin
    6389 if Sender=UnitBtn then Popup:=UnitPopup
    6390 else {if Sender=TerrainBtn then} Popup:=TerrainPopup;
    6391 InitPopup(Popup);
    6392 if FullScreen then
    6393   Popup.Popup(Left+TControl(Sender).Left,Top+TControl(Sender).Top)
    6394 else Popup.Popup(Left+TControl(Sender).Left+4,Top+TControl(Sender).Top
    6395   +GetSystemMetrics(SM_CYCAPTION)+4);
    6396 end;
    6397 
    6398 procedure TMainScreen.CityClosed(Activateuix: integer; StepFocus: boolean;
    6399   SelectFocus: boolean);
    6400 begin
    6401 if supervising then
    6402   begin
    6403   SetTroopLoc(-1);
    6404   PanelPaint
    6405   end
    6406 else
    6407   begin
    6408   if Activateuix>=0 then
    6409     begin
    6410     SetUnFocus(Activateuix);
    6411     SetTroopLoc(MyUn[Activateuix].Loc);
    6412     if SelectFocus then FocusOnLoc(TroopLoc,flRepaintPanel)
    6413     else PanelPaint
    6414     end
    6415   else if StepFocus then NextUnit(TroopLoc,true)
    6416   else
    6417     begin
    6418     SetTroopLoc(-1);
    6419     PanelPaint
    6420     end
    6421   end
    6422 end;
    6423 
    6424 procedure TMainScreen.Toggle(Sender: TObject);
    6425 begin
    6426 TMenuItem(Sender).Checked:=not TMenuItem(Sender).Checked
    6427 end;
    6428 
    6429 procedure TMainScreen.PanelBoxMouseMove(Sender: TObject;
    6430   Shift: TShiftState; x, y: integer);
    6431 var
    6432 xCentre,yCentre: integer;
    6433 begin
    6434 if Tracking and (ssLeft in Shift) then
    6435   begin
    6436   if (x>=xMini+2) and (y>=yMini+2) and (x<xMini+2+2*G.lx) and (y<yMini+2+G.ly) then
    6437     begin
    6438     xCentre:=(xwMini+(x-xMini-2) div 2+G.lx div 2+MapWidth div (xxt*4)) mod G.lx;
    6439     yCentre:=(y-yMini-2);
    6440     xw:=(xCentre-MapWidth div (xxt*4)+G.lx) mod G.lx;
    6441     if ywmax<=0 then yw:=ywcenter
    6442     else
    6443       begin
    6444       yw:=(yCentre-MapHeight div (yyt*2)+1) and not 1;
    6445       if yw<0 then yw:=0
    6446       else if yw>ywmax then yw:=ywmax;
    6447       end;
    6448     BitBlt(Buffer.Canvas.Handle,0,0,G.lx*2,G.ly,Mini.Canvas.Handle,0,0,SRCCOPY);
    6449     if ywmax<=0 then
    6450       Frame(Buffer.Canvas,x-xMini-2-MapWidth div (xxt*2),0,
    6451         x-xMini-2+MapWidth div (xxt*2)-1,
    6452         G.ly-1,MainTexture.clMark,MainTexture.clMark)
    6453     else Frame(Buffer.Canvas,x-xMini-2-MapWidth div (xxt*2),yw,
    6454       x-xMini-2+MapWidth div (xxt*2)-1,
    6455       yw+MapHeight div yyt-2,MainTexture.clMark,MainTexture.clMark);
    6456     BitBlt(Panel.Canvas.Handle,xMini+2,yMini+2,G.lx*2,G.ly,Buffer.Canvas.Handle,
    6457       0,0,SRCCOPY);
    6458     MainOffscreenPaint;
    6459     RectInvalidate(xMini+2,TopBarHeight+MapHeight-overlap+yMini+2,
    6460       xMini+2+G.lx*2,TopBarHeight+MapHeight-overlap+yMini+2+G.ly);
    6461     Update;
    6462     end
    6463   end
    6464 else Tracking:=false
    6465 end;
    6466 
    6467 procedure TMainScreen.PanelBoxMouseUp(Sender: TObject;
    6468   Button: TMouseButton; Shift: TShiftState; x, y: integer);
    6469 begin
    6470 if Tracking then
    6471   begin
    6472   Tracking:=false;
    6473   xwMini:=xw; ywMini:=yw;
    6474   MiniPaint;
    6475   PanelPaint;
    6476   end
    6477 end;
    6478 
    6479 procedure TMainScreen.MapBoxMouseMove(Sender: TObject; Shift: TShiftState; x,
    6480   y: integer);
    6481 var
    6482 MouseLoc: integer;
    6483 begin
    6484 xMouse:=x; yMouse:=y;
    6485 if (ClientMode=cEditMap) and (ssLeft in Shift) and not tracking then
    6486   begin
    6487   MouseLoc:=LocationOfScreenPixel(x,y);
    6488   if MouseLoc<>BrushLoc then
    6489     MapBoxMouseDown(nil, mbLeft, Shift, x, y);
    6490   end
    6491 (*else if idle and (UnFocus>=0) then
    6492   begin
    6493   qx:=(xMouse*32+yMouse*66+16*66) div(32*66)-1;
    6494   qy:=(yMouse*66-xMouse*32-16*66+2000*33*32) div(32*66)-999;
    6495   MouseLoc:=(xw+(qx-qy+2048) div 2-1024+G.lx) mod G.lx+G.lx*(yw+qx+qy);
    6496   ShowMoveHint(MouseLoc);
    6497   end*)
    6498 end;
    6499 
    6500 procedure TMainScreen.mShowClick(Sender: TObject);
    6501 begin
    6502 TMenuItem(Sender).Checked:=not TMenuItem(Sender).Checked;
    6503 SetMapOptions;
    6504 MapValid:=false;
    6505 PaintAllMaps;
    6506 end;
    6507 
    6508 procedure TMainScreen.mNamesClick(Sender: TObject);
    6509 var
    6510 p1: integer;
    6511 begin
    6512 mNames.Checked:=not mNames.Checked;
    6513 GenerateNames:=mNames.Checked;
    6514 for p1:=0 to nPl-1 do if Tribe[p1]<>nil then
    6515   if GenerateNames then Tribe[p1].NumberName:=-1
    6516   else Tribe[p1].NumberName:=p1;
    6517 MapValid:=false;
    6518 PaintAll;
    6519 end;
    6520 
    6521 function TMainScreen.IsPanelPixel(x,y: integer): boolean;
    6522 begin
    6523 result:= (y>=TopBarHeight+MapHeight)
    6524   or (y>=ClientHeight-PanelHeight) and ((x<xMidPanel) or (x>=xRightPanel))
    6525 end;
    6526 
    6527 procedure TMainScreen.FormMouseDown(Sender: TObject; Button: TMouseButton;
    6528   Shift: TShiftState; x, y: integer);
    6529 begin
    6530 if idle then
    6531   if (x<40) and (y<40) then
    6532     begin
    6533     if GameMode<>cMovie then
    6534       begin
    6535       InitPopup(GamePopup);
    6536       if FullScreen then
    6537         GamePopup.Popup(Left,Top+TopBarHeight-1)
    6538       else GamePopup.Popup(Left+4,Top+GetSystemMetrics(SM_CYCAPTION)+4+TopBarHeight-1);
    6539       end
    6540     end
    6541   else if IsPanelPixel(x,y) then
    6542     PanelBoxMouseDown(Sender,Button,Shift,x,y-(ClientHeight-PanelHeight))
    6543   else if (y>=TopBarHeight) and (x>=MapOffset) and (x<MapOffset+MapWidth) then
    6544     MapBoxMouseDown(Sender,Button,Shift,x-MapOffset,y-TopBarHeight)
    6545 end;
    6546 
    6547 procedure TMainScreen.FormMouseMove(Sender: TObject; Shift: TShiftState; x,
    6548   y: integer);
    6549 begin
    6550 if idle then
    6551   if IsPanelPixel(x,y) then
    6552     PanelBoxMouseMove(Sender,Shift,x,y-(ClientHeight-PanelHeight))
    6553   else if (y>=TopBarHeight) and (x>=MapOffset) and (x<MapOffset+MapWidth) then
    6554     MapBoxMouseMove(Sender,Shift,x-MapOffset,y-TopBarHeight);
    6555 end;
    6556 
    6557 procedure TMainScreen.FormMouseUp(Sender: TObject; Button: TMouseButton;
    6558   Shift: TShiftState; x, y: integer);
    6559 begin
    6560 if idle then
    6561   PanelBoxMouseUp(Sender,Button,Shift,x,y-(ClientHeight-PanelHeight));
    6562 end;
    6563 
    6564 procedure TMainScreen.FormPaint(Sender: TObject);
    6565 begin
    6566 MainOffscreenPaint;
    6567 if (MapOffset>0) or (MapOffset+MapWidth<ClientWidth) then with canvas do
    6568   begin // pillarbox, make left and right border black
    6569   if me<0 then
    6570     brush.color:=$000000
    6571   else brush.color:=EmptySpaceColor;
    6572   if xMidPanel>MapOffset then
    6573     FillRect(Rect(0,TopBarHeight,MapOffset,TopBarHeight+MapHeight-overlap))
    6574   else
    6575     begin
    6576     FillRect(Rect(0,TopBarHeight,xMidPanel,TopBarHeight+MapHeight-overlap));
    6577     FillRect(Rect(xMidPanel,TopBarHeight,MapOffset,TopBarHeight+MapHeight));
    6578     end;
    6579   if xRightPanel<MapOffset+MapWidth then
    6580     FillRect(Rect(MapOffset+MapWidth,TopBarHeight,ClientWidth,TopBarHeight+MapHeight-overlap))
    6581   else
    6582     begin
    6583     FillRect(Rect(MapOffset+MapWidth,TopBarHeight,xRightPanel,TopBarHeight+MapHeight));
    6584     FillRect(Rect(xRightPanel,TopBarHeight,ClientWidth,TopBarHeight+MapHeight-overlap));
    6585     end;
    6586   Brush.Style:=bsClear;
    6587   end;
    6588 BitBlt(Canvas.Handle,MapOffset,TopBarHeight,MapWidth,MapHeight-overlap,offscreen.Canvas.Handle,
    6589   0,0,SRCCOPY);
    6590 BitBlt(Canvas.Handle,0,0,ClientWidth,TopBarHeight,TopBar.Canvas.Handle,
    6591   0,0,SRCCOPY);
    6592 if xMidPanel>MapOffset then
    6593   BitBlt(Canvas.Handle,xMidPanel,TopBarHeight+MapHeight-overlap,
    6594     ClientWidth div 2-xMidPanel,overlap,
    6595     offscreen.Canvas.Handle,xMidPanel-MapOffset,MapHeight-overlap,SRCCOPY)
    6596 else BitBlt(Canvas.Handle,MapOffset,TopBarHeight+MapHeight-overlap,
    6597   ClientWidth div 2-MapOffset,overlap,
    6598   offscreen.Canvas.Handle,0,MapHeight-overlap,SRCCOPY);
    6599 if xRightPanel<MapOffset+MapWidth then
    6600   BitBlt(Canvas.Handle,ClientWidth div 2,TopBarHeight+MapHeight-overlap,
    6601     xRightPanel-ClientWidth div 2,overlap,
    6602     offscreen.Canvas.Handle,ClientWidth div 2-MapOffset,MapHeight-overlap,SRCCOPY)
    6603 else BitBlt(Canvas.Handle,ClientWidth div 2,TopBarHeight+MapHeight-overlap,
    6604   MapOffset+MapWidth-ClientWidth div 2,overlap,
    6605   offscreen.Canvas.Handle,ClientWidth div 2-MapOffset,MapHeight-overlap,SRCCOPY);
    6606 BitBlt(Canvas.Handle,0,TopBarHeight+MapHeight-overlap,xMidPanel,overlap,
    6607   Panel.Canvas.Handle,0,0,SRCCOPY);
    6608 BitBlt(Canvas.Handle,xRightPanel,TopBarHeight+MapHeight-overlap,Panel.Width-xRightPanel,
    6609   overlap,Panel.Canvas.Handle,xRightPanel,0,SRCCOPY);
    6610 BitBlt(Canvas.Handle,0,TopBarHeight+MapHeight,Panel.Width,PanelHeight-overlap,
    6611   Panel.Canvas.Handle,0,overlap,SRCCOPY);
    6612 if (pLogo>=0) and (G.RO[pLogo]=nil) and (AILogo[pLogo]<>nil) then
    6613   BitBlt(Canvas.Handle, xRightPanel+10-(16+64), ClientHeight-PanelHeight, 64,64,
    6614     AILogo[pLogo].Canvas.Handle,0,0,SRCCOPY);     
    6615 end;
    6616 
    6617 procedure TMainScreen.RectInvalidate(Left,Top,Rigth,Bottom: integer);
    6618 var
    6619 r0: HRgn;
    6620 begin
    6621 r0:=CreateRectRgn(Left,Top,Rigth,Bottom);
    6622 InvalidateRgn(Handle,r0,false);
    6623 DeleteObject(r0);
    6624 end;
    6625 
    6626 procedure TMainScreen.SmartRectInvalidate(Left,Top,Rigth,Bottom: integer);
    6627 var
    6628 i: integer;
    6629 r0,r1: HRgn;
    6630 begin
    6631 r0:=CreateRectRgn(Left,Top,Rigth,Bottom);
    6632 for i:=0 to ControlCount-1 do
    6633   if not (Controls[i] is TArea) and Controls[i].Visible then
    6634     begin
    6635     with Controls[i].BoundsRect do
    6636       r1:=CreateRectRgn(Left,Top,Right,Bottom);
    6637     CombineRgn(r0,r0,r1,RGN_DIFF);
    6638     DeleteObject(r1);
    6639     end;
    6640 InvalidateRgn(Handle,r0,false);
    6641 DeleteObject(r0);
    6642 end;
    6643 
    6644 procedure TMainScreen.mRepClicked(Sender: TObject);
    6645 begin
    6646 with TMenuItem(Sender) do
    6647   begin
    6648   Checked:=not Checked;
    6649   if Checked then CityRepMask:=CityRepMask or (1 shl (Tag shr 8))
    6650   else CityRepMask:=CityRepMask and not (1 shl (Tag shr 8))
    6651   end
    6652 end;
    6653 
    6654 procedure TMainScreen.mLogClick(Sender: TObject);
    6655 begin
    6656 LogDlg.Show
    6657 end;
    6658 
    6659 procedure TMainScreen.FormShow(Sender: TObject);
    6660 begin
    6661 Timer1.Enabled:=true
    6662 end;
    6663 
    6664 procedure TMainScreen.FormClose(Sender: TObject; var Action: TCloseAction);
    6665 begin
    6666 Timer1.Enabled:=false
    6667 end;
    6668 
    6669 procedure TMainScreen.Radio(Sender: TObject);
    6670 begin
    6671 TMenuItem(Sender).Checked:=true
    6672 end;
    6673 
    6674 procedure TMainScreen.mManipClick(Sender: TObject);
    6675 var
    6676 Flag: integer;
    6677 begin
    6678 with TMenuItem(Sender) do
    6679   begin
    6680   Flag:=1 shl (Tag shr 8);
    6681   if Checked then Server(sClearTestFlag,0,Flag,nil^)
    6682   else
    6683     begin
    6684     Server(sSetTestFlag,0,Flag,nil^);
    6685     Play('CHEAT');
    6686     end;
    6687   if not supervising then
    6688     begin
    6689     if Flag=tfUncover then
    6690       begin MapValid:=false; PaintAllMaps; end
    6691     else if Flag=tfAllTechs then
    6692       TellNewModels
    6693     end
    6694   end
    6695 end;
    6696 
    6697 procedure TMainScreen.MapBtnClick(Sender: TObject);
    6698 begin
    6699 with TButtonC(Sender) do
    6700   begin
    6701   MapOptionChecked:=MapOptionChecked xor (1 shl (Tag shr 8));
    6702   SetMapOptions;
    6703   ButtonIndex:=MapOptionChecked shr (Tag shr 8) and 1 +2
    6704   end;
    6705 if Sender=MapBtn0 then
    6706   begin MiniPaint; PanelPaint end // update mini map only
    6707 else begin MapValid:=false; PaintAllMaps; end; // update main map
    6708 end;
    6709 
    6710 procedure TMainScreen.GrWallBtnDownChanged(Sender: TObject);
    6711 begin
    6712 if TButtonBase(Sender).Down then
    6713   begin
    6714   MapOptionChecked:=MapOptionChecked or (1 shl moGreatWall);
    6715   TButtonBase(Sender).Hint:='';
    6716   end
    6717 else
    6718   begin
    6719   MapOptionChecked:=MapOptionChecked and not (1 shl moGreatWall);
    6720   TButtonBase(Sender).Hint:=Phrases.Lookup('CONTROLS',-1+TButtonBase(Sender).Tag and $FF);
    6721   end;
    6722 SetMapOptions;
    6723 MapValid:=false;
    6724 PaintAllMaps;
    6725 end;
    6726 
    6727 procedure TMainScreen.BareBtnDownChanged(Sender: TObject);
    6728 begin
    6729 if TButtonBase(Sender).Down then
    6730   begin
    6731   MapOptionChecked:=MapOptionChecked or (1 shl moBareTerrain);
    6732   TButtonBase(Sender).Hint:='';
    6733   end
    6734 else
    6735   begin
    6736   MapOptionChecked:=MapOptionChecked and not (1 shl moBareTerrain);
    6737   TButtonBase(Sender).Hint:=Phrases.Lookup('CONTROLS',-1+TButtonBase(Sender).Tag and $FF);
    6738   end;
    6739 SetMapOptions;
    6740 MapValid:=false;
    6741 PaintAllMaps;
    6742 end;
    6743 
    6744 procedure TMainScreen.FormKeyUp(Sender: TObject; var Key: Word;
    6745   Shift: TShiftState);
    6746 begin
    6747 if idle and (Key=VK_APPS) then
    6748   begin
    6749   InitPopup(GamePopup);
    6750   if FullScreen then
    6751     GamePopup.Popup(Left,Top+TopBarHeight-1)
    6752   else GamePopup.Popup(Left+4,Top+GetSystemMetrics(SM_CYCAPTION)+4+TopBarHeight-1);
    6753   exit
    6754   end // windows menu button calls game menu
    6755 end;
    6756 
    6757 procedure TMainScreen.CreateUnitClick(Sender: TObject);
    6758 var
    6759 p1,mix: integer;
    6760 begin
    6761 p1:=TComponent(Sender).Tag shr 16;
    6762 mix:=TComponent(Sender).Tag and $FFFF;
    6763 if Server(sCreateUnit+p1 shl 4,me,mix,EditLoc)>=rExecuted then
    6764   PaintLoc(EditLoc);
    6765 end;
    6766 
    6767 procedure TMainScreen.mSoundOffClick(Sender: TObject);
    6768 begin
    6769 SoundMode:=smOff;
    6770 end;
    6771 
    6772 procedure TMainScreen.mSoundOnClick(Sender: TObject);
    6773 begin
    6774 SoundMode:=smOn;
    6775 end;
    6776 
    6777 procedure TMainScreen.mSoundOnAltClick(Sender: TObject);
    6778 begin
    6779 SoundMode:=smOnAlt;
    6780 end;
    6781 
    6782 {procedure TMainScreen.AdviceBtnClick;
    6783 var
    6784 OldAdviceLoc: integer;
    6785 begin
    6786 DestinationMarkON:=false;
    6787 PaintDestination;
    6788 AdvisorDlg.GiveStrategyAdvice;
    6789 OldAdviceLoc:=MainMap.AdviceLoc;
    6790 MainMap.AdviceLoc:=-1;
    6791 PaintLoc(OldAdviceLoc);
    6792 end;}
    6793 
    6794 {procedure TMainScreen.SetAdviceLoc(Loc: integer; AvoidRect: TRect);
    6795 var
    6796 OldAdviceLoc,x,y: integer;
    6797 begin
    6798 if Loc<>MainMap.AdviceLoc then
    6799   begin
    6800   if Loc>=0 then
    6801     begin // center
    6802     y:=Loc div G.lx;
    6803     x:=(Loc+G.lx - AvoidRect.Right div (2*66)) mod G.lx;
    6804     Centre(y*G.lx+x);
    6805     PaintAllMaps;
    6806     end;
    6807   OldAdviceLoc:=MainMap.AdviceLoc;
    6808   MainMap.AdviceLoc:=Loc;
    6809   PaintLoc(OldAdviceLoc);
    6810   PaintLoc(MainMap.AdviceLoc);
    6811   end;
    6812 end;}
    6813 
    6814 procedure TMainScreen.UnitInfoBtnClick(Sender: TObject);
    6815 begin
    6816 if UnFocus>=0 then
    6817   UnitStatDlg.ShowNewContent_OwnModel(wmPersistent, MyUn[UnFocus].mix)
    6818 end;
    6819 
    6820 procedure TMainScreen.ViewpointClick(Sender: TObject);
    6821 begin
    6822 SetViewpoint(TMenuItem(Sender).Tag);
    6823 end;
    6824 
    6825 procedure TMainScreen.DebugMapClick(Sender: TObject);
    6826 begin
    6827 SetDebugMap(TMenuItem(Sender).Tag);
    6828 end;
    6829 
    6830 procedure TMainScreen.mSmallTilesClick(Sender: TObject);
    6831 begin
    6832 SetTileSize(33,16);
    6833 end;
    6834 
    6835 procedure TMainScreen.mNormalTilesClick(Sender: TObject);
    6836 begin
    6837 SetTileSize(48,24);
    6838 end;
    6839 
    6840 procedure TMainScreen.SetTileSize(x,y: integer);
    6841 var
    6842 i,CenterLoc: integer;
    6843 begin
    6844 CenterLoc:=(xw+MapWidth div (xxt*4)) mod G.lx+(yw+MapHeight div (yyt*2))*G.lx;
    6845 IsoEngine.ApplyTileSize(x,y);
    6846 FormResize(nil);
    6847 Centre(CenterLoc);
    6848 PaintAllMaps;
    6849 for i:=0 to Screen.FormCount-1 do
    6850   if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg) then
    6851     TBufferedDrawDlg(Screen.Forms[i]).SmartUpdateContent(false);
    6852 end;
    6853 
    6854 procedure TMainScreen.SaveSettings;
    6855 var
    6856 i,j: integer;
    6857 Reg: TRegistry;
    6858 begin
    6859 OptionChecked:=OptionChecked and soExtraMask;
    6860 for i:=0 to ComponentCount-1 do if Components[i] is TMenuItem then
    6861   for j:=0 to nSaveOption-1 do
    6862     if TMenuItem(Components[i]).Checked
    6863       and (TMenuItem(Components[i]).Tag=SaveOption[j]) then
    6864       inc(OptionChecked,1 shl j);
    6865 
    6866 Reg:=TRegistry.Create;
    6867 Reg.OpenKey('SOFTWARE\cevo\RegVer9',true);
    6868 Reg.WriteInteger('TileWidth',xxt*2);
    6869 Reg.WriteInteger('TileHeight',yyt*2);
    6870 Reg.WriteInteger('OptionChecked', OptionChecked);
    6871 Reg.WriteInteger('MapOptionChecked', MapOptionChecked);
    6872 Reg.WriteInteger('CityReport',integer(CityRepMask));
    6873 Reg.closekey;
    6874 Reg.Free;
    6875 end;
    6876 
    6877 procedure TMainScreen.MovieSpeedBtnClick(Sender: TObject);
    6878 begin
    6879 MovieSpeed:=TButtonB(Sender).Tag shr 8;
    6880 CheckMovieSpeedBtnState;
    6881 end;
     7961      end; }
     7962
     7963    procedure TMainScreen.UnitInfoBtnClick(Sender: TObject);
     7964    begin
     7965      if UnFocus >= 0 then
     7966        UnitStatDlg.ShowNewContent_OwnModel(wmPersistent, MyUn[UnFocus].mix)
     7967    end;
     7968
     7969    procedure TMainScreen.ViewpointClick(Sender: TObject);
     7970    begin
     7971      SetViewpoint(TMenuItem(Sender).Tag);
     7972    end;
     7973
     7974    procedure TMainScreen.DebugMapClick(Sender: TObject);
     7975    begin
     7976      SetDebugMap(TMenuItem(Sender).Tag);
     7977    end;
     7978
     7979    procedure TMainScreen.mSmallTilesClick(Sender: TObject);
     7980    begin
     7981      SetTileSize(33, 16);
     7982    end;
     7983
     7984    procedure TMainScreen.mNormalTilesClick(Sender: TObject);
     7985    begin
     7986      SetTileSize(48, 24);
     7987    end;
     7988
     7989    procedure TMainScreen.SetTileSize(x, y: integer);
     7990    var
     7991      i, CenterLoc: integer;
     7992    begin
     7993      CenterLoc := (xw + MapWidth div (xxt * 4)) mod G.lx +
     7994        (yw + MapHeight div (yyt * 2)) * G.lx;
     7995      IsoEngine.ApplyTileSize(x, y);
     7996      FormResize(nil);
     7997      Centre(CenterLoc);
     7998      PaintAllMaps;
     7999      for i := 0 to Screen.FormCount - 1 do
     8000        if Screen.Forms[i].Visible and (Screen.Forms[i] is TBufferedDrawDlg)
     8001        then
     8002          TBufferedDrawDlg(Screen.Forms[i]).SmartUpdateContent(false);
     8003    end;
     8004
     8005    procedure TMainScreen.SaveSettings;
     8006    var
     8007      i, j: integer;
     8008      Reg: TRegistry;
     8009    begin
     8010      OptionChecked := OptionChecked and soExtraMask;
     8011      for i := 0 to ComponentCount - 1 do
     8012        if Components[i] is TMenuItem then
     8013          for j := 0 to nSaveOption - 1 do
     8014            if TMenuItem(Components[i]).Checked and
     8015              (TMenuItem(Components[i]).Tag = SaveOption[j]) then
     8016              inc(OptionChecked, 1 shl j);
     8017
     8018      Reg := TRegistry.Create;
     8019      Reg.OpenKey('SOFTWARE\cevo\RegVer9', true);
     8020      Reg.WriteInteger('TileWidth', xxt * 2);
     8021      Reg.WriteInteger('TileHeight', yyt * 2);
     8022      Reg.WriteInteger('OptionChecked', OptionChecked);
     8023      Reg.WriteInteger('MapOptionChecked', MapOptionChecked);
     8024      Reg.WriteInteger('CityReport', integer(CityRepMask));
     8025      Reg.closekey;
     8026      Reg.free;
     8027    end;
     8028
     8029    procedure TMainScreen.MovieSpeedBtnClick(Sender: TObject);
     8030    begin
     8031      MovieSpeed := TButtonB(Sender).Tag shr 8;
     8032      CheckMovieSpeedBtnState;
     8033    end;
    68828034
    68838035initialization
     8036
    68848037QueryPerformanceFrequency(PerfFreq);
    68858038
    68868039end.
    6887 
  • trunk/LocalPlayer/Tribes.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Tribes;
    43
     
    65
    76uses
    8   Protocol,ScreenTools,
    9 
    10   Classes,Graphics,SysUtils;
     7  Protocol, ScreenTools,
     8
     9  Classes, Graphics, SysUtils;
    1110
    1211type
    13 TCityPicture=record
    14   xShield,yShield:integer;
    15   end;
    16 TModelPicture=record
    17   HGr,pix,xShield,yShield:integer;
    18   end;
    19 TModelPictureInfo=record
    20   trix,mix,pix,Hash: integer;
    21   GrName: ShortString
    22   end;
    23 
    24 TTribe=class
    25   symHGr, sympix, faceHGr, facepix, cHGr, cpix, //symbol and city graphics
    26   cAge, mixSlaves: integer;
    27   Color: TColor;
    28   NumberName: integer;
    29   CityPicture: array[0..3] of TCityPicture;
    30   ModelPicture: array[-1..256] of TModelPicture; // -1 is building site
    31   ModelName: array[-1..256] of string;
    32   constructor Create(FileName: string);
    33   destructor Destroy; override;
    34   function GetCityName(i: integer): string;
    35   {$IFNDEF SCR}procedure SetCityName(i: integer; NewName: string);{$ENDIF}
    36   {$IFNDEF SCR}function TString(Template: string): string;
    37   function TPhrase(Item: string): string;{$ENDIF}
    38   procedure SetModelPicture(const Info: TModelPictureInfo; IsNew: boolean);
    39   function ChooseModelPicture(var Picture: TModelPictureInfo;
    40     code,Turn: integer; ForceNew: boolean): boolean;
    41   procedure InitAge(Age: integer);
    42 protected
    43   CityLine0,nCityLines: integer;
    44   Name: array['a'..'z'] of string;
    45   Script: tstringlist;
     12  TCityPicture = record
     13    xShield, yShield: integer;
     14  end;
     15
     16  TModelPicture = record
     17    HGr, pix, xShield, yShield: integer;
     18  end;
     19
     20  TModelPictureInfo = record
     21    trix, mix, pix, Hash: integer;
     22    GrName: ShortString end;
     23
     24    TTribe = class symHGr, sympix, faceHGr, facepix, cHGr, cpix,
     25    // symbol and city graphics
     26    cAge, mixSlaves: integer;
     27    Color: TColor;
     28    NumberName: integer;
     29    CityPicture: array [0 .. 3] of TCityPicture;
     30    ModelPicture: array [-1 .. 256] of TModelPicture; // -1 is building site
     31    ModelName: array [-1 .. 256] of string;
     32    constructor Create(FileName: string);
     33    destructor Destroy; override;
     34    function GetCityName(i: integer): string;
     35{$IFNDEF SCR} procedure SetCityName(i: integer; NewName: string); {$ENDIF}
     36{$IFNDEF SCR} function TString(Template: string): string;
     37    function TPhrase(Item: string): string; {$ENDIF}
     38    procedure SetModelPicture(const Info: TModelPictureInfo; IsNew: boolean);
     39    function ChooseModelPicture(var Picture: TModelPictureInfo;
     40      code, Turn: integer; ForceNew: boolean): boolean;
     41    procedure InitAge(Age: integer);
     42  protected
     43    CityLine0, nCityLines: integer;
     44    Name: array ['a' .. 'z'] of string;
     45    Script: tstringlist;
    4646  end;
    4747
    4848var
    49 Tribe: array[0..nPl-1] of TTribe;
    50 HGrStdUnits: integer;
     49  Tribe: array [0 .. nPl - 1] of TTribe;
     50  HGrStdUnits: integer;
    5151
    5252procedure Init;
     
    5656procedure FindStdModelPicture(code: integer; var pix: integer;
    5757  var Name: string);
    58 function GetTribeInfo(FileName: string; var Name: string; var Color: TColor): boolean;
    59 procedure FindPosition(HGr,x,y,xmax,ymax: integer; Mark: TColor; var xp,yp: integer);
    60 
     58function GetTribeInfo(FileName: string; var Name: string;
     59  var Color: TColor): boolean;
     60procedure FindPosition(HGr, x, y, xmax, ymax: integer; Mark: TColor;
     61  var xp, yp: integer);
    6162
    6263implementation
    6364
    6465uses
    65 Directories;
    66 
     66  Directories;
    6767
    6868type
    69 TChosenModelPictureInfo=record
    70   Hash,HGr,pix: integer;
    71   ModelName: ShortString
    72   end;
    73 
    74 TPictureList=array[0..99999] of TChosenModelPictureInfo;
    75 
    76 var
    77 StdUnitScript: tstringlist;
    78 PictureList: ^TPictureList;
    79 nPictureList: integer;
    80 
    81 
    82 procedure Init;
    83 begin
    84 StdUnitScript:=tstringlist.Create;
    85 StdUnitScript.LoadFromFile(LocalizedFilePath('Tribes\StdUnits.txt'));
    86 nPictureList:=0;
    87 PictureList:=nil;
    88 end;
    89 
    90 procedure Done;
    91 begin
    92 ReallocMem(PictureList,0);
    93 StdUnitScript.Free;
    94 end;
    95 
    96 function CityName(Founder: integer): string;
    97 begin
    98 if not GenerateNames then
    99   result:=Format('%d.%d',[Founder shr 12, Founder and $FFF])
    100 else result:=Tribe[Founder shr 12].GetCityName(Founder and $FFF);
    101 end;
    102 
    103 function ModelCode(const ModelInfo: TModelInfo): integer;
    104 begin
    105 with ModelInfo do
    106   begin
    107   case Kind of
    108     mkSelfDeveloped, mkEnemyDeveloped:
    109       case Domain of {age determination}
    110         dGround:
    111           if (Attack>=Defense*4)
    112             or (Attack>0) and (MaxUpgrade<10)
    113               and (Cap and (1 shl (mcArtillery-mcFirstNonCap))<>0) then
    114             begin
    115             result:=170;
    116             if MaxUpgrade>=12 then inc(result,3)
    117             else if (MaxUpgrade>=10) or (Weight>7) then inc(result,2)
    118             else if MaxUpgrade>=4 then inc(result,1)
     69  TChosenModelPictureInfo = record
     70    Hash, HGr, pix: integer;
     71    ModelName: ShortString end;
     72
     73    TPictureList = array [0 .. 99999] of TChosenModelPictureInfo;
     74
     75  var
     76    StdUnitScript: tstringlist;
     77    PictureList: ^TPictureList;
     78    nPictureList: integer;
     79
     80    procedure Init;
     81    begin
     82      StdUnitScript := tstringlist.Create;
     83      StdUnitScript.LoadFromFile(LocalizedFilePath('Tribes\StdUnits.txt'));
     84      nPictureList := 0;
     85      PictureList := nil;
     86    end;
     87
     88    procedure Done;
     89    begin
     90      ReallocMem(PictureList, 0);
     91      StdUnitScript.Free;
     92    end;
     93
     94    function CityName(Founder: integer): string;
     95    begin
     96      if not GenerateNames then
     97        result := Format('%d.%d', [Founder shr 12, Founder and $FFF])
     98      else
     99        result := Tribe[Founder shr 12].GetCityName(Founder and $FFF);
     100    end;
     101
     102    function ModelCode(const ModelInfo: TModelInfo): integer;
     103    begin
     104      with ModelInfo do
     105      begin
     106        case Kind of
     107          mkSelfDeveloped, mkEnemyDeveloped:
     108            case Domain of { age determination }
     109              dGround:
     110                if (Attack >= Defense * 4) or (Attack > 0) and (MaxUpgrade < 10)
     111                  and (Cap and (1 shl (mcArtillery - mcFirstNonCap)) <> 0) then
     112                begin
     113                  result := 170;
     114                  if MaxUpgrade >= 12 then
     115                    inc(result, 3)
     116                  else if (MaxUpgrade >= 10) or (Weight > 7) then
     117                    inc(result, 2)
     118                  else if MaxUpgrade >= 4 then
     119                    inc(result, 1)
     120                end
     121                else
     122                begin
     123                  result := 100;
     124                  if MaxUpgrade >= 12 then
     125                    inc(result, 6)
     126                  else if (MaxUpgrade >= 10) or (Weight > 7) then
     127                    inc(result, 5)
     128                  else if MaxUpgrade >= 6 then
     129                    inc(result, 4)
     130                  else if MaxUpgrade >= 4 then
     131                    inc(result, 3)
     132                  else if MaxUpgrade >= 2 then
     133                    inc(result, 2)
     134                  else if MaxUpgrade >= 1 then
     135                    inc(result, 1);
     136                  if Speed >= 250 then
     137                    if (result >= 105) and (Attack <= Defense) then
     138                      result := 110
     139                    else
     140                      inc(result, 30)
     141                end;
     142              dSea:
     143                begin
     144                  result := 200;
     145                  if MaxUpgrade >= 8 then
     146                    inc(result, 3)
     147                  else if MaxUpgrade >= 6 then
     148                    inc(result, 2)
     149                  else if MaxUpgrade >= 3 then
     150                    inc(result, 1);
     151                  if Cap and (1 shl (mcSub - mcFirstNonCap)) <> 0 then
     152                    result := 240
     153                  else if ATrans_Fuel > 0 then
     154                    result := 220
     155                  else if (result >= 202) and (Attack = 0) and (TTrans > 0) then
     156                    result := 210;
     157                end;
     158              dAir:
     159                begin
     160                  result := 300;
     161                  if (Bombs > 0) or (TTrans > 0) then
     162                    inc(result, 10);
     163                  if Speed > 850 then
     164                    inc(result, 1)
     165                end;
     166            end;
     167          mkSpecial_TownGuard:
     168            result := 41;
     169          mkSpecial_Boat:
     170            result := 64;
     171          mkSpecial_SubCabin:
     172            result := 71;
     173          mkSpecial_Glider:
     174            result := 73;
     175          mkSlaves:
     176            result := 74;
     177          mkSettler:
     178            if Speed > 150 then
     179              result := 11
     180            else
     181              result := 10;
     182          mkDiplomat:
     183            result := 21;
     184          mkCaravan:
     185            result := 30;
     186        end;
     187      end;
     188    end;
     189
     190  var
     191    Input: string;
     192
     193    function Get: string;
     194
     195  var
     196    p: integer;
     197  begin
     198    while (Input <> '') and ((Input[1] = ' ') or (Input[1] = #9)) do
     199      Delete(Input, 1, 1);
     200    p := pos(',', Input);
     201    if p = 0 then
     202      p := Length(Input) + 1;
     203    result := Copy(Input, 1, p - 1);
     204    Delete(Input, 1, p)
     205  end;
     206
     207  function GetNum: integer;
     208
     209  var
     210    i: integer;
     211  begin
     212    val(Get, result, i);
     213    if i <> 0 then
     214      result := 0
     215  end;
     216
     217  procedure FindStdModelPicture(code: integer; var pix: integer;
     218    var Name: string);
     219
     220  var
     221    i: integer;
     222  begin
     223    for i := 0 to StdUnitScript.Count - 1 do
     224    begin // look through StdUnits
     225      Input := StdUnitScript[i];
     226      pix := GetNum;
     227      if code = GetNum then
     228      begin
     229        Name := Get;
     230        exit;
     231      end
     232    end;
     233    pix := -1
     234  end;
     235
     236  function GetTribeInfo(FileName: string; var Name: string;
     237    var Color: TColor): boolean;
     238
     239  var
     240    found: integer;
     241    TribeScript: TextFile;
     242  begin
     243    Name := '';
     244    Color := $FFFFFF;
     245    found := 0;
     246    AssignFile(TribeScript, LocalizedFilePath('Tribes\' + FileName +
     247      '.tribe.txt'));
     248    Reset(TribeScript);
     249    while not EOF(TribeScript) do
     250    begin
     251      ReadLn(TribeScript, Input);
     252      if Copy(Input, 1, 7) = '#CHOOSE' then
     253      begin
     254        Name := Copy(Input, 9, 255);
     255        found := found or 1;
     256        if found = 3 then
     257          break
     258      end
     259      else if Copy(Input, 1, 6) = '#COLOR' then
     260      begin
     261        Color := HexStringToColor(Copy(Input, 7, 255));
     262        found := found or 2;
     263        if found = 3 then
     264          break
     265      end
     266    end;
     267    CloseFile(TribeScript);
     268    result := found = 3;
     269  end;
     270
     271  constructor TTribe.Create(FileName: string);
     272
     273  var
     274    line: integer;
     275    variant: char;
     276    Item: string;
     277  begin
     278    inherited Create;
     279    for variant := 'a' to 'z' do
     280      Name[variant] := '';
     281    Script := tstringlist.Create;
     282    Script.LoadFromFile(LocalizedFilePath('Tribes\' + FileName + '.tribe.txt'));
     283    CityLine0 := 0;
     284    nCityLines := 0;
     285    for line := 0 to Script.Count - 1 do
     286    begin
     287      Input := Script[line];
     288      if (CityLine0 > 0) and (nCityLines = 0) and
     289        ((Input = '') or (Input[1] = '#')) then
     290        nCityLines := line - CityLine0;
     291      if (Length(Input) >= 3) and (Input[1] = '#') and (Input[2] in ['a' .. 'z']
     292        ) and (Input[3] = ' ') then
     293        Name[Input[2]] := Copy(Input, 4, 255)
     294      else if Copy(Input, 1, 6) = '#COLOR' then
     295        Color := HexStringToColor(Copy(Input, 7, 255))
     296      else if Copy(Input, 1, 7) = '#CITIES' then
     297        CityLine0 := line + 1
     298      else if Copy(Input, 1, 8) = '#SYMBOLS' then
     299      begin
     300        Delete(Input, 1, 9);
     301        Item := Get;
     302        sympix := GetNum;
     303        symHGr := LoadGraphicSet(Item);
     304      end
     305    end;
     306    FillChar(ModelPicture, SizeOf(ModelPicture), 0);
     307    NumberName := -1;
     308    cAge := -1;
     309    mixSlaves := -1;
     310  end;
     311
     312  destructor TTribe.Destroy;
     313  begin
     314    Script.Free;
     315    inherited Destroy;
     316  end;
     317
     318  procedure FindPosition(HGr, x, y, xmax, ymax: integer; Mark: TColor;
     319    var xp, yp: integer);
     320  begin
     321    xp := 0;
     322    while (xp < xmax) and (GrExt[HGr].Data.Canvas.Pixels[x + 1 + xp, y]
     323      <> Mark) do
     324      inc(xp);
     325    yp := 0;
     326    while (yp < ymax) and (GrExt[HGr].Data.Canvas.Pixels[x, y + 1 + yp]
     327      <> Mark) do
     328      inc(yp);
     329  end;
     330
     331  function TTribe.GetCityName(i: integer): string;
     332  begin
     333    result := '';
     334    if nCityLines > i then
     335    begin
     336      result := Script[CityLine0 + i];
     337      while (result <> '') and ((result[1] = ' ') or (result[1] = #9)) do
     338        Delete(result, 1, 1);
     339    end
     340{$IFNDEF SCR} else
     341      result := Format(TPhrase('GENCITY'), [i + 1]){$ENDIF}
     342  end;
     343
     344{$IFNDEF SCR}
     345  procedure TTribe.SetCityName(i: integer; NewName: string);
     346  begin
     347    while nCityLines <= i do
     348    begin
     349      Script.Insert(CityLine0 + nCityLines, Format(TPhrase('GENCITY'),
     350        [nCityLines + 1]));
     351      inc(nCityLines);
     352    end;
     353    Script[CityLine0 + i] := NewName;
     354  end;
     355
     356  function TTribe.TString(Template: string): string;
     357
     358  var
     359    p: integer;
     360    variant: char;
     361    CaseUp: boolean;
     362  begin
     363    repeat
     364      p := pos('#', Template);
     365      if (p = 0) or (p = Length(Template)) then
     366        break;
     367      variant := Template[p + 1];
     368      CaseUp := variant in ['A' .. 'Z'];
     369      if CaseUp then
     370        inc(variant, 32);
     371      Delete(Template, p, 2);
     372      if variant in ['a' .. 'z'] then
     373      begin
     374        if NumberName < 0 then
     375          Insert(Name[variant], Template, p)
     376        else
     377          Insert(Format('P%d', [NumberName]), Template, p);
     378        if CaseUp and (Length(Template) >= p) and
     379          (Template[p] in ['a' .. 'z', #$E0 .. #$FF]) then
     380          dec(Template[p], 32);
     381      end
     382      until false;
     383      result := Template;
     384    end;
     385
     386    function TTribe.TPhrase(Item: string): string;
     387    begin
     388      result := TString(Phrases.Lookup(Item));
     389    end;
     390{$ENDIF}
     391
     392    procedure TTribe.InitAge(Age: integer);
     393    type
     394      TLine = array [0 .. 649, 0 .. 2] of Byte;
     395    var
     396      i, x, gray: integer;
     397      Item: string;
     398    begin
     399      if Age = cAge then
     400        exit;
     401      cAge := Age;
     402      with Script do
     403      begin
     404        i := 0;
     405        while (i < Count) and
     406          (Copy(Strings[i], 1, 6) <> '#AGE' + char(48 + Age) + ' ') do
     407          inc(i);
     408        if i < Count then
     409        begin
     410          Input := Strings[i];
     411          system.Delete(Input, 1, 6);
     412          Item := Get;
     413          cpix := GetNum;
     414          // init city graphics
     415          if Age < 2 then
     416          begin
     417            if CompareText(Item, 'stdcities') = 0 then
     418              case cpix of
     419                3:
     420                  cpix := 0;
     421                6:
     422                  begin
     423                    cpix := 0;
     424                    Item := 'Nation2';
     425                  end
     426              end;
     427            cHGr := LoadGraphicSet(Item);
     428            for x := 0 to 3 do
     429              with CityPicture[x] do
     430              begin
     431                FindPosition(cHGr, x * 65, cpix * 49, 63, 47, $00FFFF,
     432                  xShield, yShield);
     433                // FindPosition(cHGr,x*65,cpix*49,$FFFFFF,xf,yf);
     434              end
     435          end
     436          else
     437            cHGr := -1;
     438
     439{$IFNDEF SCR}
     440          Get;
     441          GetNum;
     442          Item := Get;
     443          if Item = '' then
     444            faceHGr := -1
     445          else
     446          begin
     447            faceHGr := LoadGraphicSet(Item);
     448            facepix := GetNum;
     449            if GrExt[faceHGr].Data.Canvas.Pixels[facepix mod 10 * 65,
     450              facepix div 10 * 49 + 48] = $00FFFF then
     451            begin // generate shield picture
     452              GrExt[faceHGr].Data.Canvas.Pixels[facepix mod 10 * 65,
     453                facepix div 10 * 49 + 48] := $000000;
     454              gray := $B8B8B8;
     455              ImageOp_BCC(GrExt[faceHGr].Data, Templates,
     456                facepix mod 10 * 65 + 1, facepix div 10 * 49 + 1, 1, 25, 64, 48,
     457                gray, Color);
    119458            end
    120           else
    121             begin
    122             result:=100;
    123             if MaxUpgrade>=12 then inc(result,6)
    124             else if (MaxUpgrade>=10) or (Weight>7) then inc(result,5)
    125             else if MaxUpgrade>=6 then inc(result,4)
    126             else if MaxUpgrade>=4 then inc(result,3)
    127             else if MaxUpgrade>=2 then inc(result,2)
    128             else if MaxUpgrade>=1 then inc(result,1);
    129             if Speed>=250 then
    130               if (result>=105) and (Attack<=Defense) then result:=110
    131               else inc(result,30)
    132             end;
    133         dSea:
    134           begin
    135           result:=200;
    136           if MaxUpgrade>=8 then inc(result,3)
    137           else if MaxUpgrade>=6 then inc(result,2)
    138           else if MaxUpgrade>=3 then inc(result,1);
    139           if Cap and (1 shl (mcSub-mcFirstNonCap))<>0 then result:=240
    140           else if ATrans_Fuel>0 then result:=220
    141           else if (result>=202) and (Attack=0) and (TTrans>0) then result:=210;
    142459          end;
    143         dAir:
    144           begin
    145           result:=300;
    146           if (Bombs>0) or (TTrans>0) then inc(result,10);
    147           if Speed>850 then inc(result,1)
    148           end;
    149         end;
    150     mkSpecial_TownGuard: result:=41;
    151     mkSpecial_Boat: result:=64;
    152     mkSpecial_SubCabin: result:=71;
    153     mkSpecial_Glider: result:=73;
    154     mkSlaves: result:=74;
    155     mkSettler: if Speed>150 then result:=11 else result:=10;
    156     mkDiplomat: result:=21;
    157     mkCaravan: result:=30;
    158     end;
    159   end;
    160 end;
    161 
    162 var
    163 Input: string;
    164 
    165 function Get: string;
    166 var
    167 p:integer;
    168 begin
    169 while (Input<>'') and ((Input[1]=' ') or (Input[1]=#9)) do Delete(Input,1,1);
    170 p:=pos(',',Input);if p=0 then p:=Length(Input)+1;
    171 result:=Copy(Input,1,p-1);
    172 Delete(Input,1,p)
    173 end;
    174 
    175 function GetNum: integer;
    176 var
    177 i:integer;
    178 begin
    179 val(Get,result,i);
    180 if i<>0 then result:=0
    181 end;
    182 
    183 procedure FindStdModelPicture(code: integer; var pix: integer;
    184   var Name: string);
    185 var
    186 i: integer;
    187 begin
    188 for i:=0 to StdUnitScript.Count-1 do
    189   begin // look through StdUnits
    190   Input:=StdUnitScript[i];
    191   pix:=GetNum;
    192   if code=GetNum then begin Name:=Get; exit; end
    193   end;
    194 pix:=-1
    195 end;
    196 
    197 function GetTribeInfo(FileName: string; var Name: string; var Color: TColor): boolean;
    198 var
    199 found: integer;
    200 TribeScript: TextFile;
    201 begin
    202 Name:='';
    203 Color:=$FFFFFF;
    204 found:=0;
    205 AssignFile(TribeScript,LocalizedFilePath('Tribes\'+FileName+'.tribe.txt'));
    206 Reset(TribeScript);
    207 while not EOF(TribeScript) do
    208   begin
    209   ReadLn(TribeScript,Input);
    210   if Copy(Input,1,7)='#CHOOSE' then
    211     begin
    212     Name:=Copy(Input,9,255);
    213     found:=found or 1;
    214     if found=3 then break
    215     end
    216   else if Copy(Input,1,6)='#COLOR' then
    217     begin
    218     Color:=HexStringToColor(Copy(Input,7,255));
    219     found:=found or 2;
    220     if found=3 then break
    221     end
    222   end;
    223 CloseFile(TribeScript);
    224 result:= found=3;
    225 end;
    226 
    227 constructor TTribe.Create(FileName: string);
    228 var
    229 line:integer;
    230 variant: char;
    231 Item:string;
    232 begin
    233 inherited Create;
    234 for variant:='a' to 'z' do Name[variant]:='';
    235 Script:=tstringlist.Create;
    236 Script.LoadFromFile(LocalizedFilePath('Tribes\'+FileName+'.tribe.txt'));
    237 CityLine0:=0;
    238 nCityLines:=0;
    239 for line:=0 to Script.Count-1 do
    240   begin
    241   Input:=Script[line];
    242   if (CityLine0>0) and (nCityLines=0) and ((Input='') or (Input[1]='#')) then
    243     nCityLines:=line-CityLine0;
    244   if (Length(Input)>=3) and (Input[1]='#') and (Input[2] in ['a'..'z'])
    245     and (Input[3]=' ') then
    246     Name[Input[2]]:=Copy(Input,4,255)
    247   else if Copy(Input,1,6)='#COLOR' then
    248     Color:=HexStringToColor(Copy(Input,7,255))
    249   else if Copy(Input,1,7)='#CITIES' then CityLine0:=line+1
    250   else if Copy(Input,1,8)='#SYMBOLS' then
    251     begin
    252     Delete(Input,1,9);
    253     Item:=Get;
    254     sympix:=GetNum;
    255     symHGr:=LoadGraphicSet(Item);
    256     end
    257   end;
    258 FillChar(ModelPicture,SizeOf(ModelPicture),0);
    259 NumberName:=-1;
    260 cAge:=-1;
    261 mixSlaves:=-1;
    262 end;
    263 
    264 destructor TTribe.Destroy;
    265 begin
    266 Script.Free;
    267 inherited Destroy;
    268 end;
    269 
    270 procedure FindPosition(HGr,x,y,xmax,ymax: integer; Mark: TColor;
    271   var xp,yp: integer);
    272 begin
    273 xp:=0;
    274 while (xp<xmax) and (GrExt[HGr].Data.Canvas.Pixels[x+1+xp,y]<>Mark) do
    275   inc(xp);
    276 yp:=0;
    277 while (yp<ymax) and (GrExt[HGr].Data.Canvas.Pixels[x,y+1+yp]<>Mark) do
    278   inc(yp);
    279 end;
    280 
    281 function TTribe.GetCityName(i: integer): string;
    282 begin
    283 result:='';
    284 if nCityLines>i then
    285   begin
    286   result:=Script[CityLine0+i];
    287   while (result<>'') and ((result[1]=' ') or (result[1]=#9)) do
    288     Delete(result,1,1);
    289   end
    290 {$IFNDEF SCR}else result:=Format(TPhrase('GENCITY'),[i+1]){$ENDIF}
    291 end;
    292 
    293 {$IFNDEF SCR}
    294 procedure TTribe.SetCityName(i: integer; NewName: string);
    295 begin
    296 while nCityLines<=i do
    297   begin
    298   Script.Insert(CityLine0+nCityLines, Format(TPhrase('GENCITY'),
    299     [nCityLines+1]));
    300   inc(nCityLines);
    301   end;
    302 Script[CityLine0+i]:=NewName;
    303 end;
    304 
    305 function TTribe.TString(Template: string): string;
    306 var
    307 p: integer;
    308 variant: char;
    309 CaseUp: boolean;
    310 begin
    311 repeat
    312   p:=pos('#',Template);
    313   if (p=0) or (p=Length(Template)) then Break;
    314   variant:=Template[p+1];
    315   CaseUp:= variant in ['A'..'Z'];
    316   if CaseUp then inc(variant,32);
    317   Delete(Template,p,2);
    318   if variant in ['a'..'z'] then
    319     begin
    320     if NumberName<0 then Insert(Name[variant],Template,p)
    321     else Insert(Format('P%d',[NumberName]),Template,p);
    322     if CaseUp and (Length(Template)>=p) and (Template[p] in ['a'..'z',#$E0..#$FF]) then
    323       dec(Template[p],32);
    324     end
    325 until false;
    326 result:=Template;
    327 end;
    328 
    329 function TTribe.TPhrase(Item: string): string;
    330 begin
    331 result:=TString(Phrases.Lookup(Item));
    332 end;
    333460{$ENDIF}
    334 
    335 procedure TTribe.InitAge(Age: integer);
    336 type
    337 TLine=array[0..649,0..2] of Byte;
    338 var
    339 i,x,gray: integer;
    340 Item: string;
    341 begin
    342 if Age=cAge then exit;
    343 cAge:=Age;
    344 with Script do
    345   begin
    346   i:=0;
    347   while (i<Count) and (Copy(Strings[i],1,6)<>'#AGE'+char(48+Age)+' ') do
    348     inc(i);
    349   if i<Count then
    350     begin
    351     Input:=Strings[i];
    352     system.Delete(Input,1,6);
    353     Item:=Get;
    354     cpix:=GetNum;
    355     // init city graphics
    356     if age<2 then
    357       begin
    358       if CompareText(Item,'stdcities')=0 then
    359         case cpix of
    360           3: cpix:=0;
    361           6: begin cpix:=0; Item:='Nation2'; end
    362           end;
    363       cHGr:=LoadGraphicSet(Item);
    364       for x:=0 to 3 do with CityPicture[x] do
    365         begin
    366         FindPosition(cHGr,x*65,cpix*49,63,47,$00FFFF,xShield,yShield);
    367         //FindPosition(cHGr,x*65,cpix*49,$FFFFFF,xf,yf);
    368461        end
    369462      end
    370     else cHGr:=-1;
    371 
    372     {$IFNDEF SCR}
    373     Get;
    374     GetNum;
    375     Item:=Get;
    376     if Item='' then faceHGr:=-1
    377     else
    378       begin
    379       faceHGr:=LoadGraphicSet(Item);
    380       facepix:=GetNum;
    381       if GrExt[faceHGr].Data.Canvas.Pixels[facepix mod 10*65,facepix div 10*49+48]=$00FFFF then
    382         begin // generate shield picture
    383         GrExt[faceHGr].Data.Canvas.Pixels[facepix mod 10*65,facepix div 10*49+48]:=$000000;
    384         gray:=$B8B8B8;
    385         ImageOp_BCC(GrExt[faceHGr].Data,Templates,facepix mod 10*65+1,
    386           facepix div 10*49+1,1,25,64,48,gray,Color);
     463    end;
     464
     465    procedure TTribe.SetModelPicture(const Info: TModelPictureInfo;
     466      IsNew: boolean);
     467    var
     468      i: integer;
     469      ok: boolean;
     470    begin
     471      with Info do
     472      begin
     473        if not IsNew then
     474        begin
     475          i := nPictureList - 1;
     476          while (i >= 0) and (PictureList[i].Hash <> Info.Hash) do
     477            dec(i);
     478          assert(i >= 0);
     479          assert(PictureList[i].HGr = LoadGraphicSet(GrName));
     480          assert(PictureList[i].pix = pix);
     481          ModelPicture[mix].HGr := PictureList[i].HGr;
     482          ModelPicture[mix].pix := PictureList[i].pix;
     483          ModelName[mix] := PictureList[i].ModelName;
     484        end
     485        else
     486        begin
     487          with ModelPicture[mix] do
     488          begin
     489            HGr := LoadGraphicSet(GrName);
     490            pix := Info.pix;
     491            inc(GrExt[HGr].pixUsed[pix]);
     492          end;
     493          ModelName[mix] := '';
     494
     495          // read model name from tribe script
     496          ok := false;
     497          for i := 0 to Script.Count - 1 do
     498          begin
     499            Input := Script[i];
     500            if Input = '#UNITS ' + GrName then
     501              ok := true
     502            else if (Input <> '') and (Input[1] = '#') then
     503              ok := false
     504            else if ok and (GetNum = pix) then
     505            begin
     506              Get;
     507              ModelName[mix] := Get
     508            end
     509          end;
     510
     511          if ModelName[mix] = '' then
     512          begin // read model name from StdUnits.txt
     513            for i := 0 to StdUnitScript.Count - 1 do
     514            begin
     515              Input := StdUnitScript[i];
     516              if GetNum = pix then
     517              begin
     518                Get;
     519                ModelName[mix] := Get
     520              end
     521            end
     522          end;
     523
     524          if Hash <> 0 then
     525          begin
     526            if nPictureList = 0 then
     527              ReallocMem(PictureList, 64 * SizeOf(TChosenModelPictureInfo))
     528            else if (nPictureList >= 64) and
     529              (nPictureList and (nPictureList - 1) = 0) then
     530              ReallocMem(PictureList,
     531                nPictureList * (2 * SizeOf(TChosenModelPictureInfo)));
     532            PictureList[nPictureList].Hash := Info.Hash;
     533            PictureList[nPictureList].HGr := ModelPicture[mix].HGr;
     534            PictureList[nPictureList].pix := Info.pix;
     535            PictureList[nPictureList].ModelName := ModelName[mix];
     536            inc(nPictureList);
     537          end
     538        end;
     539
     540        with ModelPicture[mix] do
     541          FindPosition(HGr, pix mod 10 * 65, pix div 10 * 49, 63, 47, $FFFFFF,
     542            xShield, yShield);
     543      end;
     544    end;
     545
     546    function TTribe.ChooseModelPicture(var Picture: TModelPictureInfo;
     547      code, Turn: integer; ForceNew: boolean): boolean;
     548    var
     549      i, Cnt, HGr, used, LeastUsed: integer;
     550      TestPic: TModelPictureInfo;
     551      ok: boolean;
     552
     553      procedure check;
     554      begin
     555        TestPic.pix := GetNum;
     556        if code = GetNum then
     557        begin
     558          if ForceNew or (HGr < 0) then
     559            used := 0
     560          else
     561          begin
     562            used := 4 * GrExt[HGr].pixUsed[TestPic.pix];
     563            if HGr = HGrStdUnits then
     564              inc(used, 2); // prefer units not from StdUnits
     565          end;
     566          if used < LeastUsed then
     567          begin
     568            Cnt := 0;
     569            LeastUsed := used
     570          end;
     571          if used = LeastUsed then
     572          begin
     573            inc(Cnt);
     574            if Turn mod Cnt = 0 then
     575              Picture := TestPic
     576          end;
    387577        end
    388578      end;
    389     {$ENDIF} 
    390     end
    391   end
    392 end;
    393 
    394 procedure TTribe.SetModelPicture(const Info: TModelPictureInfo; IsNew: boolean);
    395 var
    396 i: integer;
    397 ok: boolean;
    398 begin
    399 with Info do
    400   begin
    401   if not IsNew then
    402     begin
    403     i:=nPictureList-1;
    404     while (i>=0) and (PictureList[i].Hash<>Info.Hash) do dec(i);
    405     assert(i>=0);
    406     assert(PictureList[i].HGr = LoadGraphicSet(GrName));
    407     assert(PictureList[i].pix = pix);
    408     ModelPicture[mix].HGr:=PictureList[i].HGr;
    409     ModelPicture[mix].pix:=PictureList[i].pix;
    410     ModelName[mix]:=PictureList[i].ModelName;
    411     end
    412   else
    413     begin
    414     with ModelPicture[mix] do
    415       begin
    416       HGr:=LoadGraphicSet(GrName);
    417       pix:=Info.pix;
    418       inc(GrExt[HGr].pixUsed[pix]);
     579
     580    begin
     581      // look for identical model to assign same picture again
     582      if not ForceNew and (Picture.Hash > 0) then
     583      begin
     584        for i := 0 to nPictureList - 1 do
     585          if PictureList[i].Hash = Picture.Hash then
     586          begin
     587            Picture.GrName := GrExt[PictureList[i].HGr].Name;
     588            Picture.pix := PictureList[i].pix;
     589            result := false;
     590            exit;
     591          end
    419592      end;
    420     ModelName[mix]:='';
    421 
    422     // read model name from tribe script
    423     ok:=false;
    424     for i:=0 to Script.Count-1 do
    425       begin
    426       Input:=Script[i];
    427       if Input='#UNITS '+GrName then ok:=true
    428       else if (Input<>'') and (Input[1]='#') then ok:=false
    429       else if ok and (GetNum=pix) then
    430         begin Get; ModelName[mix]:=Get end
     593
     594      Picture.pix := 0;
     595      TestPic := Picture;
     596      LeastUsed := MaxInt;
     597
     598      TestPic.GrName := 'StdUnits';
     599      HGr := HGrStdUnits;
     600      for i := 0 to StdUnitScript.Count - 1 do
     601      begin // look through StdUnits
     602        Input := StdUnitScript[i];
     603        check;
    431604      end;
    432605
    433     if ModelName[mix]='' then
    434       begin // read model name from StdUnits.txt
    435       for i:=0 to StdUnitScript.Count-1 do
     606      ok := false;
     607      for i := 0 to Script.Count - 1 do
     608      begin // look through units defined in tribe script
     609        Input := Script[i];
     610        if Copy(Input, 1, 6) = '#UNITS' then
    436611        begin
    437         Input:=StdUnitScript[i];
    438         if GetNum=pix then
    439           begin Get; ModelName[mix]:=Get end
     612          ok := true;
     613          TestPic.GrName := Copy(Input, 8, 255);
     614          HGr := nGrExt - 1;
     615          while (HGr >= 0) and (GrExt[HGr].Name <> TestPic.GrName) do
     616            dec(HGr);
    440617        end
     618        else if (Input <> '') and (Input[1] = '#') then
     619          ok := false
     620        else if ok then
     621          check;
    441622      end;
    442 
    443     if Hash<>0 then
    444       begin
    445       if nPictureList=0 then
    446         ReallocMem(PictureList, 64*sizeof(TChosenModelPictureInfo))
    447       else if (nPictureList>=64) and (nPictureList and (nPictureList-1)=0) then
    448         ReallocMem(PictureList, nPictureList*(2*sizeof(TChosenModelPictureInfo)));
    449       PictureList[nPictureList].Hash:=Info.Hash;
    450       PictureList[nPictureList].HGr:=ModelPicture[mix].HGr;
    451       PictureList[nPictureList].pix:=Info.pix;
    452       PictureList[nPictureList].ModelName:=ModelName[mix];
    453       inc(nPictureList);
    454       end
    455     end;
    456 
    457   with ModelPicture[mix] do
    458     FindPosition(HGr,pix mod 10 *65,pix div 10 *49,63,47,$FFFFFF,xShield,yShield);
    459   end;
    460 end;
    461 
    462 function TTribe.ChooseModelPicture(var Picture: TModelPictureInfo;
    463   code,Turn: integer; ForceNew: boolean): boolean;
    464 var
    465 i,Cnt,HGr,used,LeastUsed: integer;
    466 TestPic: TModelPictureInfo;
    467 ok: boolean;
    468 
    469   procedure check;
    470   begin
    471   TestPic.pix:=GetNum;
    472   if code=GetNum then
    473     begin
    474     if ForceNew or (HGr<0) then used:=0
    475     else
    476       begin
    477       used:=4*GrExt[HGr].pixUsed[TestPic.pix];
    478       if HGr=HGrStdUnits then inc(used,2); // prefer units not from StdUnits
    479       end;
    480     if used<LeastUsed then begin Cnt:=0; LeastUsed:=used end;
    481     if used=LeastUsed then
    482       begin
    483       inc(Cnt);
    484       if Turn mod Cnt=0 then Picture:=TestPic
    485       end;
    486     end
    487   end;
    488 
    489 begin
    490 // look for identical model to assign same picture again
    491 if not ForceNew and (Picture.Hash>0) then
    492   begin
    493   for i:=0 to nPictureList-1 do
    494     if PictureList[i].Hash=Picture.Hash then
    495       begin
    496       Picture.GrName:=GrExt[PictureList[i].HGr].Name;
    497       Picture.pix:=PictureList[i].pix;
    498       result:=false;
    499       exit;
    500       end
    501   end;
    502 
    503 Picture.pix:=0;
    504 TestPic:=Picture;
    505 LeastUsed:=MaxInt;
    506 
    507 TestPic.GrName:='StdUnits';
    508 HGr:=HGrStdUnits;
    509 for i:=0 to StdUnitScript.Count-1 do
    510   begin // look through StdUnits
    511   Input:=StdUnitScript[i];
    512   check;
    513   end;
    514 
    515 ok:=false;
    516 for i:=0 to Script.Count-1 do
    517   begin // look through units defined in tribe script
    518   Input:=Script[i];
    519   if Copy(Input,1,6)='#UNITS' then
    520     begin
    521     ok:=true;
    522     TestPic.GrName:=Copy(Input,8,255);
    523     HGr:=nGrExt-1;
    524     while (HGr>=0) and (GrExt[HGr].Name<>TestPic.GrName) do dec(HGr);
    525     end
    526   else if (Input<>'') and (Input[1]='#') then ok:=false
    527   else if ok then check;
    528   end;
    529 result:=true;
    530 end;
     623      result := true;
     624    end;
    531625
    532626end.
    533 
  • trunk/LocalPlayer/UnitStat.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit UnitStat;
    43
     
    65
    76uses
    8   Protocol,ClientTools,Term,ScreenTools,BaseWin,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,ButtonA,ButtonB,
     7  Protocol, ClientTools, Term, ScreenTools, BaseWin,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ButtonA,
     10  ButtonB,
    1111  ButtonBase, ButtonC;
    1212
     
    3838  protected
    3939    mixShow, // for dkOwnModel
    40     uixShow,euixShow,ecixShow,
    41     UnitLoc,AgePrepared: integer; // for dkEnemyUnit, euixShow=-1 ->
     40    uixShow, euixShow, ecixShow, UnitLoc, AgePrepared: integer;
     41    // for dkEnemyUnit, euixShow=-1 ->
    4242    mox: ^TModelInfo; // for dkEnemyModel
    43     Kind:(dkOwnModel,dkOwnUnit,dkEnemyModel,dkEnemyUnit,dkEnemyCityDefense,dkEnemyCity);
     43    Kind: (dkOwnModel, dkOwnUnit, dkEnemyModel, dkEnemyUnit, dkEnemyCityDefense,
     44      dkEnemyCity);
    4445    Back, Template: TBitmap;
    4546    procedure OffscreenPaint; override;
     
    5657
    5758const
    58 xView=71;
    59 xTotal=20; StatDown=112;
    60 yImp=133;
    61 
    62 // window size
    63 wCommon=208; hOwnModel=293; hEnemyModel=236; hEnemyUnit=212;
    64 hEnemyCityDefense=320; hEnemyCity=166; hMax=320;
    65 
     59  xView = 71;
     60  xTotal = 20;
     61  StatDown = 112;
     62  yImp = 133;
     63
     64  // window size
     65  wCommon = 208;
     66  hOwnModel = 293;
     67  hEnemyModel = 236;
     68  hEnemyUnit = 212;
     69  hEnemyCityDefense = 320;
     70  hEnemyCity = 166;
     71  hMax = 320;
    6672
    6773procedure TUnitStatDlg.FormCreate(Sender: TObject);
    6874begin
    69 inherited;
    70 AgePrepared:=-2;
    71 TitleHeight:=Screen.Height;
    72 InitButtons();
    73 
    74 Back:=TBitmap.Create;
    75 Back.PixelFormat:=pf24bit;
    76 Back.Width:=5*wCommon; Back.Height:=hMax;
    77 Template:=TBitmap.Create;
    78 LoadGraphicFile(Template, HomeDir+'Graphics\Unit', gfNoGamma);
    79 Template.PixelFormat:=pf8bit;
     75  inherited;
     76  AgePrepared := -2;
     77  TitleHeight := Screen.Height;
     78  InitButtons();
     79
     80  Back := TBitmap.Create;
     81  Back.PixelFormat := pf24bit;
     82  Back.Width := 5 * wCommon;
     83  Back.Height := hMax;
     84  Template := TBitmap.Create;
     85  LoadGraphicFile(Template, HomeDir + 'Graphics\Unit', gfNoGamma);
     86  Template.PixelFormat := pf8bit;
    8087end;
    8188
    8289procedure TUnitStatDlg.FormDestroy(Sender: TObject);
    8390begin
    84 Template.Free;
    85 Back.Free;
     91  Template.Free;
     92  Back.Free;
    8693end;
    8794
    8895procedure TUnitStatDlg.CheckAge;
    8996begin
    90 if MainTextureAge<>AgePrepared then
    91   begin
    92   AgePrepared:=MainTextureAge;
    93   bitblt(Back.Canvas.Handle,0,0,wCommon,hOwnModel,
    94     MainTexture.Image.Canvas.Handle,(wMainTexture-wCommon) div 2,
    95     (hMainTexture-hOwnModel) div 2,SRCCOPY);
    96   bitblt(Back.Canvas.Handle,wCommon,0,wCommon,hEnemyModel,
    97     MainTexture.Image.Canvas.Handle,(wMainTexture-wCommon) div 2,
    98     (hMainTexture-hEnemyModel) div 2,SRCCOPY);
    99   bitblt(Back.Canvas.Handle,2*wCommon,0,wCommon,hEnemyUnit,
    100     MainTexture.Image.Canvas.Handle,(wMainTexture-wCommon) div 2,
    101     (hMainTexture-hEnemyUnit) div 2,SRCCOPY);
    102   bitblt(Back.Canvas.Handle,3*wCommon,0,wCommon,hEnemyCityDefense,
    103     MainTexture.Image.Canvas.Handle,(wMainTexture-wCommon) div 2,
    104     (hMainTexture-hEnemyCityDefense) div 2,SRCCOPY);
    105   bitblt(Back.Canvas.Handle,4*wCommon,0,wCommon,hEnemyCity,
    106     MainTexture.Image.Canvas.Handle,(wMainTexture-wCommon) div 2,
    107     (hMainTexture-hEnemyCity) div 2,SRCCOPY);
    108   ImageOp_B(Back,Template,0,0,0,0,5*wCommon,hMax);
     97  if MainTextureAge <> AgePrepared then
     98  begin
     99    AgePrepared := MainTextureAge;
     100    bitblt(Back.Canvas.Handle, 0, 0, wCommon, hOwnModel,
     101      MainTexture.Image.Canvas.Handle, (wMainTexture - wCommon) div 2,
     102      (hMainTexture - hOwnModel) div 2, SRCCOPY);
     103    bitblt(Back.Canvas.Handle, wCommon, 0, wCommon, hEnemyModel,
     104      MainTexture.Image.Canvas.Handle, (wMainTexture - wCommon) div 2,
     105      (hMainTexture - hEnemyModel) div 2, SRCCOPY);
     106    bitblt(Back.Canvas.Handle, 2 * wCommon, 0, wCommon, hEnemyUnit,
     107      MainTexture.Image.Canvas.Handle, (wMainTexture - wCommon) div 2,
     108      (hMainTexture - hEnemyUnit) div 2, SRCCOPY);
     109    bitblt(Back.Canvas.Handle, 3 * wCommon, 0, wCommon, hEnemyCityDefense,
     110      MainTexture.Image.Canvas.Handle, (wMainTexture - wCommon) div 2,
     111      (hMainTexture - hEnemyCityDefense) div 2, SRCCOPY);
     112    bitblt(Back.Canvas.Handle, 4 * wCommon, 0, wCommon, hEnemyCity,
     113      MainTexture.Image.Canvas.Handle, (wMainTexture - wCommon) div 2,
     114      (hMainTexture - hEnemyCity) div 2, SRCCOPY);
     115    ImageOp_B(Back, Template, 0, 0, 0, 0, 5 * wCommon, hMax);
    109116  end
    110117end;
     
    112119procedure TUnitStatDlg.FormShow(Sender: TObject);
    113120var
    114 owner, mix: integer;
    115 IsSpecialUnit: boolean;
    116 begin
    117 if Kind in [dkEnemyUnit,dkEnemyCityDefense,dkEnemyCity] then
    118   begin
    119   if MyMap[UnitLoc] and fUnit<>0 then
     121  owner, mix: integer;
     122  IsSpecialUnit: boolean;
     123begin
     124  if Kind in [dkEnemyUnit, dkEnemyCityDefense, dkEnemyCity] then
     125  begin
     126    if MyMap[UnitLoc] and fUnit <> 0 then
    120127    begin // find model
    121     if euixShow<0 then
    122       begin
    123       euixShow:=MyRO.nEnemyUn-1;
    124       while (euixShow>=0) and (MyRO.EnemyUn[euixShow].Loc<>UnitLoc) do dec(euixShow);
    125       assert(euixShow>=0);
    126       end;
    127     with MyRO.EnemyUn[euixShow] do
    128       begin
    129       mox:=@MyRO.EnemyModel[emix];
    130       if Tribe[Owner].ModelPicture[mix].HGr=0 then
    131         InitEnemyModel(emix);
     128      if euixShow < 0 then
     129      begin
     130        euixShow := MyRO.nEnemyUn - 1;
     131        while (euixShow >= 0) and (MyRO.EnemyUn[euixShow].Loc <> UnitLoc) do
     132          dec(euixShow);
     133        assert(euixShow >= 0);
     134      end;
     135      with MyRO.EnemyUn[euixShow] do
     136      begin
     137        mox := @MyRO.EnemyModel[emix];
     138        if Tribe[owner].ModelPicture[mix].HGr = 0 then
     139          InitEnemyModel(emix);
    132140      end
    133141    end
    134   else mox:=nil;
    135   if Kind in [dkEnemyCityDefense,dkEnemyCity] then
     142    else
     143      mox := nil;
     144    if Kind in [dkEnemyCityDefense, dkEnemyCity] then
    136145    begin
    137     ecixShow:=MyRO.nEnemyCity-1;
    138     while (ecixShow>=0) and (MyRO.EnemyCity[ecixShow].Loc<>UnitLoc) do dec(ecixShow);
    139     assert(ecixShow>=0);
     146      ecixShow := MyRO.nEnemyCity - 1;
     147      while (ecixShow >= 0) and (MyRO.EnemyCity[ecixShow].Loc <> UnitLoc) do
     148        dec(ecixShow);
     149      assert(ecixShow >= 0);
    140150    end
    141151  end;
    142 case Kind of
    143   dkOwnModel: ClientHeight:=hOwnModel;
    144   dkOwnUnit: ClientHeight:=hEnemyUnit;
    145   dkEnemyModel: ClientHeight:=hEnemyModel;
    146   dkEnemyUnit: ClientHeight:=hEnemyUnit;
    147   dkEnemyCityDefense: ClientHeight:=hEnemyCityDefense;
    148   dkEnemyCity: ClientHeight:=hEnemyCity;
    149   end;
    150 
    151 if Kind in [dkOwnModel,dkEnemyModel] then
    152   begin
    153   Left:=UserLeft;
    154   Top:=UserTop;
    155   end
    156 else
    157   begin
    158   Left:=(Screen.Width-Width) div 2;
    159   Top:=(Screen.Height-Height) div 2;
    160   end;
    161 
    162 SwitchBtn.Visible:= not supervising and (Kind=dkOwnModel);
    163 ConscriptsBtn.Visible:= not supervising and (Kind=dkOwnModel)
    164   and (MyRO.Tech[adConscription]>=tsApplicable)
    165   and (MyModel[mixShow].Domain=dGround) and (MyModel[mixShow].Kind<mkScout);
    166 IsSpecialUnit:=false;
    167 if Kind in [dkEnemyCity,dkEnemyCityDefense] then
    168    Caption:=CityName(MyRO.EnemyCity[ecixShow].ID)
    169 else
    170   begin
    171152  case Kind of
    172153    dkOwnModel:
    173       begin
    174       owner:=me;
    175       mix:=mixShow;
    176       IsSpecialUnit:= MyModel[mix].Kind>=$10;
    177       end;
     154      ClientHeight := hOwnModel;
    178155    dkOwnUnit:
    179       begin
    180       owner:=me;
    181       mix:=MyUn[uixShow].mix;
    182       IsSpecialUnit:= MyModel[mix].Kind>=$10;
    183       end
     156      ClientHeight := hEnemyUnit;
     157    dkEnemyModel:
     158      ClientHeight := hEnemyModel;
     159    dkEnemyUnit:
     160      ClientHeight := hEnemyUnit;
     161    dkEnemyCityDefense:
     162      ClientHeight := hEnemyCityDefense;
     163    dkEnemyCity:
     164      ClientHeight := hEnemyCity;
     165  end;
     166
     167  if Kind in [dkOwnModel, dkEnemyModel] then
     168  begin
     169    Left := UserLeft;
     170    Top := UserTop;
     171  end
     172  else
     173  begin
     174    Left := (Screen.Width - Width) div 2;
     175    Top := (Screen.Height - Height) div 2;
     176  end;
     177
     178  SwitchBtn.Visible := not supervising and (Kind = dkOwnModel);
     179  ConscriptsBtn.Visible := not supervising and (Kind = dkOwnModel) and
     180    (MyRO.Tech[adConscription] >= tsApplicable) and
     181    (MyModel[mixShow].Domain = dGround) and (MyModel[mixShow].Kind < mkScout);
     182  IsSpecialUnit := false;
     183  if Kind in [dkEnemyCity, dkEnemyCityDefense] then
     184    Caption := CityName(MyRO.EnemyCity[ecixShow].ID)
     185  else
     186  begin
     187    case Kind of
     188      dkOwnModel:
     189        begin
     190          owner := me;
     191          mix := mixShow;
     192          IsSpecialUnit := MyModel[mix].Kind >= $10;
     193        end;
     194      dkOwnUnit:
     195        begin
     196          owner := me;
     197          mix := MyUn[uixShow].mix;
     198          IsSpecialUnit := MyModel[mix].Kind >= $10;
     199        end
    184200    else
    185201      begin
    186       owner:=mox.owner;
    187       mix:=mox.mix;
    188       IsSpecialUnit:= mox.Kind>=$10;
     202        owner := mox.owner;
     203        mix := mox.mix;
     204        IsSpecialUnit := mox.Kind >= $10;
    189205      end;
    190206    end;
    191   if MainScreen.mNames.Checked then
    192     Caption:=Tribe[Owner].ModelName[mix]
    193   else Caption:=Format(Tribe[Owner].TPhrase('GENMODEL'),[mix])
    194   end;
    195 if IsSpecialUnit then
    196   HelpBtn.Hint:=Phrases.Lookup('CONTROLS',6);
    197 HelpBtn.Visible:=IsSpecialUnit;
    198 OffscreenPaint;
     207    if MainScreen.mNames.Checked then
     208      Caption := Tribe[owner].ModelName[mix]
     209    else
     210      Caption := Format(Tribe[owner].TPhrase('GENMODEL'), [mix])
     211  end;
     212  if IsSpecialUnit then
     213    HelpBtn.Hint := Phrases.Lookup('CONTROLS', 6);
     214  HelpBtn.Visible := IsSpecialUnit;
     215  OffscreenPaint;
    199216end;
    200217
    201218procedure TUnitStatDlg.ShowNewContent_OwnModel(NewMode, mix: integer);
    202219begin
    203 Kind:=dkOwnModel;
    204 mixShow:=mix;
    205 inherited ShowNewContent(NewMode);
     220  Kind := dkOwnModel;
     221  mixShow := mix;
     222  inherited ShowNewContent(NewMode);
    206223end;
    207224
    208225procedure TUnitStatDlg.ShowNewContent_OwnUnit(NewMode, uix: integer);
    209226begin
    210 Kind:=dkOwnUnit;
    211 uixShow:=uix;
    212 inherited ShowNewContent(NewMode);
     227  Kind := dkOwnUnit;
     228  uixShow := uix;
     229  inherited ShowNewContent(NewMode);
    213230end;
    214231
    215232procedure TUnitStatDlg.ShowNewContent_EnemyUnit(NewMode, euix: integer);
    216233begin
    217 Kind:=dkEnemyUnit;
    218 euixShow:=euix;
    219 UnitLoc:=MyRO.EnemyUn[euix].Loc;
    220 inherited ShowNewContent(NewMode);
     234  Kind := dkEnemyUnit;
     235  euixShow := euix;
     236  UnitLoc := MyRO.EnemyUn[euix].Loc;
     237  inherited ShowNewContent(NewMode);
    221238end;
    222239
    223240procedure TUnitStatDlg.ShowNewContent_EnemyLoc(NewMode, Loc: integer);
    224241begin
    225 Kind:=dkEnemyUnit;
    226 UnitLoc:=Loc;
    227 euixShow:=-1;
    228 inherited ShowNewContent(NewMode);
     242  Kind := dkEnemyUnit;
     243  UnitLoc := Loc;
     244  euixShow := -1;
     245  inherited ShowNewContent(NewMode);
    229246end;
    230247
    231248procedure TUnitStatDlg.ShowNewContent_EnemyModel(NewMode, emix: integer);
    232249begin
    233 Kind:=dkEnemyModel;
    234 mox:=@MyRO.EnemyModel[emix];
    235 inherited ShowNewContent(NewMode);
     250  Kind := dkEnemyModel;
     251  mox := @MyRO.EnemyModel[emix];
     252  inherited ShowNewContent(NewMode);
    236253end;
    237254
    238255procedure TUnitStatDlg.ShowNewContent_EnemyCity(NewMode, Loc: integer);
    239256begin
    240 if MyMap[Loc] and fUnit<>0 then
    241   Kind:=dkEnemyCityDefense
    242 else Kind:=dkEnemyCity;
    243 UnitLoc:=Loc;
    244 euixShow:=-1;
    245 inherited ShowNewContent(NewMode);
    246 end;
    247 
    248 procedure TUnitStatDlg.FormClose(Sender: TObject;
    249   var Action: TCloseAction);
    250 begin
    251 if Kind in [dkOwnModel,dkEnemyModel] then
    252   begin UserLeft:=Left; UserTop:=Top end;
    253 if OffscreenUser=self then OffscreenUser:=nil;
     257  if MyMap[Loc] and fUnit <> 0 then
     258    Kind := dkEnemyCityDefense
     259  else
     260    Kind := dkEnemyCity;
     261  UnitLoc := Loc;
     262  euixShow := -1;
     263  inherited ShowNewContent(NewMode);
     264end;
     265
     266procedure TUnitStatDlg.FormClose(Sender: TObject; var Action: TCloseAction);
     267begin
     268  if Kind in [dkOwnModel, dkEnemyModel] then
     269  begin
     270    UserLeft := Left;
     271    UserTop := Top
     272  end;
     273  if OffscreenUser = self then
     274    OffscreenUser := nil;
    254275end;
    255276
    256277procedure TUnitStatDlg.CloseBtnClick(Sender: TObject);
    257278begin
    258 Close
     279  Close
    259280end;
    260281
    261282procedure TUnitStatDlg.OffscreenPaint;
    262283var
    263 PPicture: ^TModelPicture;
     284  PPicture: ^TModelPicture;
    264285
    265286  function IsToCount(emix: integer): boolean;
    266287  var
    267   PTestPicture: ^TModelPicture;
    268   begin
    269   if MainScreen.mNames.Checked then
     288    PTestPicture: ^TModelPicture;
     289  begin
     290    if MainScreen.mNames.Checked then
    270291    begin
    271     PTestPicture:=@Tribe[MyRO.EnemyModel[emix].Owner].ModelPicture[MyRO.EnemyModel[emix].mix];
    272     result:= (PPicture.HGr=PTestPicture.HGr) and (PPicture.pix=PTestPicture.pix)
    273       and (ModelHash(mox^)=ModelHash(MyRO.EnemyModel[emix]))
     292      PTestPicture := @Tribe[MyRO.EnemyModel[emix].owner].ModelPicture
     293        [MyRO.EnemyModel[emix].mix];
     294      result := (PPicture.HGr = PTestPicture.HGr) and
     295        (PPicture.pix = PTestPicture.pix) and
     296        (ModelHash(mox^) = ModelHash(MyRO.EnemyModel[emix]))
    274297    end
    275   else result:= (MyRO.EnemyModel[emix].Owner=mox.Owner)
    276     and (MyRO.EnemyModel[emix].mix=mox.mix)
    277   end;
    278 
    279   procedure FeatureBar(dst: TBitmap; x,y: integer; const mi: TModelInfo;
     298    else
     299      result := (MyRO.EnemyModel[emix].owner = mox.owner) and
     300        (MyRO.EnemyModel[emix].mix = mox.mix)
     301  end;
     302
     303  procedure FeatureBar(dst: TBitmap; x, y: integer; const mi: TModelInfo;
    280304    const T: TTexture);
    281305  var
    282   i,w,dx,num: integer;
    283   s: string;
    284   begin
    285   DarkGradient(dst.Canvas,x-6,y+1,180,1);
    286   with dst.Canvas do
    287     if mi.Kind>=$10 then
    288       begin
    289       s:=Phrases.Lookup('UNITSPECIAL');
    290       Font.Color:=$000000;
    291       Textout(x-1,y+1,s);
    292       Font.Color:=$B0B0B0;
    293       Textout(x-2,y,s);
     306    i, w, dx, num: integer;
     307    s: string;
     308  begin
     309    DarkGradient(dst.Canvas, x - 6, y + 1, 180, 1);
     310    with dst.Canvas do
     311      if mi.Kind >= $10 then
     312      begin
     313        s := Phrases.Lookup('UNITSPECIAL');
     314        Font.Color := $000000;
     315        Textout(x - 1, y + 1, s);
     316        Font.Color := $B0B0B0;
     317        Textout(x - 2, y, s);
    294318      end
    295     else
    296       begin
    297       Font.Color:=$000000;
    298       dx:=2;
    299       for i:=3 to nFeature-1 do
    300         begin
    301         num:=0;
    302         case i of
    303           mcSeaTrans: if mi.Domain=dSea then num:=mi.TTrans;
    304           mcCarrier: if mi.Domain=dSea then num:=mi.ATrans_Fuel;
    305           mcBombs: num:=mi.Bombs;
    306           mcFuel: if mi.Domain=dAir then num:=mi.ATrans_Fuel;
    307           mcAirTrans: if mi.Domain=dAir then num:=mi.TTrans;
    308           mcFirstNonCap..nFeature-1:
    309             if mi.Cap and (1 shl (i-mcFirstNonCap))<>0 then num:=1
     319      else
     320      begin
     321        Font.Color := $000000;
     322        dx := 2;
     323        for i := 3 to nFeature - 1 do
     324        begin
     325          num := 0;
     326          case i of
     327            mcSeaTrans:
     328              if mi.Domain = dSea then
     329                num := mi.TTrans;
     330            mcCarrier:
     331              if mi.Domain = dSea then
     332                num := mi.ATrans_Fuel;
     333            mcBombs:
     334              num := mi.Bombs;
     335            mcFuel:
     336              if mi.Domain = dAir then
     337                num := mi.ATrans_Fuel;
     338            mcAirTrans:
     339              if mi.Domain = dAir then
     340                num := mi.TTrans;
     341            mcFirstNonCap .. nFeature - 1:
     342              if mi.Cap and (1 shl (i - mcFirstNonCap)) <> 0 then
     343                num := 1
    310344          end;
    311         if (num>0) and ((i<>mcSE) or (mi.Cap and (1 shl (mcNP-mcFirstNonCap))=0)) then
     345          if (num > 0) and
     346            ((i <> mcSE) or (mi.Cap and (1 shl (mcNP - mcFirstNonCap)) = 0))
     347          then
    312348          begin
    313           if num>1 then
     349            if num > 1 then
    314350            begin
    315             s:=IntToStr(num);
    316             w:=TextWidth(s);
    317             Brush.Color:=$FFFFFF;
    318             FillRect(Rect(x-3+dx,y+2,x+w-1+dx,y+16));
    319             Brush.Style:=bsClear;
    320             Textout(x-3+dx+1,y,s);
    321             inc(dx,w+1)
     351              s := IntToStr(num);
     352              w := TextWidth(s);
     353              Brush.Color := $FFFFFF;
     354              FillRect(Rect(x - 3 + dx, y + 2, x + w - 1 + dx, y + 16));
     355              Brush.Style := bsClear;
     356              Textout(x - 3 + dx + 1, y, s);
     357              inc(dx, w + 1)
    322358            end;
    323           Brush.Color:=$C0C0C0;
    324           FrameRect(Rect(x-3+dx,y+2,x+11+dx,y+16));
    325           Brush.Style:=bsClear;
    326           Sprite(dst,HGrSystem,x-1+dx,y+4,10,10,66+i mod 11 *11,137+i div 11 *11);
    327           inc(dx,15)
     359            Brush.Color := $C0C0C0;
     360            FrameRect(Rect(x - 3 + dx, y + 2, x + 11 + dx, y + 16));
     361            Brush.Style := bsClear;
     362            Sprite(dst, HGrSystem, x - 1 + dx, y + 4, 10, 10,
     363              66 + i mod 11 * 11, 137 + i div 11 * 11);
     364            inc(dx, 15)
    328365          end;
    329366        end
    330367      end
    331   end;{featurebar}
    332 
    333   procedure NumberBarS(dst:TBitmap; x,y:integer;
    334     Cap,s: string; const T: TTexture);
    335   begin
    336   DLine(dst.Canvas,x-2,x+170,y+16,T.clBevelShade,T.clBevelLight);
    337   LoweredTextOut(dst.Canvas,-1,T,x-2,y,Cap);
    338   RisedTextout(dst.canvas,x+170-BiColorTextWidth(dst.Canvas,s),y,s);
     368  end; { featurebar }
     369
     370  procedure NumberBarS(dst: TBitmap; x, y: integer; Cap, s: string;
     371    const T: TTexture);
     372  begin
     373    DLine(dst.Canvas, x - 2, x + 170, y + 16, T.clBevelShade, T.clBevelLight);
     374    LoweredTextOut(dst.Canvas, -1, T, x - 2, y, Cap);
     375    RisedTextout(dst.Canvas, x + 170 - BiColorTextWidth(dst.Canvas, s), y, s);
    339376  end;
    340377
    341378var
    342 i,j,x,y,cix,uix,emix,InProd,Available,Destroyed,Loc,Cnt,yView,yTotal,
    343   yCaption: integer;
    344 s: string;
    345 ui: TUnitInfo;
    346 mi: TModelInfo;
    347 begin
    348 inherited;
    349 
    350 case Kind of
    351   dkOwnModel:
    352     begin
    353     bitblt(offscreen.canvas.handle,0,0,wCommon,hOwnModel,Back.Canvas.handle,0,0,SRCCOPY);
    354     yView:=13;
    355     yTotal:=92;
    356     end;
    357   dkEnemyModel:
    358     begin
    359     bitblt(offscreen.canvas.handle,0,0,wCommon,hEnemyModel,Back.Canvas.handle,wCommon,0,SRCCOPY);
    360     yView:=13;
    361     yTotal:=92;
    362     end;
    363   dkEnemyUnit,dkOwnUnit:
    364     begin
    365     bitblt(offscreen.canvas.handle,0,0,wCommon,hEnemyUnit,Back.Canvas.handle,2*wCommon,0,SRCCOPY);
    366     yView:=13;
    367     yTotal:=123;
    368     end;
    369   dkEnemyCityDefense:
    370     begin
    371     bitblt(offscreen.canvas.handle,0,0,wCommon,hEnemyCityDefense,Back.Canvas.handle,3*wCommon,0,SRCCOPY);
    372     yView:=171;
    373     yTotal:=231;
    374     end;
    375   dkEnemyCity:
    376     begin
    377     bitblt(offscreen.canvas.handle,0,0,wCommon,hEnemyCity,Back.Canvas.handle,4*wCommon,0,SRCCOPY);
    378     end;
    379   end;
    380 MarkUsedOffscreen(ClientWidth,ClientHeight);
    381 HelpBtn.Top:=yTotal+22;
    382 
    383 if Kind in [dkEnemyCityDefense,dkEnemyCity] then
     379  i, j, x, y, cix, uix, emix, InProd, Available, Destroyed, Loc, Cnt, yView,
     380    yTotal, yCaption: integer;
     381  s: string;
     382  ui: TUnitInfo;
     383  mi: TModelInfo;
     384begin
     385  inherited;
     386
     387  case Kind of
     388    dkOwnModel:
     389      begin
     390        bitblt(offscreen.Canvas.Handle, 0, 0, wCommon, hOwnModel,
     391          Back.Canvas.Handle, 0, 0, SRCCOPY);
     392        yView := 13;
     393        yTotal := 92;
     394      end;
     395    dkEnemyModel:
     396      begin
     397        bitblt(offscreen.Canvas.Handle, 0, 0, wCommon, hEnemyModel,
     398          Back.Canvas.Handle, wCommon, 0, SRCCOPY);
     399        yView := 13;
     400        yTotal := 92;
     401      end;
     402    dkEnemyUnit, dkOwnUnit:
     403      begin
     404        bitblt(offscreen.Canvas.Handle, 0, 0, wCommon, hEnemyUnit,
     405          Back.Canvas.Handle, 2 * wCommon, 0, SRCCOPY);
     406        yView := 13;
     407        yTotal := 123;
     408      end;
     409    dkEnemyCityDefense:
     410      begin
     411        bitblt(offscreen.Canvas.Handle, 0, 0, wCommon, hEnemyCityDefense,
     412          Back.Canvas.Handle, 3 * wCommon, 0, SRCCOPY);
     413        yView := 171;
     414        yTotal := 231;
     415      end;
     416    dkEnemyCity:
     417      begin
     418        bitblt(offscreen.Canvas.Handle, 0, 0, wCommon, hEnemyCity,
     419          Back.Canvas.Handle, 4 * wCommon, 0, SRCCOPY);
     420      end;
     421  end;
     422  MarkUsedOffscreen(ClientWidth, ClientHeight);
     423  HelpBtn.Top := yTotal + 22;
     424
     425  if Kind in [dkEnemyCityDefense, dkEnemyCity] then
    384426  begin // show city defense facilities
    385   cnt:=0;
    386   for i:=0 to 3 do
    387     if MyRO.EnemyCity[ecixShow].Flags and (2 shl i)<>0 then
    388       inc(cnt);
    389   x:=(wCommon-cnt*xSizeSmall) div 2 -(cnt-1)*2;
    390   for i:=0 to 3 do
    391     if MyRO.EnemyCity[ecixShow].Flags and (2 shl i)<>0 then
    392       begin
    393       case i of
    394         0: j:=imWalls;
    395         1: j:=imCoastalFort;
    396         2: j:=imMissileBat;
    397         3: j:=imBunker
     427    Cnt := 0;
     428    for i := 0 to 3 do
     429      if MyRO.EnemyCity[ecixShow].Flags and (2 shl i) <> 0 then
     430        inc(Cnt);
     431    x := (wCommon - Cnt * xSizeSmall) div 2 - (Cnt - 1) * 2;
     432    for i := 0 to 3 do
     433      if MyRO.EnemyCity[ecixShow].Flags and (2 shl i) <> 0 then
     434      begin
     435        case i of
     436          0:
     437            j := imWalls;
     438          1:
     439            j := imCoastalFort;
     440          2:
     441            j := imMissileBat;
     442          3:
     443            j := imBunker
    398444        end;
    399       Frame(offscreen.Canvas,x-1,yImp-1,x+xSizeSmall,yImp+ySizeSmall,
    400         MainTexture.clBevelLight,MainTexture.clBevelShade);
    401       BitBlt(offscreen.Canvas.Handle,x,yImp,xSizeSmall,ySizeSmall,
    402         SmallImp.Canvas.Handle,j mod 7*xSizeSmall,
    403         (j+SystemIconLines*7) div 7*ySizeSmall,SRCCOPY);
    404       inc(x,xSizeSmall+4)
    405       end;
    406   end;
    407 
    408 if Kind=dkEnemyModel then
    409   begin
    410   PPicture:=@Tribe[mox.Owner].ModelPicture[mox.mix];
    411   Available:=0;
    412   if G.Difficulty[me]=0 then // supervisor -- count stacked units too
    413     for Loc:=0 to G.lx*G.ly-1 do
    414       begin
    415       if MyMap[Loc] and fUnit<>0 then
    416         begin
    417         Server(sGetUnits,me,Loc,Cnt);
    418         for uix:=0 to Cnt-1 do
    419           if IsToCount(MyRO.EnemyUn[MyRO.nEnemyUn+uix].emix) then
    420             inc(Available);
     445        Frame(offscreen.Canvas, x - 1, yImp - 1, x + xSizeSmall,
     446          yImp + ySizeSmall, MainTexture.clBevelLight,
     447          MainTexture.clBevelShade);
     448        bitblt(offscreen.Canvas.Handle, x, yImp, xSizeSmall, ySizeSmall,
     449          SmallImp.Canvas.Handle, j mod 7 * xSizeSmall,
     450          (j + SystemIconLines * 7) div 7 * ySizeSmall, SRCCOPY);
     451        inc(x, xSizeSmall + 4)
     452      end;
     453  end;
     454
     455  if Kind = dkEnemyModel then
     456  begin
     457    PPicture := @Tribe[mox.owner].ModelPicture[mox.mix];
     458    Available := 0;
     459    if G.Difficulty[me] = 0 then // supervisor -- count stacked units too
     460      for Loc := 0 to G.lx * G.ly - 1 do
     461      begin
     462        if MyMap[Loc] and fUnit <> 0 then
     463        begin
     464          Server(sGetUnits, me, Loc, Cnt);
     465          for uix := 0 to Cnt - 1 do
     466            if IsToCount(MyRO.EnemyUn[MyRO.nEnemyUn + uix].emix) then
     467              inc(Available);
    421468        end
    422469      end
    423   else // no supervisor -- can only count stack top units
    424     for uix:=0 to MyRO.nEnemyUn-1 do
    425       if (MyRO.EnemyUn[uix].Loc>=0) and IsToCount(MyRO.EnemyUn[uix].emix) then
     470    else // no supervisor -- can only count stack top units
     471      for uix := 0 to MyRO.nEnemyUn - 1 do
     472        if (MyRO.EnemyUn[uix].Loc >= 0) and IsToCount(MyRO.EnemyUn[uix].emix)
     473        then
     474          inc(Available);
     475    Destroyed := 0;
     476    for emix := 0 to MyRO.nEnemyModel - 1 do
     477      if IsToCount(emix) then
     478        inc(Destroyed, MyRO.EnemyModel[emix].Lost);
     479  end
     480  else
     481  begin
     482    Available := 0;
     483    for uix := 0 to MyRO.nUn - 1 do
     484      if (MyUn[uix].Loc >= 0) and (MyUn[uix].mix = mixShow) then
    426485        inc(Available);
    427   Destroyed:=0;
    428   for emix:=0 to MyRO.nEnemyModel-1 do if IsToCount(emix) then
    429     inc(Destroyed,MyRO.EnemyModel[emix].Lost);
    430   end
    431 else
    432   begin
    433   Available:=0;
    434   for uix:=0 to MyRO.nUn-1 do
    435     if (MyUn[uix].Loc>=0) and (MyUn[uix].mix=mixShow) then inc(Available);
    436   InProd:=0;
    437   for cix:=0 to MyRO.nCity-1 do
    438     if (MyCity[cix].Loc>=0) and (MyCity[cix].Project and (cpImp+cpIndex)=mixShow) then
    439       inc(InProd);
    440   end;
    441 
    442 offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    443 if Kind in [dkEnemyCityDefense,dkEnemyCity] then
    444   begin
    445   NoMap.SetOutput(offscreen);
    446   NoMap.PaintCity(ClientWidth div 2,53,MyRO.EnemyCity[ecixShow],false);
    447 
    448   s:=Tribe[MyRO.EnemyCity[ecixShow].Owner].TPhrase('UNITOWNER');
    449   LoweredTextOut(offscreen.Canvas, -1, MainTexture,
    450     (ClientWidth-BiColorTextWidth(offscreen.Canvas,s)) div 2, 105, s);
    451   end;
    452 
    453 if Kind<>dkEnemyCity then
     486    InProd := 0;
     487    for cix := 0 to MyRO.nCity - 1 do
     488      if (MyCity[cix].Loc >= 0) and
     489        (MyCity[cix].Project and (cpImp + cpIndex) = mixShow) then
     490        inc(InProd);
     491  end;
     492
     493  offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     494  if Kind in [dkEnemyCityDefense, dkEnemyCity] then
     495  begin
     496    NoMap.SetOutput(offscreen);
     497    NoMap.PaintCity(ClientWidth div 2, 53, MyRO.EnemyCity[ecixShow], false);
     498
     499    s := Tribe[MyRO.EnemyCity[ecixShow].owner].TPhrase('UNITOWNER');
     500    LoweredTextOut(offscreen.Canvas, -1, MainTexture,
     501      (ClientWidth - BiColorTextWidth(offscreen.Canvas, s)) div 2, 105, s);
     502  end;
     503
     504  if Kind <> dkEnemyCity then
    454505  begin // show unit stats
    455   if Kind=dkOwnModel then
    456     MakeModelInfo(me,mixShow,MyModel[mixShow],mi)
    457   else if Kind=dkOwnUnit then
     506    if Kind = dkOwnModel then
     507      MakeModelInfo(me, mixShow, MyModel[mixShow], mi)
     508    else if Kind = dkOwnUnit then
    458509    begin
    459     MakeUnitInfo(me,MyUn[uixShow],ui);
    460     MakeModelInfo(me,MyUn[uixShow].mix,MyModel[MyUn[uixShow].mix],mi)
     510      MakeUnitInfo(me, MyUn[uixShow], ui);
     511      MakeModelInfo(me, MyUn[uixShow].mix, MyModel[MyUn[uixShow].mix], mi)
    461512    end
    462   else
     513    else
    463514    begin
    464     mi:=mox^;
    465     if Kind in [dkEnemyUnit,dkEnemyCityDefense] then
    466       ui:=MyRO.EnemyUn[euixShow]
     515      mi := mox^;
     516      if Kind in [dkEnemyUnit, dkEnemyCityDefense] then
     517        ui := MyRO.EnemyUn[euixShow]
    467518    end;
    468519
    469   with Tribe[mi.Owner].ModelPicture[mi.mix] do
     520    with Tribe[mi.owner].ModelPicture[mi.mix] do
    470521    begin
    471     if Kind in [dkOwnUnit,dkEnemyUnit,dkEnemyCityDefense] then with ui do
    472       begin
    473       {Frame(offscreen.canvas,xView-1,yView-1,xView+64,yView+48,
    474         MainTexture.clBevelShade,MainTexture.clBevelLight);
    475       RFrame(offscreen.canvas,xView-2,yView-2,xView+65,yView+49,
    476         MainTexture.clBevelShade,MainTexture.clBevelLight);}
    477       with offscreen.canvas do
    478         begin
    479         Brush.Color:=GrExt[HGrSystem].Data.Canvas.Pixels[98,67];
    480         offscreen.canvas.FillRect(Rect(xView,yView,xView+64,yView+16));
    481         Brush.Style:=bsClear;
     522      if Kind in [dkOwnUnit, dkEnemyUnit, dkEnemyCityDefense] then
     523        with ui do
     524        begin
     525          { Frame(offscreen.canvas,xView-1,yView-1,xView+64,yView+48,
     526            MainTexture.clBevelShade,MainTexture.clBevelLight);
     527            RFrame(offscreen.canvas,xView-2,yView-2,xView+65,yView+49,
     528            MainTexture.clBevelShade,MainTexture.clBevelLight); }
     529          with offscreen.Canvas do
     530          begin
     531            Brush.Color := GrExt[HGrSystem].Data.Canvas.Pixels[98, 67];
     532            offscreen.Canvas.FillRect(Rect(xView, yView, xView + 64,
     533              yView + 16));
     534            Brush.Style := bsClear;
     535          end;
     536
     537          if MyMap[Loc] and fTerrain >= fForest then
     538          begin
     539            x := 1 + 2 * (xxt * 2 + 1);
     540            y := 1 + yyt + 2 * (yyt * 3 + 1)
     541          end
     542          else
     543          begin
     544            x := integer(MyMap[Loc] and fTerrain) * (xxt * 2 + 1) + 1;
     545            y := 1 + yyt
     546          end;
     547          for j := -1 to 1 do
     548            for i := -1 to 1 do
     549              if (i + j) and 1 = 0 then
     550              begin
     551                Sprite(Buffer, HGrTerrain, i * xxt, j * yyt, xxt * 2,
     552                  yyt * 2, x, y);
     553                if MyMap[Loc] and (fTerrain or fSpecial) = fGrass or fSpecial1
     554                then
     555                  Sprite(Buffer, HGrTerrain, i * xxt, j * yyt, xxt * 2, yyt * 2,
     556                    1 + 2 * (xxt * 2 + 1), 1 + yyt + 1 * (yyt * 3 + 1))
     557                else if (MyMap[Loc] and fTerrain = fForest) and
     558                  IsJungle(Loc div G.lx) then
     559                  Sprite(Buffer, HGrTerrain, i * xxt, j * yyt, xxt * 2, yyt * 2,
     560                    1 + 7 * (xxt * 2 + 1), 1 + yyt + 19 * (yyt * 3 + 1))
     561                else if MyMap[Loc] and fTerrain >= fForest then
     562                  Sprite(Buffer, HGrTerrain, i * xxt, j * yyt, xxt * 2, yyt * 2,
     563                    1 + 7 * (xxt * 2 + 1),
     564                    1 + yyt + 2 * integer(2 + MyMap[Loc] and fTerrain - fForest)
     565                    * (yyt * 3 + 1));
     566              end;
     567          bitblt(offscreen.Canvas.Handle, xView, yView + 16, 64, 32,
     568            Buffer.Canvas.Handle, 1, 0, SRCCOPY);
     569
     570          // show unit, experience and health
     571          Sprite(offscreen, HGr, xView, yView, 64, 48, pix mod 10 * 65 + 1,
     572            pix div 10 * 49 + 1);
     573          if Flags and unFortified <> 0 then
     574            Sprite(offscreen, HGrStdUnits, xView, yView, xxu * 2, yyu * 2,
     575              1 + 6 * (xxu * 2 + 1), 1);
     576          FrameImage(offscreen.Canvas, GrExt[HGrSystem].Data, xView - 20,
     577            yView + 5, 12, 14, 121 + Exp div ExpCost * 13, 28);
     578          if Health < 100 then
     579          begin
     580            s := IntToStr(Health) + '%';
     581            LightGradient(offscreen.Canvas, xView - 45, yView + 24, 38,
     582              (ColorOfHealth(Health) and $FEFEFE shr 2) * 3);
     583            RisedTextout(offscreen.Canvas, xView - 45 + 20 -
     584              BiColorTextWidth(offscreen.Canvas, s) div 2, yView + 23, s);
     585          end;
     586
     587          if Kind = dkEnemyUnit then
     588          begin
     589            s := Tribe[mox.owner].TPhrase('UNITOWNER');
     590            LoweredTextOut(offscreen.Canvas, -1, MainTexture,
     591              (ClientWidth - BiColorTextWidth(offscreen.Canvas, s)) div 2,
     592              yView + 80, s);
     593          end
     594        end
     595      else
     596      begin
     597        FrameImage(offscreen.Canvas, BigImp, xView + 4, yView, 56, 40, 0, 0);
     598        Sprite(offscreen, HGr, xView, yView - 4, 64, 44, pix mod 10 * 65 + 1,
     599          pix div 10 * 49 + 1);
     600      end;
     601
     602      DarkGradient(offscreen.Canvas, xTotal - 6, yTotal + 1, 180, 2);
     603      RisedTextout(offscreen.Canvas, xTotal - 2, yTotal,
     604        Phrases.Lookup('UNITSTRENGTH'));
     605      s := IntToStr(mi.Attack) + '/' + IntToStr(mi.Defense);
     606      RisedTextout(offscreen.Canvas,
     607        xTotal + 170 - BiColorTextWidth(offscreen.Canvas, s), yTotal, s);
     608      FeatureBar(offscreen, xTotal, yTotal + 19, mi, MainTexture);
     609      NumberBarS(offscreen, xTotal, yTotal + 38, Phrases.Lookup('UNITSPEED'),
     610        MovementToString(mi.Speed), MainTexture);
     611      LoweredTextOut(offscreen.Canvas, -1, MainTexture, xTotal - 2, yTotal + 57,
     612        Phrases.Lookup('UNITCOST'));
     613      DLine(offscreen.Canvas, xTotal - 2, xTotal + 170, yTotal + 57 + 16,
     614        MainTexture.clBevelShade, MainTexture.clBevelLight);
     615      if G.Difficulty[me] = 0 then
     616        s := IntToStr(mi.cost)
     617      else
     618        s := IntToStr(mi.cost * BuildCostMod[G.Difficulty[me]] div 12);
     619      RisedTextout(offscreen.Canvas,
     620        xTotal + 159 - BiColorTextWidth(offscreen.Canvas, s), yTotal + 57, s);
     621      Sprite(offscreen, HGrSystem, xTotal + 160, yTotal + 57 + 5, 10,
     622        10, 88, 115);
     623
     624      if Kind = dkOwnModel then
     625      begin
     626        if MyModel[mixShow].IntroTurn > 0 then
     627        begin
     628          if MyModel[mixShow].Kind = mkEnemyDeveloped then
     629            LoweredTextOut(offscreen.Canvas, -1, MainTexture, xTotal - 2,
     630              (yTotal + StatDown - 19), Phrases.Lookup('UNITADOPT'))
     631          else
     632            LoweredTextOut(offscreen.Canvas, -1, MainTexture, xTotal - 2,
     633              (yTotal + StatDown - 19), Phrases.Lookup('UNITINTRO'));
     634          DLine(offscreen.Canvas, xTotal - 2, xTotal + 170,
     635            (yTotal + StatDown - 19) + 16, MainTexture.clTextShade,
     636            MainTexture.clTextLight);
     637          s := TurnToString(MyModel[mixShow].IntroTurn);
     638          RisedTextout(offscreen.Canvas,
     639            xTotal + 170 - BiColorTextWidth(offscreen.Canvas, s),
     640            (yTotal + StatDown - 19), s);
    482641        end;
    483642
    484       if MyMap[Loc] and fTerrain>=fForest then
    485         begin x:=1+2*(xxt*2+1); y:=1+yyt+2*(yyt*3+1) end
    486       else begin x:=integer(MyMap[Loc] and fTerrain) *(xxt*2+1)+1; y:=1+yyt end;
    487       for j:=-1 to 1 do for i:=-1 to 1 do if (i+j) and 1=0 then
    488         begin
    489         Sprite(Buffer,HGrTerrain,i*xxt,j*yyt,xxt*2,yyt*2,x,y);
    490         if MyMap[Loc] and (fTerrain or fSpecial)=fGrass or fSpecial1 then
    491           Sprite(Buffer,HGrTerrain,i*xxt,j*yyt,xxt*2,yyt*2,1+2*(xxt*2+1),
    492             1+yyt+1*(yyt*3+1))
    493         else if (MyMap[Loc] and fTerrain=fForest)
    494           and IsJungle(Loc div G.lx) then
    495           Sprite(Buffer,HGrTerrain,i*xxt,j*yyt,xxt*2,yyt*2,1+7*(xxt*2+1),
    496             1+yyt+19*(yyt*3+1))
    497         else if MyMap[Loc] and fTerrain>=fForest then
    498           Sprite(Buffer,HGrTerrain,i*xxt,j*yyt,xxt*2,yyt*2,1+7*(xxt*2+1),
    499             1+yyt+2*integer(2+MyMap[Loc] and fTerrain-fForest)*(yyt*3+1));
     643        NumberBar(offscreen, xTotal, yTotal + StatDown,
     644          Phrases.Lookup('UNITBUILT'), MyModel[mixShow].Built, MainTexture);
     645        if MyModel[mixShow].Lost > 0 then
     646          NumberBar(offscreen, xTotal, yTotal + StatDown + 19,
     647            Phrases.Lookup('UNITLOST'), MyModel[mixShow].Lost, MainTexture);
     648        if InProd > 0 then
     649          NumberBar(offscreen, xTotal, yTotal + StatDown + 57,
     650            Phrases.Lookup('UNITINPROD'), InProd, MainTexture);
     651        if Available > 0 then
     652          NumberBar(offscreen, xTotal, yTotal + StatDown + 38,
     653            Phrases.Lookup('UNITAVAILABLE'), Available, MainTexture);
     654
     655        if MyModel[mixShow].Status and msObsolete <> 0 then
     656        begin
     657          SwitchBtn.ButtonIndex := 12;
     658          SwitchBtn.Hint := Phrases.Lookup('BTN_OBSOLETE');
     659        end
     660        else
     661        begin
     662          SwitchBtn.ButtonIndex := 11;
     663          SwitchBtn.Hint := Phrases.Lookup('BTN_NONOBSOLETE');
    500664        end;
    501       BitBlt(offscreen.canvas.handle,xView,yView+16,64,32,Buffer.Canvas.Handle,1,0,
    502         SRCCOPY);
    503 
    504       // show unit, experience and health
    505       Sprite(offscreen,HGr,xView,yView,64,48,pix mod 10 *65+1,pix div 10*49+1);
    506       if Flags and unFortified<>0 then
    507         Sprite(offscreen,HGrStdUnits,xView,yView,xxu*2,yyu*2,1+6*(xxu*2+1),1);
    508       FrameImage(offscreen.canvas,GrExt[HGrSystem].Data,xView-20,yView+5,12,14,
    509         121+Exp div ExpCost *13,28);
    510       if Health<100 then
    511         begin
    512         s:=IntToStr(Health)+'%';
    513         LightGradient(offscreen.canvas,xView-45,yView+24,38,
    514           (ColorOfHealth(Health) and $FEFEFE shr 2)*3);
    515         RisedTextOut(offscreen.canvas,xView-45+20-BiColorTextWidth(offscreen.Canvas,s) div 2,
    516           yView+23,s);
    517         end;
    518 
    519       if Kind=dkEnemyUnit then
    520         begin
    521         s:=Tribe[mox.Owner].TPhrase('UNITOWNER');
    522         LoweredTextOut(offscreen.Canvas, -1, MainTexture,
    523           (ClientWidth-BiColorTextWidth(offscreen.Canvas,s)) div 2, yView+80, s);
     665        if MyModel[mixShow].Status and msAllowConscripts = 0 then
     666        begin
     667          ConscriptsBtn.ButtonIndex := 30;
     668          ConscriptsBtn.Hint := Phrases.Lookup('BTN_NOCONSCRIPTS');
     669        end
     670        else
     671        begin
     672          ConscriptsBtn.ButtonIndex := 29;
     673          ConscriptsBtn.Hint := Phrases.Lookup('BTN_ALLOWCONSCRIPTS');
    524674        end
    525675      end
    526     else
    527       begin
    528       FrameImage(offscreen.canvas,BigImp,xView+4,yView,56,40,0,0);
    529       Sprite(offscreen,HGr,xView,yView-4,64,44,pix mod 10 *65+1,pix div 10*49+1);
    530       end;
    531 
    532     DarkGradient(offscreen.Canvas,xTotal-6,yTotal+1,180,2);
    533     RisedTextOut(offscreen.Canvas,xTotal-2,yTotal,Phrases.Lookup('UNITSTRENGTH'));
    534     s:=IntToStr(mi.Attack)+'/'+IntToStr(mi.Defense);
    535     RisedTextOut(offscreen.Canvas,xTotal+170-BiColorTextWidth(Offscreen.Canvas,s),yTotal,s);
    536     FeatureBar(offscreen,xTotal,yTotal+19,mi,MainTexture);
    537     NumberBarS(offscreen,xTotal,yTotal+38,Phrases.Lookup('UNITSPEED'),MovementToString(mi.Speed),MainTexture);
    538     LoweredTextOut(offscreen.Canvas,-1,MainTexture,xTotal-2,yTotal+57,Phrases.Lookup('UNITCOST'));
    539     DLine(offscreen.Canvas,xTotal-2,xTotal+170,yTotal+57+16,
    540       MainTexture.clBevelShade,MainTexture.clBevelLight);
    541     if G.Difficulty[me]=0 then s:=IntToStr(mi.cost)
    542     else s:=IntToStr(mi.cost*BuildCostMod[G.Difficulty[me]] div 12);
    543     RisedTextout(offscreen.Canvas,xTotal+159-BiColorTextWidth(Offscreen.Canvas,s),yTotal+57,s);
    544     Sprite(offscreen,HGrSystem,xTotal+160,yTotal+57+5,10,10,88,115);
    545 
    546     if Kind=dkOwnModel then
    547       begin
    548       if MyModel[mixShow].IntroTurn>0 then
    549         begin
    550         if MyModel[mixShow].Kind=mkEnemyDeveloped then
    551           LoweredTextOut(offscreen.Canvas,-1,MainTexture,xTotal-2,(yTotal+StatDown-19),Phrases.Lookup('UNITADOPT'))
    552         else LoweredTextOut(offscreen.Canvas,-1,MainTexture,xTotal-2,(yTotal+StatDown-19),Phrases.Lookup('UNITINTRO'));
    553         DLine(offscreen.Canvas,xTotal-2,xTotal+170,(yTotal+StatDown-19)+16,
    554           MainTexture.clTextShade,MainTexture.clTextLight);
    555         s:=TurnToString(MyModel[mixShow].IntroTurn);
    556         RisedTextOut(offscreen.Canvas,xTotal+170-BiColorTextWidth(Offscreen.Canvas,s),(yTotal+StatDown-19),s);
    557         end;
    558 
    559       NumberBar(offscreen,xTotal,yTotal+StatDown,Phrases.Lookup('UNITBUILT'),MyModel[mixShow].Built,MainTexture);
    560       if MyModel[mixShow].Lost>0 then
    561         NumberBar(offscreen,xTotal,yTotal+StatDown+19,Phrases.Lookup('UNITLOST'),MyModel[mixShow].Lost,MainTexture);
    562       if InProd>0 then
    563         NumberBar(offscreen,xTotal,yTotal+StatDown+57,Phrases.Lookup('UNITINPROD'),InProd,MainTexture);
    564       if Available>0 then
    565         NumberBar(offscreen,xTotal,yTotal+StatDown+38,Phrases.Lookup('UNITAVAILABLE'),Available,MainTexture);
    566 
    567       if MyModel[mixShow].Status and msObsolete<>0 then
    568         begin
    569         SwitchBtn.ButtonIndex:=12;
    570         SwitchBtn.Hint:=Phrases.Lookup('BTN_OBSOLETE');
    571         end
    572       else
    573         begin
    574         SwitchBtn.ButtonIndex:=11;
    575         SwitchBtn.Hint:=Phrases.Lookup('BTN_NONOBSOLETE');
    576         end;
    577       if MyModel[mixShow].Status and msAllowConscripts=0 then
    578         begin
    579         ConscriptsBtn.ButtonIndex:=30;
    580         ConscriptsBtn.Hint:=Phrases.Lookup('BTN_NOCONSCRIPTS');
    581         end
    582       else
    583         begin
    584         ConscriptsBtn.ButtonIndex:=29;
    585         ConscriptsBtn.Hint:=Phrases.Lookup('BTN_ALLOWCONSCRIPTS');
    586         end
    587       end
    588     else if Kind=dkEnemyModel then
    589       begin
    590       if Destroyed>0 then
    591         NumberBar(offscreen,xTotal,yTotal+StatDown-19,Phrases.Lookup('UNITDESTROYED'),Destroyed,MainTexture);
    592       if Available>0 then
    593         NumberBar(offscreen,xTotal,yTotal+StatDown,Phrases.Lookup('UNITKNOWN'),Available,MainTexture);
     676      else if Kind = dkEnemyModel then
     677      begin
     678        if Destroyed > 0 then
     679          NumberBar(offscreen, xTotal, yTotal + StatDown - 19,
     680            Phrases.Lookup('UNITDESTROYED'), Destroyed, MainTexture);
     681        if Available > 0 then
     682          NumberBar(offscreen, xTotal, yTotal + StatDown,
     683            Phrases.Lookup('UNITKNOWN'), Available, MainTexture);
    594684      end
    595685    end;
    596686  end;
    597687
    598 offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    599 case Kind of
    600   dkOwnModel,dkEnemyModel: yCaption:=yView+46;
    601   dkEnemyUnit,dkOwnUnit: yCaption:=yView+54;
    602   dkEnemyCityDefense,dkEnemyCity: yCaption:=79;
    603   end;
    604 RisedTextOut(offscreen.Canvas, (ClientWidth-BiColorTextWidth(offscreen.Canvas,caption)) div 2, yCaption, caption);
    605 end; {OffscreenPaint}
     688  offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
     689  case Kind of
     690    dkOwnModel, dkEnemyModel:
     691      yCaption := yView + 46;
     692    dkEnemyUnit, dkOwnUnit:
     693      yCaption := yView + 54;
     694    dkEnemyCityDefense, dkEnemyCity:
     695      yCaption := 79;
     696  end;
     697  RisedTextout(offscreen.Canvas,
     698    (ClientWidth - BiColorTextWidth(offscreen.Canvas, Caption)) div 2,
     699    yCaption, Caption);
     700end; { OffscreenPaint }
    606701
    607702procedure TUnitStatDlg.ModelBoxChange(Sender: TObject);
    608703begin
    609 SmartUpdateContent
     704  SmartUpdateContent
    610705end;
    611706
    612707procedure TUnitStatDlg.SwitchBtnClick(Sender: TObject);
    613708begin
    614 MyModel[mixShow].Status:=MyModel[mixShow].Status xor msObsolete;
    615 if MyModel[mixShow].Status and msObsolete<>0 then
    616   begin
    617   SwitchBtn.ButtonIndex:=12;
    618   SwitchBtn.Hint:=Phrases.Lookup('BTN_OBSOLETE');
     709  MyModel[mixShow].Status := MyModel[mixShow].Status xor msObsolete;
     710  if MyModel[mixShow].Status and msObsolete <> 0 then
     711  begin
     712    SwitchBtn.ButtonIndex := 12;
     713    SwitchBtn.Hint := Phrases.Lookup('BTN_OBSOLETE');
    619714  end
    620 else
    621   begin
    622   SwitchBtn.ButtonIndex:=11;
    623   SwitchBtn.Hint:=Phrases.Lookup('BTN_NONOBSOLETE');
     715  else
     716  begin
     717    SwitchBtn.ButtonIndex := 11;
     718    SwitchBtn.Hint := Phrases.Lookup('BTN_NONOBSOLETE');
    624719  end
    625720end;
     
    627722procedure TUnitStatDlg.ConscriptsBtnClick(Sender: TObject);
    628723begin
    629 MyModel[mixShow].Status:=MyModel[mixShow].Status xor msAllowConscripts;
    630 if MyModel[mixShow].Status and msAllowConscripts=0 then
    631   begin
    632   ConscriptsBtn.ButtonIndex:=30;
    633   ConscriptsBtn.Hint:=Phrases.Lookup('BTN_NOCONSCRIPTS');
     724  MyModel[mixShow].Status := MyModel[mixShow].Status xor msAllowConscripts;
     725  if MyModel[mixShow].Status and msAllowConscripts = 0 then
     726  begin
     727    ConscriptsBtn.ButtonIndex := 30;
     728    ConscriptsBtn.Hint := Phrases.Lookup('BTN_NOCONSCRIPTS');
    634729  end
    635 else
    636   begin
    637   ConscriptsBtn.ButtonIndex:=29;
    638   ConscriptsBtn.Hint:=Phrases.Lookup('BTN_ALLOWCONSCRIPTS');
     730  else
     731  begin
     732    ConscriptsBtn.ButtonIndex := 29;
     733    ConscriptsBtn.Hint := Phrases.Lookup('BTN_ALLOWCONSCRIPTS');
    639734  end
    640735end;
     
    642737procedure TUnitStatDlg.HelpBtnClick(Sender: TObject);
    643738begin
    644 HelpDlg.ShowNewContent(wmPersistent, hkModel, 0)
     739  HelpDlg.ShowNewContent(wmPersistent, hkModel, 0)
    645740end;
    646741
    647742end.
    648 
  • trunk/LocalPlayer/Wonders.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Wonders;
    43
     
    65
    76uses
    8   ScreenTools,BaseWin,Protocol,
     7  ScreenTools, BaseWin, Protocol,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    1615    procedure FormCreate(Sender: TObject);
    1716    procedure CloseBtnClick(Sender: TObject);
    18     procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
    19       Y: Integer);
     17    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    2018    procedure FormShow(Sender: TObject);
    2119    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
     
    2422  public
    2523    procedure OffscreenPaint; override;
    26     procedure ShowNewContent(NewMode: integer);
     24    procedure ShowNewContent(NewMode: Integer);
    2725
    2826  private
    29     xm,ym,Selection: integer;
     27    xm, ym, Selection: Integer;
    3028  end;
    3129
     
    3634
    3735uses
    38 Term, ClientTools, Help,Tribes;
     36  Term, ClientTools, Help, Tribes;
    3937
    4038{$R *.DFM}
    4139
    4240const
    43 RingPosition: array[0..20,0..1] of integer=
    44 ((-80,-32), // Pyramids
    45 (80,-32), // Zeus
    46 (0,-64), // Gardens
    47 (0,0), // Colossus
    48 (0,64), // Lighthouse
    49 (-80,32), // GrLibrary
    50 (-90,114), // Oracle
    51 (80,32), // Sun
    52 (90,-114), // Leo
    53 (-180,0), // Magellan
    54 (90,114), // Mich
    55 (0,0), //{11;}
    56 (180,0), // Newton
    57 (-90,-114), // Bach
    58 (0,0), //{14;}
    59 (-160,-64), // Liberty
    60 (0,128), // Eiffel
    61 (160,-64), // Hoover
    62 (-160,64), // Shinkansen
    63 (0,-128), // Manhattan
    64 (160,64)); // Mir
    65 
     41  RingPosition: array [0 .. 20, 0 .. 1] of Integer = ((-80, -32), // Pyramids
     42    (80, -32), // Zeus
     43    (0, -64), // Gardens
     44    (0, 0), // Colossus
     45    (0, 64), // Lighthouse
     46    (-80, 32), // GrLibrary
     47    (-90, 114), // Oracle
     48    (80, 32), // Sun
     49    (90, -114), // Leo
     50    (-180, 0), // Magellan
     51    (90, 114), // Mich
     52    (0, 0), // {11;}
     53    (180, 0), // Newton
     54    (-90, -114), // Bach
     55    (0, 0), // {14;}
     56    (-160, -64), // Liberty
     57    (0, 128), // Eiffel
     58    (160, -64), // Hoover
     59    (-160, 64), // Shinkansen
     60    (0, -128), // Manhattan
     61    (160, 64)); // Mir
    6662
    6763procedure TWondersDlg.FormCreate(Sender: TObject);
    6864begin
    69 Canvas.Font.Assign(UniFont[ftNormal]);
    70 Canvas.Brush.Style:=bsClear;
    71 InitButtons();
     65  Canvas.Font.Assign(UniFont[ftNormal]);
     66  Canvas.Brush.Style := bsClear;
     67  InitButtons();
    7268end;
    7369
    7470procedure TWondersDlg.FormShow(Sender: TObject);
    7571begin
    76 Selection:=-1;
    77 OffscreenPaint;
    78 end;
    79 
    80 procedure TWondersDlg.ShowNewContent(NewMode: integer);
    81 begin
    82 inherited ShowNewContent(NewMode);
     72  Selection := -1;
     73  OffscreenPaint;
     74end;
     75
     76procedure TWondersDlg.ShowNewContent(NewMode: Integer);
     77begin
     78  inherited ShowNewContent(NewMode);
    8379end;
    8480
    8581procedure TWondersDlg.OffscreenPaint;
    8682type
    87 TLine=array[0..649,0..2] of Byte;
    88 
    89   procedure DarkIcon(i: integer);
     83  TLine = array [0 .. 649, 0 .. 2] of Byte;
     84
     85  procedure DarkIcon(i: Integer);
    9086  var
    91   x,y,ch,x0Dst,y0Dst,x0Src,y0Src,darken,c: integer;
    92   Src,Dst: ^TLine;
    93   begin
    94   x0Dst:=ClientWidth div 2-xSizeBig div 2+RingPosition[i,0];
    95   y0Dst:=ClientHeight div 2-ySizeBig div 2+RingPosition[i,1];
    96   x0Src:=(i mod 7)*xSizeBig;
    97   y0Src:=(i div 7+SystemIconLines)*ySizeBig;
    98   for y:=0 to ySizeBig-1 do
    99     begin
    100     Src:=BigImp.ScanLine[y0Src+y];
    101     Dst:=Offscreen.ScanLine[y0Dst+y];
    102     for x:=0 to xSizeBig-1 do
     87    X, Y, ch, x0Dst, y0Dst, x0Src, y0Src, darken, c: Integer;
     88    Src, Dst: ^TLine;
     89  begin
     90    x0Dst := ClientWidth div 2 - xSizeBig div 2 + RingPosition[i, 0];
     91    y0Dst := ClientHeight div 2 - ySizeBig div 2 + RingPosition[i, 1];
     92    x0Src := (i mod 7) * xSizeBig;
     93    y0Src := (i div 7 + SystemIconLines) * ySizeBig;
     94    for Y := 0 to ySizeBig - 1 do
     95    begin
     96      Src := BigImp.ScanLine[y0Src + Y];
     97      Dst := Offscreen.ScanLine[y0Dst + Y];
     98      for X := 0 to xSizeBig - 1 do
    10399      begin
    104       darken:=((255-Src[x0Src+x][0])*3
    105         +(255-Src[x0Src+x][1])*15
    106         +(255-Src[x0Src+x][2])*9) div 128;
    107       for ch:=0 to 2 do
     100        darken := ((255 - Src[x0Src + X][0]) * 3 + (255 - Src[x0Src + X][1]) *
     101          15 + (255 - Src[x0Src + X][2]) * 9) div 128;
     102        for ch := 0 to 2 do
    108103        begin
    109         c:=Dst[x0Dst+x][ch]-darken;
    110         if c<0 then Dst[x0Dst+x][ch]:=0
    111         else Dst[x0Dst+x][ch]:=c;
     104          c := Dst[x0Dst + X][ch] - darken;
     105          if c < 0 then
     106            Dst[x0Dst + X][ch] := 0
     107          else
     108            Dst[x0Dst + X][ch] := c;
    112109        end
    113110      end
     
    115112  end;
    116113
    117   procedure Glow(i,GlowColor: integer);
    118   begin
    119   GlowFrame(Offscreen, ClientWidth div 2-xSizeBig div 2+RingPosition[i,0],
    120     ClientHeight div 2-ySizeBig div 2+RingPosition[i,1],
    121     xSizeBig, ySizeBig, GlowColor);
     114  procedure Glow(i, GlowColor: Integer);
     115  begin
     116    GlowFrame(Offscreen, ClientWidth div 2 - xSizeBig div 2 + RingPosition[i,
     117      0], ClientHeight div 2 - ySizeBig div 2 + RingPosition[i, 1], xSizeBig,
     118      ySizeBig, GlowColor);
    122119  end;
    123120
    124121const
    125 darken=24;
    126 // space=pi/120;
    127 amax0=15734; // 1 shl 16*tan(pi/12-space);
    128 amin1=19413; // 1 shl 16*tan(pi/12+space);
    129 amax1=62191; // 1 shl 16*tan(pi/4-space);
    130 amin2=69061; // 1 shl 16*tan(pi/4+space);
    131 amax2=221246; // 1 shl 16*tan(5*pi/12-space);
    132 amin3=272977; // 1 shl 16*tan(5*pi/12+space);
     122  darken = 24;
     123  // space=pi/120;
     124  amax0 = 15734; // 1 shl 16*tan(pi/12-space);
     125  amin1 = 19413; // 1 shl 16*tan(pi/12+space);
     126  amax1 = 62191; // 1 shl 16*tan(pi/4-space);
     127  amin2 = 69061; // 1 shl 16*tan(pi/4+space);
     128  amax2 = 221246; // 1 shl 16*tan(5*pi/12-space);
     129  amin3 = 272977; // 1 shl 16*tan(5*pi/12+space);
    133130var
    134 i,x,y,r,ax,ch,c: integer;
    135 HaveWonder: boolean;
    136 Line: array[0..1] of ^TLine;
    137 s: string;
    138 begin
    139 if (OffscreenUser<>nil) and (OffscreenUser<>self) then OffscreenUser.Update;
     131  i, X, Y, r, ax, ch, c: Integer;
     132  HaveWonder: boolean;
     133  Line: array [0 .. 1] of ^TLine;
     134  s: string;
     135begin
     136  if (OffscreenUser <> nil) and (OffscreenUser <> self) then
     137    OffscreenUser.Update;
    140138  // complete working with old owner to prevent rebound
    141 OffscreenUser:=self;
    142 
    143 Fill(Offscreen.Canvas,3,3,ClientWidth-6,ClientHeight-6,
    144   (wMaintexture-ClientWidth) div 2,(hMaintexture-ClientHeight) div 2);
    145 Frame(Offscreen.Canvas,0,0,ClientWidth-1,ClientHeight-1,0,0);
    146 Frame(Offscreen.Canvas,1,1,ClientWidth-2,ClientHeight-2,MainTexture.clBevelLight,MainTexture.clBevelShade);
    147 Frame(Offscreen.Canvas,2,2,ClientWidth-3,ClientHeight-3,MainTexture.clBevelLight,MainTexture.clBevelShade);
    148 Corner(Offscreen.Canvas,1,1,0,MainTexture);
    149 Corner(Offscreen.Canvas,ClientWidth-9,1,1,MainTexture);
    150 Corner(Offscreen.Canvas,1,ClientHeight-9,2,MainTexture);
    151 Corner(Offscreen.Canvas,ClientWidth-9,ClientHeight-9,3,MainTexture);
    152 
    153 BtnFrame(Offscreen.Canvas,CloseBtn.BoundsRect,MainTexture);
    154 
    155 Offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
    156 s:=Phrases.Lookup('TITLE_WONDERS');
    157 RisedTextOut(Offscreen.Canvas,(ClientWidth-BiColorTextWidth(Offscreen.Canvas,s)) div 2-1,7,s);
    158 Offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    159 
    160 xm:=ClientWidth div 2;
    161 ym:=ClientHeight div 2;
    162 for y:=0 to 127 do
    163   begin
    164   Line[0]:=Offscreen.Scanline[ym+y];
    165   Line[1]:=Offscreen.Scanline[ym-1-y];
    166   for x:=0 to 179 do
    167     begin
    168     r:=x*x*(32*32)+y*y*(45*45);
    169     ax:=((1 shl 16 div 32)*45)*y;
    170     if (r<8*128*180*180)
    171       and ((r>=32*64*90*90) and (ax<amax2*x) and ((ax<amax0*x) or (ax>amin2*x))
    172         or (ax>amin1*x) and ((ax<amax1*x) or (ax>amin3*x))) then
    173         for i:=0 to 1 do for ch:=0 to 2 do
     139  OffscreenUser := self;
     140
     141  Fill(Offscreen.Canvas, 3, 3, ClientWidth - 6, ClientHeight - 6,
     142    (wMaintexture - ClientWidth) div 2, (hMaintexture - ClientHeight) div 2);
     143  Frame(Offscreen.Canvas, 0, 0, ClientWidth - 1, ClientHeight - 1, 0, 0);
     144  Frame(Offscreen.Canvas, 1, 1, ClientWidth - 2, ClientHeight - 2,
     145    MainTexture.clBevelLight, MainTexture.clBevelShade);
     146  Frame(Offscreen.Canvas, 2, 2, ClientWidth - 3, ClientHeight - 3,
     147    MainTexture.clBevelLight, MainTexture.clBevelShade);
     148  Corner(Offscreen.Canvas, 1, 1, 0, MainTexture);
     149  Corner(Offscreen.Canvas, ClientWidth - 9, 1, 1, MainTexture);
     150  Corner(Offscreen.Canvas, 1, ClientHeight - 9, 2, MainTexture);
     151  Corner(Offscreen.Canvas, ClientWidth - 9, ClientHeight - 9, 3, MainTexture);
     152
     153  BtnFrame(Offscreen.Canvas, CloseBtn.BoundsRect, MainTexture);
     154
     155  Offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
     156  s := Phrases.Lookup('TITLE_WONDERS');
     157  RisedTextOut(Offscreen.Canvas,
     158    (ClientWidth - BiColorTextWidth(Offscreen.Canvas, s)) div 2 - 1, 7, s);
     159  Offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
     160
     161  xm := ClientWidth div 2;
     162  ym := ClientHeight div 2;
     163  for Y := 0 to 127 do
     164  begin
     165    Line[0] := Offscreen.ScanLine[ym + Y];
     166    Line[1] := Offscreen.ScanLine[ym - 1 - Y];
     167    for X := 0 to 179 do
     168    begin
     169      r := X * X * (32 * 32) + Y * Y * (45 * 45);
     170      ax := ((1 shl 16 div 32) * 45) * Y;
     171      if (r < 8 * 128 * 180 * 180) and
     172        ((r >= 32 * 64 * 90 * 90) and (ax < amax2 * X) and
     173        ((ax < amax0 * X) or (ax > amin2 * X)) or (ax > amin1 * X) and
     174        ((ax < amax1 * X) or (ax > amin3 * X))) then
     175        for i := 0 to 1 do
     176          for ch := 0 to 2 do
    174177          begin
    175           c:=Line[i][xm+x][ch]-darken;
    176           if c<0 then Line[i][xm+x][ch]:=0
    177           else Line[i][xm+x][ch]:=c;
    178           c:=Line[i][xm-1-x][ch]-darken;
    179           if c<0 then Line[i][xm-1-x][ch]:=0
    180           else Line[i][xm-1-x][ch]:=c;
     178            c := Line[i][xm + X][ch] - darken;
     179            if c < 0 then
     180              Line[i][xm + X][ch] := 0
     181            else
     182              Line[i][xm + X][ch] := c;
     183            c := Line[i][xm - 1 - X][ch] - darken;
     184            if c < 0 then
     185              Line[i][xm - 1 - X][ch] := 0
     186            else
     187              Line[i][xm - 1 - X][ch] := c;
    181188          end
    182189    end;
    183190  end;
    184191
    185 HaveWonder:=false;
    186 for i:=0 to 20 do if Imp[i].Preq<>preNA then
    187   begin
    188   case MyRO.Wonder[i].CityID of
    189     -1: // not built yet
     192  HaveWonder := false;
     193  for i := 0 to 20 do
     194    if Imp[i].Preq <> preNA then
     195    begin
     196      case MyRO.Wonder[i].CityID of
     197        - 1: // not built yet
     198          begin
     199            Fill(Offscreen.Canvas, xm - xSizeBig div 2 + RingPosition[i, 0] - 3,
     200              ym - ySizeBig div 2 + RingPosition[i, 1] - 3, xSizeBig + 6,
     201              ySizeBig + 6, (wMaintexture - ClientWidth) div 2,
     202              (hMaintexture - ClientHeight) div 2);
     203            DarkIcon(i);
     204          end;
     205        -2: // destroyed
     206          begin
     207            HaveWonder := true;
     208            Glow(i, $000000);
     209            BitBlt(Offscreen.Canvas.Handle, xm - xSizeBig div 2 + RingPosition
     210              [i, 0], ym - ySizeBig div 2 + RingPosition[i, 1], xSizeBig,
     211              ySizeBig, BigImp.Canvas.Handle, 0, (SystemIconLines + 3) *
     212              ySizeBig, SRCCOPY);
     213          end;
     214      else
     215        begin
     216          HaveWonder := true;
     217          if MyRO.Wonder[i].EffectiveOwner >= 0 then
     218            Glow(i, Tribe[MyRO.Wonder[i].EffectiveOwner].Color)
     219          else
     220            Glow(i, $000000);
     221          BitBlt(Offscreen.Canvas.Handle, xm - xSizeBig div 2 + RingPosition[i,
     222            0], ym - ySizeBig div 2 + RingPosition[i, 1], xSizeBig, ySizeBig,
     223            BigImp.Canvas.Handle, (i mod 7) * xSizeBig,
     224            (i div 7 + SystemIconLines) * ySizeBig, SRCCOPY);
     225        end
     226      end
     227    end;
     228
     229  if not HaveWonder then
     230  begin
     231    s := Phrases.Lookup('NOWONDER');
     232    RisedTextOut(Offscreen.Canvas, xm - BiColorTextWidth(Offscreen.Canvas, s)
     233      div 2, ym - Offscreen.Canvas.TextHeight(s) div 2, s);
     234  end;
     235
     236  MarkUsedOffscreen(ClientWidth, ClientHeight);
     237end; { OffscreenPaint }
     238
     239procedure TWondersDlg.CloseBtnClick(Sender: TObject);
     240begin
     241  Close
     242end;
     243
     244procedure TWondersDlg.FormMouseMove(Sender: TObject; Shift: TShiftState;
     245  X, Y: Integer);
     246var
     247  i, OldSelection: Integer;
     248  s: string;
     249begin
     250  OldSelection := Selection;
     251  Selection := -1;
     252  for i := 0 to 20 do
     253    if (Imp[i].Preq <> preNA) and (X >= xm - xSizeBig div 2 + RingPosition[i, 0]
     254      ) and (X < xm + xSizeBig div 2 + RingPosition[i, 0]) and
     255      (Y >= ym - ySizeBig div 2 + RingPosition[i, 1]) and
     256      (Y < ym + ySizeBig div 2 + RingPosition[i, 1]) then
     257    begin
     258      Selection := i;
     259      break
     260    end;
     261  if Selection <> OldSelection then
     262  begin
     263    Fill(Canvas, 9, ClientHeight - 3 - 46, ClientWidth - 18, 44,
     264      (wMaintexture - ClientWidth) div 2, (hMaintexture - ClientHeight) div 2);
     265    if Selection >= 0 then
     266    begin
     267      if MyRO.Wonder[Selection].CityID = -1 then
     268      begin // not built yet
     269        { s:=Phrases.Lookup('IMPROVEMENTS',Selection);
     270          Canvas.Font.Color:=$000000;
     271          Canvas.TextOut(
     272          (ClientWidth-BiColorTextWidth(Canvas,s)) div 2+1,
     273          ClientHeight-3-36+1, s);
     274          Canvas.Font.Color:=MainTexture.clBevelLight;
     275          Canvas.TextOut(
     276          (ClientWidth-BiColorTextWidth(Canvas,s)) div 2,
     277          ClientHeight-3-36, s); }
     278      end
     279      else
    190280      begin
    191       Fill(Offscreen.Canvas,
    192         xm-xSizeBig div 2+RingPosition[i,0]-3,
    193         ym-ySizeBig div 2+RingPosition[i,1]-3,
    194         xSizeBig+6, ySizeBig+6,
    195         (wMaintexture-ClientWidth) div 2,(hMaintexture-ClientHeight) div 2);
    196       DarkIcon(i);
    197       end;
    198     -2: // destroyed
    199       begin
    200       HaveWonder:=true;
    201       Glow(i,$000000);
    202       BitBlt(Offscreen.Canvas.Handle, xm-xSizeBig div 2+RingPosition[i,0],
    203         ym-ySizeBig div 2+RingPosition[i,1], xSizeBig, ySizeBig,
    204         BigImp.Canvas.Handle, 0, (SystemIconLines+3)*ySizeBig, SRCCOPY);
    205       end;
    206     else
    207       begin
    208       HaveWonder:=true;
    209       if MyRO.Wonder[i].EffectiveOwner>=0 then
    210         Glow(i,Tribe[MyRO.Wonder[i].EffectiveOwner].Color)
    211       else Glow(i,$000000);
    212       BitBlt(Offscreen.Canvas.Handle, xm-xSizeBig div 2+RingPosition[i,0],
    213         ym-ySizeBig div 2+RingPosition[i,1], xSizeBig, ySizeBig,
    214         BigImp.Canvas.Handle, (i mod 7)*xSizeBig,
    215         (i div 7+SystemIconLines)*ySizeBig, SRCCOPY);
    216       end
    217     end
    218   end;
    219 
    220 if not HaveWonder then
    221   begin
    222   s:=Phrases.Lookup('NOWONDER');
    223   RisedTextout(Offscreen.Canvas,xm-BiColorTextWidth(Offscreen.Canvas,s) div 2,
    224     ym-Offscreen.Canvas.TextHeight(s) div 2, s);
    225   end;
    226 
    227 MarkUsedOffscreen(ClientWidth,ClientHeight);
    228 end; {OffscreenPaint}
    229 
    230 procedure TWondersDlg.CloseBtnClick(Sender: TObject);
    231 begin
    232 Close
    233 end;
    234 
    235 procedure TWondersDlg.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
    236   Y: Integer);
    237 var
    238 i,OldSelection: integer;
    239 s: string;
    240 begin
    241 OldSelection:=Selection;
    242 Selection:=-1;
    243 for i:=0 to 20 do
    244   if (Imp[i].Preq<>preNA) and (x>=xm-xSizeBig div 2+RingPosition[i,0])
    245     and (x<xm+xSizeBig div 2+RingPosition[i,0])
    246     and (y>=ym-ySizeBig div 2+RingPosition[i,1])
    247     and (y<ym+ySizeBig div 2+RingPosition[i,1]) then
    248     begin Selection:=i; break end;
    249 if Selection<>OldSelection then
    250   begin
    251   Fill(Canvas,9,ClientHeight-3-46,ClientWidth-18,44,
    252     (wMaintexture-ClientWidth) div 2,(hMaintexture-ClientHeight) div 2);
    253   if Selection>=0 then
    254     begin
    255     if MyRO.Wonder[Selection].CityID=-1 then
    256       begin // not built yet
    257 {      s:=Phrases.Lookup('IMPROVEMENTS',Selection);
    258       Canvas.Font.Color:=$000000;
    259       Canvas.TextOut(
    260         (ClientWidth-BiColorTextWidth(Canvas,s)) div 2+1,
    261         ClientHeight-3-36+1, s);
    262       Canvas.Font.Color:=MainTexture.clBevelLight;
    263       Canvas.TextOut(
    264         (ClientWidth-BiColorTextWidth(Canvas,s)) div 2,
    265         ClientHeight-3-36, s);}
    266       end
    267     else
    268       begin
    269       s:=Phrases.Lookup('IMPROVEMENTS',Selection);
    270       if MyRO.Wonder[Selection].CityID<>-2 then
    271         s:=Format(Phrases.Lookup('WONDEROF'),
    272           [s,CityName(MyRO.Wonder[Selection].CityID)]);
    273       LoweredTextOut(Canvas, -1, MainTexture, (ClientWidth-BiColorTextWidth(Canvas,s)) div 2,
    274         ClientHeight-3-36-10, s);
    275       if MyRO.Wonder[Selection].CityID=-2 then
    276         s:=Phrases.Lookup('DESTROYED')
    277       else if MyRO.Wonder[Selection].EffectiveOwner<0 then
    278         s:=Phrases.Lookup('EXPIRED')
    279       else s:=Tribe[MyRO.Wonder[Selection].EffectiveOwner].TPhrase('WONDEROWNER');
    280       LoweredTextOut(Canvas, -1, MainTexture, (ClientWidth-BiColorTextWidth(Canvas,s)) div 2,
    281         ClientHeight-3-36+10, s);
     281        s := Phrases.Lookup('IMPROVEMENTS', Selection);
     282        if MyRO.Wonder[Selection].CityID <> -2 then
     283          s := Format(Phrases.Lookup('WONDEROF'),
     284            [s, CityName(MyRO.Wonder[Selection].CityID)]);
     285        LoweredTextOut(Canvas, -1, MainTexture,
     286          (ClientWidth - BiColorTextWidth(Canvas, s)) div 2,
     287          ClientHeight - 3 - 36 - 10, s);
     288        if MyRO.Wonder[Selection].CityID = -2 then
     289          s := Phrases.Lookup('DESTROYED')
     290        else if MyRO.Wonder[Selection].EffectiveOwner < 0 then
     291          s := Phrases.Lookup('EXPIRED')
     292        else
     293          s := Tribe[MyRO.Wonder[Selection].EffectiveOwner]
     294            .TPhrase('WONDEROWNER');
     295        LoweredTextOut(Canvas, -1, MainTexture,
     296          (ClientWidth - BiColorTextWidth(Canvas, s)) div 2,
     297          ClientHeight - 3 - 36 + 10, s);
    282298      end
    283299    end;
     
    288304  Shift: TShiftState; X, Y: Integer);
    289305begin
    290 if Selection>=0 then
    291   HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp, Selection);
     306  if Selection >= 0 then
     307    HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp, Selection);
    292308end;
    293309
    294310end.
    295 
  • trunk/Log.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit log;
    43
     
    2827    procedure mClearClick(Sender: TObject);
    2928    procedure mSlotClick(Sender: TObject);
    30     procedure FormKeyDown(Sender: TObject; var Key: word;
    31       Shift: TShiftState);
     29    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
    3230    procedure Toggle(Sender: TObject);
    3331    procedure ListMouseDown(Sender: TObject; Button: TMouseButton;
    3432      Shift: TShiftState; X, Y: Integer);
    35     procedure FormKeyUp(Sender: TObject; var Key: Word;
    36       Shift: TShiftState);
     33    procedure FormKeyUp(Sender: TObject; var Key: word; Shift: TShiftState);
    3734  public
    3835    Host: TForm;
    39     procedure Add(Level, Turn: integer; Text: pchar);
     36    procedure Add(Level, Turn: Integer; Text: pchar);
    4037  private
    41     MaxLevel: integer;
     38    MaxLevel: Integer;
    4239  end;
    4340
     
    4845
    4946uses
    50 ClientTools,Tribes;
     47  ClientTools, Tribes;
    5148
    5249{$R *.DFM}
    5350
    5451const
    55 MaxLines=1000;
     52  MaxLines = 1000;
    5653
    5754procedure TLogDlg.FormCreate(Sender: TObject);
    5855begin
    59 MaxLevel:=0;
     56  MaxLevel := 0;
    6057end;
    6158
    6259procedure TLogDlg.mLogClick(Sender: TObject);
    6360begin
    64 MaxLevel:=TMenuItem(Sender).Tag;
    65 TMenuItem(Sender).Checked:=true;
     61  MaxLevel := TMenuItem(Sender).Tag;
     62  TMenuItem(Sender).Checked := true;
    6663end;
    6764
    68 procedure TLogDlg.Add(Level, Turn: integer; Text: pchar);
     65procedure TLogDlg.Add(Level, Turn: Integer; Text: pchar);
    6966begin
    70 if (MaxLevel>0) and (Level<=MaxLevel)
    71   or (Level=1 shl 16+1) and mInvalid.Checked
    72   or (Level=1 shl 16+2) and mTime.Checked
    73   or (Level=1 shl 16+3) and mNegotiation.Checked then
     67  if (MaxLevel > 0) and (Level <= MaxLevel) or (Level = 1 shl 16 + 1) and
     68    mInvalid.Checked or (Level = 1 shl 16 + 2) and mTime.Checked or
     69    (Level = 1 shl 16 + 3) and mNegotiation.Checked then
    7470  begin
    75   if List.Lines.Count=MaxLines then List.Lines.Delete(0);
    76   List.Lines.Add(char(48+Turn div 100 mod 10)
    77     +char(48+Turn div 10 mod 10)+char(48+Turn mod 10)+' '+Text);
    78   PostMessage(List.Handle,WM_VSCROLL,SB_BOTTOM,0);
    79   Update;
     71    if List.Lines.Count = MaxLines then
     72      List.Lines.Delete(0);
     73    List.Lines.Add(char(48 + Turn div 100 mod 10) +
     74      char(48 + Turn div 10 mod 10) + char(48 + Turn mod 10) + ' ' + Text);
     75    PostMessage(List.Handle, WM_VSCROLL, SB_BOTTOM, 0);
     76    Update;
    8077  end;
    8178end;
     
    8380procedure TLogDlg.mClearClick(Sender: TObject);
    8481begin
    85 List.Clear;
     82  List.Clear;
    8683end;
    8784
    8885procedure TLogDlg.mSlotClick(Sender: TObject);
    8986const
    90 SlotNo: array[0..2,0..2] of integer=((8,1,2),(7,0,3),(6,5,4));
     87  SlotNo: array [0 .. 2, 0 .. 2] of Integer = ((8, 1, 2), (7, 0, 3), (6, 5, 4));
    9188var
    92 x,y: integer;
    93 s: string;
     89  X, Y: Integer;
     90  s: string;
    9491begin
    95 for y:=0 to 2 do
     92  for Y := 0 to 2 do
    9693  begin
    97   s:='| ';
    98   for x:=0 to 2 do
    99     if G.Difficulty[SlotNo[y,x]]=0 then s:=s+'SUP |'
    100     else if G.Difficulty[SlotNo[y,x]]<0 then s:=s+'--- |'
    101     else
     94    s := '| ';
     95    for X := 0 to 2 do
     96      if G.Difficulty[SlotNo[Y, X]] = 0 then
     97        s := s + 'SUP |'
     98      else if G.Difficulty[SlotNo[Y, X]] < 0 then
     99        s := s + '--- |'
     100      else
    102101      begin
    103       if SlotNo[y,x] in [6..8] then
     102        if SlotNo[Y, X] in [6 .. 8] then
    104103        begin // check multi control
    105         if G.Difficulty[SlotNo[y,x]+3]>=0 then
    106           s:=s+Tribe[SlotNo[y,x]+3].TPhrase('SHORTNAME')+'+';
    107         if G.Difficulty[SlotNo[y,x]+6]>=0 then
    108           s:=s+Tribe[SlotNo[y,x]+6].TPhrase('SHORTNAME')+'+';
     104          if G.Difficulty[SlotNo[Y, X] + 3] >= 0 then
     105            s := s + Tribe[SlotNo[Y, X] + 3].TPhrase('SHORTNAME') + '+';
     106          if G.Difficulty[SlotNo[Y, X] + 6] >= 0 then
     107            s := s + Tribe[SlotNo[Y, X] + 6].TPhrase('SHORTNAME') + '+';
    109108        end;
    110       s:=s+Tribe[SlotNo[y,x]].TPhrase('SHORTNAME')+' | ';
     109        s := s + Tribe[SlotNo[Y, X]].TPhrase('SHORTNAME') + ' | ';
    111110      end;
    112   List.Lines.Add(s)
     111    List.Lines.Add(s)
    113112  end;
    114 PostMessage(List.Handle,WM_VSCROLL,SB_BOTTOM,0);
     113  PostMessage(List.Handle, WM_VSCROLL, SB_BOTTOM, 0);
    115114end;
    116115
     
    118117  Shift: TShiftState);
    119118begin
    120 if Host<>nil then
    121   Host.OnKeyDown(Sender, Key, Shift);
     119  if Host <> nil then
     120    Host.OnKeyDown(Sender, Key, Shift);
    122121end;
    123122
    124123procedure TLogDlg.Toggle(Sender: TObject);
    125124begin
    126 TMenuItem(Sender).Checked:=not TMenuItem(Sender).Checked;
     125  TMenuItem(Sender).Checked := not TMenuItem(Sender).Checked;
    127126end;
    128127
     
    130129  Shift: TShiftState; X, Y: Integer);
    131130begin
    132 if Button=mbRight then LogPopup.Popup(Left+x,Top+y);
     131  if Button = mbRight then
     132    LogPopup.Popup(Left + X, Top + Y);
    133133end;
    134134
    135 procedure TLogDlg.FormKeyUp(Sender: TObject; var Key: Word;
    136   Shift: TShiftState);
     135procedure TLogDlg.FormKeyUp(Sender: TObject; var Key: word; Shift: TShiftState);
    137136begin
    138 if Host<>nil then
    139   Host.OnKeyUp(Sender, Key, Shift);
     137  if Host <> nil then
     138    Host.OnKeyUp(Sender, Key, Shift);
    140139end;
    141140
    142141end.
    143 
  • trunk/Messg.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Messg;
    43
     
    87  ScreenTools,
    98
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,ButtonBase,ButtonA,
    11   ButtonB,Area;
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ButtonBase,
     10  ButtonA,
     11  ButtonB, Area;
    1212
    1313const
    14 WM_PLAYSOUND=WM_USER;
     14  WM_PLAYSOUND = WM_USER;
    1515
    1616type
     
    2020    procedure SmartInvalidate; virtual;
    2121  protected
    22     TitleHeight: integer; // defines area to grip the window for moving (from top)
     22    TitleHeight: integer;
     23    // defines area to grip the window for moving (from top)
    2324    procedure InitButtons();
    24     procedure OnEraseBkgnd(var m:TMessage); message WM_ERASEBKGND;
    25     procedure OnHitTest(var Msg:TMessage); message WM_NCHITTEST;
     25    procedure OnEraseBkgnd(var m: TMessage); message WM_ERASEBKGND;
     26    procedure OnHitTest(var Msg: TMessage); message WM_NCHITTEST;
    2627  end;
    2728
    2829  TBaseMessgDlg = class(TDrawDlg)
    2930    procedure FormCreate(Sender: TObject);
    30     procedure FormPaint(Sender:TObject);
     31    procedure FormPaint(Sender: TObject);
    3132  public
    3233    MessgText: string;
     
    4041    Button1: TButtonA;
    4142    Button2: TButtonA;
    42     procedure FormCreate(Sender:TObject);
    43     procedure FormPaint(Sender:TObject);
     43    procedure FormCreate(Sender: TObject);
     44    procedure FormPaint(Sender: TObject);
    4445    procedure FormShow(Sender: TObject);
    4546    procedure Button1Click(Sender: TObject);
     
    5051    OpenSound: string;
    5152  private
    52     procedure OnPlaySound(var Msg:TMessage); message WM_PLAYSOUND;
     53    procedure OnPlaySound(var Msg: TMessage); message WM_PLAYSOUND;
    5354  end;
    5455
    5556const
    56 // message kinds
    57 mkOK=1; mkOKCancel=2; mkYesNo=3;
    58 
    59 Border=3;
    60 MessageLineSpacing=20;
    61 
    62 var
    63   MessgDlg:TMessgDlg;
     57  // message kinds
     58  mkOK = 1;
     59  mkOKCancel = 2;
     60  mkYesNo = 3;
     61
     62  Border = 3;
     63  MessageLineSpacing = 20;
     64
     65var
     66  MessgDlg: TMessgDlg;
    6467
    6568procedure SimpleMessage(SimpleText: string);
    6669procedure SoundMessage(SimpleText, SoundItem: string);
    6770
    68 
    6971implementation
    7072
     
    7375constructor TDrawDlg.Create(AOwner: TComponent);
    7476begin
    75 inherited;
    76 TitleHeight:=0;
    77 end;
    78 
    79 procedure TDrawDlg.OnEraseBkgnd(var m:TMessage);
    80 begin
    81 end;
    82 
    83 procedure TDrawDlg.OnHitTest(var Msg:TMessage);
    84 var
    85 i: integer;
    86 ControlBounds: TRect;
    87 begin
    88 if BorderStyle<>bsNone then
    89   inherited
    90 else
    91   begin
    92   if integer(Msg.LParamHi)>=Top+TitleHeight then
    93     Msg.result:=HTCLIENT
     77  inherited;
     78  TitleHeight := 0;
     79end;
     80
     81procedure TDrawDlg.OnEraseBkgnd(var m: TMessage);
     82begin
     83end;
     84
     85procedure TDrawDlg.OnHitTest(var Msg: TMessage);
     86var
     87  i: integer;
     88  ControlBounds: TRect;
     89begin
     90  if BorderStyle <> bsNone then
     91    inherited
    9492  else
    95     begin
    96     for i:=0 to ControlCount-1 do if Controls[i].Visible then
    97       begin
    98       ControlBounds:=Controls[i].BoundsRect;
    99       if (integer(Msg.LParamLo)>=Left+ControlBounds.Left)
    100         and (integer(Msg.LParamLo)<Left+ControlBounds.Right)
    101         and (integer(Msg.LParamHi)>=Top+ControlBounds.Top)
    102         and (integer(Msg.LParamHi)<Top+ControlBounds.Bottom) then
     93  begin
     94    if integer(Msg.LParamHi) >= Top + TitleHeight then
     95      Msg.result := HTCLIENT
     96    else
     97    begin
     98      for i := 0 to ControlCount - 1 do
     99        if Controls[i].Visible then
    103100        begin
    104         Msg.result:=HTCLIENT;
    105         exit;
     101          ControlBounds := Controls[i].BoundsRect;
     102          if (integer(Msg.LParamLo) >= Left + ControlBounds.Left) and
     103            (integer(Msg.LParamLo) < Left + ControlBounds.Right) and
     104            (integer(Msg.LParamHi) >= Top + ControlBounds.Top) and
     105            (integer(Msg.LParamHi) < Top + ControlBounds.Bottom) then
     106          begin
     107            Msg.result := HTCLIENT;
     108            exit;
     109          end;
    106110        end;
    107       end;
    108     Msg.result:=HTCAPTION
     111      Msg.result := HTCAPTION
    109112    end;
    110113  end
     
    113116procedure TDrawDlg.InitButtons();
    114117var
    115 cix: integer;
    116 //ButtonDownSound, ButtonUpSound: string;
    117 begin
    118 //ButtonDownSound:=Sounds.Lookup('BUTTON_DOWN');
    119 //ButtonUpSound:=Sounds.Lookup('BUTTON_UP');
    120 for cix:=0 to ComponentCount-1 do
    121   if Components[cix] is TButtonBase then
    122     begin
    123     TButtonBase(Components[cix]).Graphic:=GrExt[HGrSystem].Data;
    124 //      if ButtonDownSound<>'*' then
    125 //        DownSound:=HomeDir+'Sounds\'+ButtonDownSound+'.wav';
    126 //      if ButtonUpSound<>'*' then
    127 //        UpSound:=HomeDir+'Sounds\'+ButtonUpSound+'.wav';
    128     if Components[cix] is TButtonA then
    129       TButtonA(Components[cix]).Font:=UniFont[ftButton];
    130     if Components[cix] is TButtonB then
    131       TButtonB(Components[cix]).Mask:=GrExt[HGrSystem].Mask;
     118  cix: integer;
     119  // ButtonDownSound, ButtonUpSound: string;
     120begin
     121  // ButtonDownSound:=Sounds.Lookup('BUTTON_DOWN');
     122  // ButtonUpSound:=Sounds.Lookup('BUTTON_UP');
     123  for cix := 0 to ComponentCount - 1 do
     124    if Components[cix] is TButtonBase then
     125    begin
     126      TButtonBase(Components[cix]).Graphic := GrExt[HGrSystem].Data;
     127      // if ButtonDownSound<>'*' then
     128      // DownSound:=HomeDir+'Sounds\'+ButtonDownSound+'.wav';
     129      // if ButtonUpSound<>'*' then
     130      // UpSound:=HomeDir+'Sounds\'+ButtonUpSound+'.wav';
     131      if Components[cix] is TButtonA then
     132        TButtonA(Components[cix]).Font := UniFont[ftButton];
     133      if Components[cix] is TButtonB then
     134        TButtonB(Components[cix]).Mask := GrExt[HGrSystem].Mask;
    132135    end;
    133136end;
     
    135138procedure TDrawDlg.SmartInvalidate;
    136139var
    137 i: integer;
    138 r0,r1: HRgn;
    139 begin
    140 r0:=CreateRectRgn(0,0,ClientWidth,ClientHeight);
    141 for i:=0 to ControlCount-1 do
    142   if not (Controls[i] is TArea) and Controls[i].Visible then
    143     begin
    144     with Controls[i].BoundsRect do
    145       r1:=CreateRectRgn(Left,Top,Right,Bottom);
    146     CombineRgn(r0,r0,r1,RGN_DIFF);
    147     DeleteObject(r1);
    148     end;
    149 InvalidateRgn(Handle,r0,false);
    150 DeleteObject(r0);
     140  i: integer;
     141  r0, r1: HRgn;
     142begin
     143  r0 := CreateRectRgn(0, 0, ClientWidth, ClientHeight);
     144  for i := 0 to ControlCount - 1 do
     145    if not(Controls[i] is TArea) and Controls[i].Visible then
     146    begin
     147      with Controls[i].BoundsRect do
     148        r1 := CreateRectRgn(Left, Top, Right, Bottom);
     149      CombineRgn(r0, r0, r1, RGN_DIFF);
     150      DeleteObject(r1);
     151    end;
     152  InvalidateRgn(Handle, r0, false);
     153  DeleteObject(r0);
    151154end;
    152155
    153156procedure TBaseMessgDlg.FormCreate(Sender: TObject);
    154157begin
    155 Left:=(Screen.Width-ClientWidth) div 2;
    156 Canvas.Font.Assign(UniFont[ftNormal]);
    157 Canvas.Brush.Style:=bsClear;
    158 MessgText:='';
    159 TopSpace:=0;
    160 TitleHeight:=Screen.Height;
    161 InitButtons();
    162 end;
    163 
    164 procedure TBaseMessgDlg.FormPaint(Sender:TObject);
    165 var
    166 i,cix: integer;
    167 begin
    168 PaintBackground(self,3+Border,3+Border,ClientWidth-(6+2*Border),
    169   ClientHeight-(6+2*Border));
    170 for i:=0 to Border do
    171   Frame(Canvas,i,i,ClientWidth-1-i,ClientHeight-1-i,
    172     $000000,$000000);
    173 Frame(Canvas,Border+1,Border+1,ClientWidth-(2+Border),ClientHeight-(2+Border),
    174   MainTexture.clBevelLight,MainTexture.clBevelShade);
    175 Frame(Canvas,2+Border,2+Border,ClientWidth-(3+Border),ClientHeight-(3+Border),
    176   MainTexture.clBevelLight,MainTexture.clBevelShade);
    177 SplitText(false);
    178 
    179 for cix:=0 to ControlCount-1 do
    180   if (Controls[cix].Visible) and (Controls[cix] is TButtonBase) then
    181     BtnFrame(Canvas,Controls[cix].BoundsRect,MainTexture);
     158  Left := (Screen.Width - ClientWidth) div 2;
     159  Canvas.Font.Assign(UniFont[ftNormal]);
     160  Canvas.Brush.Style := bsClear;
     161  MessgText := '';
     162  TopSpace := 0;
     163  TitleHeight := Screen.Height;
     164  InitButtons();
     165end;
     166
     167procedure TBaseMessgDlg.FormPaint(Sender: TObject);
     168var
     169  i, cix: integer;
     170begin
     171  PaintBackground(self, 3 + Border, 3 + Border, ClientWidth - (6 + 2 * Border),
     172    ClientHeight - (6 + 2 * Border));
     173  for i := 0 to Border do
     174    Frame(Canvas, i, i, ClientWidth - 1 - i, ClientHeight - 1 - i,
     175      $000000, $000000);
     176  Frame(Canvas, Border + 1, Border + 1, ClientWidth - (2 + Border),
     177    ClientHeight - (2 + Border), MainTexture.clBevelLight,
     178    MainTexture.clBevelShade);
     179  Frame(Canvas, 2 + Border, 2 + Border, ClientWidth - (3 + Border),
     180    ClientHeight - (3 + Border), MainTexture.clBevelLight,
     181    MainTexture.clBevelShade);
     182  SplitText(false);
     183
     184  for cix := 0 to ControlCount - 1 do
     185    if (Controls[cix].Visible) and (Controls[cix] is TButtonBase) then
     186      BtnFrame(Canvas, Controls[cix].BoundsRect, MainTexture);
    182187end;
    183188
    184189procedure TBaseMessgDlg.SplitText(preview: boolean);
    185190var
    186 Start,Stop,OrdinaryStop,LinesCount: integer;
    187 s: string;
    188 begin
    189 Start:=1;
    190 LinesCount:=0;
    191 while Start<Length(MessgText) do
    192   begin
    193   Stop:=Start;
    194   while(Stop<Length(MessgText)) and (MessgText[Stop]<>'\')
    195     and (BiColorTextWidth(Canvas,Copy(MessgText,Start,Stop-Start+1))
    196       <ClientWidth-56) do
    197     inc(Stop);
    198   if Stop<>Length(MessgText) then
    199     begin
    200     OrdinaryStop:=Stop;
    201     repeat dec(OrdinaryStop)
    202     until (MessgText[OrdinaryStop+1]=' ') or (MessgText[OrdinaryStop+1]='\');
    203     if (OrdinaryStop+1-Start)*2>=Stop-Start then
    204       Stop:=OrdinaryStop
    205     end;
    206   if not preview then
    207     begin
    208     s:=Copy(MessgText,Start,Stop-Start+1);
    209     LoweredTextOut(Canvas,-1,MainTexture,
    210       (ClientWidth-BiColorTextWidth(Canvas,s)) div 2,19+Border+TopSpace+LinesCount*MessageLineSpacing,s);
    211     end;
    212   Start:=Stop+2;
    213   inc(LinesCount)
    214   end;
    215 if preview then Lines:=LinesCount;
     191  Start, Stop, OrdinaryStop, LinesCount: integer;
     192  s: string;
     193begin
     194  Start := 1;
     195  LinesCount := 0;
     196  while Start < Length(MessgText) do
     197  begin
     198    Stop := Start;
     199    while (Stop < Length(MessgText)) and (MessgText[Stop] <> '\') and
     200      (BiColorTextWidth(Canvas, Copy(MessgText, Start, Stop - Start + 1)) <
     201      ClientWidth - 56) do
     202      inc(Stop);
     203    if Stop <> Length(MessgText) then
     204    begin
     205      OrdinaryStop := Stop;
     206      repeat
     207        dec(OrdinaryStop)
     208      until (MessgText[OrdinaryStop + 1] = ' ') or
     209        (MessgText[OrdinaryStop + 1] = '\');
     210      if (OrdinaryStop + 1 - Start) * 2 >= Stop - Start then
     211        Stop := OrdinaryStop
     212    end;
     213    if not preview then
     214    begin
     215      s := Copy(MessgText, Start, Stop - Start + 1);
     216      LoweredTextOut(Canvas, -1, MainTexture,
     217        (ClientWidth - BiColorTextWidth(Canvas, s)) div 2,
     218        19 + Border + TopSpace + LinesCount * MessageLineSpacing, s);
     219    end;
     220    Start := Stop + 2;
     221    inc(LinesCount)
     222  end;
     223  if preview then
     224    Lines := LinesCount;
    216225end;
    217226
    218227procedure TBaseMessgDlg.CorrectHeight;
    219228var
    220 i: integer;
    221 begin
    222 ClientHeight:=72+Border+TopSpace+Lines*MessageLineSpacing;
    223 Top:=(Screen.Height-ClientHeight) div 2;
    224 for i:=0 to ControlCount-1 do
    225   Controls[i].Top:=ClientHeight-(34+Border);
    226 end;
    227 
    228 procedure TMessgDlg.FormCreate(Sender:TObject);
    229 begin
    230 inherited;
    231 OpenSound:='';
     229  i: integer;
     230begin
     231  ClientHeight := 72 + Border + TopSpace + Lines * MessageLineSpacing;
     232  Top := (Screen.Height - ClientHeight) div 2;
     233  for i := 0 to ControlCount - 1 do
     234    Controls[i].Top := ClientHeight - (34 + Border);
     235end;
     236
     237procedure TMessgDlg.FormCreate(Sender: TObject);
     238begin
     239  inherited;
     240  OpenSound := '';
    232241end;
    233242
    234243procedure TMessgDlg.FormShow(Sender: TObject);
    235244begin
    236 Button1.Visible:=true;
    237 Button2.Visible:= not (Kind in [mkOK]);
    238 if Button2.Visible then Button1.Left:=101
    239 else Button1.Left:=159;
    240 if Kind=mkYesNo then
    241   begin
    242   Button1.Caption:=Phrases.Lookup('BTN_YES');
    243   Button2.Caption:=Phrases.Lookup('BTN_NO')
     245  Button1.Visible := true;
     246  Button2.Visible := not(Kind in [mkOK]);
     247  if Button2.Visible then
     248    Button1.Left := 101
     249  else
     250    Button1.Left := 159;
     251  if Kind = mkYesNo then
     252  begin
     253    Button1.Caption := Phrases.Lookup('BTN_YES');
     254    Button2.Caption := Phrases.Lookup('BTN_NO')
    244255  end
    245 else
    246   begin
    247   Button1.Caption:=Phrases.Lookup('BTN_OK');
    248   Button2.Caption:=Phrases.Lookup('BTN_CANCEL');
    249   end;
    250 
    251 SplitText(true);
    252 CorrectHeight;
    253 end;
    254 
    255 procedure TMessgDlg.FormPaint(Sender:TObject);
    256 begin
    257 inherited;
    258 if OpenSound<>'' then PostMessage(Handle, WM_PLAYSOUND, 0, 0);
    259 end; {FormPaint}
     256  else
     257  begin
     258    Button1.Caption := Phrases.Lookup('BTN_OK');
     259    Button2.Caption := Phrases.Lookup('BTN_CANCEL');
     260  end;
     261
     262  SplitText(true);
     263  CorrectHeight;
     264end;
     265
     266procedure TMessgDlg.FormPaint(Sender: TObject);
     267begin
     268  inherited;
     269  if OpenSound <> '' then
     270    PostMessage(Handle, WM_PLAYSOUND, 0, 0);
     271end; { FormPaint }
    260272
    261273procedure TMessgDlg.Button1Click(Sender: TObject);
    262274begin
    263 ModalResult:=mrOK;
     275  ModalResult := mrOK;
    264276end;
    265277
    266278procedure TMessgDlg.Button2Click(Sender: TObject);
    267279begin
    268 ModalResult:=mrIgnore;
     280  ModalResult := mrIgnore;
    269281end;
    270282
    271283procedure TMessgDlg.FormKeyPress(Sender: TObject; var Key: char);
    272284begin
    273 if Key=#13 then ModalResult:=mrOK
    274 //else if (Key=#27) and (Button2.Visible) then ModalResult:=mrCancel
     285  if Key = #13 then
     286    ModalResult := mrOK
     287    // else if (Key=#27) and (Button2.Visible) then ModalResult:=mrCancel
    275288end;
    276289
    277290procedure SimpleMessage(SimpleText: string);
    278291begin
    279 with MessgDlg do
    280   begin
    281   MessgText:=SimpleText;
    282   Kind:=mkOK;
    283   ShowModal;
     292  with MessgDlg do
     293  begin
     294    MessgText := SimpleText;
     295    Kind := mkOK;
     296    ShowModal;
    284297  end
    285298end;
     
    287300procedure SoundMessage(SimpleText, SoundItem: string);
    288301begin
    289 with MessgDlg do
    290   begin
    291   MessgText:=SimpleText;
    292   OpenSound:=SoundItem;
    293   Kind:=mkOK;
    294   ShowModal;
     302  with MessgDlg do
     303  begin
     304    MessgText := SimpleText;
     305    OpenSound := SoundItem;
     306    Kind := mkOK;
     307    ShowModal;
    295308  end
    296309end;
    297310
    298 procedure TMessgDlg.OnPlaySound(var Msg:TMessage);
    299 begin
    300 Play(OpenSound);
    301 OpenSound:='';
     311procedure TMessgDlg.OnPlaySound(var Msg: TMessage);
     312begin
     313  Play(OpenSound);
     314  OpenSound := '';
    302315end;
    303316
    304317end.
    305 
  • trunk/NoTerm.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit NoTerm;
    43
     
    65
    76uses
    8   ScreenTools,Protocol,Messg,
     7  ScreenTools, Protocol, Messg,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    1918    procedure FormPaint(Sender: TObject);
    2019    procedure FormCreate(Sender: TObject);
    21     procedure FormKeyDown(Sender: TObject; var Key: word;
    22       Shift: TShiftState);
     20    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
    2321  public
    2422    procedure Client(Command, Player: integer; var Data);
    2523  private
    2624    me, Active, ToldAlive, Round: integer;
    27     PerfFreq,LastShowYearTime,LastShowTurnChange,LastNewTurn: int64;
    28     TurnTime,TotalStatTime: extended;
     25    PerfFreq, LastShowYearTime, LastShowTurnChange, LastNewTurn: int64;
     26    TurnTime, TotalStatTime: extended;
    2927    G: TNewGameData;
    3028    Server: TServerCall;
    3129    Shade, State: TBitmap;
    32     WinStat, ExtStat, AloneStat: array[0..nPl-1] of integer;
    33     DisallowShowActive: array[0..nPl-1] of boolean;
    34     TimeStat: array[0..nPl-1] of extended;
     30    WinStat, ExtStat, AloneStat: array [0 .. nPl - 1] of integer;
     31    DisallowShowActive: array [0 .. nPl - 1] of boolean;
     32    TimeStat: array [0 .. nPl - 1] of extended;
    3533    Mode: (Stop, Stopped, Running, Quit);
    3634    procedure NewStat;
     
    4341  NoTermDlg: TNoTermDlg;
    4442
    45 procedure Client(Command,Player:integer;var Data); stdcall;
     43procedure Client(Command, Player: integer; var Data); stdcall;
    4644
    4745implementation
     
    5250
    5351const
    54 UpdateInterval=0.1; // seconds
    55 ShowActiveThreshold=0.05; // seconds
    56 
    57 nPlOffered=9;
    58 x0Brain=109+48+23; y0Brain=124+48+7+16;
    59 dxBrain=128; dyBrain=128;
    60 xBrain: array[0..nPlOffered-1] of integer =
    61   (x0Brain,x0Brain,x0Brain+dxBrain,x0Brain+dxBrain,x0Brain+dxBrain,x0Brain,
    62   x0Brain-dxBrain,x0Brain-dxBrain,x0Brain-dxBrain);
    63 yBrain: array[0..nPlOffered-1] of integer =
    64   (y0Brain,y0Brain-dyBrain,y0Brain-dyBrain,y0Brain,y0Brain+dyBrain,
    65   y0Brain+dyBrain,y0Brain+dyBrain,y0Brain,y0Brain-dyBrain);
    66 xActive: array[0..nPlOffered-1] of integer = (0,0,36,51,36,0,-36,-51,-36);
    67 yActive: array[0..nPlOffered-1] of integer = (0,-51,-36,0,36,51,36,0,-36);
    68 
    69 var
    70 FormsCreated: boolean;
     52  UpdateInterval = 0.1; // seconds
     53  ShowActiveThreshold = 0.05; // seconds
     54
     55  nPlOffered = 9;
     56  x0Brain = 109 + 48 + 23;
     57  y0Brain = 124 + 48 + 7 + 16;
     58  dxBrain = 128;
     59  dyBrain = 128;
     60  xBrain: array [0 .. nPlOffered - 1] of integer = (x0Brain, x0Brain,
     61    x0Brain + dxBrain, x0Brain + dxBrain, x0Brain + dxBrain, x0Brain,
     62    x0Brain - dxBrain, x0Brain - dxBrain, x0Brain - dxBrain);
     63  yBrain: array [0 .. nPlOffered - 1] of integer = (y0Brain, y0Brain - dyBrain,
     64    y0Brain - dyBrain, y0Brain, y0Brain + dyBrain, y0Brain + dyBrain,
     65    y0Brain + dyBrain, y0Brain, y0Brain - dyBrain);
     66  xActive: array [0 .. nPlOffered - 1] of integer = (0, 0, 36, 51, 36, 0,
     67    -36, -51, -36);
     68  yActive: array [0 .. nPlOffered - 1] of integer = (0, -51, -36, 0, 36, 51,
     69    36, 0, -36);
     70
     71var
     72  FormsCreated: boolean;
    7173
    7274procedure TNoTermDlg.FormCreate(Sender: TObject);
    7375begin
    74 Left:=Screen.Width-Width-8; Top:=8;
    75 Caption:=Phrases.Lookup('AIT');
    76 Canvas.Brush.Style:=bsClear;
    77 Canvas.Font.Assign(UniFont[ftSmall]);
    78 TitleHeight:=36;
    79 InitButtons();
    80 QueryPerformanceFrequency(PerfFreq);
    81 LastShowYearTime:=0;
     76  Left := Screen.Width - Width - 8;
     77  Top := 8;
     78  Caption := Phrases.Lookup('AIT');
     79  Canvas.Brush.Style := bsClear;
     80  Canvas.Font.Assign(UniFont[ftSmall]);
     81  TitleHeight := 36;
     82  InitButtons();
     83  QueryPerformanceFrequency(PerfFreq);
     84  LastShowYearTime := 0;
    8285end;
    8386
    8487procedure TNoTermDlg.NewStat;
    8588begin
    86 Round:=0;
    87 FillChar(WinStat,SizeOf(WinStat),0);
    88 FillChar(ExtStat,SizeOf(ExtStat),0);
    89 FillChar(AloneStat,SizeOf(AloneStat),0);
    90 FillChar(TimeStat,SizeOf(TimeStat),0);
    91 TotalStatTime:=0;
    92 Mode:=Stop;
     89  Round := 0;
     90  FillChar(WinStat, SizeOf(WinStat), 0);
     91  FillChar(ExtStat, SizeOf(ExtStat), 0);
     92  FillChar(AloneStat, SizeOf(AloneStat), 0);
     93  FillChar(TimeStat, SizeOf(TimeStat), 0);
     94  TotalStatTime := 0;
     95  Mode := Stop;
    9396end;
    9497
    9598procedure TNoTermDlg.EndPlaying;
    9699var
    97 EndCommand: integer;
    98 begin
    99 NewStat;
    100 if G.RO[me].Turn>0 then with MessgDlg do
     100  EndCommand: integer;
     101begin
     102  NewStat;
     103  if G.RO[me].Turn > 0 then
     104    with MessgDlg do
     105    begin
     106      MessgText := Phrases.Lookup('ENDTOUR');
     107      Kind := mkYesNo;
     108      ShowModal;
     109      if ModalResult = mrIgnore then
     110        EndCommand := sResign
     111      else
     112        EndCommand := sBreak
     113    end
     114  else
     115    EndCommand := sResign;
     116  Server(EndCommand, me, 0, nil^)
     117end;
     118
     119procedure TNoTermDlg.ShowActive(p: integer; Active: boolean);
     120begin
     121  if p < nPlOffered then
     122    Sprite(Canvas, HGrSystem, x0Brain + 28 + xActive[p],
     123      y0Brain + 28 + yActive[p], 8, 8, 81 + 9 * Byte(Active), 16);
     124end;
     125
     126procedure TNoTermDlg.ShowYear;
     127begin
     128  Fill(State.Canvas, 0, 0, 192, 20, 64, 287 + 138);
     129  RisedTextOut(State.Canvas, 0, 0, Format(Phrases.Lookup('AIT_ROUND'), [Round])
     130    + ' ' + TurnToString(G.RO[me].Turn));
     131  BitBlt(Canvas.Handle, 64, 287 + 138, 192, 20, State.Canvas.Handle, 0,
     132    0, SRCCOPY);
     133end;
     134
     135procedure TNoTermDlg.Client(Command, Player: integer; var Data);
     136var
     137  i, x, y, p: integer;
     138  ActiveDuration: extended;
     139  ShipComplete: boolean;
     140  r: TRect;
     141  now: int64;
     142begin
     143  case Command of
     144    cDebugMessage:
     145      LogDlg.Add(Player, G.RO[0].Turn, pchar(@Data));
     146
     147    cInitModule:
     148      begin
     149        Server := TInitModuleData(Data).Server;
     150        TInitModuleData(Data).Flags := aiThreaded;
     151        Shade := TBitmap.Create;
     152        Shade.Width := 64;
     153        Shade.Height := 64;
     154        for x := 0 to 63 do
     155          for y := 0 to 63 do
     156            if Odd(x + y) then
     157              Shade.Canvas.Pixels[x, y] := $FFFFFF
     158            else
     159              Shade.Canvas.Pixels[x, y] := $000000;
     160        State := TBitmap.Create;
     161        State.Width := 192;
     162        State.Height := 20;
     163        State.Canvas.Brush.Style := bsClear;
     164        State.Canvas.Font.Assign(UniFont[ftSmall]);
     165        NewStat;
     166      end;
     167
     168    cReleaseModule:
     169      begin
     170        Shade.Free;
     171        State.Free
     172      end;
     173
     174    cNewGame, cLoadGame:
     175      begin
     176        inc(Round);
     177        if Mode = Running then
     178        begin
     179          Invalidate;
     180          Update
     181        end
     182        else
     183          Show;
     184        G := TNewGameData(Data);
     185        LogDlg.mSlot.Visible := false;
     186        LogDlg.Host := nil;
     187        ToldAlive := G.RO[me].Alive;
     188        Active := -1;
     189        FillChar(DisallowShowActive, SizeOf(DisallowShowActive), 0); // false
     190        LastShowTurnChange := 0;
     191        LastNewTurn := 0;
     192        TurnTime := 1.0;
     193      end;
     194
     195    cBreakGame:
     196      begin
     197        LogDlg.List.Clear;
     198        if Mode <> Running then
     199        begin
     200          if LogDlg.Visible then
     201            LogDlg.Close;
     202          Close;
     203        end
     204      end;
     205
     206    cTurn, cResume, cContinue:
     207      begin
     208        me := Player;
     209        if Active >= 0 then
     210        begin
     211          ShowActive(Active, false);
     212          Active := -1
     213        end; // should not happen
     214
     215        QueryPerformanceCounter(now);
     216        if {$IFDEF VER100}(now.LowPart - LastShowYearTime.LowPart){$ELSE}(now - LastShowYearTime){$ENDIF} / PerfFreq >= UpdateInterval then
     217        begin
     218          ShowYear;
     219          LastShowYearTime := now;
     220        end;
     221        TurnTime :=
     222        {$IFDEF VER100}(now.LowPart - LastNewTurn.LowPart){$ELSE}(now - LastNewTurn){$ENDIF} / PerfFreq;
     223        LastNewTurn := now;
     224        if (G.RO[me].Alive <> ToldAlive) then
     225        begin
     226          for p := 1 to nPlOffered - 1 do
     227            if 1 shl p and (G.RO[me].Alive xor ToldAlive) <> 0 then
     228            begin
     229              r := Rect(xBrain[p], yBrain[p] - 16, xBrain[p] + 64,
     230                yBrain[p] - 16 + 64);
     231              InvalidateRect(Handle, @r, false);
     232            end;
     233          ToldAlive := G.RO[me].Alive;
     234        end;
     235        Application.ProcessMessages;
     236        if Mode = Quit then
     237          EndPlaying
     238        else if G.RO[me].Happened and phGameEnd <> 0 then
     239        begin // game ended, update statistics
     240          for p := 1 to nPlOffered - 1 do
     241            if bixView[p] >= 0 then
     242              if 1 shl p and G.RO[me].Alive = 0 then
     243                inc(ExtStat[p]) // extinct
     244              else if G.RO[me].Alive = 1 shl p then
     245                inc(AloneStat[p]) // only player alive
     246              else
     247              begin // alive but not alone -- check colony ship
     248                ShipComplete := true;
     249                for i := 0 to nShipPart - 1 do
     250                  if G.RO[me].Ship[p].Parts[i] < ShipNeed[i] then
     251                    ShipComplete := false;
     252                if ShipComplete then
     253                  inc(WinStat[p])
     254              end;
     255          if Mode = Running then
     256            Server(sNextRound, me, 0, nil^)
     257        end
     258        else if Mode = Running then
     259          Server(sTurn, me, 0, nil^);
     260        if Mode = Stop then
     261        begin
     262          GoBtn.ButtonIndex := 22;
     263          Mode := Stopped
     264        end
     265      end;
     266
     267    cShowTurnChange:
     268      begin
     269        QueryPerformanceCounter(now);
     270        if Active >= 0 then
     271        begin
     272          ActiveDuration :=
     273          {$IFDEF VER100}(now.LowPart - LastShowTurnChange.LowPart){$ELSE}(now - LastShowTurnChange){$ENDIF} / PerfFreq;
     274          TimeStat[Active] := TimeStat[Active] + ActiveDuration;
     275          TotalStatTime := TotalStatTime + ActiveDuration;
     276          if not DisallowShowActive[Active] then
     277            ShowActive(Active, false);
     278          DisallowShowActive[Active] := (ActiveDuration < TurnTime * 0.25) and
     279            (ActiveDuration < ShowActiveThreshold);
     280        end;
     281        LastShowTurnChange := now;
     282
     283        Active := integer(Data);
     284        if (Active >= 0) and not DisallowShowActive[Active] then
     285          ShowActive(Active, true);
     286      end
     287
     288  end
     289end;
     290
     291procedure TNoTermDlg.GoBtnClick(Sender: TObject);
     292begin
     293  if Mode = Running then
     294    Mode := Stop
     295  else if Mode = Stopped then
    101296  begin
    102   MessgText:=Phrases.Lookup('ENDTOUR');
    103   Kind:=mkYesNo;
    104   ShowModal;
    105   if ModalResult=mrIgnore then EndCommand:=sResign
    106   else EndCommand:=sBreak
     297    Mode := Running;
     298    GoBtn.ButtonIndex := 23;
     299    GoBtn.Update;
     300    Server(sTurn, me, 0, nil^);
    107301  end
    108 else EndCommand:=sResign;
    109 Server(EndCommand,me,0,nil^)
    110 end;
    111 
    112 procedure TNoTermDlg.ShowActive(p: integer; Active: boolean);
    113 begin
    114 if p<nPlOffered then
    115   Sprite(Canvas,HGrSystem,x0Brain+28+xActive[p],y0Brain+28+yActive[p],8,8,
    116     81+9*Byte(Active),16);
    117 end;
    118 
    119 procedure TNoTermDlg.ShowYear;
    120 begin
    121 Fill(State.Canvas,0,0,192,20,64,287+138);
    122 RisedTextOut(State.Canvas,0,0,Format(Phrases.Lookup('AIT_ROUND'),[Round])+' '
    123   +TurnToString(G.RO[me].Turn));
    124 BitBlt(Canvas.Handle,64,287+138,192,20,State.Canvas.Handle,0,0,SRCCOPY);
    125 end;
    126 
    127 procedure TNoTermDlg.Client(Command, Player: integer; var Data);
    128 var
    129 i,x,y,p: integer;
    130 ActiveDuration: extended;
    131 ShipComplete: boolean;
    132 r: TRect;
    133 now: int64;
    134 begin
    135 case Command of
    136   cDebugMessage:
    137     LogDlg.Add(Player, G.RO[0].Turn, pchar(@Data));
    138 
    139   cInitModule:
     302end;
     303
     304procedure TNoTermDlg.QuitBtnClick(Sender: TObject);
     305begin
     306  if Mode = Stopped then
     307    EndPlaying
     308  else
     309    Mode := Quit
     310end;
     311
     312procedure TNoTermDlg.FormPaint(Sender: TObject);
     313var
     314  i, TimeShare: integer;
     315begin
     316  Fill(Canvas, 3, 3, ClientWidth - 6, ClientHeight - 6, 0, 0);
     317  Frame(Canvas, 0, 0, ClientWidth - 1, ClientHeight - 1, $000000, $000000);
     318  Frame(Canvas, 1, 1, ClientWidth - 2, ClientHeight - 2,
     319    MainTexture.clBevelLight, MainTexture.clBevelShade);
     320  Frame(Canvas, 2, 2, ClientWidth - 3, ClientHeight - 3,
     321    MainTexture.clBevelLight, MainTexture.clBevelShade);
     322  Corner(Canvas, 1, 1, 0, MainTexture);
     323  Corner(Canvas, ClientWidth - 9, 1, 1, MainTexture);
     324  Corner(Canvas, 1, ClientHeight - 9, 2, MainTexture);
     325  Corner(Canvas, ClientWidth - 9, ClientHeight - 9, 3, MainTexture);
     326  Canvas.Font.Assign(UniFont[ftCaption]);
     327  RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, Caption)) div 2,
     328    7, Caption);
     329  Canvas.Font.Assign(UniFont[ftSmall]);
     330  for i := 1 to nPlOffered - 1 do
     331    if bixView[i] >= 0 then
    140332    begin
    141     Server:=TInitModuleData(Data).Server;
    142     TInitModuleData(Data).Flags:=aiThreaded;
    143     Shade:=TBitmap.Create;
    144     Shade.Width:=64; Shade.Height:=64;
    145     for x:=0 to 63 do for y:=0 to 63 do
    146       if Odd(x+y) then Shade.Canvas.Pixels[x,y]:=$FFFFFF
    147       else Shade.Canvas.Pixels[x,y]:=$000000;
    148     State:=TBitmap.Create;
    149     State.Width:=192; State.Height:=20;
    150     State.Canvas.Brush.Style:=bsClear;
    151     State.Canvas.Font.Assign(UniFont[ftSmall]);
    152     NewStat;
     333      Frame(Canvas, xBrain[i] - 24, yBrain[i] - 8 - 16, xBrain[i] - 24 + 111,
     334        yBrain[i] - 8 - 16 + 111, MainTexture.clBevelShade,
     335        MainTexture.clBevelShade);
     336      FrameImage(Canvas, StartDlg.BrainPicture[bixView[i]], xBrain[i],
     337        yBrain[i] - 16, 64, 64, 0, 0);
     338      if 1 shl i and G.RO[me].Alive = 0 then
     339        BitBlt(Canvas.Handle, xBrain[i], yBrain[i] - 16, 64, 64,
     340          Shade.Canvas.Handle, 0, 0, SRCAND);
     341      Sprite(Canvas, HGrSystem, xBrain[i] + 30 - 14, yBrain[i] + 53, 14,
     342        14, 1, 316);
     343      RisedTextOut(Canvas, xBrain[i] + 30 - 16 - BiColorTextWidth(Canvas,
     344        IntToStr(WinStat[i])), yBrain[i] + 51, IntToStr(WinStat[i]));
     345      Sprite(Canvas, HGrSystem, xBrain[i] + 34, yBrain[i] + 53, 14, 14,
     346        1 + 15, 316);
     347      RisedTextOut(Canvas, xBrain[i] + 34 + 16, yBrain[i] + 51,
     348        IntToStr(AloneStat[i]));
     349      Sprite(Canvas, HGrSystem, xBrain[i] + 30 - 14, yBrain[i] + 53 + 16, 14,
     350        14, 1 + 30, 316);
     351      RisedTextOut(Canvas, xBrain[i] + 30 - 16 - BiColorTextWidth(Canvas,
     352        IntToStr(ExtStat[i])), yBrain[i] + 51 + 16, IntToStr(ExtStat[i]));
     353      Sprite(Canvas, HGrSystem, xBrain[i] + 34, yBrain[i] + 53 + 16, 14, 14,
     354        1 + 45, 316);
     355      if TotalStatTime > 0 then
     356      begin
     357        TimeShare := trunc(TimeStat[i] / TotalStatTime * 100 + 0.5);
     358        RisedTextOut(Canvas, xBrain[i] + 34 + 16, yBrain[i] + 51 + 16,
     359          IntToStr(TimeShare) + '%');
     360      end;
     361      ShowActive(i, i = Active);
    153362    end;
    154 
    155   cReleaseModule:
    156     begin
    157     Shade.Free;
    158     State.Free
    159     end;
    160 
    161   cNewGame,cLoadGame:
    162     begin
    163     inc(Round);
    164     if Mode=Running then
    165       begin Invalidate; Update end
    166     else Show;
    167     G:=TNewGameData(Data);
    168     LogDlg.mSlot.Visible:=false;
    169     LogDlg.Host:=nil;
    170     ToldAlive:=G.RO[me].Alive;
    171     Active:=-1;
    172     fillchar(DisallowShowActive, sizeof(DisallowShowActive), 0); // false
    173     LastShowTurnChange:=0;
    174     LastNewTurn:=0;
    175     TurnTime:=1.0;
    176     end;
    177 
    178   cBreakGame:
    179     begin
    180     LogDlg.List.Clear;
    181     if Mode<>Running then
    182       begin
    183       if LogDlg.Visible then LogDlg.Close;
    184       Close;
    185       end
    186     end;
    187 
    188   cTurn,cResume,cContinue:
    189     begin
    190     me:=Player;
    191     if Active>=0 then
    192       begin ShowActive(Active,false); Active:=-1 end; // should not happen
    193 
    194     QueryPerformanceCounter(now);
    195     if {$IFDEF VER100}(now.LowPart-LastShowYearTime.LowPart){$ELSE}(now-LastShowYearTime){$ENDIF}/PerfFreq>=UpdateInterval then
    196       begin
    197       ShowYear;
    198       LastShowYearTime:=now;
    199       end;
    200     TurnTime:={$IFDEF VER100}(now.LowPart-LastNewTurn.LowPart){$ELSE}(now-LastNewTurn){$ENDIF}/PerfFreq;
    201     LastNewTurn:=now;
    202     if (G.RO[me].Alive<>ToldAlive) then
    203       begin
    204       for p:=1 to nPlOffered-1 do
    205         if 1 shl p and (G.RO[me].Alive xor ToldAlive)<>0 then
    206           begin
    207           r:=Rect(xBrain[p],yBrain[p]-16,xBrain[p]+64,yBrain[p]-16+64);
    208           InvalidateRect(Handle,@r,false);
    209           end;
    210       ToldAlive:=G.RO[me].Alive;
    211       end;
    212     Application.ProcessMessages;
    213     if Mode=Quit then EndPlaying
    214     else if G.RO[me].Happened and phGameEnd<>0 then
    215       begin // game ended, update statistics
    216       for p:=1 to nPlOffered-1 do if bixView[p]>=0 then
    217         if 1 shl p and G.RO[me].Alive=0 then inc(ExtStat[p]) // extinct
    218         else if G.RO[me].Alive=1 shl p then inc(AloneStat[p]) // only player alive
    219         else
    220           begin // alive but not alone -- check colony ship
    221           ShipComplete:=true;
    222           for i:=0 to nShipPart-1 do
    223             if G.RO[me].Ship[p].Parts[i]<ShipNeed[i] then
    224               ShipComplete:=false;
    225           if ShipComplete then inc(WinStat[p])
    226           end;
    227       if Mode=Running then Server(sNextRound,me,0,nil^)
    228       end
    229     else if Mode=Running then Server(sTurn,me,0,nil^);
    230     if Mode=Stop then
    231       begin
    232       GoBtn.ButtonIndex:=22;
    233       Mode:=Stopped
    234       end
    235     end;
    236 
    237   cShowTurnChange:
    238     begin
    239     QueryPerformanceCounter(now);
    240     if Active>=0 then
    241       begin
    242       ActiveDuration:={$IFDEF VER100}(now.LowPart-LastShowTurnChange.LowPart){$ELSE}(now-LastShowTurnChange){$ENDIF}/PerfFreq;
    243       TimeStat[Active]:=TimeStat[Active]+ActiveDuration;
    244       TotalStatTime:=TotalStatTime+ActiveDuration;
    245       if not DisallowShowActive[Active] then
    246         ShowActive(Active,false);
    247       DisallowShowActive[Active]:= (ActiveDuration<TurnTime*0.25) and (ActiveDuration<ShowActiveThreshold);
    248       end;
    249     LastShowTurnChange:=now;
    250 
    251     Active:=integer(Data);
    252     if (Active>=0) and not DisallowShowActive[Active] then
    253       ShowActive(Active,true);
    254     end
    255 
    256   end
    257 end;
    258 
    259 procedure TNoTermDlg.GoBtnClick(Sender: TObject);
    260 begin
    261 if Mode=Running then Mode:=Stop
    262 else if Mode=Stopped then
     363  Sprite(Canvas, HGrSystem2, x0Brain + 32 - 20, y0Brain + 32 - 20, 40,
     364    40, 115, 1);
     365  ShowYear;
     366  BtnFrame(Canvas, GoBtn.BoundsRect, MainTexture);
     367  BtnFrame(Canvas, QuitBtn.BoundsRect, MainTexture);
     368  // BtnFrame(Canvas,StatBtn.BoundsRect,MainTexture);
     369end;
     370
     371procedure Client;
     372begin
     373  if not FormsCreated then
    263374  begin
    264   Mode:=Running;
    265   GoBtn.ButtonIndex:=23;
    266   GoBtn.Update;
    267   Server(sTurn,me,0,nil^);
    268   end
    269 end;
    270 
    271 procedure TNoTermDlg.QuitBtnClick(Sender: TObject);
    272 begin
    273 if Mode=Stopped then EndPlaying
    274 else Mode:=Quit
    275 end;
    276 
    277 procedure TNoTermDlg.FormPaint(Sender: TObject);
    278 var
    279 i,TimeShare: integer;
    280 begin
    281 Fill(Canvas,3,3,ClientWidth-6, ClientHeight-6, 0,0);
    282 Frame(Canvas,0,0,ClientWidth-1,ClientHeight-1, $000000,$000000);
    283 Frame(Canvas,1,1,ClientWidth-2,ClientHeight-2,
    284   MainTexture.clBevelLight,MainTexture.clBevelShade);
    285 Frame(Canvas,2,2,ClientWidth-3,ClientHeight-3,
    286   MainTexture.clBevelLight,MainTexture.clBevelShade);
    287 Corner(Canvas,1,1,0,MainTexture);
    288 Corner(Canvas,ClientWidth-9,1,1,MainTexture);
    289 Corner(Canvas,1,ClientHeight-9,2,MainTexture);
    290 Corner(Canvas,ClientWidth-9,ClientHeight-9,3,MainTexture);
    291 Canvas.Font.Assign(UniFont[ftCaption]);
    292 RisedTextOut(Canvas,(ClientWidth-BiColorTextWidth(Canvas,Caption)) div 2,7,Caption);
    293 Canvas.Font.Assign(UniFont[ftSmall]);
    294 for i:=1 to nPlOffered-1 do if bixView[i]>=0 then
    295   begin
    296   Frame(Canvas,xBrain[i]-24,yBrain[i]-8-16,xBrain[i]-24+111,yBrain[i]-8-16+111,
    297     MainTexture.clBevelShade,MainTexture.clBevelShade);
    298   FrameImage(Canvas,StartDlg.BrainPicture[bixView[i]],xBrain[i],yBrain[i]-16,64,64,0,0);
    299   if 1 shl i and G.RO[me].Alive=0 then
    300     BitBlt(Canvas.Handle,xBrain[i],yBrain[i]-16,64,64,
    301       Shade.Canvas.Handle,0,0,SRCAND);
    302   Sprite(Canvas,HGrSystem,xBrain[i]+30-14,yBrain[i]+53,14,14,1,316);
    303   RisedTextout(Canvas,xBrain[i]+30-16-BiColorTextWidth(Canvas,IntToStr(WinStat[i])),yBrain[i]+51,IntToStr(WinStat[i]));
    304   Sprite(Canvas,HGrSystem,xBrain[i]+34,yBrain[i]+53,14,14,1+15,316);
    305   RisedTextout(Canvas,xBrain[i]+34+16,yBrain[i]+51,IntToStr(AloneStat[i]));
    306   Sprite(Canvas,HGrSystem,xBrain[i]+30-14,yBrain[i]+53+16,14,14,1+30,316);
    307   RisedTextout(Canvas,xBrain[i]+30-16-BiColorTextWidth(Canvas,IntToStr(ExtStat[i])),yBrain[i]+51+16,IntToStr(ExtStat[i]));
    308   Sprite(Canvas,HGrSystem,xBrain[i]+34,yBrain[i]+53+16,14,14,1+45,316);
    309   if TotalStatTime>0 then
    310     begin
    311     TimeShare:=trunc(TimeStat[i]/TotalStatTime*100+0.5);
    312     RisedTextout(Canvas,xBrain[i]+34+16,yBrain[i]+51+16,IntToStr(TimeShare)+'%');
    313     end;
    314   ShowActive(i, i=Active);
     375    FormsCreated := true;
     376    Application.CreateForm(TNoTermDlg, NoTermDlg);
    315377  end;
    316 Sprite(Canvas,HGrSystem2,x0Brain+32-20,y0Brain+32-20,40,40,115,1);
    317 ShowYear;
    318 BtnFrame(Canvas,GoBtn.BoundsRect,MainTexture);
    319 BtnFrame(Canvas,QuitBtn.BoundsRect,MainTexture);
    320 //BtnFrame(Canvas,StatBtn.BoundsRect,MainTexture);
    321 end;
    322 
    323 procedure Client;
    324 begin
    325 if not FormsCreated then
    326   begin
    327   FormsCreated:=true;
    328   Application.CreateForm(TNoTermDlg, NoTermDlg);
    329   end;
    330 NoTermDlg.Client(Command,Player,Data);
     378  NoTermDlg.Client(Command, Player, Data);
    331379end;
    332380
     
    334382  Shift: TShiftState);
    335383begin
    336 if (char(Key)='M') and (ssCtrl in Shift) then
    337   if LogDlg.Visible then LogDlg.Close else LogDlg.Show;
     384  if (char(Key) = 'M') and (ssCtrl in Shift) then
     385    if LogDlg.Visible then
     386      LogDlg.Close
     387    else
     388      LogDlg.Show;
    338389end;
    339390
    340391initialization
    341 FormsCreated:=false;
     392
     393FormsCreated := false;
    342394
    343395end.
    344 
  • trunk/Protocol.pas

    r2 r6  
    11{$INCLUDE switches.pas}
    22{$HINTS OFF}
    3 
    43unit Protocol;
    54
     
    76
    87const
    9 lxmax=100; lymax=96;
    10 nAdv=94; {number of advances}
    11 nImp=70; {number of improvements}
    12 nPl=15; {max number of players, don't change!}
    13 nUmax=4096; {max units/player, don't set above 4096}
    14 nCmax=1024; {max cities/player, don't set above 4096}
    15 nMmax=256; {max models/player, don't set above 1024}
    16 nExp=5; // number of experience levels
    17 ExpCost=50; {received damage required for next experience level}
    18 MaxFutureTech=25; // maximum number of future techs of one kind except computing technology
    19 MaxFutureTech_Computing=100; // maximum number of computing technology future techs
    20 CountryRadius=9;
    21 MaxCitySize=30;
    22 BasicHappy=2; {basically happy citizens}
    23 MaxPollution=240;
    24 NeedAqueductSize=8;
    25 NeedSewerSize=12;
    26 ColossusEffect=75; // percent wonder building cost
    27 UniversityFutureBonus=5; // percent per tech
    28 ResLabFutureBonus=10; // percent per tech
    29 FactoryFutureBonus=5; // percent per tech
    30 MfgPlantFutureBonus=10; // percent per tech
    31 AnarchyTurns=3;
    32 CaptureTurns=3;
    33 CancelTreatyTurns=3;
    34 PeaceEvaTurns=5; // should be less then 2*CancelTreatyTurns, so that you can't attack an ally without re-entering
    35 ColdWarTurns=40;
    36 DesertThurst=20; // damage for turn in desert
    37 ArcticThurst=20; // damage for turn in arctic
    38 FastRecovery=50;
    39 CityRecovery=20;
    40 NoCityRecovery=8;
    41 MaxMoneyPrice=$FFFF;
    42 MaxShipPartPrice=100;
    43 BombardmentDestroysCity=false;
    44 StartMoney=0;
    45 InitialCredibility=95;
    46 
    47 // ai module flags (for TInitModuleData.Flags)
    48 aiThreaded=$01;
    49 
    50 //difficulty settings
    51 MaxDiff=4; {maximum difficulty level}
    52 StorageSize: array[1..MaxDiff] of integer=(30,40,50,60);
    53 TechFormula_M: array[1..MaxDiff] of single =(2.0,2.3,2.6,4.0);
    54 TechFormula_D: array[1..MaxDiff] of single =(102.0,80.0,64.0,64.0);
    55 BuildCostMod: array[1..MaxDiff] of integer =(9,12,15,18);  // in 1/12
    56 
    57 // test flags
    58 nTestFlags=7; // max. 11
    59 tfAllTechs=$001; {all nations get all techs}
    60 tfImmImprove=$002; {city projects complete each turn}
    61 tfImmAdvance=$004; {research complete each turn}
    62 tfImmGrow=$008; {all cities grow in each turn}
    63 tfUncover=$010; // all players see like supervisor
    64 tfAllContact=$020; // all nations can contact each other
    65 tfNoRareNeed=$040; // producing colony ship requires no modern resources
    66 tfTested=$800; // at least one test flag was set
    67 
    68 {server commands
    69 IMPORTANT: lowest 4 bits must indicate size in DWORDS of data parameter,
    70 except for request commands}
    71 
    72 sctMask=$3800; // server command type
    73 sExecute=$4000; {call command-sExecute to request return value without
    74   execution}
    75 cClientEx=$8000;
    76 
    77 // Info Request Commands
    78 sctInfo=$0000;
    79 sMessage=$0000; sSetDebugMap=$0010; sGetDebugMap=$0020;
    80 {sChangeSuperView=$0030;} sRefreshDebugMap=$0040;
    81 sGetChart=$0100; // + type shl 4
    82 sGetTechCost=$0180;
    83 sGetAIInfo=$01C0;sGetAICredits=$01D0;sGetVersion=$01E0;sGetGameChanged=$01F0;
    84 sGetTileInfo=$0200;sGetCityTileInfo=$0210;sGetHypoCityTileInfo=$0220;
    85 sGetJobProgress=$0230;
    86 sGetModels=$0270;
    87 sGetUnits=$0280;sGetDefender=$0290;sGetBattleForecast=$02A0;
    88 sGetUnitReport=$02B0;sGetMoveAdvice=$02C0;sGetPlaneReturn=$02D0;
    89 sGetBattleForecastEx=$02E0;
    90 sGetCity=$0300;sGetCityReport=$0310;sGetCityAreaInfo=$0320;
    91 sGetEnemyCityReport=$0330;sGetEnemyCityAreaInfo=$0340;
    92 sGetCityTileAdvice=$0350;sGetCityReportNew=$0360;
    93 sGetEnemyCityReportNew=$0370;
    94 
    95 // Map Editor Commands
    96 sEditTile=$0710;sRandomMap=$0780;sMapGeneratorRequest=$0790;
    97 
    98 // Server Internal Commands
    99 sctInternal=sctInfo; // sctInfo - without sExecute flag, sctInternal - with sExecute flag
    100 sIntTellAboutNation=$4000; sIntHaveContact=$4010; sIntCancelTreaty=$4020;
    101 sIntTellAboutModel=$4100; {+told player shl 4}
    102 sIntDiscoverZOC=$4201; sIntExpandTerritory=$4218;
    103 sIntBuyMaterial=$4301;
    104 sIntPayPrices=$4402;
    105 sIntSetDevModel=$450D;
    106 sIntSetModelStatus=$4601;sIntSetUnitStatus=$4611;sIntSetCityStatus=$4621;
    107 sIntSetECityStatus=$4631;
    108 sIntDataChange=$4700;
    109 
    110 // Client Deactivation Commands
    111 sctEndClient=$0800;
    112 sTurn=$4800;sBreak=$4810;sResign=$4820;sNextRound=$4830;sReload=$4841;
    113 sSaveMap=$4880;sAbandonMap=$4890;
    114 // diplomacy commands equal to client, see below
    115 
    116 // General Commands
    117 sctGeneral=$1000;
    118 sClearTestFlag=$5000;sSetTestFlag=$5010;
    119 sSetGovernment=$5100;sSetRates=$5110;sRevolution=$5120;
    120 sSetResearch=$5200;sStealTech=$5210;
    121 sSetAttitude=$5300; // + concerned player shl 4
    122 sCancelTreaty=$5400;
    123 
    124 // Model Related Commands
    125 sctModel=$1800;
    126 sCreateDevModel=$5800;
    127 sSetDevModelCap=$5C00; {+value shl 4}
    128   {reserves $5CXX, $5DXX, $5EXX, $5FXX}
    129 
    130 // Unit Related Commands
    131 sctUnit=$2000;
    132 sRemoveUnit=$6000;sSetUnitHome=$6010;
    133 sSetSpyMission=$6100; // + mission shl 4
    134 sLoadUnit=$6200;sUnloadUnit=$6210;sSelectTransport=$6220;
    135 sCreateUnit=$6301; // + player shl 4
    136 sMoveUnit=$6400; {+dx and 7 shl 4 +dy and 7 shl 7}
    137   {reserves $64XX, $65XX, $66XX, $67XX}
    138 
    139 // Settlers Related Commands
    140 sctSettlers=$2800;
    141 sAddToCity=$6810;
    142 sStartJob=$6C00; {+job shl 4}
    143   {reserves $6CXX, $6DXX, $6EXX, $6FXX}
    144 
    145 // City Related Commands
    146 sctCity=$3000;
    147 sSetCityProject=$7001;sBuyCityProject=$7010;sSellCityProject=$7020;
    148 sSellCityImprovement=$7101;sRebuildCityImprovement=$7111;
    149 sSetCityTiles=$7201;
    150 
    151 // free command space
    152 sctUnused=$3800;
    153 
    154 {client commands}
    155 cInitModule=$0000;cReleaseModule=$0100;cBroadcast=$0200;
    156 cHelpOnly=$0700;cStartHelp=$0710; cStartCredits=$0720;
    157 
    158 cNewGame=$0800;cLoadGame=$0810;cMovie=$0820;
    159 cNewGameEx=$0840;cLoadGameEx=$0850;
    160 cNewMap=$0880;cReplay=$08E0;cGetReady=$08F0;cBreakGame=$0900;
    161 
    162 cTurn=$2000;cResume=$2010;cContinue=$2080;
    163 cMovieTurn=$2100; cMovieEndTurn=$2110;
    164 cEditMap=$2800;
    165 
    166 //cShowTileM=$3000;cShowTileA=$3010;cShowFoundCity=$3020;
    167 cShowUnitChanged=$3030; cShowAfterMove=$3040; cShowAfterAttack=$3050;
    168 cShowCityChanged=$3090;
    169 //cShowMove=$3100;cShowCapture=$3110;
    170 //cShowAttackBegin=$3200;cShowAttackWon=$3210;cShowAttackLost=$3220;
    171 cShowMoving=$3140; cShowCapturing=$3150; cShowAttacking=$3240;
    172 cShowMissionResult=$3300;
    173 cShowShipChange=$3400;
    174 cShowGreatLibTech=$3500;
    175 cShowTurnChange=$3700;
    176 cShowCancelTreaty=$3800; cShowEndContact=$3810;
    177 cShowCancelTreatyByAlliance=$3820; cShowSupportAllianceAgainst=$3830;
    178 cShowPeaceViolation=$3880;
    179 cShowGame=$3F00; {cShowSuperView=$3F80;} cRefreshDebugMap=$3F90;
    180 
    181 // diplomacy commands equal to server, see below
    182 
    183 cDebugMessage=$7000;
    184 cShowNego=$7010;
    185 
    186 // commands same for server and client
    187 scContact=$4900; // + concerned player shl 4 for server call
    188 scReject=$4A00;
    189 scDipStart=$4B00;
    190 scDipNotice=$4B10;
    191 scDipAccept=$4B20;
    192 scDipCancelTreaty=$4B30;
    193 scDipOffer=$4B4E;
    194 scDipBreak=$4BF0;
    195 
    196 {server return codes: flags}
    197 rExecuted=             $40000000;
    198 rEffective=            $20000000;
    199 rUnitRemoved=          $10000000;
    200 rEnemySpotted=         $08000000;
    201 
    202 {server return codes: command executed}
    203 // note: the same return code might have a different meaning for different server functions! 
    204 eOK=                   $60000000; // ok
    205 eEnemySpotted=         $68000000; // unit move ok, new enemy unit/city spotted
    206 eDied=                 $70000000; // move executed, unit died due to hostile terrain
    207 eEnemySpotted_Died=    $78000000; // unit move ok, new enemy unit/city spotted, unit died due to hostile terrain
    208 eLoaded=               $60000002; // unit move caused loading to transport ship
    209 eLost=                 $70000004; // attack executed, battle lost, unit is dead
    210 eWon=                  $60000005; // attack executed, battle won, defender destroyed
    211 eBloody=               $70000005; // attack executed, defender destroyed, unit is dead
    212 eBombarded=            $60000006; // empty enemy city bombarded
    213 eExpelled=             $60000007; // friendly unit expelled
    214 eMissionDone=          $70000008; // spy moved into city: mission done, spy no longer exists
    215 eJobDone=              $60000001; // settler job started and already done
    216 eJobDone_Died=         $70000001; // settler job started and already done, unit died due to hostile terrain
    217 eCity=                 $70000002; // city founded, settler no more exists
    218 eRemoved=              $70000000; // sRemoveUnit: unit removed
    219 eUtilized=             $70000001; // sRemoveUnit: unit utilized for city project
    220 
    221 eNotChanged=           $40000000; // ok, but no effect (e.g. current city project set again)
    222 
    223 {server return codes: command not executed}
    224 eHiddenUnit=           $20000013; // unit move: not possible, destination tile occupied by hidden foreign submarine
    225 eStealthUnit=          $2000001A; // unit move: not possible, destination tile occupied by foreign stealth unit
    226 eZOC_EnemySpotted=     $28000014; // unit move: not possible, new enemy unit spotted, ZOC violation
    227 
    228 eInvalid=              $0000; // command not allowed now or parameter out of allowed range
    229 eUnknown=              $0001; // unknown command
    230 eNoTurn=               $0002; // command only allowed during player's turn
    231 eViolation=            $0003; // general violation of game rules
    232 eNoPreq=               $0004; // the prerequisites for this command are not fully met
    233 
    234 eNoTime_Move=          $0008; // normal unit move: too few movement points left
    235 eNoTime_Load=          $0009; // load unit: too few movement points left
    236 eNoTime_Attack=        $000A; // attack: no movement points left
    237 eNoTime_Bombard=       $000B; // bombard city: too few movement points left
    238 eNoTime_Expel=         $000C; // expel spy: too few movement points left
    239 
    240 eDomainMismatch=       $0011; // move/attack: action not allowed for this unit domain
    241 eNoCapturer=           $0012; // unit move: this type of unit is not allowed to capture a city
    242 eZOC=                  $0014; // unit move: not possible, ZOC violation
    243 eTreaty=               $0015; // move/attack: not possible, peace treaty violation
    244 eDeadLands=            $0016; // sStartJob: not possible, dead lands
    245 eNoRoad=               $0017; // unit move: not possible, no road
    246 eNoNav=                $0019; // unit move: not possible, open sea without navigation
    247 eNoLoadCapacity=       $001B; // load to transport: no more transport capacity
    248 eNoBombarder=          $001C; // bombardment impossible because no attack power
    249 
    250 eMaxSize=              $0020; // add to city: bigger size not allowed due to missing aqueduct/sewer
    251 eNoCityTerrain=        $0022; // found city: not possible in this terrain
    252 eNoBridgeBuilding=     $0023;
    253 eInvalidOffer=         $0030;
    254 eOfferNotAcceptable=   $0031;
    255 eCancelTreatyRush=     $0032;
    256 eAnarchy=              $0038; // no negotiation in anarchy
    257 eColdWar=              $003F;
    258 eNoModel=              $0040; // sCreateDevModel must be called before!
    259 eTileNotAvailable=     $0050;
    260 eNoWorkerAvailable=    $0051;
    261 eOnlyOnce=             $0058; // sell/rebuild city improvement: only once per city and turn!
    262 eObsolete=             $0059; // city project: more advanced improvement already exists
    263 eOutOfControl=         $005A; // buy/sell/rebuild improvement: not in anarchy, not in captured cities
    264 
    265 eNoWay=                $0100; // sGetMoveAdvice: no way found
    266 
    267 // chart types
    268 nStat=6;
    269 stPop=0; stTerritory=1; stMil=2; stScience=3; stExplore=4; stWork=5;
    270 
    271 {tile flags: terrain type}
    272 fTerrain=$1F; // mask for terrain type
    273 fOcean=$00;fShore=$01;fGrass=$02;fDesert=$03;fPrairie=$04;
    274 fTundra=$05;fArctic=$06;fSwamp=$07;fForest=$09;fHills=$0A;fMountains=$0B;
    275 fUNKNOWN=fTerrain;
    276 
    277 {tile flags: terrain improvements}
    278 fTerImp=$0000F000; // mask for terrain improvement
    279 tiNone=$00000000;tiIrrigation=$00001000;tiFarm=$00002000;tiMine=$00003000;
    280 tiFort=$00004000;tiBase=$00005000;
    281 
    282 {tile flags: add ons}
    283 fSpecial=$00000060;fSpecial1=$00000020;fSpecial2=$00000040;
    284 fRiver=$00000080;
    285 fRoad=$00000100;fRR=$00000200;fCanal=$00000400;fPoll=$00000800;
    286 fPrefStartPos=$00200000;fStartPos=$00400000; // map editor only
    287 fDeadLands=$01000000;
    288 fModern=$06000000;fCobalt=$02000000;fUranium=$04000000;fMercury=$06000000;
    289 
    290 {tile flags: redundant helper info}
    291 fGrWall=$00010000; // tile protected by great wall
    292 fSpiedOut=$00020000;fStealthUnit=$00040000;fHiddenUnit=$00080000;
    293 fObserved=$00100000; // set if tile information is from this turn
    294 fOwned=$00200000; // set if unit/city here is own one
    295 fUnit=$00400000;fCity=$00800000;
    296 fOwnZoCUnit=$10000000; // own ZoC unit present at this tile
    297 fInEnemyZoC=$20000000; // tile is adjacent to known foreign ZoC unit (not allied)
    298 fPeace=$40000000; // tile belongs to territory of nation that we are in peace with but not allied
    299 
    300 // city project flags
    301 cpIndex=$1FF;
    302 cpConscripts=$200; // produce unit as conscripts
    303 cpDisbandCity=$400; // allow to disband city when settlers/conscripts are produced
    304 cpImp=$800; // 0: index refers to model, 1: index refers to city improvement
    305 cpRepeat=$1000; cpCompleted=$2000; cpAuto=$F000; // for internal use only
    306 
    307 // tech status indicators
    308 tsNA=-2;tsSeen=-1;tsResearched=0;tsGrLibrary=1;tsCheat=15;
    309 tsApplicable=tsResearched;
    310 
    311 // nation treaties
    312 trNoContact=-1; trNone=0; trPeace=2; trFriendlyContact=3;
    313 trAlliance=4;
    314 
    315 // attitudes
    316 nAttitude=7;
    317 atHostile=0;atIcy=1;atUncoop=2;atNeutral=3;atReceptive=4;atCordial=5;atEnth=6;
    318 
    319 // offer prices
    320 opChoose=       $00000000;
    321 opCivilReport=  $11000000; // + turn + concerned player shl 16
    322 opMilReport=    $12000000; // + turn + concerned player shl 16
    323 opMap=          $1F000000;
    324 opTreaty=       $20000000; // + suggested nation treaty
    325 opShipParts=    $30000000; // + number + part type shl 16
    326 opMoney=        $40000000; // + value
    327 opTribute=      $48000000; // obsolete
    328 opTech=         $50000000; // + advance
    329 opAllTech=      $51000000;
    330 opModel=        $58000000; // + model index
    331 opAllModel=     $59000000;
    332 
    333 opMask=         $FF000000;
    334 
    335 // improvement kinds
    336 ikTrGoods=0; ikCommon=1; ikNatLocal=2; ikNatGlobal=3; ikWonder=4; ikShipPart=5;
    337 ikNA=$7F;
    338 
    339 {model domains}
    340 nDomains=3;dGround=0;dSea=1;dAir=2;
    341 
    342 {model kinds}
    343 mkSelfDeveloped=$00;mkEnemyDeveloped=$01;
    344 mkSpecial_Boat=$08;mkSpecial_SubCabin=$0A;
    345 mkSpecial_TownGuard=$10;mkSpecial_Glider=$11;
    346 mkScout=$20;mkSlaves=$21;mkSettler=$22;mkCommando=$23;mkFreight=$24;
    347 
    348 {unit flags}
    349 unFortified=$01; unBombsLoaded=$02; unMountainDelay=$04; unConscripts=$08;
    350 unWithdrawn=$10;
    351 unMulti=$80;
    352 
    353 // unit report flags
    354 urfAlwaysSupport=$01; urfDeployed=$02;
    355 
    356 // unit moves
    357 umCapturing=$0100; umSpyMission=$0200; umBombarding=$0400; umExpelling=$0800;
    358 umShipLoading=$1000; umShipUnloading=$2000;
    359 umPlaneLoading=$4000; umPlaneUnloading=$8000;
    360 
    361 {model flags}
    362 mdZOC=$01; mdCivil=$02; mdDoubleSupport=$04;
    363 
    364 {player happened flags}
    365 phTech=$01; phStealTech=$02; phChangeGov=$08;
    366 phGliderLost=$100; phPlaneLost=$200; phPeaceViolation=$400;
    367 phPeaceEvacuation=$800;
    368 phShipComplete=$2000; phTimeUp=$4000; phExtinct=$8000; phGameEnd=$F000;
    369 
    370 {city happened flags}
    371 chDisorder=$01;chProduction=$02;chPopIncrease=$04;chPopDecrease=$08;
    372 chUnitLost=$10;chImprovementLost=$20;chProductionSabotaged=$40;
    373 chNoGrowthWarning=$80;
    374 chPollution=$100;chSiege=$200;chOldWonder=$400;chNoSettlerProd=$800;
    375 chFounded=$1000;chAfterCapture=$2000;chCaptured=$F0000;
    376 chImprovementSold=$80000000;
    377 
    378 {city info flags}
    379 ciCapital=$01; ciWalled=$02; ciCoastalFort=$04; ciMissileBat=$08;
    380 ciBunker=$10; ciSpacePort=$20;
    381 
    382 {city tile available values}
    383 faAvailable=0; faNotAvailable=1; faSiege=2; faTreaty=4; faInvalid=$FF;
    384 
    385 // battle history flags
    386 bhEnemyAttack=$01; bhMyUnitLost=$02; bhEnemyUnitLost=$04;
    387 
    388 {move advice special destinations}
    389 maNextCity=-1;
    390 
    391 {goverment forms}
    392 nGov=8;
    393 gAnarchy=0;gDespotism=1;gMonarchy=2;gRepublic=3;gFundamentalism=4;gCommunism=5;
    394 gDemocracy=6;gFuture=7;
    395 
    396 // ship change reasons
    397 scrProduction=0; scrDestruction=1; scrTrade=2; scrCapture=3;
    398 
    399 {unit jobs}
    400 nJob=15;
    401 jNone=0;jRoad=1;jRR=2;jClear=3;jIrr=4;jFarm=5;jAfforest=6;jMine=7;jCanal=8;
    402 jTrans=9;jFort=10;jPoll=11;jBase=12;jPillage=13;jCity=14;
    403 
    404 // job preconditions are:
    405 // technology JobPreq is available, no city, plus the following:
    406 //   jRoad: no river when bridge building unavailable
    407 //   jRR: road
    408 //   jClear: Terrain.ClearTerrain, Hanging Gardens for desert
    409 //   jIrr: Terrain.IrrEff
    410 //   jFarm: irrigation
    411 //   jAfforest: Terrain.AfforestTerrain
    412 //   jMine: Terrain.MineEff
    413 //   jCanal: no Mountains, no Arctic
    414 //   jTrans: Terrain.TransWork
    415 //   jPoll: pollution
    416 //   jPillage: any tile improvement
    417 //   jCity, jFort, jBase: none
    418 
    419 
    420 // spy mission
    421 nSpyMission=5;
    422 smSabotageProd=0;smStealMap=1;smStealForeignReports=2;
    423 smStealCivilReport=3;smStealMilReport=4;
    424 
    425 // resource weights
    426 rwOff=$00000000;
    427 rwMaxGrowth=$3F514141; // 120*F + 1/8*P + 1/16*T + 1/16*S
    428 rwMaxProd=$413F1F01; // 1/16*F + 120*P + 30*T + 1*S
    429 rwMaxScience=$41040408; // 1/16*F + 4*P + 4*T + 8*S
    430 rwForceProd=$F1080201; // F^1/2 * (8*P + 2*T + 1*S)
    431 rwForceScience=$F1010101; // F^1/2 * (1*P + 1*T + 1*S)
    432 
    433 {advances}
    434 adAdvancedFlight=0;adAmphibiousWarfare=1;adAstronomy=2;adAtomicTheory=3;adAutomobile=4;
    435 adBallistics=5;adBanking=6;adBridgeBuilding=7;adBronzeWorking=8;adCeremonialBurial=9;
    436 adChemistry=10;adChivalry=11;adComposites=12;adCodeOfLaws=13;adCombinedArms=14;
    437 adCombustionEngine=15;adCommunism=16;adComputers=17;adConscription=18;adConstruction=19;
    438 adTheCorporation=20;adSpaceFlight=21;adCurrency=22;adDemocracy=23;adEconomics=24;
    439 adElectricity=25;adElectronics=26;adEngineering=27;adEnvironmentalism=28;adWheel=29;
    440 adExplosives=30;adFlight=31;adIntelligence=32;adGunpowder=33;adHorsebackRiding=34;
    441 adImpulseDrive=35;adIndustrialization=36;adSmartWeapons=37;adInvention=38;adIronWorking=39;
    442 adTheLaser=40;adNuclearPower=41;adLiterature=42;adInternet=43;adMagnetism=44;
    443 adMapMaking=45;adMasonry=46;adMassProduction=47;adMathematics=48;adMedicine=49;
    444 adMetallurgy=50;adMin=51;adMobileWarfare=52;adMonarchy=53;adMysticism=54;
    445 adNavigation=55;adNuclearFission=56;adPhilosophy=57;adPhysics=58;adPlastics=59;
    446 adPoetry=60;adPottery=61;adRadio=62;adRecycling=63;adRefrigeration=64;
    447 adMonotheism=65;adTheRepublic=66;adRobotics=67;adRocketry=68;adRailroad=69;
    448 adSanitation=70;adScience=71;adWriting=72;adSeafaring=73;adSelfContainedEnvironment=74;
    449 adStealth=75;adSteamEngine=76;adSteel=77;adSyntheticFood=78;adTactics=79;
    450 adTheology=80;adTheoryOfGravity=81;adTrade=82;adTransstellarColonization=83;adUniversity=84;
    451 adAdvancedRocketry=85;adWarriorCode=86;adAlphabet=87;adPolytheism=88;adRefining=89;
    452 futComputingTechnology=90;futNanoTechnology=91;futMaterialTechnology=92;futArtificialIntelligence=93;
    453 
    454 FutureTech=[futComputingTechnology,futNanoTechnology,futMaterialTechnology,
    455   futArtificialIntelligence];
    456 
    457 adMilitary=$800; // Military Research
    458 
    459 
    460 {wonders}
    461 woPyramids=00;woZeus=01;woGardens=02;woColossus=03;woLighthouse=04;woGrLibrary=05;woOracle=06;
    462 woSun=07;woLeo=08;woMagellan=09;woMich=10;{11;}woNewton=12;woBach=13;
    463 {14;}woLiberty=15;woEiffel=16;woHoover=17;woShinkansen=18;woManhattan=19;woMir=20;
    464 
    465 {city improvements}
    466 imTrGoods=28;imBarracks=29;imGranary=30;imTemple=31;imMarket=32;imLibrary=33;imCourt=34;
    467 imWalls=35;imAqueduct=36;imBank=37;imCathedral=38;imUniversity=39;imHarbor=40;imTheater=41;
    468 imFactory=42;imMfgPlant=43;imRecycling=44;imPower=45;imHydro=46;imNuclear=47;imPlatform=48;
    469 imTownHall=49;imSewer=50;imSupermarket=51;imHighways=52;imResLab=53;imMissileBat=54;imCoastalFort=55;
    470 imAirport=56;imDockyard=57;imPalace=58;imGrWall=59;imColosseum=60;imObservatory=61;imMilAcademy=62;
    471 imBunker=63;imAlgae=64;imStockEx=65;imSpacePort=66;imShipComp=67;imShipPow=68;imShipHab=69;
    472 
    473 
    474 SettlerFood:array[0..nGov-1] of integer=(1,1,1,2,1,2,2,2);
    475 CorrLevel:array[0..nGov-1] of integer=(3,3,1,2,1,0,0,0);
    476 SupportFree:array[0..nGov-1] of integer=(2,2,1,0,2,1,0,0); // in 1/2*city size
    477 
    478 // special prerequisite values
    479 preNone=-1; preLighthouse=-2; preSun=-3; preLeo=-4; preBuilder=-5; preNA=-$FF;
    480 
    481 JobPreq: array[0..nJob-1] of integer=
    482 (preNone,preNone,adRailroad,preNone,preNone,adRefrigeration,preNone,preNone,adExplosives,adExplosives,
    483 adConstruction,preNone,adMedicine,preNone,preNone);
    484 
    485 AdvPreq: array[0..nAdv-1,0..2] of integer= {advance prerequisites}
    486 ((adFlight,adRobotics,preNone), //adAdvancedFlight
    487 (adNavigation,adTactics,preNone), //adAmphibiousWarfare
    488 (adMysticism,adAlphabet,preNone), //adAstronomy
    489 (adTheoryOfGravity,preNone,preNone), //adAtomicTheory
    490 (adCombustionEngine,adSteel,preNone), //adAutomobile
    491 (adMathematics,adMetallurgy,preNone), //adBallistics
    492 (adCurrency,adEngineering,preNone), //adBanking
    493 (adConstruction,adWheel,preNone), //adBridgeBuilding
    494 (preNone,preNone,preNone), //adBronzeWorking
    495 (preNone,preNone,preNone), //adCeremonialBurial
    496 (adScience,preNone,preNone), //adChemistry
    497 (adMonarchy,adWarriorCode,preNone), //adChivalry
    498 (adMetallurgy,adPlastics,preNone), //adComposites
    499 (adWriting,preNone,preNone), //adCodeOfLaws
    500 (adAdvancedFlight,adMobileWarfare,preNone), //adCombinedArms
    501 (adRefining,adExplosives,preNone), //adCombustionEngine
    502 (adPhilosophy,adIndustrialization,preNone), //adCommunism
    503 (adMin,preNone,preNone), //adComputers
    504 (adTheRepublic,adTactics,preNone), //adConscription
    505 (adMasonry,adAlphabet,preNone), //adConstruction
    506 (adEconomics,adDemocracy,preNone), //adTheCorporation
    507 (adAdvancedFlight,adAdvancedRocketry,preNone), //adSpaceFlight
    508 (adBronzeWorking,preNone,preNone), //adCurrency
    509 (adConscription,adIndustrialization,preNone), //adDemocracy
    510 (adBanking,adUniversity,preNone), //adEconomics
    511 (adMagnetism,preNone,preNone), //adElectricity
    512 (adRadio,adAtomicTheory,preNone), //adElectronics
    513 (adConstruction,adBronzeWorking,preNone), //adEngineering
    514 (adIndustrialization,preNone,preNone), //adEnvironmentalism
    515 (preNone,preNone,preNone), //adWheel
    516 (adChemistry,adEngineering,preNone), //adExplosives
    517 (adCombustionEngine,adPhysics,preNone), //adFlight
    518 (adTactics,adInvention,preNone), //adIntelligence
    519 (adMedicine,adIronWorking,preNone), //adGunpowder
    520 (preNone,preNone,preNone), //adHorsebackRiding
    521 (adSpaceFlight,adNuclearPower,preNone), //adImpulseDrive
    522 (adRailroad,adBanking,preNone), //adIndustrialization
    523 (adAdvancedRocketry,adTheLaser,preNone), //adIntelligenArms
    524 (adWriting,adWheel,preNone), //adInvention
    525 (adBronzeWorking,adInvention,preNone), //adIronWorking
    526 (adMin,adPhysics,preNone), //adTheLaser
    527 (adNuclearFission,preNone,preNone), //adNuclearPower
    528 (adPoetry,adTrade,preNone), //adLiterature
    529 (adDemocracy,adComputers,preNone), //adLybertarianism
    530 (adPhysics,adIronWorking,preNone), //adMagnetism
    531 (adAlphabet,preNone,preNone), //adMapMaking
    532 (preNone,preNone,preNone), //adMasonry
    533 (adAutomobile,adElectronics,adTheCorporation), //adMassProduction
    534 (adCurrency,adAlphabet,preNone), //adMathematics
    535 (adMysticism,adPottery,preNone), //adMedicine
    536 (adGunpowder,preNone,preNone), //adMetallurgy
    537 (adRobotics,adPlastics,preNone), //adMin
    538 (adAutomobile,adTactics,preNone), //adMobileWarfare
    539 (adPolytheism,preNone,preNone), //adMonarchy
    540 (adCeremonialBurial,preNone,preNone), //adMysticism
    541 (adSeafaring,adAstronomy,preNone), //adNavigation
    542 (adAtomicTheory,adMassProduction,preNone), //adNuclearFission
    543 (adMathematics,adLiterature,preNone), //adPhilosophy
    544 (adScience,preNone,preNone), //adPhysics
    545 (adMassProduction,adRefining,preNone), //adPlastics
    546 (adMysticism,adWarriorCode,preNone), //adPoetry
    547 (preNone,preNone,preNone), //adPottery
    548 (adElectricity,adEngineering,preNone), //adRadio
    549 (adEnvironmentalism,adPlastics,preNone), //adRecycling
    550 (adElectricity,preNone,preNone), //adRefrigeration
    551 (adPolytheism,adAstronomy,preNone), //adMonotheism
    552 (adLiterature,preNone,preNone), //adTheRepublic
    553 (adMassProduction,adEconomics,preNone), //adRobotics
    554 (adBallistics,adExplosives,preNone), //adRocketry
    555 (adSteamEngine,adBridgeBuilding,preNone), //adRailroad
    556 (adEnvironmentalism,adMedicine,preNone), //adSanitation
    557 (adMetallurgy,adTheology,adPhilosophy), //adScience
    558 (adAlphabet,preNone,preNone), //adWriting
    559 (adPottery,adMapMaking,preNone), //adSeafaring
    560 (adRecycling,adSyntheticFood,preNone), //adSelfContainedEnvironment
    561 (adComposites,adRadio,preNone), //adStealth
    562 (adScience,adEngineering,preNone), //adSteamEngine
    563 (adIronWorking,adRailroad,preNone), //adSteel
    564 (adChemistry,adRefrigeration,preNone), //adSyntheticFood
    565 (adWarriorCode,adUniversity,preNone), //adTactics
    566 (adMonotheism,adPoetry,preNone), //adTheology
    567 (adAstronomy,adPhysics,preNone), //adTheoryOfGravity
    568 (adCurrency,adCodeOfLaws,preNone), //adTrade
    569 (adImpulseDrive,adSelfContainedEnvironment,preNone), //adTransstellarColonization
    570 (adScience,preNone,preNone), //adUniversity
    571 (adComputers,adRocketry,preNone), //adAdvancedRocketry
    572 (preNone,preNone,preNone), //adWarriorCode
    573 (preNone,preNone,preNone), //adAlphabet
    574 (adCeremonialBurial,adHorsebackRiding,preNone), //adPolytheism
    575 (adChemistry,preNone,preNone), //adRefining
    576 (adComputers,preNone,preNone), //futResearchTechnology
    577 (adRobotics,preNone,preNone), //futProductionTechnology
    578 (adComposites,preNone,preNone), //futArmorTechnology
    579 (adSmartWeapons,preNone,preNone)); //futMissileTechnology
    580 
    581 Imp: array[0..nImp-1] of // city improvements
    582   record Kind,Preq,Cost,Maint,Expiration:integer; end=
    583 ((Kind:ikWonder;Preq:adMathematics;Cost:400;Maint:0;Expiration:adDemocracy), //woPyramids
    584 (Kind:ikWonder;Preq:adPolytheism;Cost:200;Maint:0;Expiration:adElectronics), //woZeus
    585 (Kind:ikWonder;Preq:adInvention;Cost:200;Maint:0;Expiration:adNuclearFission), //woGardens
    586 (Kind:ikWonder;Preq:adBronzeWorking;Cost:200;Maint:0;Expiration:-1), //woColossus
    587 (Kind:ikWonder;Preq:adMapMaking;Cost:200;Maint:0;Expiration:adSteel), //woLighthouse
    588 (Kind:ikWonder;Preq:adLiterature;Cost:400;Maint:0;Expiration:adPlastics), //woGrLibrary
    589 (Kind:ikWonder;Preq:adMysticism;Cost:200;Maint:0;Expiration:-1), //woOracle
    590 (Kind:ikWonder;Preq:adChivalry;Cost:300;Maint:0;Expiration:adSpaceFlight), //woSun
    591 (Kind:ikWonder;Preq:adPhilosophy;Cost:500;Maint:0;Expiration:-1), //woLeo
    592 (Kind:ikWonder;Preq:adNavigation;Cost:300;Maint:0;Expiration:-1), //woMagellan
    593 (Kind:ikWonder;Preq:adMonotheism;Cost:400;Maint:0;Expiration:-1), //woMich
    594 (Kind:ikNA;Preq:preNA), //{11}
    595 (Kind:ikWonder;Preq:adTheoryOfGravity;Cost:400;Maint:0;Expiration:-1), //woNewton
    596 (Kind:ikWonder;Preq:adTheology;Cost:400;Maint:0;Expiration:-1), //woBach
    597 (Kind:ikNA;Preq:preNA), //{14}
    598 (Kind:ikWonder;Preq:adDemocracy;Cost:500;Maint:0;Expiration:-1), //woLiberty
    599 (Kind:ikWonder;Preq:adSteel;Cost:800;Maint:0;Expiration:-1), //woEiffel
    600 (Kind:ikWonder;Preq:adElectronics;Cost:800;Maint:0;Expiration:-1), //woHoover
    601 (Kind:ikWonder;Preq:adPlastics;Cost:500;Maint:0;Expiration:-1), //woShinkansen
    602 (Kind:ikWonder;Preq:adNuclearFission;Cost:400;Maint:0;Expiration:-1), //woManhattan
    603 (Kind:ikWonder;Preq:adSpaceFlight;Cost:800;Maint:0;Expiration:-1), //woMir
    604 (Kind:ikNA;Preq:preNA), //{21}
    605 (Kind:ikNA;Preq:preNA), //{22}
    606 (Kind:ikNA;Preq:preNA), //{23}
    607 (Kind:ikNA;Preq:preNA), //{24}
    608 (Kind:ikNA;Preq:preNA), //{25}
    609 (Kind:ikNA;Preq:preNA), //{26}
    610 (Kind:ikNA;Preq:preNA), //{27}
    611 (Kind:ikTrGoods;Preq:preNone;Cost:0;Maint:0), //imTrGoods
    612 (Kind:ikCommon;Preq:adWarriorCode;Cost:40;Maint:1), //imBarracks
    613 (Kind:ikCommon;Preq:adPottery;Cost:60;Maint:1), //imGranary
    614 (Kind:ikCommon;Preq:adCeremonialBurial;Cost:40;Maint:1), //imTemple
    615 (Kind:ikCommon;Preq:adCurrency;Cost:60;Maint:1), //imMarket
    616 (Kind:ikCommon;Preq:adWriting;Cost:80;Maint:3), //imLibrary
    617 (Kind:ikCommon;Preq:adCodeOfLaws;Cost:80;Maint:2), //imCourt
    618 (Kind:ikCommon;Preq:adMasonry;Cost:80;Maint:1), //imWalls
    619 (Kind:ikCommon;Preq:adConstruction;Cost:80;Maint:1), //imAqueduct
    620 (Kind:ikCommon;Preq:adBanking;Cost:120;Maint:2), //imBank
    621 (Kind:ikCommon;Preq:adMonotheism;Cost:100;Maint:1), //imCathedral
    622 (Kind:ikCommon;Preq:adUniversity;Cost:160;Maint:5), //imUniversity
    623 (Kind:ikCommon;Preq:adSeafaring;Cost:60;Maint:1), //imHarbor
    624 (Kind:ikCommon;Preq:adPoetry;Cost:60;Maint:2), //imTheater
    625 (Kind:ikCommon;Preq:adIndustrialization;Cost:200;Maint:3), //imFactory
    626 (Kind:ikCommon;Preq:adRobotics;Cost:320;Maint:5), //imMfgPlant
    627 (Kind:ikCommon;Preq:adRecycling;Cost:320;Maint:4), //imRecycling
    628 (Kind:ikCommon;Preq:adElectricity;Cost:120;Maint:2), //imPower
    629 (Kind:ikCommon;Preq:adEnvironmentalism;Cost:120;Maint:1), //imHydro
    630 (Kind:ikCommon;Preq:adNuclearPower;Cost:240;Maint:2), //imNuclear
    631 (Kind:ikCommon;Preq:adRefining;Cost:160;Maint:2), //imPlatform
    632 (Kind:ikCommon;Preq:preNone;Cost:40;Maint:1), //imTownHall
    633 (Kind:ikCommon;Preq:adSanitation;Cost:120;Maint:2), //imSewer
    634 (Kind:ikCommon;Preq:adRefrigeration;Cost:80;Maint:2), //imSupermarket
    635 (Kind:ikCommon;Preq:adAutomobile;Cost:160;Maint:4), //imHighways
    636 (Kind:ikCommon;Preq:adComputers;Cost:240;Maint:7), //imResLab
    637 (Kind:ikCommon;Preq:adAdvancedRocketry;Cost:100;Maint:1), //imMissileBat
    638 (Kind:ikCommon;Preq:adMetallurgy;Cost:80;Maint:1), //imCoastalFort
    639 (Kind:ikCommon;Preq:adAdvancedFlight;Cost:160;Maint:1), //imAirport
    640 (Kind:ikCommon;Preq:adAmphibiousWarfare;Cost:80;Maint:1), //imDockyard
    641 (Kind:ikNatLocal;Preq:preNone;Cost:100;Maint:0), //imPalace
    642 (Kind:ikNatLocal;Preq:adEngineering;Cost:400;Maint:4), //imGrWall
    643 (Kind:ikNatLocal;Preq:adConstruction;Cost:200;Maint:4), //imColosseum
    644 (Kind:ikNatLocal;Preq:adAstronomy;Cost:300;Maint:4), //imObservatory
    645 (Kind:ikNatLocal;Preq:adTactics;Cost:100;Maint:4), //imMilAcademy
    646 (Kind:ikNatLocal;Preq:adSteel;Cost:200;Maint:2), //imBunker
    647 (Kind:ikNatLocal;Preq:adSyntheticFood;Cost:120;Maint:2), //imAlgae
    648 (Kind:ikNatGlobal;Preq:adTheCorporation;Cost:320;Maint:4), //imStockEx
    649 (Kind:ikNatLocal;Preq:adSpaceFlight;Cost:400;Maint:0), //imSpacePort
    650 (Kind:ikShipPart;Preq:adTransstellarColonization;Cost:240;Maint:0), //imShipComp
    651 (Kind:ikShipPart;Preq:adImpulseDrive;Cost:600;Maint:0), //imShipPow
    652 (Kind:ikShipPart;Preq:adSelfContainedEnvironment;Cost:800;Maint:0)); //imShipHab
    653 
    654 nImpReplacement=5;
    655 ImpReplacement: array[0..nImpReplacement-1] of
    656   record NewImp,OldImp: integer; end=
    657 ((NewImp:imSewer;OldImp:imAqueduct),
    658 (NewImp:imCourt;OldImp:imTownHall),
    659 (NewImp:imPalace;OldImp:imTownHall),
    660 (NewImp:imPalace;OldImp:imCourt),
    661 (NewImp:imMilAcademy;OldImp:imBarracks));
     8  lxmax = 100;
     9  lymax = 96;
     10  nAdv = 94; { number of advances }
     11  nImp = 70; { number of improvements }
     12  nPl = 15; { max number of players, don't change! }
     13  nUmax = 4096; { max units/player, don't set above 4096 }
     14  nCmax = 1024; { max cities/player, don't set above 4096 }
     15  nMmax = 256; { max models/player, don't set above 1024 }
     16  nExp = 5; // number of experience levels
     17  ExpCost = 50; { received damage required for next experience level }
     18  MaxFutureTech = 25;
     19  // maximum number of future techs of one kind except computing technology
     20  MaxFutureTech_Computing = 100;
     21  // maximum number of computing technology future techs
     22  CountryRadius = 9;
     23  MaxCitySize = 30;
     24  BasicHappy = 2; { basically happy citizens }
     25  MaxPollution = 240;
     26  NeedAqueductSize = 8;
     27  NeedSewerSize = 12;
     28  ColossusEffect = 75; // percent wonder building cost
     29  UniversityFutureBonus = 5; // percent per tech
     30  ResLabFutureBonus = 10; // percent per tech
     31  FactoryFutureBonus = 5; // percent per tech
     32  MfgPlantFutureBonus = 10; // percent per tech
     33  AnarchyTurns = 3;
     34  CaptureTurns = 3;
     35  CancelTreatyTurns = 3;
     36  PeaceEvaTurns = 5;
     37  // should be less then 2*CancelTreatyTurns, so that you can't attack an ally without re-entering
     38  ColdWarTurns = 40;
     39  DesertThurst = 20; // damage for turn in desert
     40  ArcticThurst = 20; // damage for turn in arctic
     41  FastRecovery = 50;
     42  CityRecovery = 20;
     43  NoCityRecovery = 8;
     44  MaxMoneyPrice = $FFFF;
     45  MaxShipPartPrice = 100;
     46  BombardmentDestroysCity = false;
     47  StartMoney = 0;
     48  InitialCredibility = 95;
     49
     50  // ai module flags (for TInitModuleData.Flags)
     51  aiThreaded = $01;
     52
     53  // difficulty settings
     54  MaxDiff = 4; { maximum difficulty level }
     55  StorageSize: array [1 .. MaxDiff] of integer = (30, 40, 50, 60);
     56  TechFormula_M: array [1 .. MaxDiff] of single = (2.0, 2.3, 2.6, 4.0);
     57  TechFormula_D: array [1 .. MaxDiff] of single = (102.0, 80.0, 64.0, 64.0);
     58  BuildCostMod: array [1 .. MaxDiff] of integer = (9, 12, 15, 18); // in 1/12
     59
     60  // test flags
     61  nTestFlags = 7; // max. 11
     62  tfAllTechs = $001; { all nations get all techs }
     63  tfImmImprove = $002; { city projects complete each turn }
     64  tfImmAdvance = $004; { research complete each turn }
     65  tfImmGrow = $008; { all cities grow in each turn }
     66  tfUncover = $010; // all players see like supervisor
     67  tfAllContact = $020; // all nations can contact each other
     68  tfNoRareNeed = $040; // producing colony ship requires no modern resources
     69  tfTested = $800; // at least one test flag was set
     70
     71  { server commands
     72    IMPORTANT: lowest 4 bits must indicate size in DWORDS of data parameter,
     73    except for request commands }
     74
     75  sctMask = $3800; // server command type
     76  sExecute = $4000; { call command-sExecute to request return value without
     77    execution }
     78  cClientEx = $8000;
     79
     80  // Info Request Commands
     81  sctInfo = $0000;
     82  sMessage = $0000;
     83  sSetDebugMap = $0010;
     84  sGetDebugMap = $0020;
     85  { sChangeSuperView=$0030; } sRefreshDebugMap = $0040;
     86  sGetChart = $0100; // + type shl 4
     87  sGetTechCost = $0180;
     88  sGetAIInfo = $01C0;
     89  sGetAICredits = $01D0;
     90  sGetVersion = $01E0;
     91  sGetGameChanged = $01F0;
     92  sGetTileInfo = $0200;
     93  sGetCityTileInfo = $0210;
     94  sGetHypoCityTileInfo = $0220;
     95  sGetJobProgress = $0230;
     96  sGetModels = $0270;
     97  sGetUnits = $0280;
     98  sGetDefender = $0290;
     99  sGetBattleForecast = $02A0;
     100  sGetUnitReport = $02B0;
     101  sGetMoveAdvice = $02C0;
     102  sGetPlaneReturn = $02D0;
     103  sGetBattleForecastEx = $02E0;
     104  sGetCity = $0300;
     105  sGetCityReport = $0310;
     106  sGetCityAreaInfo = $0320;
     107  sGetEnemyCityReport = $0330;
     108  sGetEnemyCityAreaInfo = $0340;
     109  sGetCityTileAdvice = $0350;
     110  sGetCityReportNew = $0360;
     111  sGetEnemyCityReportNew = $0370;
     112
     113  // Map Editor Commands
     114  sEditTile = $0710;
     115  sRandomMap = $0780;
     116  sMapGeneratorRequest = $0790;
     117
     118  // Server Internal Commands
     119  sctInternal = sctInfo;
     120  // sctInfo - without sExecute flag, sctInternal - with sExecute flag
     121  sIntTellAboutNation = $4000;
     122  sIntHaveContact = $4010;
     123  sIntCancelTreaty = $4020;
     124  sIntTellAboutModel = $4100; { +told player shl 4 }
     125  sIntDiscoverZOC = $4201;
     126  sIntExpandTerritory = $4218;
     127  sIntBuyMaterial = $4301;
     128  sIntPayPrices = $4402;
     129  sIntSetDevModel = $450D;
     130  sIntSetModelStatus = $4601;
     131  sIntSetUnitStatus = $4611;
     132  sIntSetCityStatus = $4621;
     133  sIntSetECityStatus = $4631;
     134  sIntDataChange = $4700;
     135
     136  // Client Deactivation Commands
     137  sctEndClient = $0800;
     138  sTurn = $4800;
     139  sBreak = $4810;
     140  sResign = $4820;
     141  sNextRound = $4830;
     142  sReload = $4841;
     143  sSaveMap = $4880;
     144  sAbandonMap = $4890;
     145  // diplomacy commands equal to client, see below
     146
     147  // General Commands
     148  sctGeneral = $1000;
     149  sClearTestFlag = $5000;
     150  sSetTestFlag = $5010;
     151  sSetGovernment = $5100;
     152  sSetRates = $5110;
     153  sRevolution = $5120;
     154  sSetResearch = $5200;
     155  sStealTech = $5210;
     156  sSetAttitude = $5300; // + concerned player shl 4
     157  sCancelTreaty = $5400;
     158
     159  // Model Related Commands
     160  sctModel = $1800;
     161  sCreateDevModel = $5800;
     162  sSetDevModelCap = $5C00; { +value shl 4 }
     163  { reserves $5CXX, $5DXX, $5EXX, $5FXX }
     164
     165  // Unit Related Commands
     166  sctUnit = $2000;
     167  sRemoveUnit = $6000;
     168  sSetUnitHome = $6010;
     169  sSetSpyMission = $6100; // + mission shl 4
     170  sLoadUnit = $6200;
     171  sUnloadUnit = $6210;
     172  sSelectTransport = $6220;
     173  sCreateUnit = $6301; // + player shl 4
     174  sMoveUnit = $6400; { +dx and 7 shl 4 +dy and 7 shl 7 }
     175  { reserves $64XX, $65XX, $66XX, $67XX }
     176
     177  // Settlers Related Commands
     178  sctSettlers = $2800;
     179  sAddToCity = $6810;
     180  sStartJob = $6C00; { +job shl 4 }
     181  { reserves $6CXX, $6DXX, $6EXX, $6FXX }
     182
     183  // City Related Commands
     184  sctCity = $3000;
     185  sSetCityProject = $7001;
     186  sBuyCityProject = $7010;
     187  sSellCityProject = $7020;
     188  sSellCityImprovement = $7101;
     189  sRebuildCityImprovement = $7111;
     190  sSetCityTiles = $7201;
     191
     192  // free command space
     193  sctUnused = $3800;
     194
     195  { client commands }
     196  cInitModule = $0000;
     197  cReleaseModule = $0100;
     198  cBroadcast = $0200;
     199  cHelpOnly = $0700;
     200  cStartHelp = $0710;
     201  cStartCredits = $0720;
     202
     203  cNewGame = $0800;
     204  cLoadGame = $0810;
     205  cMovie = $0820;
     206  cNewGameEx = $0840;
     207  cLoadGameEx = $0850;
     208  cNewMap = $0880;
     209  cReplay = $08E0;
     210  cGetReady = $08F0;
     211  cBreakGame = $0900;
     212
     213  cTurn = $2000;
     214  cResume = $2010;
     215  cContinue = $2080;
     216  cMovieTurn = $2100;
     217  cMovieEndTurn = $2110;
     218  cEditMap = $2800;
     219
     220  // cShowTileM=$3000;cShowTileA=$3010;cShowFoundCity=$3020;
     221  cShowUnitChanged = $3030;
     222  cShowAfterMove = $3040;
     223  cShowAfterAttack = $3050;
     224  cShowCityChanged = $3090;
     225  // cShowMove=$3100;cShowCapture=$3110;
     226  // cShowAttackBegin=$3200;cShowAttackWon=$3210;cShowAttackLost=$3220;
     227  cShowMoving = $3140;
     228  cShowCapturing = $3150;
     229  cShowAttacking = $3240;
     230  cShowMissionResult = $3300;
     231  cShowShipChange = $3400;
     232  cShowGreatLibTech = $3500;
     233  cShowTurnChange = $3700;
     234  cShowCancelTreaty = $3800;
     235  cShowEndContact = $3810;
     236  cShowCancelTreatyByAlliance = $3820;
     237  cShowSupportAllianceAgainst = $3830;
     238  cShowPeaceViolation = $3880;
     239  cShowGame = $3F00; { cShowSuperView=$3F80; }
     240  cRefreshDebugMap = $3F90;
     241
     242  // diplomacy commands equal to server, see below
     243
     244  cDebugMessage = $7000;
     245  cShowNego = $7010;
     246
     247  // commands same for server and client
     248  scContact = $4900; // + concerned player shl 4 for server call
     249  scReject = $4A00;
     250  scDipStart = $4B00;
     251  scDipNotice = $4B10;
     252  scDipAccept = $4B20;
     253  scDipCancelTreaty = $4B30;
     254  scDipOffer = $4B4E;
     255  scDipBreak = $4BF0;
     256
     257  { server return codes: flags }
     258  rExecuted = $40000000;
     259  rEffective = $20000000;
     260  rUnitRemoved = $10000000;
     261  rEnemySpotted = $08000000;
     262
     263  { server return codes: command executed }
     264  // note: the same return code might have a different meaning for different server functions!
     265  eOK = $60000000; // ok
     266  eEnemySpotted = $68000000; // unit move ok, new enemy unit/city spotted
     267  eDied = $70000000; // move executed, unit died due to hostile terrain
     268  eEnemySpotted_Died = $78000000;
     269  // unit move ok, new enemy unit/city spotted, unit died due to hostile terrain
     270  eLoaded = $60000002; // unit move caused loading to transport ship
     271  eLost = $70000004; // attack executed, battle lost, unit is dead
     272  eWon = $60000005; // attack executed, battle won, defender destroyed
     273  eBloody = $70000005; // attack executed, defender destroyed, unit is dead
     274  eBombarded = $60000006; // empty enemy city bombarded
     275  eExpelled = $60000007; // friendly unit expelled
     276  eMissionDone = $70000008;
     277  // spy moved into city: mission done, spy no longer exists
     278  eJobDone = $60000001; // settler job started and already done
     279  eJobDone_Died = $70000001;
     280  // settler job started and already done, unit died due to hostile terrain
     281  eCity = $70000002; // city founded, settler no more exists
     282  eRemoved = $70000000; // sRemoveUnit: unit removed
     283  eUtilized = $70000001; // sRemoveUnit: unit utilized for city project
     284
     285  eNotChanged = $40000000;
     286  // ok, but no effect (e.g. current city project set again)
     287
     288  { server return codes: command not executed }
     289  eHiddenUnit = $20000013;
     290  // unit move: not possible, destination tile occupied by hidden foreign submarine
     291  eStealthUnit = $2000001A;
     292  // unit move: not possible, destination tile occupied by foreign stealth unit
     293  eZOC_EnemySpotted = $28000014;
     294  // unit move: not possible, new enemy unit spotted, ZOC violation
     295
     296  eInvalid = $0000; // command not allowed now or parameter out of allowed range
     297  eUnknown = $0001; // unknown command
     298  eNoTurn = $0002; // command only allowed during player's turn
     299  eViolation = $0003; // general violation of game rules
     300  eNoPreq = $0004; // the prerequisites for this command are not fully met
     301
     302  eNoTime_Move = $0008; // normal unit move: too few movement points left
     303  eNoTime_Load = $0009; // load unit: too few movement points left
     304  eNoTime_Attack = $000A; // attack: no movement points left
     305  eNoTime_Bombard = $000B; // bombard city: too few movement points left
     306  eNoTime_Expel = $000C; // expel spy: too few movement points left
     307
     308  eDomainMismatch = $0011;
     309  // move/attack: action not allowed for this unit domain
     310  eNoCapturer = $0012;
     311  // unit move: this type of unit is not allowed to capture a city
     312  eZOC = $0014; // unit move: not possible, ZOC violation
     313  eTreaty = $0015; // move/attack: not possible, peace treaty violation
     314  eDeadLands = $0016; // sStartJob: not possible, dead lands
     315  eNoRoad = $0017; // unit move: not possible, no road
     316  eNoNav = $0019; // unit move: not possible, open sea without navigation
     317  eNoLoadCapacity = $001B; // load to transport: no more transport capacity
     318  eNoBombarder = $001C; // bombardment impossible because no attack power
     319
     320  eMaxSize = $0020;
     321  // add to city: bigger size not allowed due to missing aqueduct/sewer
     322  eNoCityTerrain = $0022; // found city: not possible in this terrain
     323  eNoBridgeBuilding = $0023;
     324  eInvalidOffer = $0030;
     325  eOfferNotAcceptable = $0031;
     326  eCancelTreatyRush = $0032;
     327  eAnarchy = $0038; // no negotiation in anarchy
     328  eColdWar = $003F;
     329  eNoModel = $0040; // sCreateDevModel must be called before!
     330  eTileNotAvailable = $0050;
     331  eNoWorkerAvailable = $0051;
     332  eOnlyOnce = $0058;
     333  // sell/rebuild city improvement: only once per city and turn!
     334  eObsolete = $0059; // city project: more advanced improvement already exists
     335  eOutOfControl = $005A;
     336  // buy/sell/rebuild improvement: not in anarchy, not in captured cities
     337
     338  eNoWay = $0100; // sGetMoveAdvice: no way found
     339
     340  // chart types
     341  nStat = 6;
     342  stPop = 0;
     343  stTerritory = 1;
     344  stMil = 2;
     345  stScience = 3;
     346  stExplore = 4;
     347  stWork = 5;
     348
     349  { tile flags: terrain type }
     350  fTerrain = $1F; // mask for terrain type
     351  fOcean = $00;
     352  fShore = $01;
     353  fGrass = $02;
     354  fDesert = $03;
     355  fPrairie = $04;
     356  fTundra = $05;
     357  fArctic = $06;
     358  fSwamp = $07;
     359  fForest = $09;
     360  fHills = $0A;
     361  fMountains = $0B;
     362  fUNKNOWN = fTerrain;
     363
     364  { tile flags: terrain improvements }
     365  fTerImp = $0000F000; // mask for terrain improvement
     366  tiNone = $00000000;
     367  tiIrrigation = $00001000;
     368  tiFarm = $00002000;
     369  tiMine = $00003000;
     370  tiFort = $00004000;
     371  tiBase = $00005000;
     372
     373  { tile flags: add ons }
     374  fSpecial = $00000060;
     375  fSpecial1 = $00000020;
     376  fSpecial2 = $00000040;
     377  fRiver = $00000080;
     378  fRoad = $00000100;
     379  fRR = $00000200;
     380  fCanal = $00000400;
     381  fPoll = $00000800;
     382  fPrefStartPos = $00200000;
     383  fStartPos = $00400000; // map editor only
     384  fDeadLands = $01000000;
     385  fModern = $06000000;
     386  fCobalt = $02000000;
     387  fUranium = $04000000;
     388  fMercury = $06000000;
     389
     390  { tile flags: redundant helper info }
     391  fGrWall = $00010000; // tile protected by great wall
     392  fSpiedOut = $00020000;
     393  fStealthUnit = $00040000;
     394  fHiddenUnit = $00080000;
     395  fObserved = $00100000; // set if tile information is from this turn
     396  fOwned = $00200000; // set if unit/city here is own one
     397  fUnit = $00400000;
     398  fCity = $00800000;
     399  fOwnZoCUnit = $10000000; // own ZoC unit present at this tile
     400  fInEnemyZoC = $20000000;
     401  // tile is adjacent to known foreign ZoC unit (not allied)
     402  fPeace = $40000000;
     403  // tile belongs to territory of nation that we are in peace with but not allied
     404
     405  // city project flags
     406  cpIndex = $1FF;
     407  cpConscripts = $200; // produce unit as conscripts
     408  cpDisbandCity = $400;
     409  // allow to disband city when settlers/conscripts are produced
     410  cpImp = $800; // 0: index refers to model, 1: index refers to city improvement
     411  cpRepeat = $1000;
     412  cpCompleted = $2000;
     413  cpAuto = $F000; // for internal use only
     414
     415  // tech status indicators
     416  tsNA = -2;
     417  tsSeen = -1;
     418  tsResearched = 0;
     419  tsGrLibrary = 1;
     420  tsCheat = 15;
     421  tsApplicable = tsResearched;
     422
     423  // nation treaties
     424  trNoContact = -1;
     425  trNone = 0;
     426  trPeace = 2;
     427  trFriendlyContact = 3;
     428  trAlliance = 4;
     429
     430  // attitudes
     431  nAttitude = 7;
     432  atHostile = 0;
     433  atIcy = 1;
     434  atUncoop = 2;
     435  atNeutral = 3;
     436  atReceptive = 4;
     437  atCordial = 5;
     438  atEnth = 6;
     439
     440  // offer prices
     441  opChoose = $00000000;
     442  opCivilReport = $11000000; // + turn + concerned player shl 16
     443  opMilReport = $12000000; // + turn + concerned player shl 16
     444  opMap = $1F000000;
     445  opTreaty = $20000000; // + suggested nation treaty
     446  opShipParts = $30000000; // + number + part type shl 16
     447  opMoney = $40000000; // + value
     448  opTribute = $48000000; // obsolete
     449  opTech = $50000000; // + advance
     450  opAllTech = $51000000;
     451  opModel = $58000000; // + model index
     452  opAllModel = $59000000;
     453
     454  opMask = $FF000000;
     455
     456  // improvement kinds
     457  ikTrGoods = 0;
     458  ikCommon = 1;
     459  ikNatLocal = 2;
     460  ikNatGlobal = 3;
     461  ikWonder = 4;
     462  ikShipPart = 5;
     463  ikNA = $7F;
     464
     465  { model domains }
     466  nDomains = 3;
     467  dGround = 0;
     468  dSea = 1;
     469  dAir = 2;
     470
     471  { model kinds }
     472  mkSelfDeveloped = $00;
     473  mkEnemyDeveloped = $01;
     474  mkSpecial_Boat = $08;
     475  mkSpecial_SubCabin = $0A;
     476  mkSpecial_TownGuard = $10;
     477  mkSpecial_Glider = $11;
     478  mkScout = $20;
     479  mkSlaves = $21;
     480  mkSettler = $22;
     481  mkCommando = $23;
     482  mkFreight = $24;
     483
     484  { unit flags }
     485  unFortified = $01;
     486  unBombsLoaded = $02;
     487  unMountainDelay = $04;
     488  unConscripts = $08;
     489  unWithdrawn = $10;
     490  unMulti = $80;
     491
     492  // unit report flags
     493  urfAlwaysSupport = $01;
     494  urfDeployed = $02;
     495
     496  // unit moves
     497  umCapturing = $0100;
     498  umSpyMission = $0200;
     499  umBombarding = $0400;
     500  umExpelling = $0800;
     501  umShipLoading = $1000;
     502  umShipUnloading = $2000;
     503  umPlaneLoading = $4000;
     504  umPlaneUnloading = $8000;
     505
     506  { model flags }
     507  mdZOC = $01;
     508  mdCivil = $02;
     509  mdDoubleSupport = $04;
     510
     511  { player happened flags }
     512  phTech = $01;
     513  phStealTech = $02;
     514  phChangeGov = $08;
     515  phGliderLost = $100;
     516  phPlaneLost = $200;
     517  phPeaceViolation = $400;
     518  phPeaceEvacuation = $800;
     519  phShipComplete = $2000;
     520  phTimeUp = $4000;
     521  phExtinct = $8000;
     522  phGameEnd = $F000;
     523
     524  { city happened flags }
     525  chDisorder = $01;
     526  chProduction = $02;
     527  chPopIncrease = $04;
     528  chPopDecrease = $08;
     529  chUnitLost = $10;
     530  chImprovementLost = $20;
     531  chProductionSabotaged = $40;
     532  chNoGrowthWarning = $80;
     533  chPollution = $100;
     534  chSiege = $200;
     535  chOldWonder = $400;
     536  chNoSettlerProd = $800;
     537  chFounded = $1000;
     538  chAfterCapture = $2000;
     539  chCaptured = $F0000;
     540  chImprovementSold = $80000000;
     541
     542  { city info flags }
     543  ciCapital = $01;
     544  ciWalled = $02;
     545  ciCoastalFort = $04;
     546  ciMissileBat = $08;
     547  ciBunker = $10;
     548  ciSpacePort = $20;
     549
     550  { city tile available values }
     551  faAvailable = 0;
     552  faNotAvailable = 1;
     553  faSiege = 2;
     554  faTreaty = 4;
     555  faInvalid = $FF;
     556
     557  // battle history flags
     558  bhEnemyAttack = $01;
     559  bhMyUnitLost = $02;
     560  bhEnemyUnitLost = $04;
     561
     562  { move advice special destinations }
     563  maNextCity = -1;
     564
     565  { goverment forms }
     566  nGov = 8;
     567  gAnarchy = 0;
     568  gDespotism = 1;
     569  gMonarchy = 2;
     570  gRepublic = 3;
     571  gFundamentalism = 4;
     572  gCommunism = 5;
     573  gDemocracy = 6;
     574  gFuture = 7;
     575
     576  // ship change reasons
     577  scrProduction = 0;
     578  scrDestruction = 1;
     579  scrTrade = 2;
     580  scrCapture = 3;
     581
     582  { unit jobs }
     583  nJob = 15;
     584  jNone = 0;
     585  jRoad = 1;
     586  jRR = 2;
     587  jClear = 3;
     588  jIrr = 4;
     589  jFarm = 5;
     590  jAfforest = 6;
     591  jMine = 7;
     592  jCanal = 8;
     593  jTrans = 9;
     594  jFort = 10;
     595  jPoll = 11;
     596  jBase = 12;
     597  jPillage = 13;
     598  jCity = 14;
     599
     600  // job preconditions are:
     601  // technology JobPreq is available, no city, plus the following:
     602  // jRoad: no river when bridge building unavailable
     603  // jRR: road
     604  // jClear: Terrain.ClearTerrain, Hanging Gardens for desert
     605  // jIrr: Terrain.IrrEff
     606  // jFarm: irrigation
     607  // jAfforest: Terrain.AfforestTerrain
     608  // jMine: Terrain.MineEff
     609  // jCanal: no Mountains, no Arctic
     610  // jTrans: Terrain.TransWork
     611  // jPoll: pollution
     612  // jPillage: any tile improvement
     613  // jCity, jFort, jBase: none
     614
     615  // spy mission
     616  nSpyMission = 5;
     617  smSabotageProd = 0;
     618  smStealMap = 1;
     619  smStealForeignReports = 2;
     620  smStealCivilReport = 3;
     621  smStealMilReport = 4;
     622
     623  // resource weights
     624  rwOff = $00000000;
     625  rwMaxGrowth = $3F514141; // 120*F + 1/8*P + 1/16*T + 1/16*S
     626  rwMaxProd = $413F1F01; // 1/16*F + 120*P + 30*T + 1*S
     627  rwMaxScience = $41040408; // 1/16*F + 4*P + 4*T + 8*S
     628  rwForceProd = $F1080201; // F^1/2 * (8*P + 2*T + 1*S)
     629  rwForceScience = $F1010101; // F^1/2 * (1*P + 1*T + 1*S)
     630
     631  { advances }
     632  adAdvancedFlight = 0;
     633  adAmphibiousWarfare = 1;
     634  adAstronomy = 2;
     635  adAtomicTheory = 3;
     636  adAutomobile = 4;
     637  adBallistics = 5;
     638  adBanking = 6;
     639  adBridgeBuilding = 7;
     640  adBronzeWorking = 8;
     641  adCeremonialBurial = 9;
     642  adChemistry = 10;
     643  adChivalry = 11;
     644  adComposites = 12;
     645  adCodeOfLaws = 13;
     646  adCombinedArms = 14;
     647  adCombustionEngine = 15;
     648  adCommunism = 16;
     649  adComputers = 17;
     650  adConscription = 18;
     651  adConstruction = 19;
     652  adTheCorporation = 20;
     653  adSpaceFlight = 21;
     654  adCurrency = 22;
     655  adDemocracy = 23;
     656  adEconomics = 24;
     657  adElectricity = 25;
     658  adElectronics = 26;
     659  adEngineering = 27;
     660  adEnvironmentalism = 28;
     661  adWheel = 29;
     662  adExplosives = 30;
     663  adFlight = 31;
     664  adIntelligence = 32;
     665  adGunpowder = 33;
     666  adHorsebackRiding = 34;
     667  adImpulseDrive = 35;
     668  adIndustrialization = 36;
     669  adSmartWeapons = 37;
     670  adInvention = 38;
     671  adIronWorking = 39;
     672  adTheLaser = 40;
     673  adNuclearPower = 41;
     674  adLiterature = 42;
     675  adInternet = 43;
     676  adMagnetism = 44;
     677  adMapMaking = 45;
     678  adMasonry = 46;
     679  adMassProduction = 47;
     680  adMathematics = 48;
     681  adMedicine = 49;
     682  adMetallurgy = 50;
     683  adMin = 51;
     684  adMobileWarfare = 52;
     685  adMonarchy = 53;
     686  adMysticism = 54;
     687  adNavigation = 55;
     688  adNuclearFission = 56;
     689  adPhilosophy = 57;
     690  adPhysics = 58;
     691  adPlastics = 59;
     692  adPoetry = 60;
     693  adPottery = 61;
     694  adRadio = 62;
     695  adRecycling = 63;
     696  adRefrigeration = 64;
     697  adMonotheism = 65;
     698  adTheRepublic = 66;
     699  adRobotics = 67;
     700  adRocketry = 68;
     701  adRailroad = 69;
     702  adSanitation = 70;
     703  adScience = 71;
     704  adWriting = 72;
     705  adSeafaring = 73;
     706  adSelfContainedEnvironment = 74;
     707  adStealth = 75;
     708  adSteamEngine = 76;
     709  adSteel = 77;
     710  adSyntheticFood = 78;
     711  adTactics = 79;
     712  adTheology = 80;
     713  adTheoryOfGravity = 81;
     714  adTrade = 82;
     715  adTransstellarColonization = 83;
     716  adUniversity = 84;
     717  adAdvancedRocketry = 85;
     718  adWarriorCode = 86;
     719  adAlphabet = 87;
     720  adPolytheism = 88;
     721  adRefining = 89;
     722  futComputingTechnology = 90;
     723  futNanoTechnology = 91;
     724  futMaterialTechnology = 92;
     725  futArtificialIntelligence = 93;
     726
     727  FutureTech = [futComputingTechnology, futNanoTechnology,
     728    futMaterialTechnology, futArtificialIntelligence];
     729
     730  adMilitary = $800; // Military Research
     731
     732  { wonders }
     733  woPyramids = 00;
     734  woZeus = 01;
     735  woGardens = 02;
     736  woColossus = 03;
     737  woLighthouse = 04;
     738  woGrLibrary = 05;
     739  woOracle = 06;
     740  woSun = 07;
     741  woLeo = 08;
     742  woMagellan = 09;
     743  woMich = 10; { 11; }
     744  woNewton = 12;
     745  woBach = 13;
     746  { 14; } woLiberty = 15;
     747  woEiffel = 16;
     748  woHoover = 17;
     749  woShinkansen = 18;
     750  woManhattan = 19;
     751  woMir = 20;
     752
     753  { city improvements }
     754  imTrGoods = 28;
     755  imBarracks = 29;
     756  imGranary = 30;
     757  imTemple = 31;
     758  imMarket = 32;
     759  imLibrary = 33;
     760  imCourt = 34;
     761  imWalls = 35;
     762  imAqueduct = 36;
     763  imBank = 37;
     764  imCathedral = 38;
     765  imUniversity = 39;
     766  imHarbor = 40;
     767  imTheater = 41;
     768  imFactory = 42;
     769  imMfgPlant = 43;
     770  imRecycling = 44;
     771  imPower = 45;
     772  imHydro = 46;
     773  imNuclear = 47;
     774  imPlatform = 48;
     775  imTownHall = 49;
     776  imSewer = 50;
     777  imSupermarket = 51;
     778  imHighways = 52;
     779  imResLab = 53;
     780  imMissileBat = 54;
     781  imCoastalFort = 55;
     782  imAirport = 56;
     783  imDockyard = 57;
     784  imPalace = 58;
     785  imGrWall = 59;
     786  imColosseum = 60;
     787  imObservatory = 61;
     788  imMilAcademy = 62;
     789  imBunker = 63;
     790  imAlgae = 64;
     791  imStockEx = 65;
     792  imSpacePort = 66;
     793  imShipComp = 67;
     794  imShipPow = 68;
     795  imShipHab = 69;
     796
     797  SettlerFood: array [0 .. nGov - 1] of integer = (1, 1, 1, 2, 1, 2, 2, 2);
     798  CorrLevel: array [0 .. nGov - 1] of integer = (3, 3, 1, 2, 1, 0, 0, 0);
     799  SupportFree: array [0 .. nGov - 1] of integer = (2, 2, 1, 0, 2, 1, 0, 0);
     800  // in 1/2*city size
     801
     802  // special prerequisite values
     803  preNone = -1;
     804  preLighthouse = -2;
     805  preSun = -3;
     806  preLeo = -4;
     807  preBuilder = -5;
     808  preNA = -$FF;
     809
     810  JobPreq: array [0 .. nJob - 1] of integer = (preNone, preNone, adRailroad,
     811    preNone, preNone, adRefrigeration, preNone, preNone, adExplosives,
     812    adExplosives, adConstruction, preNone, adMedicine, preNone, preNone);
     813
     814  AdvPreq: array [0 .. nAdv - 1, 0 .. 2] of integer = { advance prerequisites }
     815    ((adFlight, adRobotics, preNone), // adAdvancedFlight
     816    (adNavigation, adTactics, preNone), // adAmphibiousWarfare
     817    (adMysticism, adAlphabet, preNone), // adAstronomy
     818    (adTheoryOfGravity, preNone, preNone), // adAtomicTheory
     819    (adCombustionEngine, adSteel, preNone), // adAutomobile
     820    (adMathematics, adMetallurgy, preNone), // adBallistics
     821    (adCurrency, adEngineering, preNone), // adBanking
     822    (adConstruction, adWheel, preNone), // adBridgeBuilding
     823    (preNone, preNone, preNone), // adBronzeWorking
     824    (preNone, preNone, preNone), // adCeremonialBurial
     825    (adScience, preNone, preNone), // adChemistry
     826    (adMonarchy, adWarriorCode, preNone), // adChivalry
     827    (adMetallurgy, adPlastics, preNone), // adComposites
     828    (adWriting, preNone, preNone), // adCodeOfLaws
     829    (adAdvancedFlight, adMobileWarfare, preNone), // adCombinedArms
     830    (adRefining, adExplosives, preNone), // adCombustionEngine
     831    (adPhilosophy, adIndustrialization, preNone), // adCommunism
     832    (adMin, preNone, preNone), // adComputers
     833    (adTheRepublic, adTactics, preNone), // adConscription
     834    (adMasonry, adAlphabet, preNone), // adConstruction
     835    (adEconomics, adDemocracy, preNone), // adTheCorporation
     836    (adAdvancedFlight, adAdvancedRocketry, preNone), // adSpaceFlight
     837    (adBronzeWorking, preNone, preNone), // adCurrency
     838    (adConscription, adIndustrialization, preNone), // adDemocracy
     839    (adBanking, adUniversity, preNone), // adEconomics
     840    (adMagnetism, preNone, preNone), // adElectricity
     841    (adRadio, adAtomicTheory, preNone), // adElectronics
     842    (adConstruction, adBronzeWorking, preNone), // adEngineering
     843    (adIndustrialization, preNone, preNone), // adEnvironmentalism
     844    (preNone, preNone, preNone), // adWheel
     845    (adChemistry, adEngineering, preNone), // adExplosives
     846    (adCombustionEngine, adPhysics, preNone), // adFlight
     847    (adTactics, adInvention, preNone), // adIntelligence
     848    (adMedicine, adIronWorking, preNone), // adGunpowder
     849    (preNone, preNone, preNone), // adHorsebackRiding
     850    (adSpaceFlight, adNuclearPower, preNone), // adImpulseDrive
     851    (adRailroad, adBanking, preNone), // adIndustrialization
     852    (adAdvancedRocketry, adTheLaser, preNone), // adIntelligenArms
     853    (adWriting, adWheel, preNone), // adInvention
     854    (adBronzeWorking, adInvention, preNone), // adIronWorking
     855    (adMin, adPhysics, preNone), // adTheLaser
     856    (adNuclearFission, preNone, preNone), // adNuclearPower
     857    (adPoetry, adTrade, preNone), // adLiterature
     858    (adDemocracy, adComputers, preNone), // adLybertarianism
     859    (adPhysics, adIronWorking, preNone), // adMagnetism
     860    (adAlphabet, preNone, preNone), // adMapMaking
     861    (preNone, preNone, preNone), // adMasonry
     862    (adAutomobile, adElectronics, adTheCorporation), // adMassProduction
     863    (adCurrency, adAlphabet, preNone), // adMathematics
     864    (adMysticism, adPottery, preNone), // adMedicine
     865    (adGunpowder, preNone, preNone), // adMetallurgy
     866    (adRobotics, adPlastics, preNone), // adMin
     867    (adAutomobile, adTactics, preNone), // adMobileWarfare
     868    (adPolytheism, preNone, preNone), // adMonarchy
     869    (adCeremonialBurial, preNone, preNone), // adMysticism
     870    (adSeafaring, adAstronomy, preNone), // adNavigation
     871    (adAtomicTheory, adMassProduction, preNone), // adNuclearFission
     872    (adMathematics, adLiterature, preNone), // adPhilosophy
     873    (adScience, preNone, preNone), // adPhysics
     874    (adMassProduction, adRefining, preNone), // adPlastics
     875    (adMysticism, adWarriorCode, preNone), // adPoetry
     876    (preNone, preNone, preNone), // adPottery
     877    (adElectricity, adEngineering, preNone), // adRadio
     878    (adEnvironmentalism, adPlastics, preNone), // adRecycling
     879    (adElectricity, preNone, preNone), // adRefrigeration
     880    (adPolytheism, adAstronomy, preNone), // adMonotheism
     881    (adLiterature, preNone, preNone), // adTheRepublic
     882    (adMassProduction, adEconomics, preNone), // adRobotics
     883    (adBallistics, adExplosives, preNone), // adRocketry
     884    (adSteamEngine, adBridgeBuilding, preNone), // adRailroad
     885    (adEnvironmentalism, adMedicine, preNone), // adSanitation
     886    (adMetallurgy, adTheology, adPhilosophy), // adScience
     887    (adAlphabet, preNone, preNone), // adWriting
     888    (adPottery, adMapMaking, preNone), // adSeafaring
     889    (adRecycling, adSyntheticFood, preNone), // adSelfContainedEnvironment
     890    (adComposites, adRadio, preNone), // adStealth
     891    (adScience, adEngineering, preNone), // adSteamEngine
     892    (adIronWorking, adRailroad, preNone), // adSteel
     893    (adChemistry, adRefrigeration, preNone), // adSyntheticFood
     894    (adWarriorCode, adUniversity, preNone), // adTactics
     895    (adMonotheism, adPoetry, preNone), // adTheology
     896    (adAstronomy, adPhysics, preNone), // adTheoryOfGravity
     897    (adCurrency, adCodeOfLaws, preNone), // adTrade
     898    (adImpulseDrive, adSelfContainedEnvironment, preNone),
     899    // adTransstellarColonization
     900    (adScience, preNone, preNone), // adUniversity
     901    (adComputers, adRocketry, preNone), // adAdvancedRocketry
     902    (preNone, preNone, preNone), // adWarriorCode
     903    (preNone, preNone, preNone), // adAlphabet
     904    (adCeremonialBurial, adHorsebackRiding, preNone), // adPolytheism
     905    (adChemistry, preNone, preNone), // adRefining
     906    (adComputers, preNone, preNone), // futResearchTechnology
     907    (adRobotics, preNone, preNone), // futProductionTechnology
     908    (adComposites, preNone, preNone), // futArmorTechnology
     909    (adSmartWeapons, preNone, preNone)); // futMissileTechnology
     910
     911Imp:
     912array [0 .. nImp - 1] of // city improvements
     913  record Kind, Preq, Cost, Maint, Expiration: integer;
     914end
     915= ((Kind: ikWonder; Preq: adMathematics; Cost: 400; Maint: 0;
     916  Expiration: adDemocracy), // woPyramids
     917  (Kind: ikWonder; Preq: adPolytheism; Cost: 200; Maint: 0;
     918  Expiration: adElectronics), // woZeus
     919  (Kind: ikWonder; Preq: adInvention; Cost: 200; Maint: 0;
     920  Expiration: adNuclearFission), // woGardens
     921  (Kind: ikWonder; Preq: adBronzeWorking; Cost: 200; Maint: 0; Expiration: - 1),
     922  // woColossus
     923  (Kind: ikWonder; Preq: adMapMaking; Cost: 200; Maint: 0; Expiration: adSteel),
     924  // woLighthouse
     925  (Kind: ikWonder; Preq: adLiterature; Cost: 400; Maint: 0;
     926  Expiration: adPlastics), // woGrLibrary
     927  (Kind: ikWonder; Preq: adMysticism; Cost: 200; Maint: 0; Expiration: - 1),
     928  // woOracle
     929  (Kind: ikWonder; Preq: adChivalry; Cost: 300; Maint: 0;
     930  Expiration: adSpaceFlight), // woSun
     931  (Kind: ikWonder; Preq: adPhilosophy; Cost: 500; Maint: 0; Expiration: - 1),
     932  // woLeo
     933  (Kind: ikWonder; Preq: adNavigation; Cost: 300; Maint: 0; Expiration: - 1),
     934  // woMagellan
     935  (Kind: ikWonder; Preq: adMonotheism; Cost: 400; Maint: 0; Expiration: - 1),
     936  // woMich
     937  (Kind: ikNA; Preq: preNA), // {11}
     938  (Kind: ikWonder; Preq: adTheoryOfGravity; Cost: 400; Maint: 0;
     939  Expiration: - 1), // woNewton
     940  (Kind: ikWonder; Preq: adTheology; Cost: 400; Maint: 0; Expiration: - 1),
     941  // woBach
     942  (Kind: ikNA; Preq: preNA), // {14}
     943  (Kind: ikWonder; Preq: adDemocracy; Cost: 500; Maint: 0; Expiration: - 1),
     944  // woLiberty
     945  (Kind: ikWonder; Preq: adSteel; Cost: 800; Maint: 0; Expiration: - 1),
     946  // woEiffel
     947  (Kind: ikWonder; Preq: adElectronics; Cost: 800; Maint: 0; Expiration: - 1),
     948  // woHoover
     949  (Kind: ikWonder; Preq: adPlastics; Cost: 500; Maint: 0; Expiration: - 1),
     950  // woShinkansen
     951  (Kind: ikWonder; Preq: adNuclearFission; Cost: 400; Maint: 0;
     952  Expiration: - 1), // woManhattan
     953  (Kind: ikWonder; Preq: adSpaceFlight; Cost: 800; Maint: 0; Expiration: - 1),
     954  // woMir
     955  (Kind: ikNA; Preq: preNA), // {21}
     956  (Kind: ikNA; Preq: preNA), // {22}
     957  (Kind: ikNA; Preq: preNA), // {23}
     958  (Kind: ikNA; Preq: preNA), // {24}
     959  (Kind: ikNA; Preq: preNA), // {25}
     960  (Kind: ikNA; Preq: preNA), // {26}
     961  (Kind: ikNA; Preq: preNA), // {27}
     962  (Kind: ikTrGoods; Preq: preNone; Cost: 0; Maint: 0), // imTrGoods
     963  (Kind: ikCommon; Preq: adWarriorCode; Cost: 40; Maint: 1), // imBarracks
     964  (Kind: ikCommon; Preq: adPottery; Cost: 60; Maint: 1), // imGranary
     965  (Kind: ikCommon; Preq: adCeremonialBurial; Cost: 40; Maint: 1), // imTemple
     966  (Kind: ikCommon; Preq: adCurrency; Cost: 60; Maint: 1), // imMarket
     967  (Kind: ikCommon; Preq: adWriting; Cost: 80; Maint: 3), // imLibrary
     968  (Kind: ikCommon; Preq: adCodeOfLaws; Cost: 80; Maint: 2), // imCourt
     969  (Kind: ikCommon; Preq: adMasonry; Cost: 80; Maint: 1), // imWalls
     970  (Kind: ikCommon; Preq: adConstruction; Cost: 80; Maint: 1), // imAqueduct
     971  (Kind: ikCommon; Preq: adBanking; Cost: 120; Maint: 2), // imBank
     972  (Kind: ikCommon; Preq: adMonotheism; Cost: 100; Maint: 1), // imCathedral
     973  (Kind: ikCommon; Preq: adUniversity; Cost: 160; Maint: 5), // imUniversity
     974  (Kind: ikCommon; Preq: adSeafaring; Cost: 60; Maint: 1), // imHarbor
     975  (Kind: ikCommon; Preq: adPoetry; Cost: 60; Maint: 2), // imTheater
     976  (Kind: ikCommon; Preq: adIndustrialization; Cost: 200; Maint: 3), // imFactory
     977  (Kind: ikCommon; Preq: adRobotics; Cost: 320; Maint: 5), // imMfgPlant
     978  (Kind: ikCommon; Preq: adRecycling; Cost: 320; Maint: 4), // imRecycling
     979  (Kind: ikCommon; Preq: adElectricity; Cost: 120; Maint: 2), // imPower
     980  (Kind: ikCommon; Preq: adEnvironmentalism; Cost: 120; Maint: 1), // imHydro
     981  (Kind: ikCommon; Preq: adNuclearPower; Cost: 240; Maint: 2), // imNuclear
     982  (Kind: ikCommon; Preq: adRefining; Cost: 160; Maint: 2), // imPlatform
     983  (Kind: ikCommon; Preq: preNone; Cost: 40; Maint: 1), // imTownHall
     984  (Kind: ikCommon; Preq: adSanitation; Cost: 120; Maint: 2), // imSewer
     985  (Kind: ikCommon; Preq: adRefrigeration; Cost: 80; Maint: 2), // imSupermarket
     986  (Kind: ikCommon; Preq: adAutomobile; Cost: 160; Maint: 4), // imHighways
     987  (Kind: ikCommon; Preq: adComputers; Cost: 240; Maint: 7), // imResLab
     988  (Kind: ikCommon; Preq: adAdvancedRocketry; Cost: 100; Maint: 1),
     989  // imMissileBat
     990  (Kind: ikCommon; Preq: adMetallurgy; Cost: 80; Maint: 1), // imCoastalFort
     991  (Kind: ikCommon; Preq: adAdvancedFlight; Cost: 160; Maint: 1), // imAirport
     992  (Kind: ikCommon; Preq: adAmphibiousWarfare; Cost: 80; Maint: 1), // imDockyard
     993  (Kind: ikNatLocal; Preq: preNone; Cost: 100; Maint: 0), // imPalace
     994  (Kind: ikNatLocal; Preq: adEngineering; Cost: 400; Maint: 4), // imGrWall
     995  (Kind: ikNatLocal; Preq: adConstruction; Cost: 200; Maint: 4), // imColosseum
     996  (Kind: ikNatLocal; Preq: adAstronomy; Cost: 300; Maint: 4), // imObservatory
     997  (Kind: ikNatLocal; Preq: adTactics; Cost: 100; Maint: 4), // imMilAcademy
     998  (Kind: ikNatLocal; Preq: adSteel; Cost: 200; Maint: 2), // imBunker
     999  (Kind: ikNatLocal; Preq: adSyntheticFood; Cost: 120; Maint: 2), // imAlgae
     1000  (Kind: ikNatGlobal; Preq: adTheCorporation; Cost: 320; Maint: 4), // imStockEx
     1001  (Kind: ikNatLocal; Preq: adSpaceFlight; Cost: 400; Maint: 0), // imSpacePort
     1002  (Kind: ikShipPart; Preq: adTransstellarColonization; Cost: 240; Maint: 0),
     1003  // imShipComp
     1004  (Kind: ikShipPart; Preq: adImpulseDrive; Cost: 600; Maint: 0), // imShipPow
     1005  (Kind: ikShipPart; Preq: adSelfContainedEnvironment; Cost: 800; Maint: 0));
     1006// imShipHab
     1007
     1008nImpReplacement = 5;
     1009ImpReplacement:
     1010array [0 .. nImpReplacement - 1] of record NewImp, OldImp: integer;
     1011end
     1012= ((NewImp: imSewer; OldImp: imAqueduct), (NewImp: imCourt; OldImp: imTownHall),
     1013  (NewImp: imPalace; OldImp: imTownHall), (NewImp: imPalace; OldImp: imCourt),
     1014  (NewImp: imMilAcademy; OldImp: imBarracks));
    6621015
    6631016// colony ship
    664 nShipPart=3;
    665 spComp=0; spPow=1; spHab=2;
    666 ShipNeed: array[0..nShipPart-1] of integer=(6,4,2);
    667 ShipImpIndex: array[0..nShipPart-1] of integer=(imShipComp,imShipPow,imShipHab);
    668 
    669 GovPreq:array[1..nGov-1] of integer= {government prerequisites}
    670 (preNone,adMonarchy,adTheRepublic,adTheology,adCommunism,adDemocracy,adInternet);
    671 
    672 AgePreq:array[1..3] of integer= (adScience,adMassProduction,adTransstellarColonization);
    673 
    674 Terrain:array[0..11] of record
    675   MoveCost,Defense,ClearTerrain,IrrEff,IrrClearWork,AfforestTerrain,MineEff,
    676     MineAfforestWork,TransTerrain,TransWork:integer;
    677   FoodRes,ProdRes,TradeRes:array[0..2] of integer;
    678   Filler: array[0..12] of integer;
    679   end=
    680 ((MoveCost:1;Defense:4;ClearTerrain:-1;IrrEff:0;IrrClearWork:0;
    681   AfforestTerrain:-1;MineEff:0;MineAfforestWork:0;TransTerrain:-1;
    682   TransWork:0;FoodRes:(0,0,0);ProdRes:(0,0,0);TradeRes:(0,0,0)), {Ocn}
    683 (MoveCost:1;Defense:4;ClearTerrain:-1;IrrEff:0;IrrClearWork:0;
    684   AfforestTerrain:-1;MineEff:0;MineAfforestWork:0;TransTerrain:-1;
    685   TransWork:0;FoodRes:(1,5,1);ProdRes:(0,0,5);TradeRes:(3,3,3)), {Sho}
    686 (MoveCost:1;Defense:4;ClearTerrain:-1;IrrEff:1;IrrClearWork:600;
    687   AfforestTerrain:fForest;MineEff:0;MineAfforestWork:1800;TransTerrain:fHills;
    688   TransWork:3000;FoodRes:(3,2,2);ProdRes:(0,1,0);TradeRes:(1,1,1)), {Gra}
    689 (MoveCost:1;Defense:4;ClearTerrain:fGrass;IrrEff:0;IrrClearWork:1800;
    690   AfforestTerrain:-1;MineEff:1;MineAfforestWork:600;TransTerrain:fPrairie;
    691   TransWork:3000;FoodRes:(0,3,0);ProdRes:(1,1,4);TradeRes:(1,1,1)), {Dst}
    692 (MoveCost:1;Defense:4;ClearTerrain:-1;IrrEff:1;IrrClearWork:600;
    693   AfforestTerrain:fForest;MineEff:0;MineAfforestWork:2400;TransTerrain:-1;
    694   TransWork:0;FoodRes:(1,3,1);ProdRes:(1,1,3);TradeRes:(1,1,1)), {Pra}
    695 (MoveCost:1;Defense:4;ClearTerrain:-1;IrrEff:1;IrrClearWork:600;
    696   AfforestTerrain:-1;MineEff:0;MineAfforestWork:0;TransTerrain:fGrass;
    697   TransWork:3000;FoodRes:(1,1,1);ProdRes:(0,0,4);TradeRes:(1,6,1)), {Tun}
    698 (MoveCost:2;Defense:4;ClearTerrain:-1;IrrEff:0;IrrClearWork:0;
    699   AfforestTerrain:-1;MineEff:3;MineAfforestWork:1800;TransTerrain:-1;
    700   TransWork:0;FoodRes:(0,3,0);ProdRes:(1,1,0);TradeRes:(0,4,0)), {Arc}
    701 (MoveCost:2;Defense:6;ClearTerrain:fGrass;IrrEff:0;IrrClearWork:2400;
    702   AfforestTerrain:fForest;MineEff:0;MineAfforestWork:2400;TransTerrain:fHills;
    703   TransWork:3000;FoodRes:(1,1,1);ProdRes:(0,4,1);TradeRes:(1,1,5)), {Swa}
    704 (), {-}
    705 (MoveCost:2;Defense:6;ClearTerrain:fPrairie;IrrEff:0;IrrClearWork:600;
    706   AfforestTerrain:-1;MineEff:0;MineAfforestWork:0;TransTerrain:-1;
    707   TransWork:0;FoodRes:(1,3,1);ProdRes:(2,2,2);TradeRes:(1,1,4)), {For}
    708 (MoveCost:2;Defense:8;ClearTerrain:-1;IrrEff:1;IrrClearWork:600;
    709   AfforestTerrain:-1;MineEff:3;MineAfforestWork:1200;TransTerrain:fGrass;
    710   TransWork:6000;FoodRes:(1,1,1);ProdRes:(0,0,2);TradeRes:(0,4,0)), {Hil}
    711 (MoveCost:3;Defense:12;ClearTerrain:-1;IrrEff:0;IrrClearWork:0;
    712   AfforestTerrain:-1;MineEff:2;MineAfforestWork:1200;TransTerrain:-1;
    713   TransWork:0;FoodRes:(0,0,0);ProdRes:(1,4,1);TradeRes:(0,0,7))); {Mou}
    714 
    715 //settler work required MP
    716 PillageWork=100;
    717 CityWork=900;
    718 FarmWork=3; // *IrrClearWork
    719 RoadWork=300; // *MoveCost
    720 RoadBridgeWork=900;
    721 RRWork=600; // *MoveCost
    722 RRBridgeWork=900;
    723 CanalWork=1800;
    724 FortWork=600; // *MoveCost
    725 BaseWork=600; // *MoveCost
    726 PollWork=1800;
     1017nShipPart = 3;
     1018spComp = 0;
     1019spPow = 1;
     1020spHab = 2;
     1021ShipNeed:
     1022array [0 .. nShipPart - 1] of integer = (6, 4, 2);
     1023ShipImpIndex:
     1024array [0 .. nShipPart - 1] of integer = (imShipComp, imShipPow, imShipHab);
     1025
     1026GovPreq:
     1027array [1 .. nGov - 1] of integer = { government prerequisites }
     1028  (preNone, adMonarchy, adTheRepublic, adTheology, adCommunism, adDemocracy,
     1029  adInternet);
     1030
     1031AgePreq:
     1032array [1 .. 3] of integer = (adScience, adMassProduction,
     1033  adTransstellarColonization);
     1034
     1035Terrain:
     1036array [0 .. 11] of record MoveCost, Defense, ClearTerrain, IrrEff, IrrClearWork,
     1037  AfforestTerrain, MineEff, MineAfforestWork, TransTerrain, TransWork: integer;
     1038FoodRes, ProdRes, TradeRes: array [0 .. 2] of integer;
     1039Filler:
     1040array [0 .. 12] of integer;
     1041end
     1042= ((MoveCost: 1; Defense: 4; ClearTerrain: - 1; IrrEff: 0; IrrClearWork: 0;
     1043  AfforestTerrain: - 1; MineEff: 0; MineAfforestWork: 0; TransTerrain: - 1;
     1044  TransWork: 0; FoodRes: (0, 0, 0); ProdRes: (0, 0, 0);
     1045  TradeRes: (0, 0, 0)), { Ocn }
     1046  (MoveCost: 1; Defense: 4; ClearTerrain: - 1; IrrEff: 0; IrrClearWork: 0;
     1047  AfforestTerrain: - 1; MineEff: 0; MineAfforestWork: 0; TransTerrain: - 1;
     1048  TransWork: 0; FoodRes: (1, 5, 1); ProdRes: (0, 0, 5);
     1049  TradeRes: (3, 3, 3)), { Sho }
     1050  (MoveCost: 1; Defense: 4; ClearTerrain: - 1; IrrEff: 1; IrrClearWork: 600;
     1051  AfforestTerrain: fForest; MineEff: 0; MineAfforestWork: 1800;
     1052  TransTerrain: fHills; TransWork: 3000; FoodRes: (3, 2, 2); ProdRes: (0, 1, 0);
     1053  TradeRes: (1, 1, 1)), { Gra }
     1054  (MoveCost: 1; Defense: 4; ClearTerrain: fGrass; IrrEff: 0; IrrClearWork: 1800;
     1055  AfforestTerrain: - 1; MineEff: 1; MineAfforestWork: 600;
     1056  TransTerrain: fPrairie; TransWork: 3000; FoodRes: (0, 3, 0);
     1057  ProdRes: (1, 1, 4); TradeRes: (1, 1, 1)), { Dst }
     1058  (MoveCost: 1; Defense: 4; ClearTerrain: - 1; IrrEff: 1; IrrClearWork: 600;
     1059  AfforestTerrain: fForest; MineEff: 0; MineAfforestWork: 2400;
     1060  TransTerrain: - 1; TransWork: 0; FoodRes: (1, 3, 1); ProdRes: (1, 1, 3);
     1061  TradeRes: (1, 1, 1)), { Pra }
     1062  (MoveCost: 1; Defense: 4; ClearTerrain: - 1; IrrEff: 1; IrrClearWork: 600;
     1063  AfforestTerrain: - 1; MineEff: 0; MineAfforestWork: 0; TransTerrain: fGrass;
     1064  TransWork: 3000; FoodRes: (1, 1, 1); ProdRes: (0, 0, 4);
     1065  TradeRes: (1, 6, 1)), { Tun }
     1066  (MoveCost: 2; Defense: 4; ClearTerrain: - 1; IrrEff: 0; IrrClearWork: 0;
     1067  AfforestTerrain: - 1; MineEff: 3; MineAfforestWork: 1800; TransTerrain: - 1;
     1068  TransWork: 0; FoodRes: (0, 3, 0); ProdRes: (1, 1, 0);
     1069  TradeRes: (0, 4, 0)), { Arc }
     1070  (MoveCost: 2; Defense: 6; ClearTerrain: fGrass; IrrEff: 0; IrrClearWork: 2400;
     1071  AfforestTerrain: fForest; MineEff: 0; MineAfforestWork: 2400;
     1072  TransTerrain: fHills; TransWork: 3000; FoodRes: (1, 1, 1); ProdRes: (0, 4, 1);
     1073  TradeRes: (1, 1, 5)), { Swa }
     1074  (), { - }
     1075  (MoveCost: 2; Defense: 6; ClearTerrain: fPrairie; IrrEff: 0;
     1076  IrrClearWork: 600; AfforestTerrain: - 1; MineEff: 0; MineAfforestWork: 0;
     1077  TransTerrain: - 1; TransWork: 0; FoodRes: (1, 3, 1); ProdRes: (2, 2, 2);
     1078  TradeRes: (1, 1, 4)), { For }
     1079  (MoveCost: 2; Defense: 8; ClearTerrain: - 1; IrrEff: 1; IrrClearWork: 600;
     1080  AfforestTerrain: - 1; MineEff: 3; MineAfforestWork: 1200;
     1081  TransTerrain: fGrass; TransWork: 6000; FoodRes: (1, 1, 1); ProdRes: (0, 0, 2);
     1082  TradeRes: (0, 4, 0)), { Hil }
     1083  (MoveCost: 3; Defense: 12; ClearTerrain: - 1; IrrEff: 0; IrrClearWork: 0;
     1084  AfforestTerrain: - 1; MineEff: 2; MineAfforestWork: 1200; TransTerrain: - 1;
     1085  TransWork: 0; FoodRes: (0, 0, 0); ProdRes: (1, 4, 1);
     1086  TradeRes: (0, 0, 7))); { Mou }
     1087
     1088// settler work required MP
     1089PillageWork = 100;
     1090CityWork = 900;
     1091FarmWork = 3; // *IrrClearWork
     1092RoadWork = 300; // *MoveCost
     1093RoadBridgeWork = 900;
     1094RRWork = 600; // *MoveCost
     1095RRBridgeWork = 900;
     1096CanalWork = 1800;
     1097FortWork = 600; // *MoveCost
     1098BaseWork = 600; // *MoveCost
     1099PollWork = 1800;
    7271100
    7281101// upgrades for new unit models
    7291102// upgrade[domain,0].preq is domain precondition advance
    7301103// cost values accumulate if prerequisite is future tech / are maximized if not
    731 nUpgrade=15;
    732 upgrade: array [0..nDomains-1,0..nUpgrade-1] of
    733   record Preq,Strength,Trans,Cost: integer end=
    734 (((Preq:adWarriorCode;Strength:4;Trans:0;Cost:3),
    735 (Preq:adBronzeWorking;Strength:2;Trans:0;Cost:4),
    736 (Preq:adIronWorking;Strength:2;Trans:0;Cost:5),
    737 (Preq:adChivalry;Strength:2;Trans:0;Cost:5),
    738 (Preq:adMonotheism;Strength:3;Trans:0;Cost:7),
    739 (Preq:adGunpowder;Strength:3;Trans:0;Cost:8),
    740 (Preq:adExplosives;Strength:4;Trans:0;Cost:9),
    741 (Preq:adTactics;Strength:5;Trans:0;Cost:10),
    742 (Preq:adRadio;Strength:6;Trans:0;Cost:11),
    743 (Preq:adDemocracy;Strength:6;Trans:0;Cost:5),
    744 (Preq:adMobileWarfare;Strength:7;Trans:0;Cost:12),
    745 (Preq:adRobotics;Strength:8;Trans:0;Cost:15),
    746 (Preq:adComposites;Strength:8;Trans:0;Cost:15),
    747 (Preq:adTheLaser;Strength:8;Trans:0;Cost:14),
    748 (Preq:futMaterialTechnology;Strength:10;Trans:0;Cost:2)),
    749 ((Preq:adMapMaking;Strength:4;Trans:1;Cost:8),
    750 (Preq:adNavigation;Strength:4;Trans:0;Cost:10),
    751 (Preq:adEngineering;Strength:0;Trans:1;Cost:8),
    752 (Preq:adGunpowder;Strength:8;Trans:0;Cost:12),
    753 (Preq:adMagnetism;Strength:12;Trans:1;Cost:20),
    754 (Preq:adExplosives;Strength:16;Trans:0;Cost:24),
    755 (Preq:adSteamEngine;Strength:24;Trans:0;Cost:28),
    756 (Preq:adAmphibiousWarfare;Strength:24;Trans:1;Cost:18),
    757 (Preq:adAdvancedRocketry;Strength:32;Trans:0;Cost:38),
    758 (Preq:futMaterialTechnology;Strength:14;Trans:0;Cost:4),
    759 (Preq:futArtificialIntelligence;Strength:14;Trans:0;Cost:4),
    760 (Preq:preNA),(Preq:preNA),(Preq:preNA),(Preq:preNA)),
    761 ((Preq:adFlight;Strength:12;Trans:1;Cost:14),
    762 (Preq:adTactics;Strength:6;Trans:0;Cost:17),
    763 (Preq:adElectronics;Strength:6;Trans:0;Cost:20),
    764 (Preq:adMin;Strength:8;Trans:0;Cost:24),
    765 (Preq:adComposites;Strength:8;Trans:0;Cost:26),
    766 (Preq:adSmartWeapons;Strength:11;Trans:0;Cost:32),
    767 (Preq:futArtificialIntelligence;Strength:7;Trans:0;Cost:4),
    768 (Preq:preNA),(Preq:preNA),(Preq:preNA),(Preq:preNA),(Preq:preNA),(Preq:preNA),
    769 (Preq:preNA),(Preq:preNA)));
    770 
    771 {model features}
    772 nFeature=27;
    773 mcWeapons=0;mcArmor=1;mcMob=2;mcSeaTrans=3;mcCarrier=4;mcTurbines=5;
    774 mcBombs=6;mcFuel=7;mcAirTrans=8;mcNav=9;mcRadar=10;mcSub=11;mcArtillery=12;
    775 mcAlpine=13;mcSupplyShip=14;mcOver=15;mcAirDef=16;mcSpy=17;mcSE=18;mcNP=19;
    776 mcJet=20;mcStealth=21;mcFanatic=22;mcFirst=23;mcWill=24;mcAcademy=25;mcLine=26;
    777 mcFirstNonCap=mcNav;
    778 AutoFeature: Set of mcFirstNonCap..nFeature-1 =
    779 [mcNav,mcSE,mcNP,mcJet,mcAcademy]; // unit class advances, automatically applied if available
    780 
    781 Feature:array [0..nFeature-1] of {unit model features}
    782   record Domains,Preq,Weight,Cost: integer; end=
    783 ((Domains:7;Preq:preNone;Weight:1;Cost:1), {mcOffense}
    784 (Domains:7;Preq:preNone;Weight:1;Cost:1), {mcDefense}
    785 (Domains:1;Preq:adHorsebackRiding;Weight:1;Cost:1), {mcMob}
    786 (Domains:2;Preq:preNone;Weight:2;Cost:1), {mcSeaTrans}
    787 (Domains:2;Preq:adAdvancedFlight;Weight:2;Cost:2), {mcCarrier}
    788 (Domains:2;Preq:adPhysics;Weight:3;Cost:1), {mcTurbines}
    789 (Domains:4;Preq:adAdvancedFlight;Weight:1;Cost:1), {mcBombs}
    790 (Domains:4;Preq:preNone;Weight:1;Cost:1), {mcFuel}
    791 (Domains:4;Preq:adCombinedArms;Weight:2;Cost:1), {mcAirTrans}
    792 (Domains:2;Preq:adNavigation;Weight:0;Cost:0), {mcNav}
    793 (Domains:2;Preq:adRadio;Weight:0;Cost:1), {mcRadar}
    794 (Domains:2;Preq:adCombustionEngine;Weight:2;Cost:1), {mcSub}
    795 (Domains:3;Preq:adBallistics;Weight:1;Cost:1), {mcArtillery}
    796 (Domains:1;Preq:adTactics;Weight:2;Cost:1), {mcAlpine}
    797 (Domains:2;Preq:adMedicine;Weight:1;Cost:1), {mcSupplyShip}
    798 (Domains:1;Preq:adBridgeBuilding;Weight:0;Cost:2), {mcOver}
    799 (Domains:2;Preq:adAdvancedRocketry;Weight:1;Cost:1), {mcAirDef}
    800 (Domains:4;Preq:adIntelligence;Weight:2;Cost:1), {mcSpy}
    801 (Domains:2;Preq:adSteamEngine;Weight:0;Cost:0), {mcSE}
    802 (Domains:2;Preq:adNuclearPower;Weight:0;Cost:0), {mcNP}
    803 (Domains:4;Preq:adRocketry;Weight:0;Cost:0), {mcJet}
    804 (Domains:4;Preq:adStealth;Weight:1;Cost:2), {mcStealth}
    805 (Domains:5;Preq:adCommunism;Weight:0;Cost:1), {mcFanatic}
    806 (Domains:1;Preq:preSun;Weight:0;Cost:1), {mcFirst}
    807 (Domains:1;Preq:preSun;Weight:0;Cost:1), {mcWill}
    808 (Domains:1;Preq:preSun;Weight:0;Cost:0), {mcAcademy}
    809 (Domains:7;Preq:adMassProduction;Weight:0;Cost:0)); {mcLine}
    810 
    811 WeightPreq7: array[0..nDomains-1] of integer=
    812 (adHorsebackRiding,adSeafaring,adAdvancedFlight);
    813 WeightPreq10: array[0..nDomains-1] of integer=
    814 (adAutomobile,adSteel,preNA);
    815 
    816 INFIN=999999;
    817 
     1104nUpgrade = 15;
     1105upgrade:
     1106array [0 .. nDomains - 1, 0 .. nUpgrade - 1] of record Preq, Strength, Trans,
     1107  Cost: integer
     1108end
     1109= (((Preq: adWarriorCode; Strength: 4; Trans: 0;
     1110  Cost: 3), (Preq: adBronzeWorking; Strength: 2; Trans: 0;
     1111  Cost: 4), (Preq: adIronWorking; Strength: 2; Trans: 0;
     1112  Cost: 5), (Preq: adChivalry; Strength: 2; Trans: 0;
     1113  Cost: 5), (Preq: adMonotheism; Strength: 3; Trans: 0;
     1114  Cost: 7), (Preq: adGunpowder; Strength: 3; Trans: 0;
     1115  Cost: 8), (Preq: adExplosives; Strength: 4; Trans: 0;
     1116  Cost: 9), (Preq: adTactics; Strength: 5; Trans: 0; Cost: 10), (Preq: adRadio;
     1117  Strength: 6; Trans: 0; Cost: 11), (Preq: adDemocracy; Strength: 6; Trans: 0;
     1118  Cost: 5), (Preq: adMobileWarfare; Strength: 7; Trans: 0;
     1119  Cost: 12), (Preq: adRobotics; Strength: 8; Trans: 0;
     1120  Cost: 15), (Preq: adComposites; Strength: 8; Trans: 0;
     1121  Cost: 15), (Preq: adTheLaser; Strength: 8; Trans: 0;
     1122  Cost: 14), (Preq: futMaterialTechnology; Strength: 10; Trans: 0; Cost: 2)),
     1123  ((Preq: adMapMaking; Strength: 4; Trans: 1; Cost: 8), (Preq: adNavigation;
     1124  Strength: 4; Trans: 0; Cost: 10), (Preq: adEngineering; Strength: 0; Trans: 1;
     1125  Cost: 8), (Preq: adGunpowder; Strength: 8; Trans: 0;
     1126  Cost: 12), (Preq: adMagnetism; Strength: 12; Trans: 1;
     1127  Cost: 20), (Preq: adExplosives; Strength: 16; Trans: 0;
     1128  Cost: 24), (Preq: adSteamEngine; Strength: 24; Trans: 0;
     1129  Cost: 28), (Preq: adAmphibiousWarfare; Strength: 24; Trans: 1;
     1130  Cost: 18), (Preq: adAdvancedRocketry; Strength: 32; Trans: 0;
     1131  Cost: 38), (Preq: futMaterialTechnology; Strength: 14; Trans: 0;
     1132  Cost: 4), (Preq: futArtificialIntelligence; Strength: 14; Trans: 0;
     1133  Cost: 4), (Preq: preNA), (Preq: preNA), (Preq: preNA), (Preq: preNA)),
     1134  ((Preq: adFlight; Strength: 12; Trans: 1; Cost: 14), (Preq: adTactics;
     1135  Strength: 6; Trans: 0; Cost: 17), (Preq: adElectronics; Strength: 6; Trans: 0;
     1136  Cost: 20), (Preq: adMin; Strength: 8; Trans: 0; Cost: 24),
     1137  (Preq: adComposites; Strength: 8; Trans: 0; Cost: 26), (Preq: adSmartWeapons;
     1138  Strength: 11; Trans: 0; Cost: 32), (Preq: futArtificialIntelligence;
     1139  Strength: 7; Trans: 0; Cost: 4), (Preq: preNA), (Preq: preNA), (Preq: preNA),
     1140  (Preq: preNA), (Preq: preNA), (Preq: preNA), (Preq: preNA), (Preq: preNA)));
     1141
     1142{ model features }
     1143nFeature = 27;
     1144mcWeapons = 0;
     1145mcArmor = 1;
     1146mcMob = 2;
     1147mcSeaTrans = 3;
     1148mcCarrier = 4;
     1149mcTurbines = 5;
     1150mcBombs = 6;
     1151mcFuel = 7;
     1152mcAirTrans = 8;
     1153mcNav = 9;
     1154mcRadar = 10;
     1155mcSub = 11;
     1156mcArtillery = 12;
     1157mcAlpine = 13;
     1158mcSupplyShip = 14;
     1159mcOver = 15;
     1160mcAirDef = 16;
     1161mcSpy = 17;
     1162mcSE = 18;
     1163mcNP = 19;
     1164mcJet = 20;
     1165mcStealth = 21;
     1166mcFanatic = 22;
     1167mcFirst = 23;
     1168mcWill = 24;
     1169mcAcademy = 25;
     1170mcLine = 26;
     1171mcFirstNonCap = mcNav;
     1172AutoFeature:
     1173Set of mcFirstNonCap .. nFeature - 1 = [mcNav, mcSE, mcNP, mcJet, mcAcademy];
     1174// unit class advances, automatically applied if available
     1175
     1176Feature:
     1177array [0 .. nFeature - 1] of { unit model features }
     1178  record Domains, Preq, Weight, Cost: integer;
     1179end
     1180= ((Domains: 7; Preq: preNone; Weight: 1; Cost: 1), { mcOffense }
     1181  (Domains: 7; Preq: preNone; Weight: 1; Cost: 1), { mcDefense }
     1182  (Domains: 1; Preq: adHorsebackRiding; Weight: 1; Cost: 1), { mcMob }
     1183  (Domains: 2; Preq: preNone; Weight: 2; Cost: 1), { mcSeaTrans }
     1184  (Domains: 2; Preq: adAdvancedFlight; Weight: 2; Cost: 2), { mcCarrier }
     1185  (Domains: 2; Preq: adPhysics; Weight: 3; Cost: 1), { mcTurbines }
     1186  (Domains: 4; Preq: adAdvancedFlight; Weight: 1; Cost: 1), { mcBombs }
     1187  (Domains: 4; Preq: preNone; Weight: 1; Cost: 1), { mcFuel }
     1188  (Domains: 4; Preq: adCombinedArms; Weight: 2; Cost: 1), { mcAirTrans }
     1189  (Domains: 2; Preq: adNavigation; Weight: 0; Cost: 0), { mcNav }
     1190  (Domains: 2; Preq: adRadio; Weight: 0; Cost: 1), { mcRadar }
     1191  (Domains: 2; Preq: adCombustionEngine; Weight: 2; Cost: 1), { mcSub }
     1192  (Domains: 3; Preq: adBallistics; Weight: 1; Cost: 1), { mcArtillery }
     1193  (Domains: 1; Preq: adTactics; Weight: 2; Cost: 1), { mcAlpine }
     1194  (Domains: 2; Preq: adMedicine; Weight: 1; Cost: 1), { mcSupplyShip }
     1195  (Domains: 1; Preq: adBridgeBuilding; Weight: 0; Cost: 2), { mcOver }
     1196  (Domains: 2; Preq: adAdvancedRocketry; Weight: 1; Cost: 1), { mcAirDef }
     1197  (Domains: 4; Preq: adIntelligence; Weight: 2; Cost: 1), { mcSpy }
     1198  (Domains: 2; Preq: adSteamEngine; Weight: 0; Cost: 0), { mcSE }
     1199  (Domains: 2; Preq: adNuclearPower; Weight: 0; Cost: 0), { mcNP }
     1200  (Domains: 4; Preq: adRocketry; Weight: 0; Cost: 0), { mcJet }
     1201  (Domains: 4; Preq: adStealth; Weight: 1; Cost: 2), { mcStealth }
     1202  (Domains: 5; Preq: adCommunism; Weight: 0; Cost: 1), { mcFanatic }
     1203  (Domains: 1; Preq: preSun; Weight: 0; Cost: 1), { mcFirst }
     1204  (Domains: 1; Preq: preSun; Weight: 0; Cost: 1), { mcWill }
     1205  (Domains: 1; Preq: preSun; Weight: 0; Cost: 0), { mcAcademy }
     1206  (Domains: 7; Preq: adMassProduction; Weight: 0; Cost: 0)); { mcLine }
     1207
     1208WeightPreq7:
     1209array [0 .. nDomains - 1] of integer = (adHorsebackRiding, adSeafaring,
     1210  adAdvancedFlight);
     1211WeightPreq10:
     1212array [0 .. nDomains - 1] of integer = (adAutomobile, adSteel, preNA);
     1213
     1214INFIN = 999999;
    8181215
    8191216// for backward compatibility
    820 fRare=fDeadLands;fRare1=fCobalt;fRare2=fUranium;
    821 mkCaravan=mkFreight;mkDiplomat=mkCommando;
    822 gLybertarianism=gFuture;
    823 trCeaseFire=1;
    824 adIntelligenArms=adSmartWeapons;adIntelligentArms=adSmartWeapons;
    825 adRadioCommunication=adRadio;adLybertarianism=adInternet;
    826 futResearchTechnology=futComputingTechnology;
    827 futProductionTechnology=futNanoTechnology;
    828 futArmorTechnology=futMaterialTechnology;
    829 futMissileTechnology=futArtificialIntelligence;
    830 imNatObs=imObservatory;imElite=imMilAcademy;
    831 mcOffense=mcWeapons;mcDefense=mcArmor;mcLongRange=mcArtillery;
    832 mcHospital=mcSupplyShip;
    833 
     1217fRare = fDeadLands;
     1218fRare1 = fCobalt;
     1219fRare2 = fUranium;
     1220mkCaravan = mkFreight;
     1221mkDiplomat = mkCommando;
     1222gLybertarianism = gFuture;
     1223trCeaseFire = 1;
     1224adIntelligenArms = adSmartWeapons;
     1225adIntelligentArms = adSmartWeapons;
     1226adRadioCommunication = adRadio;
     1227adLybertarianism = adInternet;
     1228futResearchTechnology = futComputingTechnology;
     1229futProductionTechnology = futNanoTechnology;
     1230futArmorTechnology = futMaterialTechnology;
     1231futMissileTechnology = futArtificialIntelligence;
     1232imNatObs = imObservatory;
     1233imElite = imMilAcademy;
     1234mcOffense = mcWeapons;
     1235mcDefense = mcArmor;
     1236mcLongRange = mcArtillery;
     1237mcHospital = mcSupplyShip;
    8341238
    8351239type
    836 TServerCall=function(Command,Player,Subject:integer;var Data): integer; stdcall;
    837 TClientCall=procedure(Command,Player:integer;var Data); stdcall;
    838 
    839 TUn=packed record
    840   Loc, {location}
    841   Status, // free for AI use
    842   SavedStatus: LongInt; // for server internal use only
    843   ID: word; // unit number, never changes, unique within this nation
    844   mix, {model index}
    845   Home, {home city index, -1 if none}
    846   Master, {index of transporting unit, -1 if none}
    847   Movement: SmallInt; {movement left for this turn}
    848   Health, // = 100-Damage
    849   Fuel: ShortInt;
    850   Job, {current terrain improvement job}
    851   Exp, {micro experience, the level is Exp div ExpCost}
    852   TroopLoad, {number of transported ground units}
    853   AirLoad: Byte; //number of transported air units
    854   Flags: Cardinal;
     1240  TServerCall = function(Command, Player, Subject: integer; var Data)
     1241    : integer; stdcall;
     1242  TClientCall = procedure(Command, Player: integer; var Data); stdcall;
     1243
     1244  TUn = packed record
     1245    Loc, { location }
     1246    Status, // free for AI use
     1247    SavedStatus: LongInt; // for server internal use only
     1248    ID: word; // unit number, never changes, unique within this nation
     1249    mix, { model index }
     1250    Home, { home city index, -1 if none }
     1251    Master, { index of transporting unit, -1 if none }
     1252    Movement: SmallInt; { movement left for this turn }
     1253    Health, // = 100-Damage
     1254    Fuel: ShortInt;
     1255    Job, { current terrain improvement job }
     1256    Exp, { micro experience, the level is Exp div ExpCost }
     1257    TroopLoad, { number of transported ground units }
     1258    AirLoad: Byte; // number of transported air units
     1259    Flags: Cardinal;
    8551260  end;
    8561261
    857 TCity=packed record
    858   Loc, {location}
    859   Status, // free for AI use
    860   SavedStatus: LongInt; // for server internal use only
    861   ID, // founding player shl 12 + number, never changes, unique within the whole game
    862   Size: word;
    863   Project, // current production project, see city project flags
    864   Project0, // for server use only
    865   Food, //collected food in storage
    866   Pollution, //collected pollution in dump
    867   Prod, //for project collected production points
    868   Prod0: SmallInt; //for project collected production points in the beginning of the turn
    869   Flags, //what happened within the last turnaround
    870   Tiles, {currently by city exploited tiles, bitset with index
    871     (dy+3) shl 2+(dx+3) shr 1, (dx,dy) relative to central tile}
    872   N1: Cardinal; // reserved for future use
    873   Built: array[0..(nImp+3) div 4 *4 -1] of ShortInt; //array value =1 indicates built improvement
     1262  TCity = packed record
     1263    Loc, { location }
     1264    Status, // free for AI use
     1265    SavedStatus: LongInt; // for server internal use only
     1266    ID, // founding player shl 12 + number, never changes, unique within the whole game
     1267    Size: word;
     1268    Project, // current production project, see city project flags
     1269    Project0, // for server use only
     1270    Food, // collected food in storage
     1271    Pollution, // collected pollution in dump
     1272    Prod, // for project collected production points
     1273    Prod0: SmallInt;
     1274    // for project collected production points in the beginning of the turn
     1275    Flags, // what happened within the last turnaround
     1276    Tiles, { currently by city exploited tiles, bitset with index
     1277      (dy+3) shl 2+(dx+3) shr 1, (dx,dy) relative to central tile }
     1278    N1: Cardinal; // reserved for future use
     1279    Built: array [0 .. (nImp + 3) div 4 * 4 - 1] of ShortInt;
     1280    // array value =1 indicates built improvement
    8741281  end;
    8751282
    876 TModel=packed record
    877   Status, // free for AI use
    878   SavedStatus: LongInt; // for server internal use only
    879   ID, // developing player shl 12 + number, never changes, unique within the whole game
    880   IntroTurn,
    881   Built, //units built with this model
    882   Lost: word; //units of this model lost in combat
    883   Kind,
    884   Domain: Byte;
    885   Attack,
    886   Defense,
    887   Speed,
    888   Cost,
    889   MStrength: word; // construction time multipliers, only valid if kind is mkSelfDeveloped or mkEnemyDeveloped
    890   MTrans,
    891   MCost,
    892   Weight, MaxWeight: Byte; // weight and maximum weight (construction time)
    893   Upgrades, //bitarray indicating all upgrades
    894   Flags: Cardinal;
    895   Cap: array [0..(nFeature+3) div 4 *4 -1] of Byte; //special features
     1283  TModel = packed record
     1284    Status, // free for AI use
     1285    SavedStatus: LongInt; // for server internal use only
     1286    ID, // developing player shl 12 + number, never changes, unique within the whole game
     1287    IntroTurn, Built, // units built with this model
     1288    Lost: word; // units of this model lost in combat
     1289    Kind, Domain: Byte;
     1290    Attack, Defense, Speed, Cost, MStrength: word;
     1291    // construction time multipliers, only valid if kind is mkSelfDeveloped or mkEnemyDeveloped
     1292    MTrans, MCost, Weight, MaxWeight: Byte;
     1293    // weight and maximum weight (construction time)
     1294    Upgrades, // bitarray indicating all upgrades
     1295    Flags: Cardinal;
     1296    Cap: array [0 .. (nFeature + 3) div 4 * 4 - 1] of Byte; // special features
    8961297  end;
    8971298
    898 TUnitInfo=packed record
    899   Loc: LongInt;
    900   mix, // index of unit model for its owner
    901   emix: word; // index in enemy model list
    902   Owner: Byte;
    903   Health, // = 100-Damage
    904   Fuel: ShortInt;
    905   Job, //current terrain improvement job
    906   Exp, {micro experience, the level is Exp div ExpCost}
    907   Load: Byte; {number of transported units}
    908   Flags: word
     1299  TUnitInfo = packed record
     1300    Loc: LongInt;
     1301    mix, // index of unit model for its owner
     1302    emix: word; // index in enemy model list
     1303    Owner: Byte;
     1304    Health, // = 100-Damage
     1305    Fuel: ShortInt;
     1306    Job, // current terrain improvement job
     1307    Exp, { micro experience, the level is Exp div ExpCost }
     1308    Load: Byte; { number of transported units }
     1309    Flags: word end;
     1310
     1311    TCityInfo = packed record Loc, Status, // free for AI use
     1312    SavedStatus: LongInt; // for server internal use only
     1313    Owner, // last known owner, even if not alive anymore!
     1314    ID, // founding player <<12 + number, never changes, unique within the whole game
     1315    Size, Flags: word;
    9091316  end;
    9101317
    911 TCityInfo=packed record
    912   Loc,
    913   Status, // free for AI use
    914   SavedStatus: LongInt; // for server internal use only
    915   Owner, // last known owner, even if not alive anymore!
    916   ID, // founding player <<12 + number, never changes, unique within the whole game
    917   Size,
    918   Flags: word;
     1318  TModelInfo = packed record
     1319    Owner, // Player which owns the model
     1320    mix, // index of unit model for its owner
     1321    ID: word; // developing player shl 12 + number, never changes, unique within the whole game
     1322    Kind, Domain: Byte;
     1323    Attack, Defense, Speed, Cost: word;
     1324    TTrans, // ground unit transport capability
     1325    ATrans_Fuel: Byte; // air unit transport capability resp. fuel
     1326    Bombs: word; // additional attack with bombs
     1327    Cap: Cardinal; // special features, bitset with index Feature-mcFirstNonCap
     1328    MaxUpgrade, // maximum used upgrade
     1329    Weight: Byte;
     1330    Lost: word;
    9191331  end;
    9201332
    921 TModelInfo=packed record
    922   Owner, //Player which owns the model
    923   mix, //index of unit model for its owner
    924   ID: word; // developing player shl 12 + number, never changes, unique within the whole game
    925   Kind,
    926   Domain: Byte;
    927   Attack,
    928   Defense,
    929   Speed,
    930   Cost: word;
    931   TTrans, //ground unit transport capability
    932   ATrans_Fuel: Byte; //air unit transport capability resp. fuel
    933   Bombs: word; //additional attack with bombs
    934   Cap: Cardinal; //special features, bitset with index Feature-mcFirstNonCap
    935   MaxUpgrade, //maximum used upgrade
    936   Weight: Byte;
    937   Lost: word;
     1333  TBattle = packed record
     1334    Enemy, Flags: Byte;
     1335    Turn, mix, mixEnemy: word;
     1336    ToLoc, FromLoc: integer;
    9381337  end;
    9391338
    940 TBattle=packed record
    941   Enemy, Flags: byte;
    942   Turn, mix, mixEnemy: word;
    943   ToLoc, FromLoc: integer;
     1339  TWonderInfo = record
     1340    CityID, // -2 if destroyed, -1 if never completed, >=0 ID of city
     1341    EffectiveOwner: integer
     1342    // owning player if effective, -1 if expired or not built
     1343    end;
     1344
     1345    TShipInfo = record Parts: array [0 .. nShipPart - 1] of integer;
    9441346  end;
    9451347
    946 TWonderInfo=record
    947   CityID, // -2 if destroyed, -1 if never completed, >=0 ID of city
    948   EffectiveOwner: integer // owning player if effective, -1 if expired or not built
    949   end;
    950 
    951 TShipInfo=record
    952   Parts: array[0..nShipPart-1] of integer;
    953   end;
    954 
    955 TEnemyReport=record
    956   TurnOfContact, TurnOfCivilReport, TurnOfMilReport,
    957   Attitude,
    958   Credibility: integer; // 0..100, last update: ToC
    959   Treaty: array[0..nPl-1] of integer;
     1348  TEnemyReport = record
     1349    TurnOfContact, TurnOfCivilReport, TurnOfMilReport, Attitude,
     1350      Credibility: integer; // 0..100, last update: ToC
     1351    Treaty: array [0 .. nPl - 1] of integer;
    9601352    // diplomatic status with other nations, last update: ToCR
    961   Government, // gAnarchy..gDemocracy, last update: ToCR
    962   Money, // last update: ToCR
    963   ResearchTech, ResearchDone: integer; // last update: ToCR
    964   Tech: array[0..(nAdv+3) div 4 *4 -1] of ShortInt;
     1353    Government, // gAnarchy..gDemocracy, last update: ToCR
     1354    Money, // last update: ToCR
     1355    ResearchTech, ResearchDone: integer; // last update: ToCR
     1356    Tech: array [0 .. (nAdv + 3) div 4 * 4 - 1] of ShortInt;
    9651357    // tech status indicator, last update: ToCR
    966   nModelCounted: integer;
     1358    nModelCounted: integer;
    9671359    // number of models with info in UnCount, last update: ToMR
    968   UnCount: array[0..INFIN] of word;
     1360    UnCount: array [0 .. INFIN] of word;
    9691361    // number of available units for each model, last update: ToMR
    9701362  end;
    9711363
    972 TMoveAdviceData=record
    973   ToLoc,nStep,MoreTurns,MaxHostile_MovementLeft: integer;
    974   dx,dy: array[0..24] of integer;
     1364  TMoveAdviceData = record
     1365    ToLoc, nStep, MoreTurns, MaxHostile_MovementLeft: integer;
     1366    dx, dy: array [0 .. 24] of integer;
    9751367  end;
    976 TPlaneReturnData=record
    977   Loc,Fuel,Movement: integer;
     1368
     1369  TPlaneReturnData = record
     1370    Loc, Fuel, Movement: integer;
    9781371  end;
    979 TTileInfo=record
    980   Food,Prod,Trade,ExplCity:integer
     1372
     1373  TTileInfo = record
     1374    Food, Prod, Trade, ExplCity: integer end;
     1375    TCityReport = record HypoTiles, HypoTax, HypoLux, Working, Happy, FoodRep,
     1376      ProdRep, Trade, PollRep, Corruption, Tax, Lux, Science, Support, Eaten,
     1377      ProdCost, Storage, Deployed: integer;
    9811378  end;
    982 TCityReport=record
    983   HypoTiles,HypoTax,HypoLux,Working,Happy,FoodRep,ProdRep,Trade,PollRep,
    984     Corruption,Tax,Lux,Science,Support,Eaten,ProdCost,Storage,Deployed:integer;
     1379
     1380  TCityReportNew = record
     1381    HypoTiles,
     1382    // tiles that should be considered as exploited (for the current adjustment, set this to -1 or to TCity.Tiles of the city)
     1383    HypoTaxRate, HypoLuxuryRate,
     1384    // tax and luxury rate that should be assumed (for current rates, set this to -1 or to RO.TaxRate resp. RO.LuxRate)
     1385    Morale, FoodSupport, MaterialSupport,
     1386    // food and material taken for unit support
     1387    ProjectCost, // material cost of current project
     1388    Storage, // size of food storage
     1389    Deployed, // number of units causing unrest (unrest=2*deployed)
     1390    CollectedControl, CollectedFood, CollectedMaterial, CollectedTrade,
     1391    // raw control, food, material and trade as collected by the citizens
     1392    Working, // number of exploited tiles including city tile
     1393    FoodSurplus, Production, AddPollution,
     1394    // food surplus, production gain and pollution after all effects
     1395    Corruption, Tax, Science, Luxury,
     1396    // corruption, tax, science and wealth after all effects
     1397    HappinessBalance: integer;
     1398    // = (Morale+Wealth+Control) - (Size+Unrest), value < 0 means disorder
    9851399  end;
    986 TCityReportNew=record
    987   HypoTiles, // tiles that should be considered as exploited (for the current adjustment, set this to -1 or to TCity.Tiles of the city)
    988   HypoTaxRate,HypoLuxuryRate, // tax and luxury rate that should be assumed (for current rates, set this to -1 or to RO.TaxRate resp. RO.LuxRate)
    989   Morale,
    990   FoodSupport,MaterialSupport, // food and material taken for unit support
    991   ProjectCost, // material cost of current project
    992   Storage, // size of food storage
    993   Deployed, // number of units causing unrest (unrest=2*deployed)
    994   CollectedControl,CollectedFood,CollectedMaterial,CollectedTrade, // raw control, food, material and trade as collected by the citizens
    995   Working, // number of exploited tiles including city tile
    996   FoodSurplus,Production,AddPollution, // food surplus, production gain and pollution after all effects
    997   Corruption,Tax,Science,Luxury, // corruption, tax, science and wealth after all effects
    998   HappinessBalance: integer; // = (Morale+Wealth+Control) - (Size+Unrest), value < 0 means disorder
     1400
     1401  TCityTileAdviceData = record
     1402    ResourceWeights, Tiles: Cardinal;
     1403    CityReport: TCityReport;
    9991404  end;
    1000 TCityTileAdviceData=record
    1001   ResourceWeights, Tiles: cardinal;
    1002   CityReport: TCityReport;
     1405
     1406  TGetCityData = record
     1407    Owner: integer;
     1408    c: TCity end;
     1409    TCityAreaInfo = record Available: array [0 .. 26] of integer;
    10031410  end;
    1004 TGetCityData=record
    1005   Owner: integer;
    1006   c: TCity
     1411
     1412  TUnitReport = record
     1413    FoodSupport, ProdSupport, ReportFlags: integer;
    10071414  end;
    1008 TCityAreaInfo=record
    1009   Available: array [0..26] of integer;
    1010   end;
    1011 TUnitReport=record
    1012   FoodSupport, ProdSupport, ReportFlags: integer;
    1013   end;
    1014 TJobProgressData=array[0..nJob-1] of record
    1015   Required, Done, NextTurnPlus: integer;
    1016   end;
    1017 TBattleForecast=record
    1018   pAtt,mixAtt,HealthAtt,ExpAtt,FlagsAtt,Movement,EndHealthDef,
    1019     EndHealthAtt: integer;
    1020   end;
    1021 TBattleForecastEx=record
    1022   pAtt,mixAtt,HealthAtt,ExpAtt,FlagsAtt,Movement,EndHealthDef,
    1023     EndHealthAtt: integer; // must be same as in TBattleForecast
    1024   AStr,DStr,ABaseDamage,DBaseDamage: integer;
    1025   end;
    1026 TShowMove=record
    1027   Owner,Health,mix,emix,Flags,FromLoc,dx,dy,EndHealth,EndHealthDef,
    1028     Fuel,Exp,Load: integer;
    1029   end;
    1030 TShowShipChange=record
    1031   Reason, Ship1Owner, Ship2Owner: integer;
    1032   Ship1Change, Ship2Change: array[0..nShipPart-1] of integer;
    1033   end;
    1034 TOffer=record
    1035   nDeliver, nCost: integer;
    1036   Price: array[0..11] of Cardinal;
    1037   end;
    1038 TChart=array [0..INFIN] of integer;
    1039 TEditTileData=record
    1040   Loc, NewTile: integer
    1041   end;
    1042 TCreateUnitData=record
    1043   Loc, p, mix: integer;
    1044   end;
    1045 
    1046 TTileList= array[0..INFIN] of Cardinal;
    1047 TTileObservedLastList= array[0..INFIN] of SmallInt;
    1048 TOwnerList= array[0..INFIN] of ShortInt;
    1049 TByteList= array[0..INFIN] of Byte;
    1050 TIntList=array[0..INFIN] of integer;
    1051 TCityList= array[0..INFIN] of TCity;
    1052 TUnList= array[0..INFIN] of TUn;
    1053 TModelList= array[0..INFIN] of TModel;
    1054 TEnemyUnList=array[0..INFIN] of TUnitInfo;
    1055 TEnemyCityList=array[0..INFIN] of TCityInfo;
    1056 TEnemyModelList=array[0..INFIN] of TModelInfo;
    1057 TBattleList=array[0..INFIN] of TBattle;
    1058 
    1059 TPlayerContext=record
    1060   Data: pointer;
    1061   Map:^TTileList; {the playground, a list of tiles with index = location, see tile flags}
    1062   MapObservedLast:^TTileObservedLastList;
    1063     // turn in which the tile was observed last, index = location
    1064   Territory:^TOwnerList; // nation to which's territory a tile belongs, -1 indicates none
    1065   Un:^TUnList; {units}
    1066   City:^TCityList; {cities}
    1067   Model:^TModelList; {unit models}
    1068   EnemyUn:^TEnemyUnList; //known units of enemy players
    1069   EnemyCity:^TEnemyCityList; //known cities of enemy players
    1070   EnemyModel:^TEnemyModelList; //known unit models of enemy players
    1071   EnemyReport: array[0..nPl-1] of ^TEnemyReport;
    1072 
    1073   TestFlags, //options turned on in the "Manipulation" menu
    1074   Turn, //current turn
    1075   Alive, {bitset of IDs of players still alive, flag 1 shl p for player p}
    1076   Happened, //flags indicate what happened within the last turnaround
    1077   AnarchyStart, // start turn of anarchy, <0 if not in anarchy
    1078   Credibility, // own credibility
    1079   MaxCredibility, // maximum credibility still to achieve
    1080   nUn, {number of units}
    1081   nCity, {number of cities}
    1082   nModel, {number of developed unit models}
    1083   nEnemyUn,nEnemyCity,nEnemyModel,
    1084   Government, {gAnarchy..gDemocracy}
    1085   Money,TaxRate,LuxRate,
    1086   Research, {collected research points for currently researched tech}
    1087   ResearchTech: integer; //currently researched tech
    1088   DevModel: TModel; {unit model currently under development}
    1089   Tech: array[0..(nAdv+3) div 4 *4 -1] of ShortInt; {tech status indicator}
    1090   Attitude: array[0..nPl-1] of integer; // attitude to other nations
    1091   Treaty: array[0..nPl-1] of integer; // treaty with other nations
    1092   EvaStart: array[0..nPl-1] of integer; // peace treaty: start of evacuation period
    1093   Tribute: array[0..nPl-1] of integer; // no longer in use
    1094   TributePaid: array[0..nPl-1] of integer; // no longer in use
    1095   Wonder: array[0..27] of TWonderInfo;
    1096   Ship: array[0..nPl-1] of TShipInfo;
    1097   NatBuilt: array[28..(nImp+3) div 4 *4 -1] of ShortInt;
    1098   nBattleHistory: integer;
    1099   BattleHistory:^TBattleList; // complete list of all my battles in the whole game
    1100   BorderHelper:^TByteList;
    1101   LastCancelTreaty: array[0..nPl-1] of integer; // turn of last treaty cancel
    1102   OracleIncome: integer;
    1103   DefaultDebugMap:^TIntList;
    1104   Filler: array[0..879] of byte;
    1105   end;
    1106 
    1107 TInitModuleData=record
    1108   Server: TServerCall;
    1109   DataVersion, DataSize, Flags: integer;
    1110   end;
    1111 TNewGameData=record
    1112   lx,ly,LandMass,MaxTurn: integer;
    1113   Difficulty: array[0..nPl-1] of integer;
    1114     {difficulty levels of the players, if it's 0 this player is the supervisor,
    1115     -1 for unused slots}
    1116   RO: array[0..nPl-1] of ^TPlayerContext;
    1117   AssemblyPath: array[0..255] of char;
    1118   SuperVisorRO: array[0..nPl-1] of ^TPlayerContext;
    1119   end;
    1120 TNewGameExData=record
    1121   lx,ly,LandMass,MaxTurn,RND: integer;
    1122   Difficulty: array[0..nPl-1] of integer;
    1123     {difficulty levels of the players, if it's 0 this player is the supervisor,
    1124     -1 for unused slots}
    1125   Controlled: integer;
    1126   end;
    1127 TShowNegoData=record
    1128   pSender, pTarget, Action: integer;
    1129   Offer: TOffer;
    1130   end;
     1415
     1416  TJobProgressData = array [0 .. nJob - 1] of record Required, Done,
     1417    NextTurnPlus: integer;
     1418end;
     1419TBattleForecast = record pAtt, mixAtt, HealthAtt, ExpAtt, FlagsAtt, Movement,
     1420  EndHealthDef, EndHealthAtt: integer;
     1421end;
     1422TBattleForecastEx = record pAtt, mixAtt, HealthAtt, ExpAtt, FlagsAtt, Movement,
     1423  EndHealthDef, EndHealthAtt: integer; // must be same as in TBattleForecast
     1424AStr, DStr, ABaseDamage, DBaseDamage: integer;
     1425end;
     1426TShowMove = record Owner, Health, mix, emix, Flags, FromLoc, dx, dy, EndHealth,
     1427  EndHealthDef, Fuel, Exp, Load: integer;
     1428end;
     1429TShowShipChange = record Reason, Ship1Owner, Ship2Owner: integer;
     1430Ship1Change, Ship2Change: array [0 .. nShipPart - 1] of integer;
     1431end;
     1432TOffer = record nDeliver, nCost: integer;
     1433Price:
     1434array [0 .. 11] of Cardinal;
     1435end;
     1436TChart = array [0 .. INFIN] of integer;
     1437TEditTileData = record Loc, NewTile: integer
     1438end;
     1439TCreateUnitData = record Loc, p, mix: integer;
     1440end;
     1441
     1442TTileList = array [0 .. INFIN] of Cardinal;
     1443TTileObservedLastList = array [0 .. INFIN] of SmallInt;
     1444TOwnerList = array [0 .. INFIN] of ShortInt;
     1445TByteList = array [0 .. INFIN] of Byte;
     1446TIntList = array [0 .. INFIN] of integer;
     1447TCityList = array [0 .. INFIN] of TCity;
     1448TUnList = array [0 .. INFIN] of TUn;
     1449TModelList = array [0 .. INFIN] of TModel;
     1450TEnemyUnList = array [0 .. INFIN] of TUnitInfo;
     1451TEnemyCityList = array [0 .. INFIN] of TCityInfo;
     1452TEnemyModelList = array [0 .. INFIN] of TModelInfo;
     1453TBattleList = array [0 .. INFIN] of TBattle;
     1454
     1455TPlayerContext = record Data: pointer;
     1456Map:
     1457^TTileList;
     1458{ the playground, a list of tiles with index = location, see tile flags }
     1459MapObservedLast:
     1460^TTileObservedLastList;
     1461// turn in which the tile was observed last, index = location
     1462Territory:
     1463^TOwnerList; // nation to which's territory a tile belongs, -1 indicates none
     1464Un:
     1465^TUnList; { units }
     1466City:
     1467^TCityList; { cities }
     1468Model:
     1469^TModelList; { unit models }
     1470EnemyUn:
     1471^TEnemyUnList; // known units of enemy players
     1472EnemyCity:
     1473^TEnemyCityList; // known cities of enemy players
     1474EnemyModel:
     1475^TEnemyModelList; // known unit models of enemy players
     1476EnemyReport:
     1477array [0 .. nPl - 1] of ^TEnemyReport;
     1478
     1479TestFlags, // options turned on in the "Manipulation" menu
     1480Turn, // current turn
     1481Alive, { bitset of IDs of players still alive, flag 1 shl p for player p }
     1482Happened, // flags indicate what happened within the last turnaround
     1483AnarchyStart, // start turn of anarchy, <0 if not in anarchy
     1484Credibility, // own credibility
     1485MaxCredibility, // maximum credibility still to achieve
     1486nUn, { number of units }
     1487nCity, { number of cities }
     1488nModel, { number of developed unit models }
     1489nEnemyUn, nEnemyCity, nEnemyModel, Government, { gAnarchy..gDemocracy }
     1490Money, TaxRate, LuxRate, Research,
     1491{ collected research points for currently researched tech }
     1492ResearchTech: integer; // currently researched tech
     1493DevModel:
     1494TModel; { unit model currently under development }
     1495Tech:
     1496array [0 .. (nAdv + 3) div 4 * 4 - 1] of ShortInt; { tech status indicator }
     1497Attitude:
     1498array [0 .. nPl - 1] of integer; // attitude to other nations
     1499Treaty:
     1500array [0 .. nPl - 1] of integer; // treaty with other nations
     1501EvaStart:
     1502array [0 .. nPl - 1] of integer; // peace treaty: start of evacuation period
     1503Tribute:
     1504array [0 .. nPl - 1] of integer; // no longer in use
     1505TributePaid:
     1506array [0 .. nPl - 1] of integer; // no longer in use
     1507Wonder:
     1508array [0 .. 27] of TWonderInfo;
     1509Ship:
     1510array [0 .. nPl - 1] of TShipInfo;
     1511NatBuilt:
     1512array [28 .. (nImp + 3) div 4 * 4 - 1] of ShortInt;
     1513nBattleHistory:
     1514integer;
     1515BattleHistory:
     1516^TBattleList; // complete list of all my battles in the whole game
     1517BorderHelper:
     1518^TByteList;
     1519LastCancelTreaty:
     1520array [0 .. nPl - 1] of integer; // turn of last treaty cancel
     1521OracleIncome:
     1522integer;
     1523DefaultDebugMap:
     1524^TIntList;
     1525Filler:
     1526array [0 .. 879] of Byte;
     1527end;
     1528
     1529TInitModuleData = record Server: TServerCall;
     1530DataVersion, DataSize, Flags: integer;
     1531end;
     1532TNewGameData = record lx, ly, LandMass, MaxTurn: integer;
     1533Difficulty:
     1534array [0 .. nPl - 1] of integer;
     1535{ difficulty levels of the players, if it's 0 this player is the supervisor,
     1536  -1 for unused slots }
     1537RO:
     1538array [0 .. nPl - 1] of ^TPlayerContext;
     1539AssemblyPath:
     1540array [0 .. 255] of char;
     1541SuperVisorRO:
     1542array [0 .. nPl - 1] of ^TPlayerContext;
     1543end;
     1544TNewGameExData = record lx, ly, LandMass, MaxTurn, RND: integer;
     1545Difficulty:
     1546array [0 .. nPl - 1] of integer;
     1547{ difficulty levels of the players, if it's 0 this player is the supervisor,
     1548  -1 for unused slots }
     1549Controlled:
     1550integer;
     1551end;
     1552TShowNegoData = record pSender, pTarget, Action: integer;
     1553Offer:
     1554TOffer;
     1555end;
    11311556
    11321557const
    1133 {predefined unit models:}
    1134 nSpecialModel=9;
    1135 SpecialModel: array[0..nSpecialModel-1] of TModel=
    1136 ((Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1137   Kind:mkSettler;Domain:dGround;Attack:0;Defense:10;Speed:150;Cost:40;
    1138   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1139   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Settlers}
    1140 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1141   Kind:mkSettler;Domain:dGround;Attack:0;Defense:20;Speed:300;Cost:40;
    1142   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1143   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Engineers}
    1144 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1145   Kind:mkSelfDeveloped;Domain:dGround;Attack:6;Defense:6;Speed:150;Cost:10;
    1146   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1147   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Militia}
    1148 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1149   Kind:mkSpecial_TownGuard;Domain:dGround;Attack:4;Defense:6;Speed:150;Cost:20;
    1150   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1151   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Town Guard}
    1152 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1153   Kind:mkDiplomat;Domain:dGround;Attack:12;Defense:12;Speed:250;Cost:20;
    1154   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1155   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Special Commando}
    1156 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1157   Kind:mkCaravan;Domain:dGround;Attack:0;Defense:6;Speed:150;Cost:60;
    1158   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1159   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Freight}
    1160 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1161   Kind:mkSpecial_Boat;Domain:dSea;Attack:0;Defense:3;Speed:250;Cost:20;
    1162   MStrength:0;MTrans:1;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1163   Cap:(0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Longboat}
    1164 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1165   Kind:mkSlaves;Domain:dGround;Attack:0;Defense:15;Speed:150;Cost:40;
    1166   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1167   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), {Slaves}
    1168 {(Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1169   Kind:mkSpecial_Carriage;Domain:dGround;Attack:50;Defense:30;Speed:250;Cost:50;
    1170   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1171   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),}
    1172 {(Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1173   Kind:mkSpecial_SubCabin;Domain:dSea;Attack:16;Defense:1;Speed:350;Cost:40;
    1174   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1175   Cap:(0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),}
    1176 (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
    1177   Kind:mkSpecial_Glider;Domain:dAir;Attack:6;Defense:6;Speed:450;Cost:30;
    1178   MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
    1179   Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0)));
    1180 
    1181 SpecialModelPreq: array[0..nSpecialModel-1] of integer=
    1182 (preNone,adExplosives,preNone,preNone,(*adWri,*)adIntelligence,adTrade,
    1183 (*adTheCorporation,adHorsebackRiding,adAutomobile,adNavigation,
    1184 adCombustionEngine,*)adMapMaking,preBuilder,{preLeo,preLighthouse,}preLeo);
    1185 
     1558  { predefined unit models: }
     1559  nSpecialModel = 9;
     1560  SpecialModel: array [0 .. nSpecialModel - 1] of TModel = ((Status: 0;
     1561    SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0; Kind: mkSettler;
     1562    Domain: dGround; Attack: 0; Defense: 10; Speed: 150; Cost: 40; MStrength: 0;
     1563    MTrans: 0; MCost: 0; Weight: 0; MaxWeight: 0; Upgrades: 0; Flags: 0;
     1564    Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1565    0, 0, 0, 0, 0)), { Settlers }
     1566    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1567    Kind: mkSettler; Domain: dGround; Attack: 0; Defense: 20; Speed: 300;
     1568    Cost: 40; MStrength: 0; MTrans: 0; MCost: 0; Weight: 0; MaxWeight: 0;
     1569    Upgrades: 0; Flags: 0; Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1570    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), { Engineers }
     1571    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1572    Kind: mkSelfDeveloped; Domain: dGround; Attack: 6; Defense: 6; Speed: 150;
     1573    Cost: 10; MStrength: 0; MTrans: 0; MCost: 0; Weight: 0; MaxWeight: 0;
     1574    Upgrades: 0; Flags: 0; Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1575    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), { Militia }
     1576    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1577    Kind: mkSpecial_TownGuard; Domain: dGround; Attack: 4; Defense: 6;
     1578    Speed: 150; Cost: 20; MStrength: 0; MTrans: 0; MCost: 0; Weight: 0;
     1579    MaxWeight: 0; Upgrades: 0; Flags: 0;
     1580    Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1581    0, 0, 0, 0, 0)), { Town Guard }
     1582    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1583    Kind: mkDiplomat; Domain: dGround; Attack: 12; Defense: 12; Speed: 250;
     1584    Cost: 20; MStrength: 0; MTrans: 0; MCost: 0; Weight: 0; MaxWeight: 0;
     1585    Upgrades: 0; Flags: 0; Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1586    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), { Special Commando }
     1587    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1588    Kind: mkCaravan; Domain: dGround; Attack: 0; Defense: 6; Speed: 150;
     1589    Cost: 60; MStrength: 0; MTrans: 0; MCost: 0; Weight: 0; MaxWeight: 0;
     1590    Upgrades: 0; Flags: 0; Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1591    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), { Freight }
     1592    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1593    Kind: mkSpecial_Boat; Domain: dSea; Attack: 0; Defense: 3; Speed: 250;
     1594    Cost: 20; MStrength: 0; MTrans: 1; MCost: 0; Weight: 0; MaxWeight: 0;
     1595    Upgrades: 0; Flags: 0; Cap: (0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1596    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), { Longboat }
     1597    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1598    Kind: mkSlaves; Domain: dGround; Attack: 0; Defense: 15; Speed: 150;
     1599    Cost: 40; MStrength: 0; MTrans: 0; MCost: 0; Weight: 0; MaxWeight: 0;
     1600    Upgrades: 0; Flags: 0; Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1601    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), { Slaves }
     1602    { (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
     1603      Kind:mkSpecial_Carriage;Domain:dGround;Attack:50;Defense:30;Speed:250;Cost:50;
     1604      MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
     1605      Cap:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), }
     1606    { (Status:0;SavedStatus:0;ID:0;IntroTurn:0;Built:0;Lost:0;
     1607      Kind:mkSpecial_SubCabin;Domain:dSea;Attack:16;Defense:1;Speed:350;Cost:40;
     1608      MStrength:0;MTrans:0;MCost:0;Weight:0;MaxWeight:0;Upgrades:0;Flags:0;
     1609      Cap:(0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), }
     1610    (Status: 0; SavedStatus: 0; ID: 0; IntroTurn: 0; Built: 0; Lost: 0;
     1611    Kind: mkSpecial_Glider; Domain: dAir; Attack: 6; Defense: 6; Speed: 450;
     1612    Cost: 30; MStrength: 0; MTrans: 0; MCost: 0; Weight: 0; MaxWeight: 0;
     1613    Upgrades: 0; Flags: 0; Cap: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1614    0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)));
     1615
     1616  SpecialModelPreq: array [0 .. nSpecialModel - 1] of integer = (preNone,
     1617    adExplosives, preNone, preNone, (* adWri, *) adIntelligence, adTrade,
     1618    (* adTheCorporation,adHorsebackRiding,adAutomobile,adNavigation,
     1619      adCombustionEngine, *) adMapMaking, preBuilder,
     1620    { preLeo,preLighthouse, } preLeo);
    11861621
    11871622procedure MakeUnitInfo(p: integer; const u: TUn; var ui: TUnitInfo);
    1188 procedure MakeModelInfo(p, mix: integer; const m: TModel; var mi: TModelInfo);
    1189 function IsSameModel(const mi1,mi2: TModelInfo): boolean;
    1190 function SpecialTile(Loc,TerrType,lx: integer): integer;
    1191 
     1623  procedure MakeModelInfo(p, mix: integer; const m: TModel; var mi: TModelInfo);
     1624    function IsSameModel(const mi1, mi2: TModelInfo): boolean;
     1625      function SpecialTile(Loc, TerrType, lx: integer): integer;
    11921626
    11931627implementation
     
    11951629procedure MakeUnitInfo(p: integer; const u: TUn; var ui: TUnitInfo);
    11961630begin
    1197 ui.Owner:=p;
    1198 ui.Loc:=u.Loc;
    1199 ui.Health:=u.Health;
    1200 ui.Fuel:=u.Fuel;
    1201 ui.Job:=u.Job;
    1202 ui.Exp:=u.Exp;
    1203 ui.Load:=u.TroopLoad+u.AirLoad;
    1204 ui.mix:=u.mix;
    1205 ui.Flags:=u.Flags;
     1631  ui.Owner := p;
     1632  ui.Loc := u.Loc;
     1633  ui.Health := u.Health;
     1634  ui.Fuel := u.Fuel;
     1635  ui.Job := u.Job;
     1636  ui.Exp := u.Exp;
     1637  ui.Load := u.TroopLoad + u.AirLoad;
     1638  ui.mix := u.mix;
     1639  ui.Flags := u.Flags;
    12061640end;
    12071641
    12081642procedure MakeModelInfo(p, mix: integer; const m: TModel; var mi: TModelInfo);
    12091643var
    1210 i: integer;
     1644  i: integer;
    12111645begin
    1212 mi.Owner:=p;
    1213 mi.mix:=mix;
    1214 mi.ID:=m.ID;
    1215 mi.Domain:=m.Domain;
    1216 if m.Kind=mkEnemyDeveloped then mi.Kind:=mkSelfDeveloped // important for IsSameModel()
    1217 else mi.Kind:=m.Kind;
    1218 mi.Attack:=m.Attack;
    1219 mi.Defense:=m.Defense;
    1220 mi.Speed:=m.Speed;
    1221 mi.Cost:=m.Cost;
    1222 if mi.Domain=dAir then
     1646  mi.Owner := p;
     1647  mi.mix := mix;
     1648  mi.ID := m.ID;
     1649  mi.Domain := m.Domain;
     1650  if m.Kind = mkEnemyDeveloped then
     1651    mi.Kind := mkSelfDeveloped // important for IsSameModel()
     1652  else
     1653    mi.Kind := m.Kind;
     1654  mi.Attack := m.Attack;
     1655  mi.Defense := m.Defense;
     1656  mi.Speed := m.Speed;
     1657  mi.Cost := m.Cost;
     1658  if mi.Domain = dAir then
    12231659  begin
    1224   mi.TTrans:=m.Cap[mcAirTrans]*m.MTrans;
    1225   mi.ATrans_Fuel:=m.Cap[mcFuel];
     1660    mi.TTrans := m.Cap[mcAirTrans] * m.MTrans;
     1661    mi.ATrans_Fuel := m.Cap[mcFuel];
    12261662  end
    1227 else
     1663  else
    12281664  begin
    1229   mi.TTrans:=m.Cap[mcSeaTrans]*m.MTrans;
    1230   mi.ATrans_Fuel:=m.Cap[mcCarrier]*m.MTrans;
     1665    mi.TTrans := m.Cap[mcSeaTrans] * m.MTrans;
     1666    mi.ATrans_Fuel := m.Cap[mcCarrier] * m.MTrans;
    12311667  end;
    1232 mi.Bombs:=m.Cap[mcBombs]*m.MStrength*2;
    1233 mi.Cap:=0;
    1234 for i:=mcFirstNonCap to nFeature-1 do if m.Cap[i]>0 then
    1235   mi.Cap:=mi.Cap or (1 shl (i-mcFirstNonCap));
    1236 mi.MaxUpgrade:=0;
    1237 for i:=1 to nUpgrade-1 do if m.Upgrades and (1 shl i)<>0 then
    1238   mi.MaxUpgrade:=i;
    1239 mi.Weight:=m.Weight;
    1240 mi.Lost:=0;
     1668  mi.Bombs := m.Cap[mcBombs] * m.MStrength * 2;
     1669  mi.Cap := 0;
     1670  for i := mcFirstNonCap to nFeature - 1 do
     1671    if m.Cap[i] > 0 then
     1672      mi.Cap := mi.Cap or (1 shl (i - mcFirstNonCap));
     1673  mi.MaxUpgrade := 0;
     1674  for i := 1 to nUpgrade - 1 do
     1675    if m.Upgrades and (1 shl i) <> 0 then
     1676      mi.MaxUpgrade := i;
     1677  mi.Weight := m.Weight;
     1678  mi.Lost := 0;
    12411679end;
    12421680
    1243 function IsSameModel(const mi1,mi2: TModelInfo): boolean;
     1681function IsSameModel(const mi1, mi2: TModelInfo): boolean;
    12441682type
    1245 TModelInfo_Compare=array[0..5] of Cardinal;
     1683  TModelInfo_Compare = array [0 .. 5] of Cardinal;
    12461684var
    1247 Compare1, Compare2: ^TModelInfo_Compare;
     1685  Compare1, Compare2: ^TModelInfo_Compare;
    12481686begin
    1249 Compare1:=@mi1; Compare2:=@mi2;
    1250 result:=(Compare1[1] and $FFFF0000=Compare2[1] and $FFFF0000)
    1251   and (Compare1[2]=Compare2[2]) and (Compare1[3]=Compare2[3])
    1252   and (Compare1[4]=Compare2[4]) and (Compare1[5]=Compare2[5])
     1687  Compare1 := @mi1;
     1688  Compare2 := @mi2;
     1689  result := (Compare1[1] and $FFFF0000 = Compare2[1] and $FFFF0000) and
     1690    (Compare1[2] = Compare2[2]) and (Compare1[3] = Compare2[3]) and
     1691    (Compare1[4] = Compare2[4]) and (Compare1[5] = Compare2[5])
    12531692end;
    12541693
    1255 function SpecialTile(Loc,TerrType,lx: integer): integer;
     1694function SpecialTile(Loc, TerrType, lx: integer): integer;
    12561695var
    1257 x,y,qx,qy,a: integer;
     1696  x, y, qx, qy, a: integer;
    12581697begin
    1259 if TerrType=fOcean then result:=0
    1260 else
     1698  if TerrType = fOcean then
     1699    result := 0
     1700  else
    12611701  begin
    1262   y:=Loc div lx;
    1263   x:=Loc-y*lx;
    1264   if TerrType=fGrass then {formula for productive grassland}
    1265     if Odd((lymax+x-y shr 1) shr 1+x+(y+1) shr 1) then result:=1
    1266     else result:=0
    1267   else {formula for special resources}
     1702    y := Loc div lx;
     1703    x := Loc - y * lx;
     1704    if TerrType = fGrass then { formula for productive grassland }
     1705      if Odd((lymax + x - y shr 1) shr 1 + x + (y + 1) shr 1) then
     1706        result := 1
     1707      else
     1708        result := 0
     1709    else { formula for special resources }
    12681710    begin
    1269     a:=4*x-y+9980;
    1270     qx:=a div 10;
    1271     if (qx*10=a) and (qx and 3<>0) then
     1711      a := 4 * x - y + 9980;
     1712      qx := a div 10;
     1713      if (qx * 10 = a) and (qx and 3 <> 0) then
    12721714      begin
    1273       qy:=(y+x) div 5;
    1274       if qy and 3<>qx shr 2 and 1 *2 then
    1275         if (TerrType=fArctic) or (TerrType=fSwamp) then result:=1
    1276         else if TerrType=fShore then
     1715        qy := (y + x) div 5;
     1716        if qy and 3 <> qx shr 2 and 1 * 2 then
     1717          if (TerrType = fArctic) or (TerrType = fSwamp) then
     1718            result := 1
     1719          else if TerrType = fShore then
    12771720          begin
    1278           if (qx+qy) and 1=0 then
    1279             if qx and 3=2 then result:=2
    1280             else result:=1
    1281           else result:=0
     1721            if (qx + qy) and 1 = 0 then
     1722              if qx and 3 = 2 then
     1723                result := 2
     1724              else
     1725                result := 1
     1726            else
     1727              result := 0
    12821728          end
    1283         else result:=(qx+qy) and 1+1
    1284       else result:=0;
     1729          else
     1730            result := (qx + qy) and 1 + 1
     1731        else
     1732          result := 0;
    12851733      end
    1286     else result:=0;
     1734      else
     1735        result := 0;
    12871736    end
    12881737  end;
     
    12901739
    12911740begin
    1292 assert(sizeof(TPlayerContext)=2048);
    1293 assert(sizeof(TModel)-2*sizeof(LongInt)-4*sizeof(word)=sIntSetDevModel and $F *4);
     1741  assert(sizeof(TPlayerContext) = 2048);
     1742  assert(sizeof(TModel) - 2 * sizeof(LongInt) - 4 * sizeof(word)
     1743    = sIntSetDevModel and $F * 4);
     1744
    12941745end.
    1295 
  • trunk/ScreenTools.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit ScreenTools;
    43
     
    87  StringTables,
    98
    10 
    11   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Menus;
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Menus;
    1210
    1311type
    14 TTexture=record
    15   Image: TBitmap;
    16   clBevelLight,clBevelShade,clTextLight,clTextShade,clLitText,clMark,clPage,clCover: TColor
    17   end;
    18 
    19 function ChangeResolution(x,y,bpp,freq: integer): boolean;
    20 procedure RestoreResolution;
    21 function Play(Item: string; Index: integer =-1): boolean;
    22 procedure PreparePlay(Item: string; Index: integer =-1);
    23 procedure EmptyMenu(MenuItems: TMenuItem; Keep: integer = 0);
    24 function turntoyear(Turn: integer): integer;
    25 function TurnToString(Turn: integer): string;
    26 function MovementToString(Movement: integer): string;
    27 procedure BtnFrame(ca:TCanvas;p:TRect;const T: TTexture);
    28 procedure EditFrame(ca:TCanvas;p:TRect;const T: TTexture);
    29 function HexStringToColor(s: string): integer;
    30 function LoadGraphicFile(bmp: TBitmap; Path: string; Options: integer= 0): boolean;
    31 function LoadLocalizedGraphicFile(bmp: TBitmap; Path: string; Options: integer= 0): boolean;
    32 function LoadGraphicSet(Name: string): integer;
    33 procedure Dump(dst:TBitmap; HGr,xDst,yDst,Width,Height,xGr,yGr:integer);
    34 procedure Sprite(Canvas: TCanvas; HGr,xDst,yDst,Width,Height,xGr,yGr: integer); overload;
    35 procedure Sprite(dst:TBitmap; HGr,xDst,yDst,Width,Height,xGr,yGr:integer); overload;
    36 procedure MakeBlue(Dst: TBitmap; x,y,w,h: integer);
    37 procedure ImageOp_B(Dst,Src: TBitmap; xDst,yDst,xSrc,ySrc,w,h: integer);
    38 procedure ImageOp_BCC(Dst,Src: TBitmap; xDst,yDst,xSrc,ySrc,w,h,Color1,Color2: integer);
    39 procedure ImageOp_CCC(Bmp: TBitmap; x,y,w,h,Color0,Color1,Color2: integer);
    40 procedure SLine(ca: TCanvas; x0,x1,y: integer; cl: TColor);
    41 procedure DLine(ca: TCanvas; x0,x1,y: integer; cl0,cl1: TColor);
    42 procedure Frame(ca: TCanvas;x0,y0,x1,y1:integer;cl0,cl1:TColor);
    43 procedure RFrame(ca: TCanvas;x0,y0,x1,y1:integer;cl0,cl1:TColor);
    44 procedure CFrame(ca: TCanvas; x0,y0,x1,y1,Corner: integer; cl: TColor);
    45 procedure FrameImage(ca: TCanvas; src:TBitmap; x,y,width,height,xSrc,ySrc: integer; IsControl: boolean = false);
    46 procedure GlowFrame(dst: TBitmap; x0,y0,width,height: integer; cl: TColor);
    47 procedure InitOrnament;
    48 procedure InitCityMark(const T: TTexture);
    49 procedure Fill(ca: TCanvas;Left,Top,Width,Height,xOffset,yOffset: integer);
    50 procedure FillLarge(ca: TCanvas; x0,y0,x1,y1,xm: integer);
    51 procedure FillSeamless(ca: TCanvas;Left,Top,Width,Height,xOffset,yOffset: integer;const Texture: TBitmap);
    52 procedure FillRectSeamless(ca: TCanvas;x0,y0,x1,y1,xOffset,yOffset: integer;
    53   const Texture: TBitmap);
    54 procedure PaintBackground(Form: TForm; Left,Top,Width,Height: integer);
    55 procedure Corner(ca: TCanvas; x,y,Kind:integer; const T: TTexture);
    56 procedure BiColorTextOut(ca: TCanvas; clMain, clBack: TColor;
    57   x,y:integer; s:string);
    58 procedure LoweredTextOut(ca: TCanvas; cl: TColor; const T: TTexture;
    59   x,y:integer; s:string);
    60 function BiColorTextWidth(ca: TCanvas; s: string): integer;
    61 procedure RisedTextOut(ca: TCanvas; x,y:integer; s:string);
    62 procedure LightGradient(ca: TCanvas; x,y,width,Color:integer);
    63 procedure DarkGradient(ca: TCanvas; x,y,width,Kind:integer);
    64 procedure VLightGradient(ca: TCanvas; x,y,height,Color:integer);
    65 procedure VDarkGradient(ca: TCanvas; x,y,height,Kind:integer);
    66 procedure NumberBar(dst:TBitmap; x,y:integer; Cap:string; val: integer;
    67   const T: TTexture);
    68 procedure CountBar(dst:TBitmap; x,y,w:integer; Kind:integer; Cap:string;
    69   val: integer; const T: TTexture);
    70 procedure PaintProgressBar(ca: TCanvas; Kind,x,y,pos,Growth,max: integer;
    71   const T: TTexture);
    72 procedure PaintRelativeProgressBar(ca: TCanvas; Kind,x,y,size,pos,Growth,
    73   max: integer; IndicateComplete: boolean; const T: TTexture);
    74 procedure PaintLogo(ca: TCanvas; x,y,clLight,clShade: integer);
    75 function SetMainTextureByAge(Age: integer): boolean;
    76 
    77 const
    78 nGrExtmax=64;
    79 wMainTexture=640; hMainTexture=480;
    80 
    81 // template positions in Template.bmp
    82 xLogo=1; yLogo=1; wLogo=122; hLogo=23; // logo
    83 xBBook=1; yBBook=74; wBBook=143; hBBook=73; // big book
    84 xSBook=72; ySBook=37; wSBook=72; hSBook=36; // small book
    85 xNation=1; yNation=25;
    86 xCoal=1; yCoal=148;
    87 
    88 // Icons.bmp structure
    89 xSizeBig=56; ySizeBig=40;
    90 
    91 GlowRange=8;
    92 
    93 EmptySpaceColor=$101010;
    94 
    95 // template positions in System2.bmp
    96 xOrna=156; yOrna=1; wOrna=27; hOrna=26; // ornament
    97 
    98 // sound modes
    99 smOff=0; smOn=1; smOnAlt=2;
    100 
    101 // color matrix
    102 clkAge0=1; cliTexture=0; cliBevelLight=cliTexture+1; cliBevelShade=cliTexture+2;
    103 cliTextLight=cliTexture+3; cliTextShade=cliTexture+4; cliLitText=cliTexture+5;
    104 cliMark=cliTexture+6; cliDimmedText=cliTexture+7;
    105 cliRoad=8; cliHouse=cliRoad+1; cliImp=cliRoad+2; cliImpProject=cliRoad+3;
    106 cliPage=13; cliCover=cliPage+1;
    107 clkMisc=5; cliPaper=0; cliPaperText=1; cliPaperCaption=2;
    108 clkCity=6; cliPlains=0; cliPrairie=1; cliHills=2; cliTundra=3; cliWater=4;
    109 
    110 // LoadGraphicFile options
    111 gfNoError=$01; gfNoGamma=$02; gfJPG=$04;
    112 
    113 type
    114 TGrExtDescr=record {don't use dynamic strings here!}
    115   Name:string[31];
    116   Data,Mask:TBitmap;
    117   pixUsed: array[Byte] of Byte;
    118   end;
    119 TGrExtDescrSize=record {for size calculation only - must be the same as
    120   TGrExtDescr, but without pixUsed}
    121   Name:string[31];
    122   Data,Mask:TBitmap;
    123   end;
    124 
    125 TFontType=(ftNormal, ftSmall, ftTiny, ftCaption, ftButton);
    126 
    127 var
    128 Phrases, Phrases2, Sounds: TStringTable;
    129 nGrExt: integer;
    130 GrExt:array[0..nGrExtmax-1] of ^TGrExtDescr;
    131 HGrSystem, HGrSystem2, ClickFrameColor,SoundMode, MainTextureAge: integer;
    132 MainTexture: TTexture;
    133 Templates,Colors,Paper,BigImp,LogoBuffer: TBitmap;
    134 FullScreen,GenerateNames,InitOrnamentDone,Phrases2FallenBackToEnglish: boolean;
    135 
    136 UniFont: array[TFontType] of TFont;
     12  TTexture = record
     13    Image: TBitmap;
     14    clBevelLight, clBevelShade, clTextLight, clTextShade, clLitText, clMark,
     15      clPage, clCover: TColor end;
     16
     17    function ChangeResolution(x, y, bpp, freq: integer): boolean;
     18    procedure RestoreResolution;
     19    function Play(Item: string; Index: integer = -1): boolean;
     20    procedure PreparePlay(Item: string; Index: integer = -1);
     21    procedure EmptyMenu(MenuItems: TMenuItem; Keep: integer = 0);
     22    function turntoyear(Turn: integer): integer;
     23    function TurnToString(Turn: integer): string;
     24    function MovementToString(Movement: integer): string;
     25    procedure BtnFrame(ca: TCanvas; p: TRect; const T: TTexture);
     26    procedure EditFrame(ca: TCanvas; p: TRect; const T: TTexture);
     27    function HexStringToColor(s: string): integer;
     28    function LoadGraphicFile(bmp: TBitmap; Path: string;
     29      Options: integer = 0): boolean;
     30    function LoadLocalizedGraphicFile(bmp: TBitmap; Path: string;
     31      Options: integer = 0): boolean;
     32    function LoadGraphicSet(Name: string): integer;
     33    procedure Dump(dst: TBitmap; HGr, xDst, yDst, Width, Height, xGr,
     34      yGr: integer);
     35    procedure Sprite(Canvas: TCanvas; HGr, xDst, yDst, Width, Height, xGr,
     36      yGr: integer); overload;
     37    procedure Sprite(dst: TBitmap; HGr, xDst, yDst, Width, Height, xGr,
     38      yGr: integer); overload;
     39    procedure MakeBlue(dst: TBitmap; x, y, w, h: integer);
     40    procedure ImageOp_B(dst, Src: TBitmap; xDst, yDst, xSrc, ySrc, w,
     41      h: integer);
     42    procedure ImageOp_BCC(dst, Src: TBitmap; xDst, yDst, xSrc, ySrc, w, h,
     43      Color1, Color2: integer);
     44    procedure ImageOp_CCC(bmp: TBitmap; x, y, w, h, Color0, Color1,
     45      Color2: integer);
     46    procedure SLine(ca: TCanvas; x0, x1, y: integer; cl: TColor);
     47    procedure DLine(ca: TCanvas; x0, x1, y: integer; cl0, cl1: TColor);
     48    procedure Frame(ca: TCanvas; x0, y0, x1, y1: integer; cl0, cl1: TColor);
     49    procedure RFrame(ca: TCanvas; x0, y0, x1, y1: integer; cl0, cl1: TColor);
     50    procedure CFrame(ca: TCanvas; x0, y0, x1, y1, Corner: integer; cl: TColor);
     51    procedure FrameImage(ca: TCanvas; Src: TBitmap;
     52      x, y, Width, Height, xSrc, ySrc: integer; IsControl: boolean = false);
     53    procedure GlowFrame(dst: TBitmap; x0, y0, Width, Height: integer;
     54      cl: TColor);
     55    procedure InitOrnament;
     56    procedure InitCityMark(const T: TTexture);
     57    procedure Fill(ca: TCanvas; Left, Top, Width, Height, xOffset,
     58      yOffset: integer);
     59    procedure FillLarge(ca: TCanvas; x0, y0, x1, y1, xm: integer);
     60    procedure FillSeamless(ca: TCanvas; Left, Top, Width, Height, xOffset,
     61      yOffset: integer; const Texture: TBitmap);
     62    procedure FillRectSeamless(ca: TCanvas;
     63      x0, y0, x1, y1, xOffset, yOffset: integer; const Texture: TBitmap);
     64    procedure PaintBackground(Form: TForm; Left, Top, Width, Height: integer);
     65    procedure Corner(ca: TCanvas; x, y, Kind: integer; const T: TTexture);
     66    procedure BiColorTextOut(ca: TCanvas; clMain, clBack: TColor; x, y: integer;
     67      s: string);
     68    procedure LoweredTextOut(ca: TCanvas; cl: TColor; const T: TTexture;
     69      x, y: integer; s: string);
     70    function BiColorTextWidth(ca: TCanvas; s: string): integer;
     71    procedure RisedTextOut(ca: TCanvas; x, y: integer; s: string);
     72    procedure LightGradient(ca: TCanvas; x, y, Width, Color: integer);
     73    procedure DarkGradient(ca: TCanvas; x, y, Width, Kind: integer);
     74    procedure VLightGradient(ca: TCanvas; x, y, Height, Color: integer);
     75    procedure VDarkGradient(ca: TCanvas; x, y, Height, Kind: integer);
     76    procedure NumberBar(dst: TBitmap; x, y: integer; Cap: string; val: integer;
     77      const T: TTexture);
     78    procedure CountBar(dst: TBitmap; x, y, w: integer; Kind: integer;
     79      Cap: string; val: integer; const T: TTexture);
     80    procedure PaintProgressBar(ca: TCanvas;
     81      Kind, x, y, pos, Growth, max: integer; const T: TTexture);
     82    procedure PaintRelativeProgressBar(ca: TCanvas;
     83      Kind, x, y, size, pos, Growth, max: integer; IndicateComplete: boolean;
     84      const T: TTexture);
     85    procedure PaintLogo(ca: TCanvas; x, y, clLight, clShade: integer);
     86    function SetMainTextureByAge(Age: integer): boolean;
     87
     88  const
     89    nGrExtmax = 64;
     90    wMainTexture = 640;
     91    hMainTexture = 480;
     92
     93    // template positions in Template.bmp
     94    xLogo = 1;
     95    yLogo = 1;
     96    wLogo = 122;
     97    hLogo = 23; // logo
     98    xBBook = 1;
     99    yBBook = 74;
     100    wBBook = 143;
     101    hBBook = 73; // big book
     102    xSBook = 72;
     103    ySBook = 37;
     104    wSBook = 72;
     105    hSBook = 36; // small book
     106    xNation = 1;
     107    yNation = 25;
     108    xCoal = 1;
     109    yCoal = 148;
     110
     111    // Icons.bmp structure
     112    xSizeBig = 56;
     113    ySizeBig = 40;
     114
     115    GlowRange = 8;
     116
     117    EmptySpaceColor = $101010;
     118
     119    // template positions in System2.bmp
     120    xOrna = 156;
     121    yOrna = 1;
     122    wOrna = 27;
     123    hOrna = 26; // ornament
     124
     125    // sound modes
     126    smOff = 0;
     127    smOn = 1;
     128    smOnAlt = 2;
     129
     130    // color matrix
     131    clkAge0 = 1;
     132    cliTexture = 0;
     133    cliBevelLight = cliTexture + 1;
     134    cliBevelShade = cliTexture + 2;
     135    cliTextLight = cliTexture + 3;
     136    cliTextShade = cliTexture + 4;
     137    cliLitText = cliTexture + 5;
     138    cliMark = cliTexture + 6;
     139    cliDimmedText = cliTexture + 7;
     140    cliRoad = 8;
     141    cliHouse = cliRoad + 1;
     142    cliImp = cliRoad + 2;
     143    cliImpProject = cliRoad + 3;
     144    cliPage = 13;
     145    cliCover = cliPage + 1;
     146    clkMisc = 5;
     147    cliPaper = 0;
     148    cliPaperText = 1;
     149    cliPaperCaption = 2;
     150    clkCity = 6;
     151    cliPlains = 0;
     152    cliPrairie = 1;
     153    cliHills = 2;
     154    cliTundra = 3;
     155    cliWater = 4;
     156
     157    // LoadGraphicFile options
     158    gfNoError = $01;
     159    gfNoGamma = $02;
     160    gfJPG = $04;
     161
     162  type
     163    TGrExtDescr = record { don't use dynamic strings here! }
     164      Name: string[31];
     165      Data, Mask: TBitmap;
     166      pixUsed: array [Byte] of Byte;
     167    end;
     168
     169    TGrExtDescrSize = record { for size calculation only - must be the same as
     170        TGrExtDescr, but without pixUsed }
     171      Name: string[31];
     172      Data, Mask: TBitmap;
     173    end;
     174
     175    TFontType = (ftNormal, ftSmall, ftTiny, ftCaption, ftButton);
     176
     177  var
     178    Phrases, Phrases2, Sounds: TStringTable;
     179    nGrExt: integer;
     180    GrExt: array [0 .. nGrExtmax - 1] of ^TGrExtDescr;
     181    HGrSystem, HGrSystem2, ClickFrameColor, SoundMode, MainTextureAge: integer;
     182    MainTexture: TTexture;
     183    Templates, Colors, Paper, BigImp, LogoBuffer: TBitmap;
     184    FullScreen, GenerateNames, InitOrnamentDone,
     185      Phrases2FallenBackToEnglish: boolean;
     186
     187    UniFont: array [TFontType] of TFont;
    137188
    138189implementation
     
    141192  Directories, Sound, ButtonBase, ButtonA, ButtonB,
    142193
    143   Registry,JPEG;
    144 
    145 var
    146 StartResolution: TDeviceMode;
    147 ResolutionChanged: boolean;
    148 
    149 Gamma: integer; // global gamma correction (cent)
    150 GammaLUT: array[0..255] of byte;
    151 
    152 
    153 function ChangeResolution(x,y,bpp,freq: integer): boolean;
    154 var
    155 DevMode: TDeviceMode;
    156 begin
    157 EnumDisplaySettings(nil, 0, DevMode);
    158 DevMode.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL
    159   or DM_DISPLAYFREQUENCY;
    160 DevMode.dmPelsWidth:=x;
    161 DevMode.dmPelsHeight:=y;
    162 DevMode.dmBitsPerPel:=bpp;
    163 DevMode.dmDisplayFrequency:=freq;
    164 result:= ChangeDisplaySettings(DevMode,0)=DISP_CHANGE_SUCCESSFUL;
    165 if result then
    166   ResolutionChanged:=true;
     194  Registry, JPEG;
     195
     196var
     197  StartResolution: TDeviceMode;
     198  ResolutionChanged: boolean;
     199
     200  Gamma: integer; // global gamma correction (cent)
     201  GammaLUT: array [0 .. 255] of Byte;
     202
     203function ChangeResolution(x, y, bpp, freq: integer): boolean;
     204var
     205  DevMode: TDeviceMode;
     206begin
     207  EnumDisplaySettings(nil, 0, DevMode);
     208  DevMode.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL or
     209    DM_DISPLAYFREQUENCY;
     210  DevMode.dmPelsWidth := x;
     211  DevMode.dmPelsHeight := y;
     212  DevMode.dmBitsPerPel := bpp;
     213  DevMode.dmDisplayFrequency := freq;
     214  result := ChangeDisplaySettings(DevMode, 0) = DISP_CHANGE_SUCCESSFUL;
     215  if result then
     216    ResolutionChanged := true;
    167217end;
    168218
    169219procedure RestoreResolution;
    170220begin
    171 if ResolutionChanged then
    172   ChangeDisplaySettings(StartResolution,0);
    173 ResolutionChanged:=false;
    174 end;
    175 
    176 function Play(Item: string; Index: integer =-1): boolean;
     221  if ResolutionChanged then
     222    ChangeDisplaySettings(StartResolution, 0);
     223  ResolutionChanged := false;
     224end;
     225
     226function Play(Item: string; Index: integer = -1): boolean;
    177227{$IFNDEF DEBUG}
    178228var
    179 WAVFileName: string;
     229  WAVFileName: string;
    180230{$ENDIF}
    181231begin
    182232{$IFNDEF DEBUG}
    183 if (Sounds=nil) or (SoundMode=smOff) or (Item='') then
    184   begin result:=true; exit; end;
    185 WAVFileName:=Sounds.Lookup(Item, Index);
    186 assert(WAVFileName[1]<>'[');
    187 result:=(WAVFileName<>'') and (WAVFileName[1]<>'[') and (WAVFileName<>'*');
    188 if result then
    189 //  SndPlaySound(pchar(HomeDir+'Sounds\'+WAVFileName+'.wav'),SND_ASYNC)
    190   PlaySound(HomeDir+'Sounds\'+WAVFileName)
     233  if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
     234  begin
     235    result := true;
     236    exit;
     237  end;
     238  WAVFileName := Sounds.Lookup(Item, Index);
     239  assert(WAVFileName[1] <> '[');
     240  result := (WAVFileName <> '') and (WAVFileName[1] <> '[') and
     241    (WAVFileName <> '*');
     242  if result then
     243    // SndPlaySound(pchar(HomeDir+'Sounds\'+WAVFileName+'.wav'),SND_ASYNC)
     244    PlaySound(HomeDir + 'Sounds\' + WAVFileName)
    191245{$ENDIF}
    192246end;
    193247
    194 procedure PreparePlay(Item: string; Index: integer =-1);
     248procedure PreparePlay(Item: string; Index: integer = -1);
    195249{$IFNDEF DEBUG}
    196250var
    197 WAVFileName: string;
     251  WAVFileName: string;
    198252{$ENDIF}
    199253begin
    200254{$IFNDEF DEBUG}
    201 if (Sounds=nil) or (SoundMode=smOff) or (Item='') then exit;
    202 WAVFileName:=Sounds.Lookup(Item, Index);
    203 assert(WAVFileName[1]<>'[');
    204 if (WAVFileName<>'') and (WAVFileName[1]<>'[') and (WAVFileName<>'*') then
    205   PrepareSound(HomeDir+'Sounds\'+WAVFileName)
     255  if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
     256    exit;
     257  WAVFileName := Sounds.Lookup(Item, Index);
     258  assert(WAVFileName[1] <> '[');
     259  if (WAVFileName <> '') and (WAVFileName[1] <> '[') and (WAVFileName <> '*')
     260  then
     261    PrepareSound(HomeDir + 'Sounds\' + WAVFileName)
    206262{$ENDIF}
    207263end;
     
    209265procedure EmptyMenu(MenuItems: TMenuItem; Keep: integer = 0);
    210266var
    211 m: TMenuItem;
    212 begin
    213 while MenuItems.Count>Keep do
    214   begin
    215   m:=MenuItems[MenuItems.Count-1];
    216   MenuItems.Delete(MenuItems.Count-1);
    217   m.Free;
     267  m: TMenuItem;
     268begin
     269  while MenuItems.Count > Keep do
     270  begin
     271    m := MenuItems[MenuItems.Count - 1];
     272    MenuItems.Delete(MenuItems.Count - 1);
     273    m.Free;
    218274  end;
    219275end;
     
    221277function turntoyear(Turn: integer): integer;
    222278var
    223 i: integer;
    224 begin
    225 result:=-4000;
    226 for i:=1 to Turn do
    227   if result<-1000 then inc(result,50)       // 0..60
    228   else if result<0 then inc(result,25)      // 60..100
    229   else if result<1500 then inc(result,20)   // 100..175
    230   else if result<1750 then inc(result,10)   // 175..200
    231   else if result<1850 then inc(result,2)    // 200..250
    232   else inc(result);
     279  i: integer;
     280begin
     281  result := -4000;
     282  for i := 1 to Turn do
     283    if result < -1000 then
     284      inc(result, 50) // 0..60
     285    else if result < 0 then
     286      inc(result, 25) // 60..100
     287    else if result < 1500 then
     288      inc(result, 20) // 100..175
     289    else if result < 1750 then
     290      inc(result, 10) // 175..200
     291    else if result < 1850 then
     292      inc(result, 2) // 200..250
     293    else
     294      inc(result);
    233295end;
    234296
    235297function TurnToString(Turn: integer): string;
    236298var
    237 year: integer;
    238 begin
    239 if GenerateNames then
    240   begin
    241   year:=turntoyear(Turn);
    242   if year<0 then result:=Format(Phrases.Lookup('BC'),[-year])
    243   else result:=Format(Phrases.Lookup('AD'),[year]);
    244   end
    245 else result:=IntToStr(Turn)
     299  year: integer;
     300begin
     301  if GenerateNames then
     302  begin
     303    year := turntoyear(Turn);
     304    if year < 0 then
     305      result := Format(Phrases.Lookup('BC'), [-year])
     306    else
     307      result := Format(Phrases.Lookup('AD'), [year]);
     308  end
     309  else
     310    result := IntToStr(Turn)
    246311end;
    247312
    248313function MovementToString(Movement: integer): string;
    249314begin
    250 if Movement>=1000 then
    251   begin
    252   result:=char(48+Movement div 1000);
    253   Movement:=Movement mod 1000;
    254   end
    255 else result:='';
    256 result:=result+char(48+Movement div 100);
    257 Movement:=Movement mod 100;
    258 if Movement>0 then
    259   begin
    260   result:=result+'.'+char(48+Movement div 10);
    261   Movement:=Movement mod 10;
    262   if Movement>0 then
    263     result:=result+char(48+Movement);
    264   end
    265 end;
    266 
    267 procedure BtnFrame(ca:TCanvas;p:TRect;const T: TTexture);
    268 begin
    269 RFrame(ca,p.Left-1,p.Top-1,p.Right,p.Bottom,T.clBevelShade,T.clBevelLight)
    270 end;
    271 
    272 procedure EditFrame(ca:TCanvas;p:TRect;const T: TTexture);
    273 begin
    274 Frame(ca,p.Left-1,p.Top-1,p.Right,p.Bottom,$000000,$000000);
    275 Frame(ca,p.Left-2,p.Top-2,p.Right+1,p.Bottom+1,$000000,$000000);
    276 Frame(ca,p.Left-3,p.Top-3,p.Right+2,p.Bottom+1,$000000,$000000);
    277 RFrame(ca,p.Left-4,p.Top-4,p.Right+3,p.Bottom+2,T.clBevelShade,T.clBevelLight)
     315  if Movement >= 1000 then
     316  begin
     317    result := char(48 + Movement div 1000);
     318    Movement := Movement mod 1000;
     319  end
     320  else
     321    result := '';
     322  result := result + char(48 + Movement div 100);
     323  Movement := Movement mod 100;
     324  if Movement > 0 then
     325  begin
     326    result := result + '.' + char(48 + Movement div 10);
     327    Movement := Movement mod 10;
     328    if Movement > 0 then
     329      result := result + char(48 + Movement);
     330  end
     331end;
     332
     333procedure BtnFrame(ca: TCanvas; p: TRect; const T: TTexture);
     334begin
     335  RFrame(ca, p.Left - 1, p.Top - 1, p.Right, p.Bottom, T.clBevelShade,
     336    T.clBevelLight)
     337end;
     338
     339procedure EditFrame(ca: TCanvas; p: TRect; const T: TTexture);
     340begin
     341  Frame(ca, p.Left - 1, p.Top - 1, p.Right, p.Bottom, $000000, $000000);
     342  Frame(ca, p.Left - 2, p.Top - 2, p.Right + 1, p.Bottom + 1, $000000, $000000);
     343  Frame(ca, p.Left - 3, p.Top - 3, p.Right + 2, p.Bottom + 1, $000000, $000000);
     344  RFrame(ca, p.Left - 4, p.Top - 4, p.Right + 3, p.Bottom + 2, T.clBevelShade,
     345    T.clBevelLight)
    278346end;
    279347
     
    282350  function HexCharToInt(x: char): integer;
    283351  begin
    284   case x of
    285     '0'..'9': result:=ord(x)-48;
    286     'A'..'F': result:=ord(x)-65+10;
    287     'a'..'f': result:=ord(x)-97+10;
    288     else result:=0
     352    case x of
     353      '0' .. '9':
     354        result := ord(x) - 48;
     355      'A' .. 'F':
     356        result := ord(x) - 65 + 10;
     357      'a' .. 'f':
     358        result := ord(x) - 97 + 10;
     359    else
     360      result := 0
    289361    end
    290362  end;
    291363
    292364begin
    293 while (Length(s)>0) and (s[1]=' ') do Delete(s,1,1);
    294 s:=s+'000000';
    295 if Gamma=100 then
    296   result:=$10*HexCharToInt(s[1])+$1*HexCharToInt(s[2])
    297     +$1000*HexCharToInt(s[3])+$100*HexCharToInt(s[4])
    298     +$100000*HexCharToInt(s[5])+$10000*HexCharToInt(s[6])
    299 else result:=GammaLUT[$10*HexCharToInt(s[1])+HexCharToInt(s[2])]
    300   +$100*GammaLUT[$10*HexCharToInt(s[3])+HexCharToInt(s[4])]
    301   +$10000*GammaLUT[$10*HexCharToInt(s[5])+HexCharToInt(s[6])];
     365  while (Length(s) > 0) and (s[1] = ' ') do
     366    Delete(s, 1, 1);
     367  s := s + '000000';
     368  if Gamma = 100 then
     369    result := $10 * HexCharToInt(s[1]) + $1 * HexCharToInt(s[2]) + $1000 *
     370      HexCharToInt(s[3]) + $100 * HexCharToInt(s[4]) + $100000 *
     371      HexCharToInt(s[5]) + $10000 * HexCharToInt(s[6])
     372  else
     373    result := GammaLUT[$10 * HexCharToInt(s[1]) + HexCharToInt(s[2])] + $100 *
     374      GammaLUT[$10 * HexCharToInt(s[3]) + HexCharToInt(s[4])] + $10000 *
     375      GammaLUT[$10 * HexCharToInt(s[5]) + HexCharToInt(s[6])];
    302376end;
    303377
    304378procedure ApplyGamma(Start, Stop: pbyte);
    305379begin
    306 while integer(Start)<integer(Stop) do
    307   begin Start^:=GammaLUT[Start^]; inc(Start); end;
     380  while integer(Start) < integer(Stop) do
     381  begin
     382    Start^ := GammaLUT[Start^];
     383    inc(Start);
     384  end;
    308385end;
    309386
    310387function LoadGraphicFile(bmp: TBitmap; Path: string; Options: integer): boolean;
    311388type
    312 TLine=array[0..9999,0..2] of Byte;
    313 var
    314 FirstLine, LastLine: ^TLine;
    315 jtex: tjpegimage;
    316 begin
    317 result:=true;
    318 if Options and gfJPG<>0 then
    319   begin
    320   jtex:=tjpegimage.create;
    321   try
    322     jtex.loadfromfile(Path+'.jpg');
    323   except
    324     result:=false;
    325     end;
    326   if result then
    327     begin
    328     if Options and gfNoGamma=0 then
    329       bmp.PixelFormat:=pf24bit;
    330     bmp.width:=jtex.width; bmp.height:=jtex.height;
    331     bmp.canvas.draw(0,0,jtex);
    332     end;
    333   jtex.free;
    334   end
    335 else
    336   begin
    337   try
    338     bmp.LoadFromFile(Path+'.bmp');
    339   except
    340     result:=false;
    341     end;
    342   if result then
    343     begin
    344     if Options and gfNoGamma=0 then
    345       bmp.PixelFormat:=pf24bit;
     389  TLine = array [0 .. 9999, 0 .. 2] of Byte;
     390var
     391  FirstLine, LastLine: ^TLine;
     392  jtex: tjpegimage;
     393begin
     394  result := true;
     395  if Options and gfJPG <> 0 then
     396  begin
     397    jtex := tjpegimage.create;
     398    try
     399      jtex.loadfromfile(Path + '.jpg');
     400    except
     401      result := false;
     402    end;
     403    if result then
     404    begin
     405      if Options and gfNoGamma = 0 then
     406        bmp.PixelFormat := pf24bit;
     407      bmp.Width := jtex.Width;
     408      bmp.Height := jtex.Height;
     409      bmp.Canvas.draw(0, 0, jtex);
     410    end;
     411    jtex.Free;
     412  end
     413  else
     414  begin
     415    try
     416      bmp.loadfromfile(Path + '.bmp');
     417    except
     418      result := false;
     419    end;
     420    if result then
     421    begin
     422      if Options and gfNoGamma = 0 then
     423        bmp.PixelFormat := pf24bit;
    346424    end
    347425  end;
    348 if not result then
    349   begin
    350   if Options and gfNoError=0 then
    351     Application.MessageBox(PChar(Format(Phrases.Lookup('FILENOTFOUND'),[Path])), 'C-evo', 0);
    352   exit;
    353   end;
    354 if (Options and gfNoGamma=0) and (Gamma<>100) then
    355   begin
    356   FirstLine:=bmp.ScanLine[0];
    357   LastLine:=bmp.ScanLine[bmp.Height-1];
    358   if integer(FirstLine)<integer(LastLine) then
    359     ApplyGamma(pointer(FirstLine), @LastLine[bmp.Width])
    360   else ApplyGamma(pointer(LastLine), @FirstLine[bmp.Width])
    361   end
    362 end;
    363 
    364 function LoadLocalizedGraphicFile(bmp: TBitmap; Path: string; Options: integer): boolean;
     426  if not result then
     427  begin
     428    if Options and gfNoError = 0 then
     429      Application.MessageBox(PChar(Format(Phrases.Lookup('FILENOTFOUND'),
     430        [Path])), 'C-evo', 0);
     431    exit;
     432  end;
     433  if (Options and gfNoGamma = 0) and (Gamma <> 100) then
     434  begin
     435    FirstLine := bmp.ScanLine[0];
     436    LastLine := bmp.ScanLine[bmp.Height - 1];
     437    if integer(FirstLine) < integer(LastLine) then
     438      ApplyGamma(pointer(FirstLine), @LastLine[bmp.Width])
     439    else
     440      ApplyGamma(pointer(LastLine), @FirstLine[bmp.Width])
     441  end
     442end;
     443
     444function LoadLocalizedGraphicFile(bmp: TBitmap; Path: string;
     445  Options: integer): boolean;
    365446type
    366 TLine=array[0..9999,0..2] of Byte;
    367 var
    368 FirstLine, LastLine: ^TLine;
    369 jtex: tjpegimage;
    370 begin
    371 result:=true;
    372 if Options and gfJPG<>0 then
    373   begin
    374   jtex:=tjpegimage.create;
    375   try
    376     jtex.loadfromfile(LocalizedFilePath(Path+'.jpg'));
    377   except
    378     result:=false;
    379     end;
    380   if result then
    381     begin
    382     if Options and gfNoGamma=0 then
    383       bmp.PixelFormat:=pf24bit;
    384     bmp.width:=jtex.width; bmp.height:=jtex.height;
    385     bmp.canvas.draw(0,0,jtex);
    386     end;
    387   jtex.free;
    388   end
    389 else
    390   begin
    391   try
    392     bmp.LoadFromFile(LocalizedFilePath(Path+'.bmp'));
    393   except
    394     result:=false;
    395     end;
    396   if result then
    397     begin
    398     if Options and gfNoGamma=0 then
    399       bmp.PixelFormat:=pf24bit;
     447  TLine = array [0 .. 9999, 0 .. 2] of Byte;
     448var
     449  FirstLine, LastLine: ^TLine;
     450  jtex: tjpegimage;
     451begin
     452  result := true;
     453  if Options and gfJPG <> 0 then
     454  begin
     455    jtex := tjpegimage.create;
     456    try
     457      jtex.loadfromfile(LocalizedFilePath(Path + '.jpg'));
     458    except
     459      result := false;
     460    end;
     461    if result then
     462    begin
     463      if Options and gfNoGamma = 0 then
     464        bmp.PixelFormat := pf24bit;
     465      bmp.Width := jtex.Width;
     466      bmp.Height := jtex.Height;
     467      bmp.Canvas.draw(0, 0, jtex);
     468    end;
     469    jtex.Free;
     470  end
     471  else
     472  begin
     473    try
     474      bmp.loadfromfile(LocalizedFilePath(Path + '.bmp'));
     475    except
     476      result := false;
     477    end;
     478    if result then
     479    begin
     480      if Options and gfNoGamma = 0 then
     481        bmp.PixelFormat := pf24bit;
    400482    end
    401483  end;
    402 if not result then
    403   begin
    404   if Options and gfNoError=0 then
    405     Application.MessageBox(PChar(Format(Phrases.Lookup('FILENOTFOUND'),[Path])), 'C-evo', 0);
    406   exit;
    407   end;
    408 if (Options and gfNoGamma=0) and (Gamma<>100) then
    409   begin
    410   FirstLine:=bmp.ScanLine[0];
    411   LastLine:=bmp.ScanLine[bmp.Height-1];
    412   if integer(FirstLine)<integer(LastLine) then
    413     ApplyGamma(pointer(FirstLine), @LastLine[bmp.Width])
    414   else ApplyGamma(pointer(LastLine), @FirstLine[bmp.Width])
     484  if not result then
     485  begin
     486    if Options and gfNoError = 0 then
     487      Application.MessageBox(PChar(Format(Phrases.Lookup('FILENOTFOUND'),
     488        [Path])), 'C-evo', 0);
     489    exit;
     490  end;
     491  if (Options and gfNoGamma = 0) and (Gamma <> 100) then
     492  begin
     493    FirstLine := bmp.ScanLine[0];
     494    LastLine := bmp.ScanLine[bmp.Height - 1];
     495    if integer(FirstLine) < integer(LastLine) then
     496      ApplyGamma(pointer(FirstLine), @LastLine[bmp.Width])
     497    else
     498      ApplyGamma(pointer(LastLine), @FirstLine[bmp.Width])
    415499  end
    416500end;
     
    418502function LoadGraphicSet(Name: string): integer;
    419503type
    420 TLine=array[0..999,0..2] of Byte;
    421 var
    422 i,x,y,xmax,OriginalColor: integer;
    423 FileName: string;
    424 Source: TBitmap;
    425 DataLine, MaskLine: ^TLine;
    426 begin
    427 i:=0;
    428 while (i<nGrExt) and (GrExt[i].Name<>Name) do inc(i);
    429 result:=i;
    430 if i=nGrExt then
    431   begin
    432   FileName:=HomeDir+'Graphics\'+Name;
    433   Source:=TBitmap.Create;
    434   try
    435     Source.LoadFromFile(FileName+'.bmp')
    436   except
    437     result:=-1;
    438     Application.MessageBox(PChar(Format(Phrases.Lookup('FILENOTFOUND'),['Graphics\'+Name])), 'C-evo', 0);
    439     exit;
    440     end;
    441 
    442   GetMem(GrExt[nGrExt],SizeOf(TGrExtDescrSize)+Source.Height div 49 *10);
    443   GrExt[nGrExt].Name:=Name;
    444 
    445   xmax:=Source.Width-1; // allows 4-byte access even for last pixel
    446   if xmax>970 then xmax:=970;
    447 
    448   GrExt[nGrExt].Data:=Source;
    449   GrExt[nGrExt].Data.PixelFormat:=pf24bit;
    450   GrExt[nGrExt].Mask:=TBitmap.Create;
    451   GrExt[nGrExt].Mask.PixelFormat:=pf24bit;
    452   GrExt[nGrExt].Mask.Width:=Source.Width;
    453   GrExt[nGrExt].Mask.Height:=Source.Height;
    454 
    455   for y:=0 to Source.Height-1 do
    456     begin
    457     DataLine:=GrExt[nGrExt].Data.ScanLine[y];
    458     MaskLine:=GrExt[nGrExt].Mask.ScanLine[y];
    459     for x:=0 to xmax-1 do
     504  TLine = array [0 .. 999, 0 .. 2] of Byte;
     505var
     506  i, x, y, xmax, OriginalColor: integer;
     507  FileName: string;
     508  Source: TBitmap;
     509  DataLine, MaskLine: ^TLine;
     510begin
     511  i := 0;
     512  while (i < nGrExt) and (GrExt[i].Name <> Name) do
     513    inc(i);
     514  result := i;
     515  if i = nGrExt then
     516  begin
     517    FileName := HomeDir + 'Graphics\' + Name;
     518    Source := TBitmap.create;
     519    try
     520      Source.loadfromfile(FileName + '.bmp')
     521    except
     522      result := -1;
     523      Application.MessageBox(PChar(Format(Phrases.Lookup('FILENOTFOUND'),
     524        ['Graphics\' + Name])), 'C-evo', 0);
     525      exit;
     526    end;
     527
     528    GetMem(GrExt[nGrExt], SizeOf(TGrExtDescrSize) + Source.Height div 49 * 10);
     529    GrExt[nGrExt].Name := Name;
     530
     531    xmax := Source.Width - 1; // allows 4-byte access even for last pixel
     532    if xmax > 970 then
     533      xmax := 970;
     534
     535    GrExt[nGrExt].Data := Source;
     536    GrExt[nGrExt].Data.PixelFormat := pf24bit;
     537    GrExt[nGrExt].Mask := TBitmap.create;
     538    GrExt[nGrExt].Mask.PixelFormat := pf24bit;
     539    GrExt[nGrExt].Mask.Width := Source.Width;
     540    GrExt[nGrExt].Mask.Height := Source.Height;
     541
     542    for y := 0 to Source.Height - 1 do
     543    begin
     544      DataLine := GrExt[nGrExt].Data.ScanLine[y];
     545      MaskLine := GrExt[nGrExt].Mask.ScanLine[y];
     546      for x := 0 to xmax - 1 do
    460547      begin
    461       OriginalColor:=Cardinal((@DataLine[x])^) and $FFFFFF;
    462       if (OriginalColor=$FF00FF) or (OriginalColor=$7F007F) then
     548        OriginalColor := Cardinal((@DataLine[x])^) and $FFFFFF;
     549        if (OriginalColor = $FF00FF) or (OriginalColor = $7F007F) then
    463550        begin // transparent
    464         Cardinal((@MaskLine[x])^):=$FFFFFF;
    465         Cardinal((@DataLine[x])^):=Cardinal((@DataLine[x])^) and $FF000000
     551          Cardinal((@MaskLine[x])^) := $FFFFFF;
     552          Cardinal((@DataLine[x])^) := Cardinal((@DataLine[x])^) and $FF000000
    466553        end
    467       else
     554        else
    468555        begin
    469         Cardinal((@MaskLine[x])^):=$000000; // non-transparent
    470         if Gamma<>100 then
     556          Cardinal((@MaskLine[x])^) := $000000; // non-transparent
     557          if Gamma <> 100 then
    471558          begin
    472           DataLine[x,0]:=GammaLUT[DataLine[x,0]];
    473           DataLine[x,1]:=GammaLUT[DataLine[x,1]];
    474           DataLine[x,2]:=GammaLUT[DataLine[x,2]];
     559            DataLine[x, 0] := GammaLUT[DataLine[x, 0]];
     560            DataLine[x, 1] := GammaLUT[DataLine[x, 1]];
     561            DataLine[x, 2] := GammaLUT[DataLine[x, 2]];
    475562          end
    476563        end
     
    478565    end;
    479566
    480   FillChar(GrExt[nGrExt].pixUsed,GrExt[nGrExt].Data.Height div 49 *10,0);
    481   inc(nGrExt)
    482   end
    483 end;
    484 
    485 procedure Dump(dst:TBitmap; HGr,xDst,yDst,Width,Height,xGr,yGr: integer);
    486 begin
    487 BitBlt(dst.Canvas.Handle,xDst,yDst,Width,Height,
    488   GrExt[HGr].Data.Canvas.Handle,xGr,yGr,SRCCOPY);
    489 end;
    490 
    491 procedure MakeBlue(Dst: TBitmap; x,y,w,h: integer);
     567    FillChar(GrExt[nGrExt].pixUsed, GrExt[nGrExt].Data.Height div 49 * 10, 0);
     568    inc(nGrExt)
     569  end
     570end;
     571
     572procedure Dump(dst: TBitmap; HGr, xDst, yDst, Width, Height, xGr, yGr: integer);
     573begin
     574  BitBlt(dst.Canvas.Handle, xDst, yDst, Width, Height,
     575    GrExt[HGr].Data.Canvas.Handle, xGr, yGr, SRCCOPY);
     576end;
     577
     578procedure MakeBlue(dst: TBitmap; x, y, w, h: integer);
    492579type
    493 TLine=array[0..99999,0..2] of Byte;
    494 PLine=^TLine;
    495 
    496   procedure BlueLine(line: PLine; length: integer);
     580  TLine = array [0 .. 99999, 0 .. 2] of Byte;
     581  PLine = ^TLine;
     582
     583  procedure BlueLine(line: PLine; Length: integer);
    497584  var
     585    i: integer;
     586  begin
     587    for i := 0 to Length - 1 do
     588    begin
     589      line[i, 0] := line[i, 0] div 2;
     590      line[i, 1] := line[i, 1] div 2;
     591      line[i, 2] := line[i, 2] div 2;
     592    end
     593  end;
     594
     595var
    498596  i: integer;
    499   begin
    500   for i:=0 to length-1 do
    501     begin
    502     line[i,0]:=line[i,0] div 2;
    503     line[i,1]:=line[i,1] div 2;
    504     line[i,2]:=line[i,2] div 2;
    505     end
    506   end;
    507 
    508 var
    509 i: integer;
    510 begin
    511 for i:=0 to h-1 do
    512   BlueLine(@(PLine(Dst.ScanLine[y+i])[x]),w)
    513 end;
    514 
    515 procedure ImageOp_B(Dst,Src: TBitmap; xDst,yDst,xSrc,ySrc,w,h: integer);
     597begin
     598  for i := 0 to h - 1 do
     599    BlueLine(@(PLine(dst.ScanLine[y + i])[x]), w)
     600end;
     601
     602procedure ImageOp_B(dst, Src: TBitmap; xDst, yDst, xSrc, ySrc, w, h: integer);
    516603// Src is template
    517604// X channel = background amp (old Dst content), 128=original brightness
    518605type
    519 TPixel=array[0..2] of Byte;
    520 var
    521 i,Brightness,test: integer;
    522 PixelSrc: ^byte;
    523 PixelDst: ^TPixel;
    524 begin
    525 assert(Src.PixelFormat=pf8bit);
    526 assert(Dst.PixelFormat=pf24bit);
    527 if xDst<0 then
    528   begin w:=w+xDst; xSrc:=xSrc-xDst; xDst:=0; end;
    529 if yDst<0 then
    530   begin h:=h+yDst; ySrc:=ySrc-yDst; yDst:=0; end;
    531 if xDst+w>Dst.Width then
    532   w:=Dst.Width-xDst;
    533 if yDst+h>Dst.Height then
    534   h:=Dst.Height-yDst;
    535 if (w<0) or (h<0) then
    536   exit;
    537 
    538 h:=yDst+h;
    539 while yDst<h do
    540   begin
    541   PixelDst:=pointer(integer(Dst.ScanLine[yDst])+3*xDst);
    542   PixelSrc:=pointer(integer(Src.ScanLine[ySrc])+xSrc);
    543   for i:=0 to w-1 do
    544     begin
    545     Brightness:=PixelSrc^;
    546     test:=(PixelDst[2]*Brightness) shr 7;
    547     if test>=256 then PixelDst[2]:=255
    548     else PixelDst[2]:=test; // Red
    549     test:=(PixelDst[1]*Brightness) shr 7;
    550     if test>=256 then PixelDst[1]:=255
    551     else PixelDst[1]:=test; // Green
    552     test:=(PixelDst[0]*Brightness) shr 7;
    553     if test>=256 then PixelDst[2]:=255
    554     else PixelDst[0]:=test; // Blue
    555     PixelDst:=pointer(integer(PixelDst)+3);
    556     PixelSrc:=pointer(integer(PixelSrc)+1);
    557     end;
    558   inc(yDst);
    559   inc(ySrc);
    560   end
    561 end;
    562 
    563 procedure ImageOp_BCC(Dst,Src: TBitmap; xDst,yDst,xSrc,ySrc,w,h,Color1,Color2: integer);
     606  TPixel = array [0 .. 2] of Byte;
     607var
     608  i, Brightness, test: integer;
     609  PixelSrc: ^Byte;
     610  PixelDst: ^TPixel;
     611begin
     612  assert(Src.PixelFormat = pf8bit);
     613  assert(dst.PixelFormat = pf24bit);
     614  if xDst < 0 then
     615  begin
     616    w := w + xDst;
     617    xSrc := xSrc - xDst;
     618    xDst := 0;
     619  end;
     620  if yDst < 0 then
     621  begin
     622    h := h + yDst;
     623    ySrc := ySrc - yDst;
     624    yDst := 0;
     625  end;
     626  if xDst + w > dst.Width then
     627    w := dst.Width - xDst;
     628  if yDst + h > dst.Height then
     629    h := dst.Height - yDst;
     630  if (w < 0) or (h < 0) then
     631    exit;
     632
     633  h := yDst + h;
     634  while yDst < h do
     635  begin
     636    PixelDst := pointer(integer(dst.ScanLine[yDst]) + 3 * xDst);
     637    PixelSrc := pointer(integer(Src.ScanLine[ySrc]) + xSrc);
     638    for i := 0 to w - 1 do
     639    begin
     640      Brightness := PixelSrc^;
     641      test := (PixelDst[2] * Brightness) shr 7;
     642      if test >= 256 then
     643        PixelDst[2] := 255
     644      else
     645        PixelDst[2] := test; // Red
     646      test := (PixelDst[1] * Brightness) shr 7;
     647      if test >= 256 then
     648        PixelDst[1] := 255
     649      else
     650        PixelDst[1] := test; // Green
     651      test := (PixelDst[0] * Brightness) shr 7;
     652      if test >= 256 then
     653        PixelDst[2] := 255
     654      else
     655        PixelDst[0] := test; // Blue
     656      PixelDst := pointer(integer(PixelDst) + 3);
     657      PixelSrc := pointer(integer(PixelSrc) + 1);
     658    end;
     659    inc(yDst);
     660    inc(ySrc);
     661  end
     662end;
     663
     664procedure ImageOp_BCC(dst, Src: TBitmap; xDst, yDst, xSrc, ySrc, w, h, Color1,
     665  Color2: integer);
    564666// Src is template
    565667// B channel = background amp (old Dst content), 128=original brightness
     
    567669// R channel = Color2 amp, 128=original brightness
    568670type
    569 TLine=array[0..9999,0..2] of Byte;
    570 var
    571 ix,iy,amp1,amp2,trans,Value: integer;
    572 SrcLine,DstLine: ^TLine;
    573 begin
    574 if xDst<0 then
    575   begin w:=w+xDst; xSrc:=xSrc-xDst; xDst:=0; end;
    576 if yDst<0 then
    577   begin h:=h+yDst; ySrc:=ySrc-yDst; yDst:=0; end;
    578 if xDst+w>Dst.Width then
    579   w:=Dst.Width-xDst;
    580 if yDst+h>Dst.Height then
    581   h:=Dst.Height-yDst;
    582 if (w<0) or (h<0) then
    583   exit;
    584 
    585 for iy:=0 to h-1 do
    586   begin
    587   SrcLine:=Src.ScanLine[ySrc+iy];
    588   DstLine:=Dst.ScanLine[yDst+iy];
    589   for ix:=0 to w-1 do
    590     begin
    591     trans:=SrcLine[xSrc+ix,0]*2; // green channel = transparency
    592     amp1:=SrcLine[xSrc+ix,1]*2;
    593     amp2:=SrcLine[xSrc+ix,2]*2;
    594     if trans<>$FF then
     671  TLine = array [0 .. 9999, 0 .. 2] of Byte;
     672var
     673  ix, iy, amp1, amp2, trans, Value: integer;
     674  SrcLine, DstLine: ^TLine;
     675begin
     676  if xDst < 0 then
     677  begin
     678    w := w + xDst;
     679    xSrc := xSrc - xDst;
     680    xDst := 0;
     681  end;
     682  if yDst < 0 then
     683  begin
     684    h := h + yDst;
     685    ySrc := ySrc - yDst;
     686    yDst := 0;
     687  end;
     688  if xDst + w > dst.Width then
     689    w := dst.Width - xDst;
     690  if yDst + h > dst.Height then
     691    h := dst.Height - yDst;
     692  if (w < 0) or (h < 0) then
     693    exit;
     694
     695  for iy := 0 to h - 1 do
     696  begin
     697    SrcLine := Src.ScanLine[ySrc + iy];
     698    DstLine := dst.ScanLine[yDst + iy];
     699    for ix := 0 to w - 1 do
     700    begin
     701      trans := SrcLine[xSrc + ix, 0] * 2; // green channel = transparency
     702      amp1 := SrcLine[xSrc + ix, 1] * 2;
     703      amp2 := SrcLine[xSrc + ix, 2] * 2;
     704      if trans <> $FF then
    595705      begin
    596       Value:=(DstLine[xDst+ix][0]*trans+(Color2 shr 16 and $FF)*amp2+(Color1 shr 16 and $FF)*amp1) div $FF;
    597       if Value<256 then
    598         DstLine[xDst+ix][0]:=Value
    599       else DstLine[xDst+ix][0]:=255;
    600       Value:=(DstLine[xDst+ix][1]*trans+(Color2 shr 8 and $FF)*amp2+(Color1 shr 8 and $FF)*amp1) div $FF;
    601       if Value<256 then
    602         DstLine[xDst+ix][1]:=Value
    603       else DstLine[xDst+ix][1]:=255;
    604       Value:=(DstLine[xDst+ix][2]*trans+(Color2 and $FF)*amp2+(Color1 and $FF)*amp1) div $FF;
    605       if Value<256 then
    606         DstLine[xDst+ix][2]:=Value
    607       else DstLine[xDst+ix][2]:=255;
     706        Value := (DstLine[xDst + ix][0] * trans + (Color2 shr 16 and $FF) * amp2
     707          + (Color1 shr 16 and $FF) * amp1) div $FF;
     708        if Value < 256 then
     709          DstLine[xDst + ix][0] := Value
     710        else
     711          DstLine[xDst + ix][0] := 255;
     712        Value := (DstLine[xDst + ix][1] * trans + (Color2 shr 8 and $FF) * amp2
     713          + (Color1 shr 8 and $FF) * amp1) div $FF;
     714        if Value < 256 then
     715          DstLine[xDst + ix][1] := Value
     716        else
     717          DstLine[xDst + ix][1] := 255;
     718        Value := (DstLine[xDst + ix][2] * trans + (Color2 and $FF) * amp2 +
     719          (Color1 and $FF) * amp1) div $FF;
     720        if Value < 256 then
     721          DstLine[xDst + ix][2] := Value
     722        else
     723          DstLine[xDst + ix][2] := 255;
    608724      end
    609725    end
     
    611727end;
    612728
    613 procedure ImageOp_CCC(Bmp: TBitmap; x,y,w,h,Color0,Color1,Color2: integer);
     729procedure ImageOp_CCC(bmp: TBitmap; x, y, w, h, Color0, Color1,
     730  Color2: integer);
    614731// Bmp is template
    615732// B channel = Color0 amp, 128=original brightness
     
    617734// R channel = Color2 amp, 128=original brightness
    618735type
    619 TPixel=array[0..2] of Byte;
    620 var
    621 i,Red,Green: integer;
    622 Pixel: ^TPixel;
    623 begin
    624 assert(Bmp.PixelFormat=pf24bit);
    625 h:=y+h;
    626 while y<h do
    627   begin
    628   Pixel:=pointer(integer(Bmp.ScanLine[y])+3*x);
    629   for i:=0 to w-1 do
    630     begin
    631     Red:=       (Pixel[0]*(Color0        and $0000FF)
    632                 +Pixel[1]*(Color1        and $0000FF)
    633                 +Pixel[2]*(Color2        and $0000FF)) shr 8;
    634     Green:=     (Pixel[0]*(Color0 shr  8 and $0000FF)
    635                 +Pixel[1]*(Color1 shr  8 and $0000FF)
    636                 +Pixel[2]*(Color2 shr  8 and $0000FF)) shr 8;
    637     Pixel[0]:=  (Pixel[0]*(Color0 shr 16 and $0000FF)
    638                 +Pixel[1]*(Color1 shr 16 and $0000FF)
    639                 +Pixel[2]*(Color2 shr 16 and $0000FF)) shr 8; // Blue
    640     Pixel[1]:=Green;
    641     Pixel[2]:=Red;
    642     Pixel:=pointer(integer(pixel)+3);
    643     end;
    644   inc(y);
    645   end
    646 end;
    647 
    648 procedure Sprite(Canvas: TCanvas; HGr,xDst,yDst,Width,Height,xGr,yGr: integer);
    649 begin
    650 BitBlt(Canvas.Handle,xDst,yDst,Width,Height,
    651   GrExt[HGr].Mask.Canvas.Handle,xGr,yGr,SRCAND);
    652 BitBlt(Canvas.Handle,xDst,yDst,Width,Height,
    653   GrExt[HGr].Data.Canvas.Handle,xGr,yGr,SRCPAINT);
    654 end;
    655 
    656 procedure Sprite(dst:TBitmap; HGr,xDst,yDst,Width,Height,xGr,yGr: integer);
    657 begin
    658 BitBlt(dst.Canvas.Handle,xDst,yDst,Width,Height,
    659   GrExt[HGr].Mask.Canvas.Handle,xGr,yGr,SRCAND);
    660 BitBlt(dst.Canvas.Handle,xDst,yDst,Width,Height,
    661   GrExt[HGr].Data.Canvas.Handle,xGr,yGr,SRCPAINT);
    662 end;
    663 
    664 procedure SLine(ca: TCanvas; x0,x1,y: integer; cl: TColor);
    665 begin
    666 with ca do
    667   begin
    668   Pen.Color:=cl; MoveTo(x0,y); LineTo(x1+1,y);
    669   end
    670 end;
    671 
    672 procedure DLine(ca: TCanvas; x0,x1,y: integer; cl0,cl1: TColor);
    673 begin
    674 with ca do
    675   begin
    676   Pen.Color:=cl0; MoveTo(x0,y); LineTo(x1,y);
    677   Pen.Color:=cl1; MoveTo(x0+1,y+1); LineTo(x1+1,y+1);
    678   Pixels[x0,y+1]:=cl0; Pixels[x1,y]:=cl1;
    679   end
    680 end;
    681 
    682 procedure Frame(ca: TCanvas;x0,y0,x1,y1:integer;cl0,cl1:TColor);
    683 begin
    684 with ca do
    685   begin
    686   MoveTo(x0,y1);
    687   Pen.Color:=cl0;LineTo(x0,y0);LineTo(x1,y0);
    688   Pen.Color:=cl1;LineTo(x1,y1);LineTo(x0,y1);
    689   end
    690 end;
    691 
    692 procedure RFrame(ca: TCanvas;x0,y0,x1,y1:integer;cl0,cl1:TColor);
    693 begin
    694 with ca do
    695   begin
    696   Pen.Color:=cl0;
    697   MoveTo(x0,y0+1);LineTo(x0,y1);
    698   MoveTo(x0+1,y0);LineTo(x1,y0);
    699   Pen.Color:=cl1;
    700   MoveTo(x1,y0+1);LineTo(x1,y1);
    701   MoveTo(x0+1,y1);LineTo(x1,y1);
    702   end
    703 end;
    704 
    705 procedure CFrame(ca: TCanvas; x0,y0,x1,y1,Corner: integer; cl: TColor);
    706 begin
    707 with ca do
    708   begin
    709   Pen.Color:=cl;
    710   MoveTo(x0,y0+Corner-1);LineTo(x0,y0);LineTo(x0+Corner,y0);
    711   MoveTo(x1,y0+Corner-1);LineTo(x1,y0);LineTo(x1-Corner,y0);
    712   MoveTo(x1,y1-Corner+1);LineTo(x1,y1);LineTo(x1-Corner,y1);
    713   MoveTo(x0,y1-Corner+1);LineTo(x0,y1);LineTo(x0+Corner,y1);
    714   end
    715 end;
    716 
    717 procedure FrameImage(ca: TCanvas; src:TBitmap; x,y,width,height,xSrc,ySrc: integer;
    718   IsControl: boolean = false);
    719 begin
    720 if IsControl then
    721   begin
    722   Frame(ca,x-1,y-1,x+width,y+height,$B0B0B0,$FFFFFF);
    723   RFrame(ca,x-2,y-2,x+width+1,y+height+1,$FFFFFF,$B0B0B0);
    724   end
    725 else Frame(ca,x-1,y-1,x+width,y+height,$000000,$000000);
    726 BitBlt(ca.Handle,x,y,width,height,src.Canvas.Handle,xSrc,ySrc,SRCCOPY);
    727 end;
    728 
    729 procedure GlowFrame(dst: TBitmap; x0,y0,width,height: integer; cl: TColor);
     736  TPixel = array [0 .. 2] of Byte;
     737var
     738  i, Red, Green: integer;
     739  Pixel: ^TPixel;
     740begin
     741  assert(bmp.PixelFormat = pf24bit);
     742  h := y + h;
     743  while y < h do
     744  begin
     745    Pixel := pointer(integer(bmp.ScanLine[y]) + 3 * x);
     746    for i := 0 to w - 1 do
     747    begin
     748      Red := (Pixel[0] * (Color0 and $0000FF) + Pixel[1] * (Color1 and $0000FF)
     749        + Pixel[2] * (Color2 and $0000FF)) shr 8;
     750      Green := (Pixel[0] * (Color0 shr 8 and $0000FF) + Pixel[1] *
     751        (Color1 shr 8 and $0000FF) + Pixel[2] * (Color2 shr 8 and
     752        $0000FF)) shr 8;
     753      Pixel[0] := (Pixel[0] * (Color0 shr 16 and $0000FF) + Pixel[1] *
     754        (Color1 shr 16 and $0000FF) + Pixel[2] * (Color2 shr 16 and $0000FF))
     755        shr 8; // Blue
     756      Pixel[1] := Green;
     757      Pixel[2] := Red;
     758      Pixel := pointer(integer(Pixel) + 3);
     759    end;
     760    inc(y);
     761  end
     762end;
     763
     764procedure Sprite(Canvas: TCanvas; HGr, xDst, yDst, Width, Height, xGr,
     765  yGr: integer);
     766begin
     767  BitBlt(Canvas.Handle, xDst, yDst, Width, Height,
     768    GrExt[HGr].Mask.Canvas.Handle, xGr, yGr, SRCAND);
     769  BitBlt(Canvas.Handle, xDst, yDst, Width, Height,
     770    GrExt[HGr].Data.Canvas.Handle, xGr, yGr, SRCPAINT);
     771end;
     772
     773procedure Sprite(dst: TBitmap; HGr, xDst, yDst, Width, Height, xGr,
     774  yGr: integer);
     775begin
     776  BitBlt(dst.Canvas.Handle, xDst, yDst, Width, Height,
     777    GrExt[HGr].Mask.Canvas.Handle, xGr, yGr, SRCAND);
     778  BitBlt(dst.Canvas.Handle, xDst, yDst, Width, Height,
     779    GrExt[HGr].Data.Canvas.Handle, xGr, yGr, SRCPAINT);
     780end;
     781
     782procedure SLine(ca: TCanvas; x0, x1, y: integer; cl: TColor);
     783begin
     784  with ca do
     785  begin
     786    Pen.Color := cl;
     787    MoveTo(x0, y);
     788    LineTo(x1 + 1, y);
     789  end
     790end;
     791
     792procedure DLine(ca: TCanvas; x0, x1, y: integer; cl0, cl1: TColor);
     793begin
     794  with ca do
     795  begin
     796    Pen.Color := cl0;
     797    MoveTo(x0, y);
     798    LineTo(x1, y);
     799    Pen.Color := cl1;
     800    MoveTo(x0 + 1, y + 1);
     801    LineTo(x1 + 1, y + 1);
     802    Pixels[x0, y + 1] := cl0;
     803    Pixels[x1, y] := cl1;
     804  end
     805end;
     806
     807procedure Frame(ca: TCanvas; x0, y0, x1, y1: integer; cl0, cl1: TColor);
     808begin
     809  with ca do
     810  begin
     811    MoveTo(x0, y1);
     812    Pen.Color := cl0;
     813    LineTo(x0, y0);
     814    LineTo(x1, y0);
     815    Pen.Color := cl1;
     816    LineTo(x1, y1);
     817    LineTo(x0, y1);
     818  end
     819end;
     820
     821procedure RFrame(ca: TCanvas; x0, y0, x1, y1: integer; cl0, cl1: TColor);
     822begin
     823  with ca do
     824  begin
     825    Pen.Color := cl0;
     826    MoveTo(x0, y0 + 1);
     827    LineTo(x0, y1);
     828    MoveTo(x0 + 1, y0);
     829    LineTo(x1, y0);
     830    Pen.Color := cl1;
     831    MoveTo(x1, y0 + 1);
     832    LineTo(x1, y1);
     833    MoveTo(x0 + 1, y1);
     834    LineTo(x1, y1);
     835  end
     836end;
     837
     838procedure CFrame(ca: TCanvas; x0, y0, x1, y1, Corner: integer; cl: TColor);
     839begin
     840  with ca do
     841  begin
     842    Pen.Color := cl;
     843    MoveTo(x0, y0 + Corner - 1);
     844    LineTo(x0, y0);
     845    LineTo(x0 + Corner, y0);
     846    MoveTo(x1, y0 + Corner - 1);
     847    LineTo(x1, y0);
     848    LineTo(x1 - Corner, y0);
     849    MoveTo(x1, y1 - Corner + 1);
     850    LineTo(x1, y1);
     851    LineTo(x1 - Corner, y1);
     852    MoveTo(x0, y1 - Corner + 1);
     853    LineTo(x0, y1);
     854    LineTo(x0 + Corner, y1);
     855  end
     856end;
     857
     858procedure FrameImage(ca: TCanvas; Src: TBitmap;
     859  x, y, Width, Height, xSrc, ySrc: integer; IsControl: boolean = false);
     860begin
     861  if IsControl then
     862  begin
     863    Frame(ca, x - 1, y - 1, x + Width, y + Height, $B0B0B0, $FFFFFF);
     864    RFrame(ca, x - 2, y - 2, x + Width + 1, y + Height + 1, $FFFFFF, $B0B0B0);
     865  end
     866  else
     867    Frame(ca, x - 1, y - 1, x + Width, y + Height, $000000, $000000);
     868  BitBlt(ca.Handle, x, y, Width, Height, Src.Canvas.Handle, xSrc, ySrc,
     869    SRCCOPY);
     870end;
     871
     872procedure GlowFrame(dst: TBitmap; x0, y0, Width, Height: integer; cl: TColor);
    730873type
    731 TLine=array[0..649,0..2] of Byte;
    732 var
    733 x,y,ch,r: integer;
    734 DstLine: ^TLine;
    735 begin
    736 for y:=-GlowRange+1 to height-1+GlowRange-1 do
    737   begin
    738   DstLine:=dst.ScanLine[y0+y];
    739   for x:=-GlowRange+1 to width-1+GlowRange-1 do
    740     begin
    741     if x<0 then
    742       if y<0 then r:=round(sqrt(sqr(x)+sqr(y)))
    743       else if y>=height then r:=round(sqrt(sqr(x)+sqr(y-(height-1))))
    744       else r:=-x
    745     else if x>=width then
    746       if y<0 then r:=round(sqrt(sqr(x-(width-1))+sqr(y)))
    747       else if y>=height then r:=round(sqrt(sqr(x-(width-1))+sqr(y-(height-1))))
    748       else r:=x-(width-1)
    749     else if y<0 then r:=-y
    750     else if y>=height then r:=y-(height-1)
    751     else continue;
    752     if r=0 then r:=1;
    753     if r<GlowRange then
    754       for ch:=0 to 2 do
    755         DstLine[x0+x][2-ch]:=(DstLine[x0+x][2-ch]*(r-1)
    756           +(cl shr (8*ch) and $FF)*(GlowRange-r)) div (GlowRange-1);
     874  TLine = array [0 .. 649, 0 .. 2] of Byte;
     875var
     876  x, y, ch, r: integer;
     877  DstLine: ^TLine;
     878begin
     879  for y := -GlowRange + 1 to Height - 1 + GlowRange - 1 do
     880  begin
     881    DstLine := dst.ScanLine[y0 + y];
     882    for x := -GlowRange + 1 to Width - 1 + GlowRange - 1 do
     883    begin
     884      if x < 0 then
     885        if y < 0 then
     886          r := round(sqrt(sqr(x) + sqr(y)))
     887        else if y >= Height then
     888          r := round(sqrt(sqr(x) + sqr(y - (Height - 1))))
     889        else
     890          r := -x
     891      else if x >= Width then
     892        if y < 0 then
     893          r := round(sqrt(sqr(x - (Width - 1)) + sqr(y)))
     894        else if y >= Height then
     895          r := round(sqrt(sqr(x - (Width - 1)) + sqr(y - (Height - 1))))
     896        else
     897          r := x - (Width - 1)
     898      else if y < 0 then
     899        r := -y
     900      else if y >= Height then
     901        r := y - (Height - 1)
     902      else
     903        continue;
     904      if r = 0 then
     905        r := 1;
     906      if r < GlowRange then
     907        for ch := 0 to 2 do
     908          DstLine[x0 + x][2 - ch] :=
     909            (DstLine[x0 + x][2 - ch] * (r - 1) + (cl shr (8 * ch) and $FF) *
     910            (GlowRange - r)) div (GlowRange - 1);
    757911    end;
    758912  end
     
    761915procedure InitOrnament;
    762916var
    763 x,y,p,light,shade: integer;
    764 begin
    765 if InitOrnamentDone then exit;
    766 light:=MainTexture.clBevelLight; // and $FCFCFC shr 2*3+MainTexture.clBevelShade and $FCFCFC shr 2;
    767 shade:=MainTexture.clBevelShade and $FCFCFC shr 2*3+MainTexture.clBevelLight and $FCFCFC shr 2;
    768 for x:=0 to wOrna-1 do for y:=0 to hOrna-1 do
    769   begin
    770   p:=GrExt[HGrSystem2].Data.Canvas.Pixels[xOrna+x,yOrna+y];
    771   if p=$0000FF then
    772     GrExt[HGrSystem2].Data.Canvas.Pixels[xOrna+x,yOrna+y]:=light
    773   else if p=$FF0000 then
    774     GrExt[HGrSystem2].Data.Canvas.Pixels[xOrna+x,yOrna+y]:=shade
    775   end;
    776 InitOrnamentDone:=true
     917  x, y, p, light, shade: integer;
     918begin
     919  if InitOrnamentDone then
     920    exit;
     921  light := MainTexture.clBevelLight;
     922  // and $FCFCFC shr 2*3+MainTexture.clBevelShade and $FCFCFC shr 2;
     923  shade := MainTexture.clBevelShade and $FCFCFC shr 2 * 3 +
     924    MainTexture.clBevelLight and $FCFCFC shr 2;
     925  for x := 0 to wOrna - 1 do
     926    for y := 0 to hOrna - 1 do
     927    begin
     928      p := GrExt[HGrSystem2].Data.Canvas.Pixels[xOrna + x, yOrna + y];
     929      if p = $0000FF then
     930        GrExt[HGrSystem2].Data.Canvas.Pixels[xOrna + x, yOrna + y] := light
     931      else if p = $FF0000 then
     932        GrExt[HGrSystem2].Data.Canvas.Pixels[xOrna + x, yOrna + y] := shade
     933    end;
     934  InitOrnamentDone := true
    777935end;
    778936
    779937procedure InitCityMark(const T: TTexture);
    780938var
    781 x,y,intensity: integer;
    782 begin
    783 for x:=0 to 9 do for y:=0 to 9 do
    784   if GrExt[HGrSystem].Mask.Canvas.Pixels[66+x,47+y]=0 then
    785     begin
    786     intensity:=GrExt[HGrSystem].Data.Canvas.Pixels[66+x,47+y] and $FF;
    787     GrExt[HGrSystem].Data.Canvas.Pixels[77+x,47+y]:=
    788       T.clMark and $FF *intensity div $FF
    789       +T.clMark shr 8 and $FF *intensity div $FF shl 8
    790       +T.clMark shr 16 and $FF *intensity div $FF shl 16
    791     end;
    792 bitblt(GrExt[HGrSystem].Mask.Canvas.Handle,77,47,10,10,
    793   GrExt[HGrSystem].Mask.Canvas.Handle,66,47,SRCCOPY);
    794 end;
    795 
    796 procedure Fill(ca: TCanvas;Left,Top,Width,Height,xOffset,yOffset: integer);
    797 begin
    798 assert((left+xOffset>=0) and (left+xOffset+width<=wMainTexture)
    799   and (top+yOffset>=0) and (top+yOffset+height<=hMainTexture));
    800 bitblt(ca.handle,left,top,width,height,MainTexture.Image.Canvas.Handle,left+xOffset,top+yOffset,SRCCOPY);
    801 end;
    802 
    803 procedure FillLarge(ca: TCanvas; x0,y0,x1,y1,xm: integer);
     939  x, y, intensity: integer;
     940begin
     941  for x := 0 to 9 do
     942    for y := 0 to 9 do
     943      if GrExt[HGrSystem].Mask.Canvas.Pixels[66 + x, 47 + y] = 0 then
     944      begin
     945        intensity := GrExt[HGrSystem].Data.Canvas.Pixels
     946          [66 + x, 47 + y] and $FF;
     947        GrExt[HGrSystem].Data.Canvas.Pixels[77 + x, 47 + y] := T.clMark and
     948          $FF * intensity div $FF + T.clMark shr 8 and
     949          $FF * intensity div $FF shl 8 + T.clMark shr 16 and
     950          $FF * intensity div $FF shl 16
     951      end;
     952  BitBlt(GrExt[HGrSystem].Mask.Canvas.Handle, 77, 47, 10, 10,
     953    GrExt[HGrSystem].Mask.Canvas.Handle, 66, 47, SRCCOPY);
     954end;
     955
     956procedure Fill(ca: TCanvas; Left, Top, Width, Height, xOffset,
     957  yOffset: integer);
     958begin
     959  assert((Left + xOffset >= 0) and (Left + xOffset + Width <= wMainTexture) and
     960    (Top + yOffset >= 0) and (Top + yOffset + Height <= hMainTexture));
     961  BitBlt(ca.Handle, Left, Top, Width, Height, MainTexture.Image.Canvas.Handle,
     962    Left + xOffset, Top + yOffset, SRCCOPY);
     963end;
     964
     965procedure FillLarge(ca: TCanvas; x0, y0, x1, y1, xm: integer);
    804966
    805967  function band(i: integer): integer;
    806968  var
    807   n: integer;
    808   begin
    809   n:=((hMainTexture div 2) div (y1-y0))*2;
    810   while hMainTexture div 2+(i+1)*(y1-y0)>hMainTexture do
    811     dec(i,n);
    812   while hMainTexture div 2+i*(y1-y0)<0 do
    813     inc(i,n);
    814   result:=i;
    815   end;
    816 
    817 var
    818 i: integer;
    819 begin
    820 for i:=0 to (x1-xm) div wMainTexture-1 do
    821   bitblt(ca.handle,xm+i*wMainTexture,y0,wMainTexture,y1-y0,
    822     MainTexture.Image.canvas.handle,0,hMainTexture div 2+band(i)*(y1-y0),SRCCOPY);
    823 bitblt(ca.handle,xm+((x1-xm) div wMainTexture)*wMainTexture,y0,
    824   x1-(xm+((x1-xm) div wMainTexture)*wMainTexture),y1-y0,
    825   MainTexture.Image.canvas.handle,0,
    826   hMainTexture div 2+band((x1-xm) div wMainTexture)*(y1-y0),SRCCOPY);
    827 for i:=0 to (xm-x0) div wMainTexture-1 do
    828   bitblt(ca.handle,xm-(i+1)*wMainTexture,y0,wMainTexture,y1-y0,
    829   MainTexture.Image.canvas.handle,0,hMainTexture div 2+band(-i-1)*(y1-y0),SRCCOPY);
    830 bitblt(ca.handle,x0,y0,xm-((xm-x0) div wMainTexture)*wMainTexture-x0,y1-y0,
    831   MainTexture.Image.canvas.handle,((xm-x0) div wMainTexture+1)*wMainTexture-(xm-x0),
    832   hMainTexture div 2+band(-(xm-x0) div wMainTexture-1)*(y1-y0),SRCCOPY);
    833 end;
    834 
    835 procedure FillSeamless(ca: TCanvas;Left,Top,Width,Height,xOffset,yOffset: integer;
    836   const Texture: TBitmap);
    837 var
    838 x,y,x0cut,y0cut,x1cut,y1cut: integer;
    839 begin
    840 while xOffset<0 do inc(xOffset,Texture.Width);
    841 while yOffset<0 do inc(yOffset,Texture.Height);
    842 for y:=(Top+yOffset) div Texture.Height to (Top+yOffset+Height-1) div Texture.Height do
    843   begin
    844   y0cut:=Top+yOffset-y*Texture.Height;
    845   if y0cut<0 then y0cut:=0;
    846   y1cut:=(y+1)*Texture.Height-(Top+yOffset+Height);
    847   if y1cut<0 then y1cut:=0;
    848   for x:=(Left+xOffset) div Texture.Width to (Left+xOffset+Width-1) div Texture.Width do
    849     begin
    850     x0cut:=Left+xOffset-x*Texture.Width;
    851     if x0cut<0 then x0cut:=0;
    852     x1cut:=(x+1)*Texture.Width-(Left+xOffset+Width);
    853     if x1cut<0 then x1cut:=0;
    854     BitBlt(ca.Handle,x*Texture.Width+x0cut-xOffset,y*Texture.Height+y0cut-yOffset,
    855       Texture.Width-x0cut-x1cut,Texture.Height-y0cut-y1cut,
    856       Texture.Canvas.Handle,x0cut,y0cut,SRCCOPY);
     969    n: integer;
     970  begin
     971    n := ((hMainTexture div 2) div (y1 - y0)) * 2;
     972    while hMainTexture div 2 + (i + 1) * (y1 - y0) > hMainTexture do
     973      dec(i, n);
     974    while hMainTexture div 2 + i * (y1 - y0) < 0 do
     975      inc(i, n);
     976    result := i;
     977  end;
     978
     979var
     980  i: integer;
     981begin
     982  for i := 0 to (x1 - xm) div wMainTexture - 1 do
     983    BitBlt(ca.Handle, xm + i * wMainTexture, y0, wMainTexture, y1 - y0,
     984      MainTexture.Image.Canvas.Handle, 0, hMainTexture div 2 + band(i) *
     985      (y1 - y0), SRCCOPY);
     986  BitBlt(ca.Handle, xm + ((x1 - xm) div wMainTexture) * wMainTexture, y0,
     987    x1 - (xm + ((x1 - xm) div wMainTexture) * wMainTexture), y1 - y0,
     988    MainTexture.Image.Canvas.Handle, 0, hMainTexture div 2 +
     989    band((x1 - xm) div wMainTexture) * (y1 - y0), SRCCOPY);
     990  for i := 0 to (xm - x0) div wMainTexture - 1 do
     991    BitBlt(ca.Handle, xm - (i + 1) * wMainTexture, y0, wMainTexture, y1 - y0,
     992      MainTexture.Image.Canvas.Handle, 0, hMainTexture div 2 + band(-i - 1) *
     993      (y1 - y0), SRCCOPY);
     994  BitBlt(ca.Handle, x0, y0, xm - ((xm - x0) div wMainTexture) * wMainTexture -
     995    x0, y1 - y0, MainTexture.Image.Canvas.Handle,
     996    ((xm - x0) div wMainTexture + 1) * wMainTexture - (xm - x0),
     997    hMainTexture div 2 + band(-(xm - x0) div wMainTexture - 1) *
     998    (y1 - y0), SRCCOPY);
     999end;
     1000
     1001procedure FillSeamless(ca: TCanvas; Left, Top, Width, Height, xOffset,
     1002  yOffset: integer; const Texture: TBitmap);
     1003var
     1004  x, y, x0cut, y0cut, x1cut, y1cut: integer;
     1005begin
     1006  while xOffset < 0 do
     1007    inc(xOffset, Texture.Width);
     1008  while yOffset < 0 do
     1009    inc(yOffset, Texture.Height);
     1010  for y := (Top + yOffset) div Texture.Height to (Top + yOffset + Height - 1)
     1011    div Texture.Height do
     1012  begin
     1013    y0cut := Top + yOffset - y * Texture.Height;
     1014    if y0cut < 0 then
     1015      y0cut := 0;
     1016    y1cut := (y + 1) * Texture.Height - (Top + yOffset + Height);
     1017    if y1cut < 0 then
     1018      y1cut := 0;
     1019    for x := (Left + xOffset) div Texture.Width to (Left + xOffset + Width - 1)
     1020      div Texture.Width do
     1021    begin
     1022      x0cut := Left + xOffset - x * Texture.Width;
     1023      if x0cut < 0 then
     1024        x0cut := 0;
     1025      x1cut := (x + 1) * Texture.Width - (Left + xOffset + Width);
     1026      if x1cut < 0 then
     1027        x1cut := 0;
     1028      BitBlt(ca.Handle, x * Texture.Width + x0cut - xOffset,
     1029        y * Texture.Height + y0cut - yOffset, Texture.Width - x0cut - x1cut,
     1030        Texture.Height - y0cut - y1cut, Texture.Canvas.Handle, x0cut,
     1031        y0cut, SRCCOPY);
    8571032    end
    8581033  end;
    8591034end;
    8601035
    861 procedure FillRectSeamless(ca: TCanvas;x0,y0,x1,y1,xOffset,yOffset: integer;
    862   const Texture: TBitmap);
    863 begin
    864 FillSeamless(ca,x0,y0,x1-x0,y1-y0,xOffset,yOffset,Texture);
    865 end;
    866 
    867 procedure PaintBackground(Form: TForm; Left,Top,Width,Height: integer);
    868 begin
    869 Fill(Form.Canvas,Left,Top,Width,Height,(wMaintexture-Form.ClientWidth) div 2,
    870   (hMaintexture-Form.ClientHeight) div 2);
    871 end;
    872 
    873 procedure Corner(ca: TCanvas; x,y,Kind:integer; const T: TTexture);
    874 begin
    875 {BitBlt(ca.Handle,x,y,8,8,GrExt[T.HGr].Mask.Canvas.Handle,
    876   T.xGr+29+Kind*9,T.yGr+89,SRCAND);
    877 BitBlt(ca.Handle,x,y,8,8,GrExt[T.HGr].Data.Canvas.Handle,
    878   T.xGr+29+Kind*9,T.yGr+89,SRCPAINT);}
    879 end;
    880 
    881 procedure BiColorTextOut(ca: TCanvas; clMain, clBack: TColor;
    882   x,y:integer; s:string);
    883 
    884   procedure PaintIcon(x,y,Kind: integer);
    885   begin
    886   BitBlt(ca.Handle,x,y+6,10,10,GrExt[HGrSystem].Mask.Canvas.Handle,
    887     66+Kind mod 11 *11,115+Kind div 11 *11,SRCAND);
    888   BitBlt(ca.Handle,x,y+6,10,10,GrExt[HGrSystem].Data.Canvas.Handle,
    889     66+Kind mod 11 *11,115+Kind div 11 *11,SRCPAINT);
    890   end;
    891 
    892 var
    893 p,xp: integer;
    894 sp: string;
    895 shadow: boolean;
    896 begin
    897 inc(x); inc(y);
    898 for shadow:=true downto false do with ca do
    899   if not shadow or (clBack<>$7F007F) then
    900     begin
    901     if shadow then Font.Color:=clBack
    902     else Font.Color:=clMain;
    903     sp:=s;
    904     xp:=x;
     1036procedure FillRectSeamless(ca: TCanvas; x0, y0, x1, y1, xOffset,
     1037  yOffset: integer; const Texture: TBitmap);
     1038begin
     1039  FillSeamless(ca, x0, y0, x1 - x0, y1 - y0, xOffset, yOffset, Texture);
     1040end;
     1041
     1042procedure PaintBackground(Form: TForm; Left, Top, Width, Height: integer);
     1043begin
     1044  Fill(Form.Canvas, Left, Top, Width, Height, (wMainTexture - Form.ClientWidth)
     1045    div 2, (hMainTexture - Form.ClientHeight) div 2);
     1046end;
     1047
     1048procedure Corner(ca: TCanvas; x, y, Kind: integer; const T: TTexture);
     1049begin
     1050  { BitBlt(ca.Handle,x,y,8,8,GrExt[T.HGr].Mask.Canvas.Handle,
     1051    T.xGr+29+Kind*9,T.yGr+89,SRCAND);
     1052    BitBlt(ca.Handle,x,y,8,8,GrExt[T.HGr].Data.Canvas.Handle,
     1053    T.xGr+29+Kind*9,T.yGr+89,SRCPAINT); }
     1054end;
     1055
     1056procedure BiColorTextOut(ca: TCanvas; clMain, clBack: TColor; x, y: integer;
     1057  s: string);
     1058
     1059  procedure PaintIcon(x, y, Kind: integer);
     1060  begin
     1061    BitBlt(ca.Handle, x, y + 6, 10, 10, GrExt[HGrSystem].Mask.Canvas.Handle,
     1062      66 + Kind mod 11 * 11, 115 + Kind div 11 * 11, SRCAND);
     1063    BitBlt(ca.Handle, x, y + 6, 10, 10, GrExt[HGrSystem].Data.Canvas.Handle,
     1064      66 + Kind mod 11 * 11, 115 + Kind div 11 * 11, SRCPAINT);
     1065  end;
     1066
     1067var
     1068  p, xp: integer;
     1069  sp: string;
     1070  shadow: boolean;
     1071begin
     1072  inc(x);
     1073  inc(y);
     1074  for shadow := true downto false do
     1075    with ca do
     1076      if not shadow or (clBack <> $7F007F) then
     1077      begin
     1078        if shadow then
     1079          Font.Color := clBack
     1080        else
     1081          Font.Color := clMain;
     1082        sp := s;
     1083        xp := x;
     1084        repeat
     1085          p := pos('%', sp);
     1086          if (p = 0) or (p + 1 > Length(sp)) or
     1087            not(sp[p + 1] in ['c', 'f', 'l', 'm', 'n', 'o', 'p', 'r', 't', 'w'])
     1088          then
     1089          begin
     1090            ca.Textout(xp, y, sp);
     1091            break
     1092          end
     1093          else
     1094          begin
     1095            Textout(xp, y, copy(sp, 1, p - 1));
     1096            inc(xp, ca.TextWidth(copy(sp, 1, p - 1)));
     1097            if not shadow then
     1098              case sp[p + 1] of
     1099                'c':
     1100                  PaintIcon(xp + 1, y, 6);
     1101                'f':
     1102                  PaintIcon(xp + 1, y, 0);
     1103                'l':
     1104                  PaintIcon(xp + 1, y, 8);
     1105                'm':
     1106                  PaintIcon(xp + 1, y, 17);
     1107                'n':
     1108                  PaintIcon(xp + 1, y, 7);
     1109                'o':
     1110                  PaintIcon(xp + 1, y, 16);
     1111                'p':
     1112                  PaintIcon(xp + 1, y, 2);
     1113                'r':
     1114                  PaintIcon(xp + 1, y, 12);
     1115                't':
     1116                  PaintIcon(xp + 1, y, 4);
     1117                'w':
     1118                  PaintIcon(xp + 1, y, 13);
     1119              end;
     1120            inc(xp, 10);
     1121            Delete(sp, 1, p + 1);
     1122          end
     1123          until false;
     1124          dec(x);
     1125          dec(y);
     1126        end
     1127      end;
     1128
     1129  function BiColorTextWidth(ca: TCanvas; s: string): integer;
     1130  var
     1131    p: integer;
     1132  begin
     1133    result := 1;
    9051134    repeat
    906       p:=pos('%',sp);
    907       if (p=0) or (p+1>length(sp))
    908         or not (sp[p+1] in ['c','f','l','m','n','o','p','r','t','w']) then
    909         begin ca.Textout(xp,y,sp); break end
     1135      p := pos('%', s);
     1136      if (p = 0) or (p = Length(s)) then
     1137      begin
     1138        inc(result, ca.TextWidth(s));
     1139        break
     1140      end
    9101141      else
     1142      begin
     1143        if not(s[p + 1] in ['c', 'f', 'l', 'm', 'n', 'o', 'p', 'r', 't', 'w'])
     1144        then
     1145          inc(result, ca.TextWidth(copy(s, 1, p + 1)))
     1146        else
     1147          inc(result, ca.TextWidth(copy(s, 1, p - 1)) + 10);
     1148        Delete(s, 1, p + 1);
     1149      end
     1150      until false;
     1151    end;
     1152
     1153    procedure LoweredTextOut(ca: TCanvas; cl: TColor; const T: TTexture;
     1154      x, y: integer; s: string);
     1155    begin
     1156      if cl = -2 then
     1157        BiColorTextOut(ca, (T.clBevelShade and $FEFEFE) shr 1,
     1158          T.clBevelLight, x, y, s)
     1159      else if cl < 0 then
     1160        BiColorTextOut(ca, T.clTextShade, T.clTextLight, x, y, s)
     1161      else
     1162        BiColorTextOut(ca, cl, T.clTextLight, x, y, s)
     1163    end;
     1164
     1165    procedure RisedTextOut(ca: TCanvas; x, y: integer; s: string);
     1166    begin
     1167      BiColorTextOut(ca, $FFFFFF, $000000, x, y, s)
     1168    end;
     1169
     1170    procedure Gradient(ca: TCanvas; x, y, dx, dy, Width, Height, Color: integer;
     1171      Brightness: array of integer);
     1172    var
     1173      i, r, g, b: integer;
     1174    begin
     1175      with ca do
     1176      begin
     1177        for i := 0 to 15 do
     1178        begin // gradient
     1179          r := Color and $FF + Brightness[i];
     1180          if r < 0 then
     1181            r := 0
     1182          else if r >= 256 then
     1183            r := 255;
     1184          g := Color shr 8 and $FF + Brightness[i];
     1185          if g < 0 then
     1186            g := 0
     1187          else if g >= 256 then
     1188            g := 255;
     1189          b := Color shr 16 and $FF + Brightness[i];
     1190          if b < 0 then
     1191            b := 0
     1192          else if b >= 256 then
     1193            b := 255;
     1194          Pen.Color := r + g shl 8 + b shl 16;
     1195          MoveTo(x + dx * i, y + dy * i);
     1196          LineTo(x + dx * i + Width, y + dy * i + Height);
     1197        end;
     1198        Pen.Color := $000000;
     1199        MoveTo(x + 1, y + 16 * dy + Height);
     1200        LineTo(x + 16 * dx + Width, y + 16 * dy + Height);
     1201        LineTo(x + 16 * dx + Width, y);
     1202      end
     1203    end;
     1204
     1205    procedure LightGradient(ca: TCanvas; x, y, Width, Color: integer);
     1206    const
     1207      Brightness: array [0 .. 15] of integer = (16, 12, 8, 4, 0, -4, -8, -12,
     1208        -16, -20, -24, -28, -32, -36, -40, -44);
     1209    begin
     1210      Gradient(ca, x, y, 0, 1, Width, 0, Color, Brightness)
     1211    end;
     1212
     1213    procedure DarkGradient(ca: TCanvas; x, y, Width, Kind: integer);
     1214    const
     1215      Brightness: array [0 .. 15] of integer = (16, 12, 8, 4, 0, -4, -8,
     1216        -12 - 24, -16 + 16, -20, -24, -28, -32, -36, -40, -44);
     1217    begin
     1218      Gradient(ca, x, y, 0, 1, Width, 0, GrExt[HGrSystem].Data.Canvas.Pixels
     1219        [187, 137 + Kind], Brightness)
     1220    end;
     1221
     1222    procedure VLightGradient(ca: TCanvas; x, y, Height, Color: integer);
     1223    const
     1224      Brightness: array [0 .. 15] of integer = (16, 12, 8, 4, 0, -4, -8, -12,
     1225        -16, -20, -24, -28, -32, -36, -40, -44);
     1226    begin
     1227      Gradient(ca, x, y, 1, 0, 0, Height, Color, Brightness)
     1228    end;
     1229
     1230    procedure VDarkGradient(ca: TCanvas; x, y, Height, Kind: integer);
     1231    const
     1232      Brightness: array [0 .. 15] of integer = (16, 12, 8, 4, 0, -4, -8,
     1233        -12 - 24, -16 + 16, -20, -24, -28, -32, -36, -40, -44);
     1234    begin
     1235      Gradient(ca, x, y, 1, 0, 0, Height, GrExt[HGrSystem].Data.Canvas.Pixels
     1236        [187, 137 + Kind], Brightness)
     1237    end;
     1238
     1239    procedure NumberBar(dst: TBitmap; x, y: integer; Cap: string; val: integer;
     1240      const T: TTexture);
     1241    var
     1242      s: string;
     1243    begin
     1244      if val > 0 then
     1245      begin
     1246        DLine(dst.Canvas, x - 2, x + 170, y + 16, T.clBevelShade,
     1247          T.clBevelLight);
     1248        LoweredTextOut(dst.Canvas, -1, T, x - 2, y, Cap);
     1249        s := IntToStr(val);
     1250        RisedTextOut(dst.Canvas, x + 170 - BiColorTextWidth(dst.Canvas,
     1251          s), y, s);
     1252      end
     1253    end;
     1254
     1255    procedure CountBar(dst: TBitmap; x, y, w: integer; Kind: integer;
     1256      Cap: string; val: integer; const T: TTexture);
     1257    var
     1258      i, sd, ld, cl, xIcon, yIcon: integer;
     1259      s: string;
     1260    begin
     1261      // val:=random(40); //!!!
     1262      if val = 0 then
     1263        exit;
     1264      assert(Kind >= 0);
     1265      with dst.Canvas do
     1266      begin
     1267        // xIcon:=x+100;
     1268        // yIcon:=y;
     1269        // DLine(dst.Canvas,x-2,x+170+32,y+16,T.clBevelShade,T.clBevelLight);
     1270
     1271        xIcon := x - 5;
     1272        yIcon := y + 15;
     1273        DLine(dst.Canvas, x - 2, xIcon + w + 2, yIcon + 16, T.clBevelShade,
     1274          T.clBevelLight);
     1275
     1276        s := IntToStr(val);
     1277        if val < 0 then
     1278          cl := $0000FF
     1279        else
     1280          cl := -1;
     1281        LoweredTextOut(dst.Canvas, cl, T, x - 2, y, Cap);
     1282        LoweredTextOut(dst.Canvas, cl, T,
     1283          xIcon + w + 2 - BiColorTextWidth(dst.Canvas, s), yIcon, s);
     1284
     1285        if (Kind = 12) and (val >= 100) then
     1286        begin // science with symbol for 100
     1287          val := val div 10;
     1288          sd := 14 * (val div 10 + val mod 10 - 1);
     1289          if sd = 0 then
     1290            sd := 1;
     1291          if sd < w - 44 then
     1292            ld := sd
     1293          else
     1294            ld := w - 44;
     1295          for i := 0 to val mod 10 - 1 do
     1296          begin
     1297            BitBlt(Handle, xIcon + 4 + i * (14 * ld div sd), yIcon + 2 + 1, 14,
     1298              14, GrExt[HGrSystem].Mask.Canvas.Handle, 67 + Kind mod 8 * 15,
     1299              70 + Kind div 8 * 15, SRCAND);
     1300            Sprite(dst, HGrSystem, xIcon + 3 + i * (14 * ld div sd), yIcon + 2,
     1301              14, 14, 67 + Kind mod 8 * 15, 70 + Kind div 8 * 15);
     1302          end;
     1303          for i := 0 to val div 10 - 1 do
     1304          begin
     1305            BitBlt(dst.Canvas.Handle, xIcon + 4 + (val mod 10) *
     1306              (14 * ld div sd) + i * (14 * ld div sd), yIcon + 3, 14, 14,
     1307              GrExt[HGrSystem].Mask.Canvas.Handle, 67 + 7 mod 8 * 15,
     1308              70 + 7 div 8 * 15, SRCAND);
     1309            Sprite(dst, HGrSystem, xIcon + 3 + (val mod 10) * (14 * ld div sd) +
     1310              i * (14 * ld div sd), yIcon + 2, 14, 14, 67 + 7 mod 8 * 15,
     1311              70 + 7 div 8 * 15);
     1312          end;
     1313        end
     1314        else
    9111315        begin
    912         Textout(xp,y,copy(sp,1,p-1));
    913         inc(xp,ca.TextWidth(copy(sp,1,p-1)));
    914         if not shadow then
    915           case sp[p+1] of
    916             'c': PaintIcon(xp+1,y,6);
    917             'f': PaintIcon(xp+1,y,0);
    918             'l': PaintIcon(xp+1,y,8);
    919             'm': PaintIcon(xp+1,y,17);
    920             'n': PaintIcon(xp+1,y,7);
    921             'o': PaintIcon(xp+1,y,16);
    922             'p': PaintIcon(xp+1,y,2);
    923             'r': PaintIcon(xp+1,y,12);
    924             't': PaintIcon(xp+1,y,4);
    925             'w': PaintIcon(xp+1,y,13);
    926             end;
    927         inc(xp,10);
    928         delete(sp,1,p+1);
     1316          val := abs(val);
     1317          if val mod 10 = 0 then
     1318            sd := 14 * (val div 10 - 1)
     1319          else
     1320            sd := 10 * (val mod 10 - 1) + 14 * (val div 10);
     1321          if sd = 0 then
     1322            sd := 1;
     1323          if sd < w - 44 then
     1324            ld := sd
     1325          else
     1326            ld := w - 44;
     1327          for i := 0 to val div 10 - 1 do
     1328          begin
     1329            BitBlt(Handle, xIcon + 4 + i * (14 * ld div sd), yIcon + 3, 14, 14,
     1330              GrExt[HGrSystem].Mask.Canvas.Handle, 67 + Kind mod 8 * 15,
     1331              70 + Kind div 8 * 15, SRCAND);
     1332            Sprite(dst, HGrSystem, xIcon + 3 + i * (14 * ld div sd), yIcon + 2,
     1333              14, 14, 67 + Kind mod 8 * 15, 70 + Kind div 8 * 15);
     1334          end;
     1335          for i := 0 to val mod 10 - 1 do
     1336          begin
     1337            BitBlt(dst.Canvas.Handle, xIcon + 4 + (val div 10) *
     1338              (14 * ld div sd) + i * (10 * ld div sd), yIcon + 7, 10, 10,
     1339              GrExt[HGrSystem].Mask.Canvas.Handle, 66 + Kind mod 11 * 11,
     1340              115 + Kind div 11 * 11, SRCAND);
     1341            Sprite(dst, HGrSystem, xIcon + 3 + (val div 10) * (14 * ld div sd) +
     1342              i * (10 * ld div sd), yIcon + 6, 10, 10, 66 + Kind mod 11 * 11,
     1343              115 + Kind div 11 * 11)
     1344          end;
    9291345        end
    930     until false;
    931     dec(x); dec(y);
    932     end
    933 end;
    934 
    935 function BiColorTextWidth(ca: TCanvas; s: string): integer;
    936 var
    937 p: integer;
    938 begin
    939 result:=1;
    940 repeat
    941   p:=pos('%',s);
    942   if (p=0) or (p=Length(s)) then
    943     begin inc(result,ca.TextWidth(s)); break end
    944   else
    945     begin
    946     if not (s[p+1] in ['c','f','l','m','n','o','p','r','t','w']) then
    947       inc(result,ca.TextWidth(copy(s,1,p+1)))
    948     else inc(result,ca.TextWidth(copy(s,1,p-1))+10);
    949     delete(s,1,p+1);
    950     end
    951 until false;
    952 end;
    953 
    954 procedure LoweredTextOut(ca: TCanvas; cl: TColor; const T: TTexture;
    955   x,y:integer; s:string);
    956 begin
    957 if cl=-2 then
    958   BiColorTextOut(ca, (T.clBevelShade and $FEFEFE) shr 1, T.clBevelLight, x, y, s)
    959 else if cl<0 then
    960   BiColorTextOut(ca, T.clTextShade, T.clTextLight, x, y, s)
    961 else BiColorTextOut(ca, cl, T.clTextLight, x, y, s)
    962 end;
    963 
    964 procedure RisedTextOut(ca: TCanvas; x,y:integer; s:string);
    965 begin
    966 BiColorTextOut(ca, $FFFFFF, $000000, x, y, s)
    967 end;
    968 
    969 procedure Gradient(ca: TCanvas; x,y,dx,dy,width,height,Color:integer; Brightness: array of integer);
    970 var
    971 i,r,g,b: integer;
    972 begin
    973 with ca do
    974   begin
    975   for i:=0 to 15 do
    976     begin // gradient
    977     r:=Color and $FF+Brightness[i];
    978     if r<0 then r:=0
    979     else if r>=256 then r:=255;
    980     g:=Color shr 8 and $FF+Brightness[i];
    981     if g<0 then g:=0
    982     else if g>=256 then g:=255;
    983     b:=Color shr 16 and $FF+Brightness[i];
    984     if b<0 then b:=0
    985     else if b>=256 then b:=255;
    986     pen.color:=r+g shl 8+b shl 16;
    987     MoveTo(x+dx*i,y+dy*i);
    988     LineTo(x+dx*i+width,y+dy*i+height);
    989     end;
    990   pen.color:=$000000;
    991   MoveTo(x+1,y+16*dy+height);
    992   LineTo(x+16*dx+width,y+16*dy+height);
    993   LineTo(x+16*dx+width,y);
    994   end
    995 end;
    996 
    997 procedure LightGradient(ca: TCanvas; x,y,width,Color:integer);
    998 const
    999 Brightness: array[0..15] of integer=
    1000 (16,12,8,4,0,-4,-8,-12,-16,-20,-24,-28,-32,-36,-40,-44);
    1001 begin
    1002 Gradient(ca,x,y,0,1,width,0,Color,Brightness)
    1003 end;
    1004 
    1005 procedure DarkGradient(ca: TCanvas; x,y,width,Kind:integer);
    1006 const
    1007 Brightness: array[0..15] of integer=
    1008 (16,12,8,4,0,-4,-8,-12-24,-16+16,-20,-24,-28,-32,-36,-40,-44);
    1009 begin
    1010 Gradient(ca,x,y,0,1,width,0,
    1011   GrExt[HGrSystem].Data.Canvas.Pixels[187,137+Kind],Brightness)
    1012 end;
    1013 
    1014 procedure VLightGradient(ca: TCanvas; x,y,height,Color:integer);
    1015 const
    1016 Brightness: array[0..15] of integer=
    1017 (16,12,8,4,0,-4,-8,-12,-16,-20,-24,-28,-32,-36,-40,-44);
    1018 begin
    1019 Gradient(ca,x,y,1,0,0,height,Color,Brightness)
    1020 end;
    1021 
    1022 procedure VDarkGradient(ca: TCanvas; x,y,height,Kind:integer);
    1023 const
    1024 Brightness: array[0..15] of integer=
    1025 (16,12,8,4,0,-4,-8,-12-24,-16+16,-20,-24,-28,-32,-36,-40,-44);
    1026 begin
    1027 Gradient(ca,x,y,1,0,0,height,
    1028   GrExt[HGrSystem].Data.Canvas.Pixels[187,137+Kind],Brightness)
    1029 end;
    1030 
    1031 procedure NumberBar(dst:TBitmap; x,y:integer;
    1032   Cap:string; val: integer; const T: TTexture);
    1033 var
    1034 s:string;
    1035 begin
    1036 if val>0 then
    1037   begin
    1038   DLine(dst.Canvas,x-2,x+170,y+16,T.clBevelShade,T.clBevelLight);
    1039   LoweredTextOut(dst.Canvas,-1,T,x-2,y,Cap);
    1040   s:=IntToStr(val);
    1041   RisedTextout(dst.canvas,x+170-BiColorTextWidth(dst.Canvas,s),y,s);
    1042   end
    1043 end;
    1044 
    1045 procedure CountBar(dst:TBitmap; x,y,w:integer; Kind:integer;
    1046   Cap:string; val: integer; const T: TTexture);
    1047 var
    1048 i,sd,ld,cl,xIcon,yIcon: integer;
    1049 s:string;
    1050 begin
    1051 //val:=random(40); //!!!
    1052 if val=0 then exit;
    1053 assert(Kind>=0);
    1054 with dst.Canvas do
    1055   begin
    1056 //  xIcon:=x+100;
    1057 //  yIcon:=y;
    1058 //  DLine(dst.Canvas,x-2,x+170+32,y+16,T.clBevelShade,T.clBevelLight);
    1059 
    1060   xIcon:=x-5;
    1061   yIcon:=y+15;
    1062   DLine(dst.Canvas,x-2,xIcon+w+2,yIcon+16,T.clBevelShade,T.clBevelLight);
    1063 
    1064   s:=IntToStr(val);
    1065   if val<0 then cl:=$0000FF
    1066   else cl:=-1;
    1067   LoweredTextOut(dst.Canvas,cl,T,x-2,y,Cap);
    1068   LoweredTextout(dst.canvas,cl,T,xIcon+w+2-BiColorTextWidth(dst.Canvas,s),yIcon,s);
    1069 
    1070   if (Kind=12) and (val>=100) then
    1071     begin // science with symbol for 100
    1072     val:=val div 10;
    1073     sd:=14*(val div 10+val mod 10-1);
    1074     if sd=0 then sd:=1;
    1075     if sd<w-44 then ld:=sd else ld:=w-44;
    1076     for i:=0 to val mod 10-1 do
     1346      end
     1347    end; // CountBar
     1348
     1349    procedure PaintProgressBar(ca: TCanvas;
     1350      Kind, x, y, pos, Growth, max: integer; const T: TTexture);
     1351    var
     1352      i: integer;
     1353    begin
     1354      if pos > max then
     1355        pos := max;
     1356      if Growth < 0 then
    10771357      begin
    1078       BitBlt(Handle,xIcon+4+i*(14*ld div sd),yIcon+2+1,14,14,
    1079         GrExt[HGrSystem].Mask.Canvas.Handle,
    1080         67+Kind mod 8 *15,70+Kind div 8 *15,SRCAND);
    1081       Sprite(dst,HGrSystem,xIcon+3+i*(14*ld div sd),yIcon+2,14,14,
    1082         67+Kind mod 8 *15,70+Kind div 8 *15);
    1083       end;
    1084     for i:=0 to val div 10-1 do
     1358        pos := pos + Growth;
     1359        if pos < 0 then
     1360        begin
     1361          Growth := Growth - pos;
     1362          pos := 0
     1363        end
     1364      end
     1365      else if pos + Growth > max then
     1366        Growth := max - pos;
     1367      Frame(ca, x - 1, y - 1, x + max, y + 7, $000000, $000000);
     1368      RFrame(ca, x - 2, y - 2, x + max + 1, y + 8, T.clBevelShade,
     1369        T.clBevelLight);
     1370      with ca do
    10851371      begin
    1086       BitBlt(dst.Canvas.Handle,xIcon+4+(val mod 10)*(14*ld div sd)
    1087         +i*(14*ld div sd),yIcon+3,14,14,
    1088         GrExt[HGrSystem].Mask.Canvas.Handle,67+7 mod 8 *15,70+7 div 8 *15,
    1089         SRCAND);
    1090       Sprite(dst,HGrSystem,xIcon+3+(val mod 10)*(14*ld div sd)
    1091         +i*(14*ld div sd),yIcon+2,14,14,67+7 mod 8 *15,70+7 div 8 *15);
    1092       end;
    1093     end
    1094   else
    1095     begin
    1096     val:=abs(val);
    1097     if val mod 10=0 then sd:=14*(val div 10-1)
    1098     else sd:=10*(val mod 10-1)+14*(val div 10);
    1099     if sd=0 then sd:=1;
    1100     if sd<w-44 then ld:=sd else ld:=w-44;
    1101     for i:=0 to val div 10-1 do
    1102       begin
    1103       BitBlt(Handle,xIcon+4+i*(14*ld div sd),yIcon+3,14,14,
    1104         GrExt[HGrSystem].Mask.Canvas.Handle,67+Kind mod 8 *15,70+Kind div 8 *15,SRCAND);
    1105       Sprite(dst,HGrSystem,xIcon+3+i*(14*ld div sd),yIcon+2,14,14,67+Kind mod 8 *15,
    1106         70+Kind div 8 *15);
    1107       end;
    1108     for i:=0 to val mod 10-1 do
    1109       begin
    1110       BitBlt(dst.Canvas.Handle,xIcon+4+(val div 10)*(14*ld div sd)
    1111         +i*(10*ld div sd),yIcon+7,10,10,GrExt[HGrSystem].Mask.Canvas.Handle,
    1112         66+Kind mod 11 *11,115+Kind div 11 *11,SRCAND);
    1113       Sprite(dst,HGrSystem,xIcon+3+(val div 10)*(14*ld div sd)
    1114         +i*(10*ld div sd),yIcon+6,10,10,66+Kind mod 11 *11,115+Kind div 11 *11)
    1115       end;
    1116     end
    1117   end
    1118 end; //CountBar
    1119 
    1120 procedure PaintProgressBar(ca: TCanvas; Kind,x,y,pos,Growth,max: integer;
    1121   const T: TTexture);
    1122 var
    1123 i: integer;
    1124 begin
    1125 if pos>max then pos:=max;
    1126 if Growth<0 then
    1127   begin
    1128   pos:=pos+Growth;
    1129   if pos<0 then begin Growth:=Growth-pos; pos:=0 end
    1130   end
    1131 else if pos+Growth>max then Growth:=max-pos;
    1132 Frame(ca,x-1,y-1,x+max,y+7,$000000,$000000);
    1133 RFrame(ca,x-2,y-2,x+max+1,y+8,T.clBevelShade,T.clBevelLight);
    1134 with ca do
    1135   begin
    1136   for i:=0 to pos div 8-1 do
    1137     BitBlt(Handle,x+i*8,y,8,7,GrExt[HGrSystem].Data.Canvas.Handle,104,
    1138       9+8*Kind,SRCCOPY);
    1139   BitBlt(Handle,x+8*(pos div 8),y,
    1140     pos-8*(pos div 8),7,GrExt[HGrSystem].Data.Canvas.Handle,104,9+8*Kind,SRCCOPY);
    1141   if Growth>0 then
    1142     begin
    1143     for i:=0 to Growth div 8-1 do
    1144       BitBlt(Handle,x+pos+i*8,y,8,7,GrExt[HGrSystem].Data.Canvas.Handle,112,
    1145         9+8*Kind,SRCCOPY);
    1146     BitBlt(Handle,x+pos+8*(Growth div 8),y,
    1147       Growth-8*(Growth div 8),7,GrExt[HGrSystem].Data.Canvas.Handle,112,
    1148       9+8*Kind,SRCCOPY);
    1149     end
    1150   else if Growth<0 then
    1151     begin
    1152     for i:=0 to -Growth div 8-1 do
    1153       BitBlt(Handle,x+pos+i*8,y,8,7,GrExt[HGrSystem].Data.Canvas.Handle,104,1,
    1154         SRCCOPY);
    1155     BitBlt(Handle,x+pos+8*(-Growth div 8),y,
    1156       -Growth-8*(-Growth div 8),7,GrExt[HGrSystem].Data.Canvas.Handle,104,1,
    1157       SRCCOPY);
    1158     end;
    1159   Brush.Color:=$000000;
    1160   FillRect(Rect(x+pos+abs(Growth),y,x+max,y+7));
    1161   Brush.Style:=bsClear;
    1162   end
    1163 end;
    1164 
    1165 // pos and growth are relative to max, set size independent
    1166 procedure PaintRelativeProgressBar(ca: TCanvas; Kind,x,y,size,pos,Growth,
    1167   max: integer; IndicateComplete: boolean; const T: TTexture);
    1168 begin
    1169 if Growth>0 then
    1170   PaintProgressBar(ca,Kind,x,y,pos*size div max,
    1171     (Growth*size+max div 2) div max,size,T)
    1172 else PaintProgressBar(ca,Kind,x,y,pos*size div max,
    1173   (Growth*size-max div 2) div max,size,T);
    1174 if IndicateComplete and (pos+Growth>=max) then
    1175   Sprite(ca, HGrSystem, x+size-10, y-7, 23, 16, 1, 129);
    1176 end;
    1177 
    1178 procedure PaintLogo(ca: TCanvas; x,y,clLight,clShade: integer);
    1179 begin
    1180 BitBlt(LogoBuffer.Canvas.Handle,0,0,wLogo,hLogo,ca.handle,x,y,SRCCOPY);
    1181 ImageOp_BCC(LogoBuffer,Templates,0,0,1,1,wLogo,hLogo,clLight,clShade);
    1182 BitBlt(ca.handle,x,y,wLogo,hLogo,LogoBuffer.Canvas.Handle,0,0,SRCCOPY);
    1183 end;
    1184 
    1185 function SetMainTextureByAge(Age: integer): boolean;
    1186 begin
    1187 if Age<>MainTextureAge then with MainTexture do
    1188   begin
    1189   MainTextureAge:=Age;
    1190   LoadGraphicFile(Image,HomeDir+'Graphics\Texture'+inttostr(Age+1), gfJPG);
    1191   clBevelLight:=Colors.Canvas.Pixels[clkAge0+Age,cliBevelLight];
    1192   clBevelShade:=Colors.Canvas.Pixels[clkAge0+Age,cliBevelShade];
    1193   clTextLight:=Colors.Canvas.Pixels[clkAge0+Age,cliTextLight];
    1194   clTextShade:=Colors.Canvas.Pixels[clkAge0+Age,cliTextShade];
    1195   clLitText:=Colors.Canvas.Pixels[clkAge0+Age,cliLitText];
    1196   clMark:=Colors.Canvas.Pixels[clkAge0+Age,cliMark];
    1197   clPage:=Colors.Canvas.Pixels[clkAge0+Age,cliPage];
    1198   clCover:=Colors.Canvas.Pixels[clkAge0+Age,cliCover];
    1199   result:=true
    1200   end
    1201 else result:=false
    1202 end;
    1203 
    1204 
    1205 var
    1206 i,p,size: integer;
    1207 s: string;
    1208 fontscript: TextFile;
    1209 section: TFontType;
    1210 Reg: TRegistry;
     1372        for i := 0 to pos div 8 - 1 do
     1373          BitBlt(Handle, x + i * 8, y, 8, 7,
     1374            GrExt[HGrSystem].Data.Canvas.Handle, 104, 9 + 8 * Kind, SRCCOPY);
     1375        BitBlt(Handle, x + 8 * (pos div 8), y, pos - 8 * (pos div 8), 7,
     1376          GrExt[HGrSystem].Data.Canvas.Handle, 104, 9 + 8 * Kind, SRCCOPY);
     1377        if Growth > 0 then
     1378        begin
     1379          for i := 0 to Growth div 8 - 1 do
     1380            BitBlt(Handle, x + pos + i * 8, y, 8, 7,
     1381              GrExt[HGrSystem].Data.Canvas.Handle, 112, 9 + 8 * Kind, SRCCOPY);
     1382          BitBlt(Handle, x + pos + 8 * (Growth div 8), y,
     1383            Growth - 8 * (Growth div 8), 7, GrExt[HGrSystem].Data.Canvas.Handle,
     1384            112, 9 + 8 * Kind, SRCCOPY);
     1385        end
     1386        else if Growth < 0 then
     1387        begin
     1388          for i := 0 to -Growth div 8 - 1 do
     1389            BitBlt(Handle, x + pos + i * 8, y, 8, 7,
     1390              GrExt[HGrSystem].Data.Canvas.Handle, 104, 1, SRCCOPY);
     1391          BitBlt(Handle, x + pos + 8 * (-Growth div 8), y,
     1392            -Growth - 8 * (-Growth div 8), 7,
     1393            GrExt[HGrSystem].Data.Canvas.Handle, 104, 1, SRCCOPY);
     1394        end;
     1395        Brush.Color := $000000;
     1396        FillRect(Rect(x + pos + abs(Growth), y, x + max, y + 7));
     1397        Brush.Style := bsClear;
     1398      end
     1399    end;
     1400
     1401    // pos and growth are relative to max, set size independent
     1402    procedure PaintRelativeProgressBar(ca: TCanvas;
     1403      Kind, x, y, size, pos, Growth, max: integer; IndicateComplete: boolean;
     1404      const T: TTexture);
     1405    begin
     1406      if Growth > 0 then
     1407        PaintProgressBar(ca, Kind, x, y, pos * size div max,
     1408          (Growth * size + max div 2) div max, size, T)
     1409      else
     1410        PaintProgressBar(ca, Kind, x, y, pos * size div max,
     1411          (Growth * size - max div 2) div max, size, T);
     1412      if IndicateComplete and (pos + Growth >= max) then
     1413        Sprite(ca, HGrSystem, x + size - 10, y - 7, 23, 16, 1, 129);
     1414    end;
     1415
     1416    procedure PaintLogo(ca: TCanvas; x, y, clLight, clShade: integer);
     1417    begin
     1418      BitBlt(LogoBuffer.Canvas.Handle, 0, 0, wLogo, hLogo, ca.Handle, x,
     1419        y, SRCCOPY);
     1420      ImageOp_BCC(LogoBuffer, Templates, 0, 0, 1, 1, wLogo, hLogo,
     1421        clLight, clShade);
     1422      BitBlt(ca.Handle, x, y, wLogo, hLogo, LogoBuffer.Canvas.Handle, 0,
     1423        0, SRCCOPY);
     1424    end;
     1425
     1426    function SetMainTextureByAge(Age: integer): boolean;
     1427    begin
     1428      if Age <> MainTextureAge then
     1429        with MainTexture do
     1430        begin
     1431          MainTextureAge := Age;
     1432          LoadGraphicFile(Image, HomeDir + 'Graphics\Texture' +
     1433            IntToStr(Age + 1), gfJPG);
     1434          clBevelLight := Colors.Canvas.Pixels[clkAge0 + Age, cliBevelLight];
     1435          clBevelShade := Colors.Canvas.Pixels[clkAge0 + Age, cliBevelShade];
     1436          clTextLight := Colors.Canvas.Pixels[clkAge0 + Age, cliTextLight];
     1437          clTextShade := Colors.Canvas.Pixels[clkAge0 + Age, cliTextShade];
     1438          clLitText := Colors.Canvas.Pixels[clkAge0 + Age, cliLitText];
     1439          clMark := Colors.Canvas.Pixels[clkAge0 + Age, cliMark];
     1440          clPage := Colors.Canvas.Pixels[clkAge0 + Age, cliPage];
     1441          clCover := Colors.Canvas.Pixels[clkAge0 + Age, cliCover];
     1442          result := true
     1443        end
     1444      else
     1445        result := false
     1446    end;
     1447
     1448    var
     1449      i, p, size: integer;
     1450      s: string;
     1451      fontscript: TextFile;
     1452      section: TFontType;
     1453      Reg: TRegistry;
    12111454
    12121455initialization
    1213 Reg:=TRegistry.Create;
    1214 Reg.OpenKey('SOFTWARE\cevo\RegVer9',true);
     1456
     1457Reg := TRegistry.create;
     1458Reg.OpenKey('SOFTWARE\cevo\RegVer9', true);
    12151459try
    1216   Gamma:=Reg.ReadInteger('Gamma');
     1460  Gamma := Reg.ReadInteger('Gamma');
    12171461except
    1218   Gamma:=100;
    1219   Reg.WriteInteger('Gamma',Gamma);
    1220   end;
     1462  Gamma := 100;
     1463  Reg.WriteInteger('Gamma', Gamma);
     1464end;
    12211465Reg.closekey;
    12221466Reg.Free;
    12231467
    1224 if Gamma<>100 then
    1225   begin
    1226   GammaLUT[0]:=0;
    1227   for i:=1 to 255 do
    1228     begin
    1229     p:=round(255.0*exp(ln(i/255.0)*100.0/Gamma));
    1230     assert((p>=0) and (p<256));
    1231     GammaLUT[i]:=p;
    1232     end;
    1233   end;
     1468if Gamma <> 100 then
     1469begin
     1470  GammaLUT[0] := 0;
     1471  for i := 1 to 255 do
     1472  begin
     1473    p := round(255.0 * exp(ln(i / 255.0) * 100.0 / Gamma));
     1474    assert((p >= 0) and (p < 256));
     1475    GammaLUT[i] := p;
     1476  end;
     1477end;
    12341478
    12351479EnumDisplaySettings(nil, $FFFFFFFF, StartResolution);
    1236 ResolutionChanged:=false;
    1237 
    1238 Phrases:=TStringTable.Create;
    1239 Phrases2:=TStringTable.Create;
    1240 Phrases2FallenBackToEnglish:=false;
    1241 if FileExists(DataDir+'Localization\Language.txt') then
    1242   begin
    1243   Phrases.LoadFromFile(DataDir+'Localization\Language.txt');
    1244   if FileExists(DataDir+'Localization\Language2.txt') then
    1245     Phrases2.LoadFromFile(DataDir+'Localization\Language2.txt')
     1480ResolutionChanged := false;
     1481
     1482Phrases := TStringTable.create;
     1483Phrases2 := TStringTable.create;
     1484Phrases2FallenBackToEnglish := false;
     1485if FileExists(DataDir + 'Localization\Language.txt') then
     1486begin
     1487  Phrases.loadfromfile(DataDir + 'Localization\Language.txt');
     1488  if FileExists(DataDir + 'Localization\Language2.txt') then
     1489    Phrases2.loadfromfile(DataDir + 'Localization\Language2.txt')
    12461490  else
    1247     begin
    1248     Phrases2.LoadFromFile(HomeDir+'Language2.txt');
    1249     Phrases2FallenBackToEnglish:=true;
    1250     end
    1251   end
     1491  begin
     1492    Phrases2.loadfromfile(HomeDir + 'Language2.txt');
     1493    Phrases2FallenBackToEnglish := true;
     1494  end
     1495end
    12521496else
    1253   begin
    1254   Phrases.LoadFromFile(HomeDir+'Language.txt');
    1255   Phrases2.LoadFromFile(HomeDir+'Language2.txt');
    1256   end;
    1257 Sounds:=TStringTable.Create;
    1258 if not Sounds.LoadFromFile(HomeDir+'Sounds\sound.txt') then
    1259   begin Sounds.Free; Sounds:=nil end;
    1260 
    1261 for section:=Low(TFontType) to High(TFontType) do
    1262   UniFont[section]:=TFont.Create;
    1263 
    1264 LogoBuffer:=TBitmap.Create;
    1265 LogoBuffer.PixelFormat:=pf24bit;
    1266 LogoBuffer.Width:=wBBook;
    1267 LogoBuffer.Height:=hBBook;
    1268 
    1269 section:=ftNormal;
    1270 AssignFile(fontscript,LocalizedFilePath('Fonts.txt'));
     1497begin
     1498  Phrases.loadfromfile(HomeDir + 'Language.txt');
     1499  Phrases2.loadfromfile(HomeDir + 'Language2.txt');
     1500end;
     1501Sounds := TStringTable.create;
     1502if not Sounds.loadfromfile(HomeDir + 'Sounds\sound.txt') then
     1503begin
     1504  Sounds.Free;
     1505  Sounds := nil
     1506end;
     1507
     1508for section := Low(TFontType) to High(TFontType) do
     1509  UniFont[section] := TFont.create;
     1510
     1511LogoBuffer := TBitmap.create;
     1512LogoBuffer.PixelFormat := pf24bit;
     1513LogoBuffer.Width := wBBook;
     1514LogoBuffer.Height := hBBook;
     1515
     1516section := ftNormal;
     1517AssignFile(fontscript, LocalizedFilePath('Fonts.txt'));
    12711518try
    12721519  Reset(fontscript);
    12731520  while not eof(fontscript) do
    1274     begin
    1275     ReadLn(fontscript,s);
    1276     if s<>'' then
    1277       if s[1]='#' then
     1521  begin
     1522    ReadLn(fontscript, s);
     1523    if s <> '' then
     1524      if s[1] = '#' then
     1525      begin
     1526        s := TrimRight(s);
     1527        if s = '#SMALL' then
     1528          section := ftSmall
     1529        else if s = '#TINY' then
     1530          section := ftTiny
     1531        else if s = '#CAPTION' then
     1532          section := ftCaption
     1533        else if s = '#BUTTON' then
     1534          section := ftButton
     1535        else
     1536          section := ftNormal;
     1537      end
     1538      else
     1539      begin
     1540        p := pos(',', s);
     1541        if p > 0 then
    12781542        begin
    1279         s:=TrimRight(s);
    1280         if s='#SMALL' then section:=ftSmall
    1281         else if s='#TINY' then section:=ftTiny
    1282         else if s='#CAPTION' then section:=ftCaption
    1283         else if s='#BUTTON' then section:=ftButton
    1284         else section:=ftNormal;
     1543          UniFont[section].Name := Trim(copy(s, 1, p - 1));
     1544          size := 0;
     1545          for i := p + 1 to Length(s) do
     1546            case s[i] of
     1547              '0' .. '9':
     1548                size := size * 10 + Byte(s[i]) - 48;
     1549              'B', 'b':
     1550                UniFont[section].Style := UniFont[section].Style + [fsBold];
     1551              'I', 'i':
     1552                UniFont[section].Style := UniFont[section].Style + [fsItalic];
     1553            end;
     1554          UniFont[section].size :=
     1555            round(size * 72 / UniFont[section].PixelsPerInch);
    12851556        end
    1286       else
    1287         begin
    1288         p:=pos(',',s);
    1289         if p>0 then
    1290           begin
    1291           UniFont[section].Name:=Trim(copy(s,1,p-1));
    1292           size:=0;
    1293           for i:=p+1 to length(s) do
    1294             case s[i] of
    1295               '0'..'9': size:=size*10+byte(s[i])-48;
    1296               'B','b': UniFont[section].Style:=UniFont[section].Style+[fsBold];
    1297               'I','i': UniFont[section].Style:=UniFont[section].Style+[fsItalic];
    1298               end;
    1299           UniFont[section].Size:=Round(size * 72/UniFont[section].PixelsPerInch);
    1300           end
    1301         end
    1302     end;
     1557      end
     1558  end;
    13031559  CloseFile(fontscript);
    13041560except
    1305   end;
    1306 
    1307 nGrExt:=0;
    1308 HGrSystem:=LoadGraphicSet('System');
    1309 HGrSystem2:=LoadGraphicSet('System2');
    1310 Templates:=TBitmap.Create;
    1311 LoadGraphicFile(Templates, HomeDir+'Graphics\Templates', gfNoGamma);
    1312 Templates.PixelFormat:=pf24bit;
    1313 Colors:=TBitmap.Create;
    1314 LoadGraphicFile(Colors,HomeDir+'Graphics\Colors');
    1315 Paper:=TBitmap.Create;
    1316 LoadGraphicFile(Paper,HomeDir+'Graphics\Paper',gfJPG);
    1317 BigImp:=TBitmap.Create;
    1318 LoadGraphicFile(BigImp, HomeDir+'Graphics\Icons');
    1319 MainTexture.Image:=TBitmap.Create;
    1320 MainTextureAge:=-2;
    1321 ClickFrameColor:=GrExt[HGrSystem].Data.Canvas.Pixels[187,175];
    1322 InitOrnamentDone:=false;
    1323 GenerateNames:=true;
     1561end;
     1562
     1563nGrExt := 0;
     1564HGrSystem := LoadGraphicSet('System');
     1565HGrSystem2 := LoadGraphicSet('System2');
     1566Templates := TBitmap.create;
     1567LoadGraphicFile(Templates, HomeDir + 'Graphics\Templates', gfNoGamma);
     1568Templates.PixelFormat := pf24bit;
     1569Colors := TBitmap.create;
     1570LoadGraphicFile(Colors, HomeDir + 'Graphics\Colors');
     1571Paper := TBitmap.create;
     1572LoadGraphicFile(Paper, HomeDir + 'Graphics\Paper', gfJPG);
     1573BigImp := TBitmap.create;
     1574LoadGraphicFile(BigImp, HomeDir + 'Graphics\Icons');
     1575MainTexture.Image := TBitmap.create;
     1576MainTextureAge := -2;
     1577ClickFrameColor := GrExt[HGrSystem].Data.Canvas.Pixels[187, 175];
     1578InitOrnamentDone := false;
     1579GenerateNames := true;
    13241580
    13251581finalization
     1582
    13261583RestoreResolution;
    1327 for i:=0 to nGrExt-1 do
    1328   begin
    1329   GrExt[i].Data.Free; GrExt[i].Mask.Free;
     1584for i := 0 to nGrExt - 1 do
     1585begin
     1586  GrExt[i].Data.Free;
     1587  GrExt[i].Mask.Free;
    13301588  FreeMem(GrExt[i]);
    1331   end;
    1332 for section:=Low(TFontType) to High(TFontType) do
     1589end;
     1590for section := Low(TFontType) to High(TFontType) do
    13331591  UniFont[section].Free;
    13341592Phrases.Free;
    1335 if Sounds<>nil then Sounds.Free;
     1593if Sounds <> nil then
     1594  Sounds.Free;
    13361595LogoBuffer.Free;
    13371596BigImp.Free;
     
    13421601
    13431602end.
    1344 
  • trunk/Sound.pas

    r2 r6  
    44
    55uses
    6 Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,MMSystem;
    7 
     6  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, MMSystem;
    87
    98function PrepareSound(FileName: string): integer;
    109procedure PlaySound(FileName: string);
    1110
    12 
    1311type
    14 TSoundPlayer = class(TForm)
    15 private
    16   procedure OnMCI(var m:TMessage); message MM_MCINOTIFY;
     12  TSoundPlayer = class(TForm)
     13  private
     14    procedure OnMCI(var m: TMessage); message MM_MCINOTIFY;
    1715  end;
    18 
    1916
    2017implementation
     
    2219{$R *.DFM}
    2320
    24 
    2521type
    26 TSound = class
    27 public
    28   FDeviceID: word;
    29   FFileName: string;
    30   constructor Create(const FileName : string);
    31   destructor Destroy; override;
    32   procedure Play(HWND: DWORD);
    33   procedure Stop;
    34   procedure Reset;
     22  TSound = class
     23  public
     24    FDeviceID: word;
     25    FFileName: string;
     26    constructor Create(const FileName: string);
     27    destructor Destroy; override;
     28    procedure Play(HWND: DWORD);
     29    procedure Stop;
     30    procedure Reset;
    3531  end;
    36 
    3732
    3833constructor TSound.Create(const FileName: string);
    3934var
    40 OpenParm: TMCI_Open_Parms;
     35  OpenParm: TMCI_Open_Parms;
    4136begin
    42 FDeviceID:=0;
    43 FFileName:=FileName;
    44 if FileExists(FFileName) then
     37  FDeviceID := 0;
     38  FFileName := FileName;
     39  if FileExists(FFileName) then
    4540  begin
    46   OpenParm.dwCallback:=0;
    47   OpenParm.lpstrDeviceType:='WaveAudio';
    48   OpenParm.lpstrElementName:=PChar(FFileName);
    49   mciSendCommand(0, MCI_Open,
    50     MCI_WAIT or MCI_OPEN_ELEMENT or MCI_OPEN_SHAREABLE, integer(@OpenParm));
    51   FDeviceID:=OpenParm.wDeviceID;
     41    OpenParm.dwCallback := 0;
     42    OpenParm.lpstrDeviceType := 'WaveAudio';
     43    OpenParm.lpstrElementName := PChar(FFileName);
     44    mciSendCommand(0, MCI_Open, MCI_WAIT or MCI_OPEN_ELEMENT or
     45      MCI_OPEN_SHAREABLE, integer(@OpenParm));
     46    FDeviceID := OpenParm.wDeviceID;
    5247  end
    5348end;
     
    5550destructor TSound.Destroy;
    5651begin
    57 if FDeviceID<>0 then
    58   mciSendCommand(FDeviceID, MCI_CLOSE, MCI_WAIT, 0);
    59 inherited Destroy;
     52  if FDeviceID <> 0 then
     53    mciSendCommand(FDeviceID, MCI_CLOSE, MCI_WAIT, 0);
     54  inherited Destroy;
    6055end;
    6156
    62 procedure TSound.Play(HWND: dword);
     57procedure TSound.Play(HWND: DWORD);
    6358var
    64 PlayParm: TMCI_Play_Parms;
     59  PlayParm: TMCI_Play_Parms;
    6560begin
    66 if FDeviceID<>0 then
     61  if FDeviceID <> 0 then
    6762  begin
    68   PlayParm.dwCallback:=HWND;
    69   mciSendCommand(FDeviceID, MCI_PLAY, MCI_NOTIFY, integer(@PlayParm));
     63    PlayParm.dwCallback := HWND;
     64    mciSendCommand(FDeviceID, MCI_PLAY, MCI_NOTIFY, integer(@PlayParm));
    7065  end
    7166end;
     
    7368procedure TSound.Stop;
    7469begin
    75 mciSendCommand(FDeviceID, MCI_STOP, 0, 0);
     70  mciSendCommand(FDeviceID, MCI_STOP, 0, 0);
    7671end;
    7772
    7873procedure TSound.Reset;
    7974begin
    80 mciSendCommand(FDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
     75  mciSendCommand(FDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
    8176end;
    8277
    83 
    8478type
    85 TSoundList=array[0..99999] of TSound;
     79  TSoundList = array [0 .. 99999] of TSound;
    8680
    8781var
    88 nSoundList: integer;
    89 SoundPlayer: TSoundPlayer;
    90 SoundList: ^TSoundList;
    91 PlayingSound: TSound;
    92 
     82  nSoundList: integer;
     83  SoundPlayer: TSoundPlayer;
     84  SoundList: ^TSoundList;
     85  PlayingSound: TSound;
    9386
    9487procedure TSoundPlayer.OnMCI(var m: TMessage);
    9588begin
    96 if (m.wParam=MCI_Notify_Successful) and (PlayingSound<>nil) then
     89  if (m.wParam = MCI_Notify_Successful) and (PlayingSound <> nil) then
    9790  begin
    98   PlayingSound.Reset;
    99   PlayingSound:=nil;
     91    PlayingSound.Reset;
     92    PlayingSound := nil;
    10093  end;
    10194end;
    10295
    103 
    10496function PrepareSound(FileName: string): integer;
    10597begin
    106 for result:=1 to Length(FileName) do
    107   FileName[result]:=upcase(FileName[result]);
    108 result:=0;
    109 while (result<nSoundList) and (SoundList[result].FFileName<>FileName) do
    110   inc(result);
    111 if result=nSoundList then
     98  for result := 1 to Length(FileName) do
     99    FileName[result] := upcase(FileName[result]);
     100  result := 0;
     101  while (result < nSoundList) and (SoundList[result].FFileName <> FileName) do
     102    inc(result);
     103  if result = nSoundList then
    112104  begin // first time this sound is played
    113   if nSoundList=0 then
    114     ReallocMem(SoundList, 16*4)
    115   else if (nSoundList>=16) and (nSoundList and (nSoundList-1)=0) then
    116     ReallocMem(SoundList, nSoundList*(2*4));
    117   inc(nSoundList);
    118   SoundList[result]:=TSound.Create(FileName);
     105    if nSoundList = 0 then
     106      ReallocMem(SoundList, 16 * 4)
     107    else if (nSoundList >= 16) and (nSoundList and (nSoundList - 1) = 0) then
     108      ReallocMem(SoundList, nSoundList * (2 * 4));
     109    inc(nSoundList);
     110    SoundList[result] := TSound.Create(FileName);
    119111  end;
    120112end;
     
    122114procedure PlaySound(FileName: string);
    123115begin
    124 if PlayingSound<>nil then
    125   exit;
    126 if SoundPlayer=nil then
    127   Application.CreateForm(TSoundPlayer, SoundPlayer);
    128 PlayingSound:=SoundList[PrepareSound(FileName)];
    129 if PlayingSound.FDeviceID=0 then PlayingSound:=nil
    130 else PlayingSound.Play(SoundPlayer.Handle);
     116  if PlayingSound <> nil then
     117    exit;
     118  if SoundPlayer = nil then
     119    Application.CreateForm(TSoundPlayer, SoundPlayer);
     120  PlayingSound := SoundList[PrepareSound(FileName)];
     121  if PlayingSound.FDeviceID = 0 then
     122    PlayingSound := nil
     123  else
     124    PlayingSound.Play(SoundPlayer.Handle);
    131125end;
    132126
    133127var
    134 i: integer;
     128  i: integer;
    135129
    136130initialization
    137 nSoundList:=0;
    138 SoundList:=nil;
    139 PlayingSound:=nil;
    140 SoundPlayer:=nil;
     131
     132nSoundList := 0;
     133SoundList := nil;
     134PlayingSound := nil;
     135SoundPlayer := nil;
    141136
    142137finalization
    143 if PlayingSound<>nil then
    144   begin
     138
     139if PlayingSound <> nil then
     140begin
    145141  PlayingSound.Stop;
    146142  Sleep(222);
    147   end;
    148 for i:=0 to nSoundList-1 do
     143end;
     144for i := 0 to nSoundList - 1 do
    149145  SoundList[i].Free;
    150 ReallocMem(SoundList,0);
     146ReallocMem(SoundList, 0);
    151147
    152148end.
    153 
  • trunk/Start.pas

    r4 r6  
    11{$INCLUDE switches}
    2 
    32unit Start;
    43
     
    65
    76uses
    8   GameServer,Messg,ButtonBase,ButtonA,ButtonC,ButtonB,Area,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,StdCtrls,
    11   Menus,Registry;
    12 
     7  GameServer, Messg, ButtonBase, ButtonA, ButtonC, ButtonB, Area,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls,
     10  Menus, Registry;
    1311
    1412const
    15 // main actions
    16 nMainActions=5; maConfig=0; maManual=1; maCredits=2; maAIDev=3; maWeb=4;
    17 
     13  // main actions
     14  nMainActions = 5;
     15  maConfig = 0;
     16  maManual = 1;
     17  maCredits = 2;
     18  maAIDev = 3;
     19  maWeb = 4;
    1820
    1921type
     
    3537    AutoEnemyDownBtn: TButtonC;
    3638    ReplayBtn: TButtonB;
    37     procedure StartBtnClick(Sender:TObject);
    38     procedure FormPaint(Sender:TObject);
    39     procedure FormShow(Sender:TObject);
    40     procedure FormDestroy(Sender:TObject);
    41     procedure FormCreate(Sender:TObject);
     39    procedure StartBtnClick(Sender: TObject);
     40    procedure FormPaint(Sender: TObject);
     41    procedure FormShow(Sender: TObject);
     42    procedure FormDestroy(Sender: TObject);
     43    procedure FormCreate(Sender: TObject);
    4244    procedure BrainClick(Sender: TObject);
    4345    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
     
    5557    procedure FormHide(Sender: TObject);
    5658    procedure QuitBtnClick(Sender: TObject);
    57     procedure FormKeyDown(Sender: TObject; var Key: Word;
    58       Shift: TShiftState);
     59    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    5960    procedure CustomizeBtnClick(Sender: TObject);
    6061    procedure AutoDiffUpBtnClick(Sender: TObject);
    6162    procedure AutoDiffDownBtnClick(Sender: TObject);
    6263    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
    63       Shift: TShiftState; X, Y: Integer);
    64     procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
    65       Y: Integer);
     64      Shift: TShiftState; x, y: integer);
     65    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; x, y: integer);
    6666    procedure AutoEnemyUpBtnClick(Sender: TObject);
    6767    procedure AutoEnemyDownBtnClick(Sender: TObject);
    6868    procedure ReplayBtnClick(Sender: TObject);
    6969  public
    70     BrainPicture: array[0..maxBrain-1] of TBitmap;
     70    BrainPicture: array [0 .. maxBrain - 1] of TBitmap;
    7171    EmptyPicture: TBitmap;
    7272    procedure UpdateFormerGames;
     
    7474  private
    7575    WorldSize, StartLandMass, MaxTurn, AutoEnemies, AutoDiff, MultiControl,
    76     MiniWidth, MiniHeight, SelectedAction,
    77     Page, ShowTab, Tab, Diff0, bixDefault,
    78     nMapLandTiles,nMapStartPositions,
    79     LoadTurn, LastTurn, {last turn of selected former game}
    80     SlotAvailable,
    81     bixPopup: integer; {brain concerned by brain context menu}
    82     ListIndex: array[0..3] of integer;
     76      MiniWidth, MiniHeight, SelectedAction, Page, ShowTab, Tab, Diff0,
     77      bixDefault, nMapLandTiles, nMapStartPositions, LoadTurn, LastTurn,
     78    { last turn of selected former game }
     79    SlotAvailable, bixPopup: integer; { brain concerned by brain context menu }
     80    ListIndex: array [0 .. 3] of integer;
    8381    MapFileName: string;
    8482    FormerGames, Maps: TStringList;
    85     LogoBuffer,
    86     Mini:TBitmap; {game world sample preview}
    87     MiniColors: array[0..11,0..1] of TColor;
    88 //    BookDate: string;
    89     DiffUpBtn: array[0..8] of TButtonC;
    90     DiffDownBtn: array[0..8] of TButtonC;
    91     MultiBtn: array[6..8] of TButtonC;
    92     MiniMode: (mmNone,mmPicture,mmMultiPlayer);
    93     ActionsOffered: set of 0..nMainActions-1;
     83    LogoBuffer, Mini: TBitmap; { game world sample preview }
     84    MiniColors: array [0 .. 11, 0 .. 1] of TColor;
     85    // BookDate: string;
     86    DiffUpBtn: array [0 .. 8] of TButtonC;
     87    DiffDownBtn: array [0 .. 8] of TButtonC;
     88    MultiBtn: array [6 .. 8] of TButtonC;
     89    MiniMode: (mmNone, mmPicture, mmMultiPlayer);
     90    ActionsOffered: set of 0 .. nMainActions - 1;
    9491    TurnValid, Tracking: boolean;
    9592    procedure InitPopup(PopupIndex: integer);
     
    9895    procedure ChangeTab(NewTab: integer);
    9996    procedure UnlistBackupFile(FileName: string);
    100     procedure SmartInvalidate(x0,y0,x1,y1: integer; invalidateTab0: boolean = false);
    101   end;
    102 
    103 var
    104   StartDlg:TStartDlg;
     97    procedure SmartInvalidate(x0, y0, x1, y1: integer;
     98      invalidateTab0: boolean = false);
     99  end;
     100
     101var
     102  StartDlg: TStartDlg;
    105103
    106104implementation
    107105
    108106uses
    109 Directories, Protocol, Direct, ScreenTools, Inp, Back,
    110 
    111 ShellAPI;
     107  Directories, Protocol, Direct, ScreenTools, Inp, Back,
     108
     109  ShellAPI;
    112110
    113111{$R *.DFM}
    114112
    115113const
    116 // predefined world size
    117 // attention: lx*ly+1 must be prime!
    118 {nWorldSize=8;
    119 lxpre: array[0..nWorldSize-1] of integer =(30,40,50,60,70,90,110,130);
    120 lypre: array[0..nWorldSize-1] of integer =(46,52,60,70,84,94,110,130);
    121 DefaultWorldTiles=4200;}
    122 nWorldSize=6;
    123 lxpre: array[0..nWorldSize-1] of integer =(30,40,50,60,75,100);
    124 lypre: array[0..nWorldSize-1] of integer =(46,52,60,70,82,96);
    125 DefaultWorldTiles=4150;
    126 DefaultWorldSize=3;
    127 DefaultLandMass=30;
    128 
    129 nPlOffered=9;
    130 yMain=14;
    131 xActionIcon=55; xAction=111; yAction=60; ActionPitch=56; ActionSideBorder=24;
    132 ActionBottomBorder=10;
    133 wBuffer=91;
    134 x0Mini=437; y0Mini=178;
    135 xTurnSlider=346; yTurnSlider=262; wTurnSlider=168;
    136 yLogo=74;
    137 xDefault=234; yDefault=148;
    138 x0Brain=146; y0Brain=148;
    139 dxBrain=104; dyBrain=80;
    140 xBrain: array[0..nPlOffered-1] of integer =
    141   (x0Brain,x0Brain,x0Brain+dxBrain,x0Brain+dxBrain,x0Brain+dxBrain,x0Brain,
    142   x0Brain-dxBrain,x0Brain-dxBrain,x0Brain-dxBrain);
    143 yBrain: array[0..nPlOffered-1] of integer =
    144   (y0Brain,y0Brain-dyBrain,y0Brain-dyBrain,y0Brain,y0Brain+dyBrain,
    145   y0Brain+dyBrain,y0Brain+dyBrain,y0Brain,y0Brain-dyBrain);
    146 TabOffset=-115; TabSize=159; TabHeight=40;
    147 
    148 MaxWidthMapLogo=96; MaxHeightMapLogo=96;
    149 
    150 InitAlive: array[1..nPl] of integer=
    151 (1,1+2,1+2+32,1+2+8+128,1+2+8+32+128,1+2+8+16+64+128,1+2+4+16+32+64+256,
    152 511-32,511,511-32,511,511-32,511,511-32,511);
    153 InitMulti: array[nPlOffered+1..nPl] of integer=
    154 (256,256,256+128,256+128,256+128+64,256+128+64);
    155 
    156 pgStartRandom=0; pgStartMap=1; pgNoLoad=2; pgLoad=3; pgEditRandom=4;
    157 pgEditMap=5; pgMain=6;
    158 
    159 OfferMultiple=[6,7,8];
    160 
    161 PlayerAutoDiff: array[1..5] of integer=(1,1,2,2,3);
    162 EnemyAutoDiff: array[1..5] of integer=(4,3,2,1,1);
    163 
    164 
    165 procedure TStartDlg.FormCreate(Sender:TObject);
    166 var
    167 x,y,i,ResolutionX,ResolutionY,ResolutionBPP,ResolutionFreq,ScreenMode: integer;
    168 DefaultAI,s: string;
    169 r0,r1: HRgn;
    170 Reg: TRegistry;
    171 FirstStart: boolean;
    172 begin
    173 Reg:=TRegistry.Create;
    174 FirstStart:=not Reg.KeyExists('SOFTWARE\cevo\RegVer9\Start');
    175 
    176 if FirstStart then
    177   begin
    178   // initialize AI assignment
    179   Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start',true);
    180   for i:=0 to nPlOffered-1 do
    181     begin
    182     if i=0 then s:=':StdIntf'
    183     else s:='StdAI';
    184     Reg.WriteString('Control'+IntToStr(i),s);
    185     Reg.WriteInteger('Diff'+IntToStr(i),2);
     114  // predefined world size
     115  // attention: lx*ly+1 must be prime!
     116  { nWorldSize=8;
     117    lxpre: array[0..nWorldSize-1] of integer =(30,40,50,60,70,90,110,130);
     118    lypre: array[0..nWorldSize-1] of integer =(46,52,60,70,84,94,110,130);
     119    DefaultWorldTiles=4200; }
     120  nWorldSize = 6;
     121  lxpre: array [0 .. nWorldSize - 1] of integer = (30, 40, 50, 60, 75, 100);
     122  lypre: array [0 .. nWorldSize - 1] of integer = (46, 52, 60, 70, 82, 96);
     123  DefaultWorldTiles = 4150;
     124  DefaultWorldSize = 3;
     125  DefaultLandMass = 30;
     126
     127  nPlOffered = 9;
     128  yMain = 14;
     129  xActionIcon = 55;
     130  xAction = 111;
     131  yAction = 60;
     132  ActionPitch = 56;
     133  ActionSideBorder = 24;
     134  ActionBottomBorder = 10;
     135  wBuffer = 91;
     136  x0Mini = 437;
     137  y0Mini = 178;
     138  xTurnSlider = 346;
     139  yTurnSlider = 262;
     140  wTurnSlider = 168;
     141  yLogo = 74;
     142  xDefault = 234;
     143  yDefault = 148;
     144  x0Brain = 146;
     145  y0Brain = 148;
     146  dxBrain = 104;
     147  dyBrain = 80;
     148  xBrain: array [0 .. nPlOffered - 1] of integer = (x0Brain, x0Brain,
     149    x0Brain + dxBrain, x0Brain + dxBrain, x0Brain + dxBrain, x0Brain,
     150    x0Brain - dxBrain, x0Brain - dxBrain, x0Brain - dxBrain);
     151  yBrain: array [0 .. nPlOffered - 1] of integer = (y0Brain, y0Brain - dyBrain,
     152    y0Brain - dyBrain, y0Brain, y0Brain + dyBrain, y0Brain + dyBrain,
     153    y0Brain + dyBrain, y0Brain, y0Brain - dyBrain);
     154  TabOffset = -115;
     155  TabSize = 159;
     156  TabHeight = 40;
     157
     158  MaxWidthMapLogo = 96;
     159  MaxHeightMapLogo = 96;
     160
     161  InitAlive: array [1 .. nPl] of integer = (1, 1 + 2, 1 + 2 + 32,
     162    1 + 2 + 8 + 128, 1 + 2 + 8 + 32 + 128, 1 + 2 + 8 + 16 + 64 + 128,
     163    1 + 2 + 4 + 16 + 32 + 64 + 256, 511 - 32, 511, 511 - 32, 511, 511 - 32, 511,
     164    511 - 32, 511);
     165  InitMulti: array [nPlOffered + 1 .. nPl] of integer = (256, 256, 256 + 128,
     166    256 + 128, 256 + 128 + 64, 256 + 128 + 64);
     167
     168  pgStartRandom = 0;
     169  pgStartMap = 1;
     170  pgNoLoad = 2;
     171  pgLoad = 3;
     172  pgEditRandom = 4;
     173  pgEditMap = 5;
     174  pgMain = 6;
     175
     176  OfferMultiple = [6, 7, 8];
     177
     178  PlayerAutoDiff: array [1 .. 5] of integer = (1, 1, 2, 2, 3);
     179  EnemyAutoDiff: array [1 .. 5] of integer = (4, 3, 2, 1, 1);
     180
     181procedure TStartDlg.FormCreate(Sender: TObject);
     182var
     183  x, y, i, ResolutionX, ResolutionY, ResolutionBPP, ResolutionFreq,
     184    ScreenMode: integer;
     185  DefaultAI, s: string;
     186  r0, r1: HRgn;
     187  Reg: TRegistry;
     188  FirstStart: boolean;
     189begin
     190  Reg := TRegistry.Create;
     191  FirstStart := not Reg.KeyExists('SOFTWARE\cevo\RegVer9\Start');
     192
     193  if FirstStart then
     194  begin
     195    // initialize AI assignment
     196    Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start', true);
     197    for i := 0 to nPlOffered - 1 do
     198    begin
     199      if i = 0 then
     200        s := ':StdIntf'
     201      else
     202        s := 'StdAI';
     203      Reg.WriteString('Control' + IntToStr(i), s);
     204      Reg.WriteInteger('Diff' + IntToStr(i), 2);
    186205    end;
    187   Reg.WriteInteger('MultiControl',0);
    188   Reg.closekey;
    189 
    190   // register file type: "cevo Book" -- fails with no administrator rights!
    191   try
    192     Reg.RootKey:=HKEY_CLASSES_ROOT;
    193     Reg.OpenKey ('.cevo',true);
    194     Reg.WriteString ('','cevoBook');
     206    Reg.WriteInteger('MultiControl', 0);
    195207    Reg.closekey;
    196     Reg.OpenKey ('cevoBook',true);
    197     Reg.WriteString ('','cevo Book');
     208
     209    // register file type: "cevo Book" -- fails with no administrator rights!
     210    try
     211      Reg.RootKey := HKEY_CLASSES_ROOT;
     212      Reg.OpenKey('.cevo', true);
     213      Reg.WriteString('', 'cevoBook');
     214      Reg.closekey;
     215      Reg.OpenKey('cevoBook', true);
     216      Reg.WriteString('', 'cevo Book');
     217      Reg.closekey;
     218      Reg.OpenKey('cevoBook\DefaultIcon', true);
     219      Reg.WriteString('', ParamStr(0) + ',0');
     220      Reg.closekey;
     221      Reg.OpenKey('cevoBook\shell\open\command', true);
     222      Reg.WriteString('', ParamStr(0) + ' "%1"');
     223      Reg.closekey;
     224    except
     225    end;
     226  end
     227  else
     228  begin
     229    Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start', false);
     230    try
     231      WorldSize := Reg.ReadInteger('WorldSize');
     232      StartLandMass := Reg.ReadInteger('LandMass');
     233      MaxTurn := Reg.ReadInteger('MaxTurn');
     234      DefaultAI := Reg.ReadString('DefaultAI');
     235      AutoEnemies := Reg.ReadInteger('AutoEnemies');
     236      AutoDiff := Reg.ReadInteger('AutoDiff');
     237    except
     238      FirstStart := true;
     239    end;
    198240    Reg.closekey;
    199     Reg.OpenKey ('cevoBook\DefaultIcon',true);
    200     Reg.WriteString ('',ParamStr(0)+',0');
     241  end;
     242
     243  FullScreen := true;
     244  if FirstStart then
     245  begin
     246    WorldSize := DefaultWorldSize;
     247    StartLandMass := DefaultLandMass;
     248    MaxTurn := 800;
     249    DefaultAI := 'StdAI';
     250    AutoEnemies := 8;
     251    AutoDiff := 1;
     252  end
     253  else
     254  begin
     255    Reg.OpenKey('SOFTWARE\cevo\RegVer9', false);
     256    try
     257      ScreenMode := Reg.ReadInteger('ScreenMode');
     258      FullScreen := ScreenMode > 0;
     259      if Reg.ValueExists('ResolutionX') then
     260        ResolutionX := Reg.ReadInteger('ResolutionX');
     261      if Reg.ValueExists('ResolutionY') then
     262        ResolutionY := Reg.ReadInteger('ResolutionY');
     263      if Reg.ValueExists('ResolutionBPP') then
     264        ResolutionBPP := Reg.ReadInteger('ResolutionBPP');
     265      if Reg.ValueExists('ResolutionFreq') then
     266        ResolutionFreq := Reg.ReadInteger('ResolutionFreq');
     267      if ScreenMode = 2 then
     268        ChangeResolution(ResolutionX, ResolutionY, ResolutionBPP,
     269          ResolutionFreq);
     270    except
     271    end;
    201272    Reg.closekey;
    202     Reg.OpenKey ('cevoBook\shell\open\command',true);
    203     Reg.WriteString ('',ParamStr(0)+' "%1"');
    204     Reg.closekey;
    205   except
    206     end;
    207   end
    208 else
    209   begin
    210   Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start',false);
    211   try
    212     WorldSize:=Reg.ReadInteger('WorldSize');
    213     StartLandMass:=Reg.ReadInteger('LandMass');
    214     MaxTurn:=Reg.ReadInteger('MaxTurn');
    215     DefaultAI:=Reg.ReadString('DefaultAI');
    216     AutoEnemies:=Reg.ReadInteger('AutoEnemies');
    217     AutoDiff:=Reg.ReadInteger('AutoDiff');
    218   except
    219     FirstStart:=true;
    220     end;
    221   Reg.closekey;
    222   end;
    223 
    224 FullScreen:=true;
    225 if FirstStart then
    226   begin
    227   WorldSize:=DefaultWorldSize;
    228   StartLandMass:=DefaultLandMass;
    229   MaxTurn:=800;
    230   DefaultAI:='StdAI';
    231   AutoEnemies:=8;
    232   AutoDiff:=1;
    233   end
    234 else
    235   begin
    236   Reg.OpenKey('SOFTWARE\cevo\RegVer9',false);
    237   try
    238     ScreenMode:=Reg.ReadInteger('ScreenMode');
    239     FullScreen:= ScreenMode>0;
    240     if Reg.ValueExists('ResolutionX') then
    241       ResolutionX:=Reg.ReadInteger('ResolutionX');
    242     if Reg.ValueExists('ResolutionY') then
    243         ResolutionY:=Reg.ReadInteger('ResolutionY');
    244     if Reg.ValueExists('ResolutionBPP') then
    245         ResolutionBPP:=Reg.ReadInteger('ResolutionBPP');
    246     if Reg.ValueExists('ResolutionFreq') then
    247         ResolutionFreq:=Reg.ReadInteger('ResolutionFreq');
    248     if ScreenMode=2 then
    249       ChangeResolution(ResolutionX,ResolutionY,ResolutionBPP,ResolutionFreq);
    250   except
    251     end;
    252   Reg.closekey;
    253   end;
    254 Reg.Free;
    255 
    256 ActionsOffered:=[maManual,maCredits,maWeb];
    257 if FileExists(HomeDir+'Configurator.exe') then
    258   include(ActionsOffered,maConfig);
    259 if FileExists(HomeDir+'AI Template\AI development manual.html') then
    260   include(ActionsOffered,maAIDev);
    261 
    262 bixDefault:=-1;
    263 for i:=bixRandom to nBrain-1 do
    264   if AnsiCompareFileName(DefaultAI,Brain[i].FileName)=0 then bixDefault:=i;
    265 if (bixDefault=bixRandom) and (nBrain<bixFirstAI+2) then
    266   bixDefault:=-1;
    267 if (bixDefault<0) and (nBrain>bixFirstAI) then bixDefault:=bixFirstAI; // default AI not found, use any
    268 
    269 DirectDlg.left:=(screen.width-DirectDlg.width) div 2;
    270 DirectDlg.top:=(screen.height-DirectDlg.height) div 2;
    271 
    272 if FullScreen then
    273   begin
    274   Left:=(Screen.Width-800)*3 div 8;
    275   Top:=Screen.Height-ClientHeight-(Screen.Height-600) div 3;
    276 
    277   r0:=CreateRectRgn(0,0,ClientWidth,ClientHeight);
    278   r1:=CreateRectRgn(TabOffset+4*TabSize+2,0,ClientWidth,TabHeight);
    279   CombineRgn(r0,r0,r1,RGN_DIFF);
    280   //DeleteObject(r1);
    281   r1:=CreateRectRgn(QuitBtn.left,QuitBtn.Top,QuitBtn.left+QuitBtn.Width,
    282     QuitBtn.Top+QuitBtn.Height);
    283   CombineRgn(r0,r0,r1,RGN_OR);
    284   //DeleteObject(r1);
    285   SetWindowRgn(Handle,r0,false);
    286   //DeleteObject(r0); // causes crash with Windows 95
    287   end
    288 else
    289   begin
    290   Left:=(Screen.Width-Width) div 2;
    291   Top:=(Screen.Height-Height) div 2;
    292   end;
    293 
    294 Canvas.Font.Assign(UniFont[ftNormal]);
    295 Canvas.Brush.Style:=bsClear;
    296 
    297 QuitBtn.Hint:=Phrases.Lookup('STARTCONTROLS',0);
    298 ReplayBtn.Hint:=Phrases.Lookup('BTN_REPLAY');
    299 for i:=0 to nPlOffered-1 do
    300   begin
    301   DiffUpBtn[i]:=TButtonC.Create(self);
    302   DiffUpBtn[i].Graphic:=GrExt[HGrSystem].Data;
    303   DiffUpBtn[i].Left:=xBrain[i]-18;
    304   DiffUpBtn[i].Top:=yBrain[i]+39;
    305   DiffUpBtn[i].ButtonIndex:=1;
    306   DiffUpBtn[i].Parent:=self;
    307   DiffUpBtn[i].OnClick:=DiffBtnClick;
    308   DiffDownBtn[i]:=TButtonC.Create(self);
    309   DiffDownBtn[i].Graphic:=GrExt[HGrSystem].Data;
    310   DiffDownBtn[i].Left:=xBrain[i]-18;
    311   DiffDownBtn[i].Top:=yBrain[i]+51;
    312   DiffDownBtn[i].ButtonIndex:=0;
    313   DiffDownBtn[i].Parent:=self;
    314   DiffDownBtn[i].OnClick:=DiffBtnClick;
    315   end;
    316 for i:=6 to 8 do
    317   begin
    318   MultiBtn[i]:=TButtonC.Create(self);
    319   MultiBtn[i].Graphic:=GrExt[HGrSystem].Data;
    320   MultiBtn[i].Left:=xBrain[i]-18;
    321   MultiBtn[i].Top:=yBrain[i];
    322   MultiBtn[i].Parent:=self;
    323   MultiBtn[i].OnClick:=MultiBtnClick;
    324   end;
    325 
    326 x:=BiColorTextWidth(Canvas,Phrases.Lookup('STARTCONTROLS',7)) div 2;
    327 CustomizeBtn.Left:=x0Brain+32-16-x;
    328 if AutoDiff<0 then CustomizeBtn.ButtonIndex:=3
    329 else CustomizeBtn.ButtonIndex:=2;
    330 
    331 BrainPicture[0]:=TBitmap.Create;
    332 BrainPicture[0].Width:=64; BrainPicture[0].Height:=64;
    333 BitBlt(BrainPicture[0].Canvas.Handle,0,0,64,64,
    334   GrExt[HGrSystem2].Data.Canvas.Handle,1,111,SRCCOPY);
    335 BrainPicture[1]:=TBitmap.Create;
    336 BrainPicture[1].Width:=64; BrainPicture[1].Height:=64;
    337 BitBlt(BrainPicture[1].Canvas.Handle,0,0,64,64,
    338   GrExt[HGrSystem2].Data.Canvas.Handle,66,111,SRCCOPY);
    339 BrainPicture[2]:=TBitmap.Create;
    340 BrainPicture[2].Width:=64; BrainPicture[2].Height:=64;
    341 BitBlt(BrainPicture[2].Canvas.Handle,0,0,64,64,
    342   GrExt[HGrSystem2].Data.Canvas.Handle,131,111,SRCCOPY);
    343 BrainPicture[3]:=TBitmap.Create;
    344 BrainPicture[3].Width:=64; BrainPicture[3].Height:=64;
    345 BitBlt(BrainPicture[3].Canvas.Handle,0,0,64,64,
    346   GrExt[HGrSystem2].Data.Canvas.Handle,131,46,SRCCOPY);
    347 for i:=bixFirstAI to nBrain-1 do
    348   begin
    349   BrainPicture[i]:=TBitmap.Create;
    350   if not LoadGraphicFile(BrainPicture[i], HomeDir+Brain[i].FileName, gfNoError) then
    351     begin
    352     BrainPicture[i].Width:=64; BrainPicture[i].Height:=64;
    353     with BrainPicture[i].Canvas do
    354       begin
    355       Brush.Color:=$904830;
    356       FillRect(Rect(0,0,64,64));
    357       Font.Assign(UniFont[ftTiny]);
    358       Font.Style:=[];
    359       Font.Color:=$5FDBFF;
    360       Textout(32-TextWidth(Brain[i].FileName) div 2,
    361         32-TextHeight(Brain[i].FileName) div 2,Brain[i].FileName);
     273  end;
     274  Reg.Free;
     275
     276  ActionsOffered := [maManual, maCredits, maWeb];
     277  if FileExists(HomeDir + 'Configurator.exe') then
     278    include(ActionsOffered, maConfig);
     279  if FileExists(HomeDir + 'AI Template\AI development manual.html') then
     280    include(ActionsOffered, maAIDev);
     281
     282  bixDefault := -1;
     283  for i := bixRandom to nBrain - 1 do
     284    if AnsiCompareFileName(DefaultAI, Brain[i].FileName) = 0 then
     285      bixDefault := i;
     286  if (bixDefault = bixRandom) and (nBrain < bixFirstAI + 2) then
     287    bixDefault := -1;
     288  if (bixDefault < 0) and (nBrain > bixFirstAI) then
     289    bixDefault := bixFirstAI; // default AI not found, use any
     290
     291  DirectDlg.left := (screen.width - DirectDlg.width) div 2;
     292  DirectDlg.top := (screen.height - DirectDlg.height) div 2;
     293
     294  if FullScreen then
     295  begin
     296    left := (screen.width - 800) * 3 div 8;
     297    top := screen.height - ClientHeight - (screen.height - 600) div 3;
     298
     299    r0 := CreateRectRgn(0, 0, ClientWidth, ClientHeight);
     300    r1 := CreateRectRgn(TabOffset + 4 * TabSize + 2, 0, ClientWidth, TabHeight);
     301    CombineRgn(r0, r0, r1, RGN_DIFF);
     302    // DeleteObject(r1);
     303    r1 := CreateRectRgn(QuitBtn.left, QuitBtn.top, QuitBtn.left + QuitBtn.width,
     304      QuitBtn.top + QuitBtn.height);
     305    CombineRgn(r0, r0, r1, RGN_OR);
     306    // DeleteObject(r1);
     307    SetWindowRgn(Handle, r0, false);
     308    // DeleteObject(r0); // causes crash with Windows 95
     309  end
     310  else
     311  begin
     312    left := (screen.width - width) div 2;
     313    top := (screen.height - height) div 2;
     314  end;
     315
     316  Canvas.Font.Assign(UniFont[ftNormal]);
     317  Canvas.Brush.Style := bsClear;
     318
     319  QuitBtn.Hint := Phrases.Lookup('STARTCONTROLS', 0);
     320  ReplayBtn.Hint := Phrases.Lookup('BTN_REPLAY');
     321  for i := 0 to nPlOffered - 1 do
     322  begin
     323    DiffUpBtn[i] := TButtonC.Create(self);
     324    DiffUpBtn[i].Graphic := GrExt[HGrSystem].Data;
     325    DiffUpBtn[i].left := xBrain[i] - 18;
     326    DiffUpBtn[i].top := yBrain[i] + 39;
     327    DiffUpBtn[i].ButtonIndex := 1;
     328    DiffUpBtn[i].Parent := self;
     329    DiffUpBtn[i].OnClick := DiffBtnClick;
     330    DiffDownBtn[i] := TButtonC.Create(self);
     331    DiffDownBtn[i].Graphic := GrExt[HGrSystem].Data;
     332    DiffDownBtn[i].left := xBrain[i] - 18;
     333    DiffDownBtn[i].top := yBrain[i] + 51;
     334    DiffDownBtn[i].ButtonIndex := 0;
     335    DiffDownBtn[i].Parent := self;
     336    DiffDownBtn[i].OnClick := DiffBtnClick;
     337  end;
     338  for i := 6 to 8 do
     339  begin
     340    MultiBtn[i] := TButtonC.Create(self);
     341    MultiBtn[i].Graphic := GrExt[HGrSystem].Data;
     342    MultiBtn[i].left := xBrain[i] - 18;
     343    MultiBtn[i].top := yBrain[i];
     344    MultiBtn[i].Parent := self;
     345    MultiBtn[i].OnClick := MultiBtnClick;
     346  end;
     347
     348  x := BiColorTextWidth(Canvas, Phrases.Lookup('STARTCONTROLS', 7)) div 2;
     349  CustomizeBtn.left := x0Brain + 32 - 16 - x;
     350  if AutoDiff < 0 then
     351    CustomizeBtn.ButtonIndex := 3
     352  else
     353    CustomizeBtn.ButtonIndex := 2;
     354
     355  BrainPicture[0] := TBitmap.Create;
     356  BrainPicture[0].width := 64;
     357  BrainPicture[0].height := 64;
     358  BitBlt(BrainPicture[0].Canvas.Handle, 0, 0, 64, 64,
     359    GrExt[HGrSystem2].Data.Canvas.Handle, 1, 111, SRCCOPY);
     360  BrainPicture[1] := TBitmap.Create;
     361  BrainPicture[1].width := 64;
     362  BrainPicture[1].height := 64;
     363  BitBlt(BrainPicture[1].Canvas.Handle, 0, 0, 64, 64,
     364    GrExt[HGrSystem2].Data.Canvas.Handle, 66, 111, SRCCOPY);
     365  BrainPicture[2] := TBitmap.Create;
     366  BrainPicture[2].width := 64;
     367  BrainPicture[2].height := 64;
     368  BitBlt(BrainPicture[2].Canvas.Handle, 0, 0, 64, 64,
     369    GrExt[HGrSystem2].Data.Canvas.Handle, 131, 111, SRCCOPY);
     370  BrainPicture[3] := TBitmap.Create;
     371  BrainPicture[3].width := 64;
     372  BrainPicture[3].height := 64;
     373  BitBlt(BrainPicture[3].Canvas.Handle, 0, 0, 64, 64,
     374    GrExt[HGrSystem2].Data.Canvas.Handle, 131, 46, SRCCOPY);
     375  for i := bixFirstAI to nBrain - 1 do
     376  begin
     377    BrainPicture[i] := TBitmap.Create;
     378    if not LoadGraphicFile(BrainPicture[i], HomeDir + Brain[i].FileName,
     379      gfNoError) then
     380    begin
     381      BrainPicture[i].width := 64;
     382      BrainPicture[i].height := 64;
     383      with BrainPicture[i].Canvas do
     384      begin
     385        Brush.Color := $904830;
     386        FillRect(Rect(0, 0, 64, 64));
     387        Font.Assign(UniFont[ftTiny]);
     388        Font.Style := [];
     389        Font.Color := $5FDBFF;
     390        Textout(32 - TextWidth(Brain[i].FileName) div 2,
     391          32 - TextHeight(Brain[i].FileName) div 2, Brain[i].FileName);
    362392      end
    363393    end
    364394  end;
    365395
    366 EmptyPicture:=TBitmap.Create;
    367 EmptyPicture.PixelFormat:=pf24bit;
    368 EmptyPicture.Width:=64; EmptyPicture.Height:=64;
    369 LogoBuffer:=TBitmap.Create;
    370 LogoBuffer.PixelFormat:=pf24bit;
    371 LogoBuffer.Width:=wBuffer; LogoBuffer.Height:=56;
    372 
    373 Mini:=TBitmap.Create;
    374 for x:=0 to 11 do for y:=0 to 1 do
    375   MiniColors[x,y]:=GrExt[HGrSystem].Data.Canvas.Pixels[66+x,67+y];
    376 InitButtons();
    377 
    378 bixView[0]:=bixTerm;
    379 SlotAvailable:=-1;
    380 Tab:=2;
    381 Diff0:=2;
    382 TurnValid:=false;
    383 Tracking:=false;
    384 FormerGames:=TStringList.Create;
    385 UpdateFormerGames;
    386 ShowTab:=2; // always start with new book page
    387 MapFileName:='';
    388 Maps:=TStringList.Create;
    389 UpdateMaps;
    390 end;
    391 
    392 procedure TStartDlg.FormDestroy(Sender:TObject);
    393 var
    394 i: integer;
    395 begin
    396 FormerGames.Free;
    397 Maps.Free;
    398 Mini.Free;
    399 EmptyPicture.Free;
    400 LogoBuffer.Free;
    401 for i:=0 to nBrain-1 do
    402   BrainPicture[i].Free;
    403 end;
    404 
    405 procedure TStartDlg.SmartInvalidate(x0,y0,x1,y1: integer; InvalidateTab0: boolean);
    406 var
    407 i: integer;
    408 r0,r1: HRgn;
    409 begin
    410 r0:=CreateRectRgn(x0,y0,x1,y1);
    411 for i:=0 to ControlCount-1 do
    412   if not (Controls[i] is TArea) and Controls[i].Visible then
    413     begin
    414     with Controls[i].BoundsRect do
    415       r1:=CreateRectRgn(Left,Top,Right,Bottom);
    416     CombineRgn(r0,r0,r1,RGN_DIFF);
     396  EmptyPicture := TBitmap.Create;
     397  EmptyPicture.PixelFormat := pf24bit;
     398  EmptyPicture.width := 64;
     399  EmptyPicture.height := 64;
     400  LogoBuffer := TBitmap.Create;
     401  LogoBuffer.PixelFormat := pf24bit;
     402  LogoBuffer.width := wBuffer;
     403  LogoBuffer.height := 56;
     404
     405  Mini := TBitmap.Create;
     406  for x := 0 to 11 do
     407    for y := 0 to 1 do
     408      MiniColors[x, y] := GrExt[HGrSystem].Data.Canvas.Pixels[66 + x, 67 + y];
     409  InitButtons();
     410
     411  bixView[0] := bixTerm;
     412  SlotAvailable := -1;
     413  Tab := 2;
     414  Diff0 := 2;
     415  TurnValid := false;
     416  Tracking := false;
     417  FormerGames := TStringList.Create;
     418  UpdateFormerGames;
     419  ShowTab := 2; // always start with new book page
     420  MapFileName := '';
     421  Maps := TStringList.Create;
     422  UpdateMaps;
     423end;
     424
     425procedure TStartDlg.FormDestroy(Sender: TObject);
     426var
     427  i: integer;
     428begin
     429  FormerGames.Free;
     430  Maps.Free;
     431  Mini.Free;
     432  EmptyPicture.Free;
     433  LogoBuffer.Free;
     434  for i := 0 to nBrain - 1 do
     435    BrainPicture[i].Free;
     436end;
     437
     438procedure TStartDlg.SmartInvalidate(x0, y0, x1, y1: integer;
     439  invalidateTab0: boolean);
     440var
     441  i: integer;
     442  r0, r1: HRgn;
     443begin
     444  r0 := CreateRectRgn(x0, y0, x1, y1);
     445  for i := 0 to ControlCount - 1 do
     446    if not(Controls[i] is TArea) and Controls[i].Visible then
     447    begin
     448      with Controls[i].BoundsRect do
     449        r1 := CreateRectRgn(left, top, Right, Bottom);
     450      CombineRgn(r0, r0, r1, RGN_DIFF);
     451      DeleteObject(r1);
     452    end;
     453  if not invalidateTab0 then
     454  begin
     455    r1 := CreateRectRgn(0, 0, 6 + 36, 3 + 38); // tab 0 icon
     456    CombineRgn(r0, r0, r1, RGN_DIFF);
    417457    DeleteObject(r1);
     458  end;
     459  InvalidateRgn(Handle, r0, false);
     460  DeleteObject(r0);
     461end;
     462
     463procedure TStartDlg.FormPaint(Sender: TObject);
     464const
     465  TabNames: array [0 .. 3] of integer = (0, 11, 3, 4);
     466
     467  procedure DrawAction(y, IconIndex: integer; HeaderItem, TextItem: string);
     468  begin
     469    Canvas.Font.Assign(UniFont[ftCaption]);
     470    Canvas.Font.Style := Canvas.Font.Style + [fsUnderline];
     471    RisedTextOut(Canvas, xAction, y - 3, Phrases2.Lookup(HeaderItem));
     472    Canvas.Font.Assign(UniFont[ftNormal]);
     473    BiColorTextOut(Canvas, Colors.Canvas.Pixels[clkAge0 - 1, cliDimmedText],
     474      $000000, xAction, y + 21, Phrases2.Lookup(TextItem));
     475    BitBlt(LogoBuffer.Canvas.Handle, 0, 0, 50, 50, Canvas.Handle,
     476      xActionIcon - 2, y - 2, SRCCOPY);
     477    GlowFrame(LogoBuffer, 8, 8, 34, 34, $202020);
     478    BitBlt(Canvas.Handle, xActionIcon - 2, y - 2, 50, 50,
     479      LogoBuffer.Canvas.Handle, 0, 0, SRCCOPY);
     480    BitBlt(Canvas.Handle, xActionIcon, y, 40, 40, BigImp.Canvas.Handle,
     481      (IconIndex mod 7) * xSizeBig + 8, (IconIndex div 7) * ySizeBig, SRCCOPY);
     482    RFrame(Canvas, xActionIcon - 1, y - 1, xActionIcon + 40, y + 40,
     483      $000000, $000000);
     484  end;
     485
     486var
     487  i, w, h, xMini, yMini, y: integer;
     488  s: string;
     489begin
     490  PaintBackground(self, 3, 3, TabOffset + 4 * TabSize - 4, TabHeight - 3);
     491  PaintBackground(self, 3, TabHeight + 3, ClientWidth - 6,
     492    ClientHeight - TabHeight - 6);
     493  with Canvas do
     494  begin
     495    Brush.Color := $000000;
     496    FillRect(Rect(0, 1, ClientWidth, 3));
     497    FillRect(Rect(TabOffset + 4 * TabSize + 2, 0, ClientWidth, TabHeight));
     498    Brush.Style := bsClear;
     499  end;
     500  if Page in [pgStartRandom, pgStartMap] then
     501  begin
     502    Frame(Canvas, 328, yMain + 112 - 15, ClientWidth, Up2Btn.top + 38,
     503      MainTexture.clBevelShade, MainTexture.clBevelLight);
     504    if AutoDiff > 0 then
     505    begin
     506      Frame(Canvas, -1 { x0Brain-dxBrain } ,
     507        yMain + 112 - 15 { Up1Btn.Top-12 }{ y0Brain-dyBrain } ,
     508        x0Brain + dxBrain + 64, Up2Btn.top + 38 { y0Brain+dyBrain+64 } ,
     509        MainTexture.clBevelShade, MainTexture.clBevelLight);
     510    end
     511  end
     512  else if Page <> pgMain then
     513    Frame(Canvas, 328, Up1Btn.top - 15, ClientWidth, Up2Btn.top + 38,
     514      MainTexture.clBevelShade, MainTexture.clBevelLight);
     515  Frame(Canvas, 0, 0, ClientWidth - 1, ClientHeight - 1, 0, 0);
     516
     517  // draw tabs
     518  Frame(Canvas, 2, 2 + 2 * integer(Tab <> 0), TabOffset + (0 + 1) * TabSize - 1,
     519    TabHeight, MainTexture.clBevelLight, MainTexture.clBevelShade);
     520  Frame(Canvas, 1, 1 + 2 * integer(Tab <> 0), TabOffset + (0 + 1) * TabSize,
     521    TabHeight, MainTexture.clBevelLight, MainTexture.clBevelShade);
     522  Canvas.Pixels[1, 1 + 2 * integer(Tab <> 0)] := MainTexture.clBevelShade;
     523  for i := 1 to 3 do
     524  begin
     525    Frame(Canvas, TabOffset + i * TabSize + 2, 2 + 2 * integer(Tab <> i),
     526      TabOffset + (i + 1) * TabSize - 1, TabHeight, MainTexture.clBevelLight,
     527      MainTexture.clBevelShade);
     528    Frame(Canvas, TabOffset + i * TabSize + 1, 1 + 2 * integer(Tab <> i),
     529      TabOffset + (i + 1) * TabSize, TabHeight, MainTexture.clBevelLight,
     530      MainTexture.clBevelShade);
     531    Canvas.Pixels[TabOffset + i * TabSize + 1, 1 + 2 * integer(Tab <> i)] :=
     532      MainTexture.clBevelShade;
     533  end;
     534  Canvas.Font.Assign(UniFont[ftNormal]);
     535  for i := 1 to 3 do
     536  begin
     537    s := Phrases.Lookup('STARTCONTROLS', TabNames[i]);
     538    RisedTextOut(Canvas, TabOffset + i * TabSize + 1 +
     539      (TabSize - BiColorTextWidth(Canvas, s)) div 2,
     540      10 + 2 * integer(Tab <> i), s);
     541  end;
     542  Frame(Canvas, TabOffset + 4 * TabSize + 1, -1, ClientWidth, TabHeight,
     543    $000000, $000000);
     544  Frame(Canvas, 1, TabHeight + 1, ClientWidth - 2, ClientHeight - 2,
     545    MainTexture.clBevelLight, MainTexture.clBevelShade);
     546  Frame(Canvas, 2, TabHeight + 2, ClientWidth - 3, ClientHeight - 3,
     547    MainTexture.clBevelLight, MainTexture.clBevelShade);
     548  if Tab = 0 then
     549  begin
     550    PaintBackground(self, 3, TabHeight - 1, TabSize - 4 - 3 + TabOffset + 3, 4);
     551    Canvas.Pixels[2, TabHeight] := MainTexture.clBevelLight;
     552  end
     553  else
     554  begin
     555    PaintBackground(self, TabOffset + 3 + Tab * TabSize, TabHeight - 1,
     556      TabSize - 4, 4);
     557    Canvas.Pixels[TabOffset + Tab * TabSize + 2, TabHeight] :=
     558      MainTexture.clBevelLight;
     559  end;
     560  Canvas.Pixels[TabOffset + (Tab + 1) * TabSize - 1, TabHeight + 1] :=
     561    MainTexture.clBevelShade;
     562  if Tab < 3 then
     563    Frame(Canvas, TabOffset + (Tab + 1) * TabSize + 1, 3,
     564      TabOffset + (Tab + 1) * TabSize + 2, TabHeight, MainTexture.clBevelShade,
     565      MainTexture.clBevelShade); // Tab shadow
     566  BitBlt(LogoBuffer.Canvas.Handle, 0, 0, 36, 36, Canvas.Handle, 6,
     567    3 + 2 * integer(Tab <> 0), SRCCOPY);
     568  ImageOp_BCC(LogoBuffer, Templates, 0, 0, 145, 38, 36, 27, $BFBF20, $4040DF);
     569  // logo part 1
     570  ImageOp_BCC(LogoBuffer, Templates, 10, 27, 155, 38 + 27, 26, 9, $BFBF20,
     571    $4040DF); // logo part 2
     572  BitBlt(Canvas.Handle, 6, 3 + 2 * integer(Tab <> 0), 36, 36,
     573    LogoBuffer.Canvas.Handle, 0, 0, SRCCOPY);
     574
     575  if Page = pgMain then
     576  begin
     577    if SelectedAction >= 0 then // mark selected action
     578      for i := 0 to (ClientWidth - 2 * ActionSideBorder) div wBuffer + 1 do
     579      begin
     580        w := ClientWidth - 2 * ActionSideBorder - i * wBuffer;
     581        if w > wBuffer then
     582          w := wBuffer;
     583        h := ActionPitch;
     584        if yAction + SelectedAction * ActionPitch - 8 + h > ClientHeight - ActionBottomBorder
     585        then
     586          h := ClientHeight - ActionBottomBorder -
     587            (yAction + SelectedAction * ActionPitch - 8);
     588        BitBlt(LogoBuffer.Canvas.Handle, 0, 0, w, h, Canvas.Handle,
     589          ActionSideBorder + i * wBuffer, yAction + SelectedAction * ActionPitch
     590          - 8, SRCCOPY);
     591        MakeBlue(LogoBuffer, 0, 0, w, h);
     592        BitBlt(Canvas.Handle, ActionSideBorder + i * wBuffer,
     593          yAction + SelectedAction * ActionPitch - 8, w, h,
     594          LogoBuffer.Canvas.Handle, 0, 0, SRCCOPY);
     595      end;
     596    y := yAction;
     597    for i := 0 to nMainActions - 1 do
     598    begin
     599      if i in ActionsOffered then
     600        case i of
     601          maConfig:
     602            DrawAction(y, 25, 'ACTIONHEADER_CONFIG', 'ACTION_CONFIG');
     603          maManual:
     604            DrawAction(y, 19, 'ACTIONHEADER_MANUAL', 'ACTION_MANUAL');
     605          maCredits:
     606            DrawAction(y, 22, 'ACTIONHEADER_CREDITS', 'ACTION_CREDITS');
     607          maAIDev:
     608            DrawAction(y, 24, 'ACTIONHEADER_AIDEV', 'ACTION_AIDEV');
     609          maWeb:
     610            begin
     611              Canvas.Font.Assign(UniFont[ftCaption]);
     612              // Canvas.Font.Style:=Canvas.Font.Style+[fsUnderline];
     613              RisedTextOut(Canvas, xActionIcon + 99, y,
     614                Phrases2.Lookup('ACTIONHEADER_WEB'));
     615              Canvas.Font.Assign(UniFont[ftNormal]);
     616              BitBlt(LogoBuffer.Canvas.Handle, 0, 0, 91, 25, Canvas.Handle,
     617                xActionIcon, y + 2, SRCCOPY);
     618              ImageOp_BCC(LogoBuffer, Templates, 0, 0, 1, 400, 91, 25, 0,
     619                Colors.Canvas.Pixels[clkAge0 - 1, cliDimmedText]);
     620              BitBlt(Canvas.Handle, xActionIcon, y + 2, 91, 25,
     621                LogoBuffer.Canvas.Handle, 0, 0, SRCCOPY);
     622            end;
     623        end;
     624      inc(y, ActionPitch);
     625    end
     626  end
     627  else if Page in [pgStartRandom, pgStartMap] then
     628  begin
     629    DLine(Canvas, 344, 514, y0Mini + 61 + 19, MainTexture.clBevelLight,
     630      MainTexture.clBevelShade);
     631    RisedTextOut(Canvas, 344, y0Mini + 61, Phrases.Lookup('STARTCONTROLS', 10));
     632    s := TurnToString(MaxTurn);
     633    RisedTextOut(Canvas, 514 - BiColorTextWidth(Canvas, s), y0Mini + 61, s);
     634    s := Phrases.Lookup('STARTCONTROLS', 7);
     635    w := Canvas.TextWidth(s);
     636    LoweredTextOut(Canvas, -2, MainTexture, x0Brain + 32 - w div 2,
     637      y0Brain + dyBrain + 69, s);
     638
     639    InitOrnament;
     640    if AutoDiff < 0 then
     641    begin
     642      for i := 12 to 19 do
     643        if (i < 13) or (i > 17) then
     644        begin
     645          BitBlt(Canvas.Handle, 9 + i * 27, yLogo - 2, wOrna, hOrna,
     646            GrExt[HGrSystem2].Mask.Canvas.Handle, xOrna, yOrna, SRCAND);
     647          BitBlt(Canvas.Handle, 9 + i * 27, yLogo - 2, wOrna, hOrna,
     648            GrExt[HGrSystem2].Data.Canvas.Handle, xOrna, yOrna, SRCPAINT);
     649        end;
     650      PaintLogo(Canvas, 69 + 11 * 27, yLogo, MainTexture.clBevelLight,
     651        MainTexture.clBevelShade);
     652
     653      for i := 0 to nPlOffered - 1 do
     654        if 1 shl i and SlotAvailable <> 0 then
     655        begin
     656          if bixView[i] >= 0 then
     657            FrameImage(Canvas, BrainPicture[bixView[i]], xBrain[i], yBrain[i],
     658              64, 64, 0, 0, true)
     659          else
     660            FrameImage(Canvas, EmptyPicture, xBrain[i], yBrain[i], 64, 64,
     661              0, 0, true);
     662          if bixView[i] >= bixTerm then
     663          begin
     664            BitBlt(Canvas.Handle, xBrain[i] - 18, yBrain[i] + 19, 12, 14,
     665              GrExt[HGrSystem].Data.Canvas.Handle, 134 + (Difficulty[i] - 1) *
     666              13, 28, SRCCOPY);
     667            Frame(Canvas, xBrain[i] - 19, yBrain[i] + 18, xBrain[i] - 18 + 12,
     668              yBrain[i] + (19 + 14), $000000, $000000);
     669            RFrame(Canvas, DiffUpBtn[i].left - 1, DiffUpBtn[i].top - 1,
     670              DiffUpBtn[i].left + 12, DiffUpBtn[i].top + 24,
     671              MainTexture.clBevelShade, MainTexture.clBevelLight);
     672            with Canvas do
     673            begin
     674              Brush.Color := $000000;
     675              FillRect(Rect(xBrain[i] - 5, yBrain[i] + 25, xBrain[i] - 2,
     676                yBrain[i] + 27));
     677              Brush.Style := bsClear;
     678            end;
     679            if i in OfferMultiple then
     680            begin
     681              RFrame(Canvas, MultiBtn[i].left - 1, MultiBtn[i].top - 1,
     682                MultiBtn[i].left + 12, MultiBtn[i].top + 12,
     683                MainTexture.clBevelShade, MainTexture.clBevelLight);
     684              BitBlt(Canvas.Handle, xBrain[i] - 31, yBrain[i], 13, 12,
     685                GrExt[HGrSystem].Data.Canvas.Handle, 88, 47, SRCCOPY);
     686            end
     687          end;
     688          if bixView[i] >= 0 then
     689          begin
     690            DiffUpBtn[i].Hint := Format(Phrases.Lookup('STARTCONTROLS', 9),
     691              [Brain[bixView[i]].Name]);
     692            DiffDownBtn[i].Hint := DiffUpBtn[i].Hint;
     693          end
     694        end;
     695    end
     696    else
     697    begin
     698      DLine(Canvas, 24, 198, yMain + 140 + 19, MainTexture.clBevelLight,
     699        MainTexture.clBevelShade);
     700      RisedTextOut(Canvas, 24 { x0Brain+32-BiColorTextWidth(Canvas,s) div 2 } ,
     701        yMain + 140 { y0Mini-77 } , Phrases.Lookup('STARTCONTROLS', 15));
     702      if Page = pgStartRandom then
     703        s := IntToStr(AutoEnemies)
     704      else if nMapStartPositions = 0 then
     705        s := '0'
     706      else
     707        s := IntToStr(nMapStartPositions - 1);
     708      RisedTextOut(Canvas, 198 - BiColorTextWidth(Canvas, s), yMain + 140, s);
     709      DLine(Canvas, 24, xDefault - 6, yMain + 164 + 19,
     710        MainTexture.clBevelLight, MainTexture.clBevelShade);
     711      RisedTextOut(Canvas, 24 { x0Brain+32-BiColorTextWidth(Canvas,s) div 2 } ,
     712        yMain + 164 { y0Mini-77 } , Phrases.Lookup('STARTCONTROLS', 16));
     713      if AutoDiff = 1 then
     714        FrameImage(Canvas, BrainPicture[bixBeginner], xDefault, yDefault, 64,
     715          64, 0, 0, false)
     716      else
     717        FrameImage(Canvas, BrainPicture[bixDefault], xDefault, yDefault, 64, 64,
     718          0, 0, true);
     719      DLine(Canvas, 56, 272, y0Mini + 61 + 19, MainTexture.clBevelLight,
     720        MainTexture.clBevelShade);
     721      RisedTextOut(Canvas, 56, y0Mini + 61,
     722        Phrases.Lookup('STARTCONTROLS', 14));
     723      s := Phrases.Lookup('AUTODIFF', AutoDiff - 1);
     724      RisedTextOut(Canvas, 272 - BiColorTextWidth(Canvas, s), y0Mini + 61, s);
     725
     726      for i := 0 to 19 do
     727        if (i < 2) or (i > 6) then
     728        begin
     729          BitBlt(Canvas.Handle, 9 + i * 27, yLogo - 2, wOrna, hOrna,
     730            GrExt[HGrSystem2].Mask.Canvas.Handle, xOrna, yOrna, SRCAND);
     731          BitBlt(Canvas.Handle, 9 + i * 27, yLogo - 2, wOrna, hOrna,
     732            GrExt[HGrSystem2].Data.Canvas.Handle, xOrna, yOrna, SRCPAINT);
     733        end;
     734      PaintLogo(Canvas, 69, yLogo, MainTexture.clBevelLight,
     735        MainTexture.clBevelShade);
     736    end
     737  end
     738  else if Page = pgLoad then
     739  begin
     740    // RisedTextOut(Canvas,x0Mini+2-BiColorTextWidth(Canvas,BookDate) div 2,y0Mini-73,BookDate);
     741    if LastTurn > 0 then
     742    begin
     743      PaintProgressBar(Canvas, 6, xTurnSlider, yTurnSlider, 0,
     744        LoadTurn * wTurnSlider div LastTurn, wTurnSlider, MainTexture);
     745      Frame(Canvas, xTurnSlider - 2, yTurnSlider - 2, xTurnSlider + wTurnSlider
     746        + 1, yTurnSlider + 8, $B0B0B0, $FFFFFF);
     747      RFrame(Canvas, xTurnSlider - 3, yTurnSlider - 3, xTurnSlider + wTurnSlider
     748        + 2, yTurnSlider + 9, $FFFFFF, $B0B0B0);
     749    end
     750    else
     751      DLine(Canvas, 344, 514, y0Mini + 61 + 19, MainTexture.clBevelLight,
     752        MainTexture.clBevelShade);
     753    RisedTextOut(Canvas, 344, y0Mini + 61, Phrases.Lookup('STARTCONTROLS', 8));
     754    s := TurnToString(LoadTurn);
     755    RisedTextOut(Canvas, 514 - BiColorTextWidth(Canvas, s), y0Mini + 61, s);
     756  end
     757  else if Page = pgEditRandom then
     758  begin
     759    DLine(Canvas, 344, 514, y0Mini - 77 + 19, MainTexture.clBevelLight,
     760      MainTexture.clBevelShade);
     761    RisedTextOut(Canvas, 344, y0Mini - 77, Phrases.Lookup('STARTCONTROLS', 5));
     762    s := IntToStr((lxpre[WorldSize] * lypre[WorldSize] * 20 +
     763      DefaultWorldTiles div 2) div DefaultWorldTiles * 5) + '%';
     764    RisedTextOut(Canvas, 514 - BiColorTextWidth(Canvas, s), y0Mini - 77, s);
     765    DLine(Canvas, 344, 514, y0Mini + 61 + 19, MainTexture.clBevelLight,
     766      MainTexture.clBevelShade);
     767    RisedTextOut(Canvas, 344, y0Mini + 61, Phrases.Lookup('STARTCONTROLS', 6));
     768    s := IntToStr(StartLandMass) + '%';
     769    RisedTextOut(Canvas, 514 - BiColorTextWidth(Canvas, s), y0Mini + 61, s);
     770  end
     771  else if Page = pgEditMap then
     772  begin
     773    // DLine(Canvas,344,514,y0Mini+61+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
     774    s := Format(Phrases2.Lookup('MAPPROP'),
     775      [(nMapLandTiles * 100 + 556) div 1112,
     776      // 1112 is typical for world with 100% size and default land mass
     777      nMapStartPositions]);
     778    RisedTextOut(Canvas, x0Mini - BiColorTextWidth(Canvas, s) div 2,
     779      y0Mini + 61, s);
     780  end;
     781
     782  if StartBtn.Visible then
     783    BtnFrame(Canvas, StartBtn.BoundsRect, MainTexture);
     784  if Up2Btn.Visible then
     785    RFrame(Canvas, Up2Btn.left - 1, Up2Btn.top - 1, Up2Btn.left + 12,
     786      Up2Btn.top + 24, MainTexture.clBevelShade, MainTexture.clBevelLight);
     787  if Up1Btn.Visible then
     788    RFrame(Canvas, Up1Btn.left - 1, Up1Btn.top - 1, Up1Btn.left + 12,
     789      Up1Btn.top + 24, MainTexture.clBevelShade, MainTexture.clBevelLight);
     790  if AutoDiffUpBtn.Visible then
     791    RFrame(Canvas, AutoDiffUpBtn.left - 1, AutoDiffUpBtn.top - 1,
     792      AutoDiffUpBtn.left + 12, AutoDiffUpBtn.top + 24, MainTexture.clBevelShade,
     793      MainTexture.clBevelLight);
     794  if AutoEnemyUpBtn.Visible then
     795    RFrame(Canvas, AutoEnemyUpBtn.left - 1, AutoEnemyUpBtn.top - 1,
     796      AutoEnemyUpBtn.left + 12, AutoEnemyUpBtn.top + 24,
     797      MainTexture.clBevelShade, MainTexture.clBevelLight);
     798  if CustomizeBtn.Visible then
     799    RFrame(Canvas, CustomizeBtn.left - 1, CustomizeBtn.top - 1,
     800      CustomizeBtn.left + 12, CustomizeBtn.top + 12, MainTexture.clBevelShade,
     801      MainTexture.clBevelLight);
     802  if List.Visible then
     803    EditFrame(Canvas, List.BoundsRect, MainTexture);
     804  if RenameBtn.Visible then
     805    BtnFrame(Canvas, RenameBtn.BoundsRect, MainTexture);
     806  if DeleteBtn.Visible then
     807    BtnFrame(Canvas, DeleteBtn.BoundsRect, MainTexture);
     808  if Page = pgLoad then
     809    BtnFrame(Canvas, ReplayBtn.BoundsRect, MainTexture);
     810
     811  if not(Page in [pgMain, pgNoLoad]) then
     812  begin
     813    xMini := x0Mini - MiniWidth;
     814    yMini := y0Mini - MiniHeight div 2;
     815    Frame(Canvas, xMini, yMini, xMini + 3 + MiniWidth * 2,
     816      yMini + 3 + MiniHeight, MainTexture.clBevelLight,
     817      MainTexture.clBevelShade);
     818    Frame(Canvas, xMini + 1, yMini + 1, xMini + 2 + MiniWidth * 2,
     819      yMini + 2 + MiniHeight, MainTexture.clBevelShade,
     820      MainTexture.clBevelLight);
     821  end;
     822  s := '';
     823  if MiniMode = mmPicture then
     824  begin
     825    BitBlt(Canvas.Handle, xMini + 2, yMini + 2, MiniWidth * 2, MiniHeight,
     826      Mini.Canvas.Handle, 0, 0, SRCCOPY);
     827    if Page = pgStartRandom then
     828      s := Phrases.Lookup('RANMAP')
     829  end
     830  else if MiniMode = mmMultiPlayer then
     831    s := Phrases.Lookup('MPMAP')
     832  else if Page = pgStartMap then
     833    s := Copy(MapFileName, 1, Length(MapFileName) - 9)
     834  else if Page = pgEditMap then
     835    s := List.Items[List.ItemIndex]
     836  else if Page = pgNoLoad then
     837    s := Phrases.Lookup('NOGAMES');
     838  if s <> '' then
     839    RisedTextOut(Canvas, x0Mini + 2 - BiColorTextWidth(Canvas, s) div 2,
     840      y0Mini - 8, s);
     841end;
     842
     843procedure TStartDlg.FormShow(Sender: TObject);
     844type
     845  TLine = array [0 .. 99999999] of Byte;
     846var
     847  i, x, y: integer;
     848  PictureLine: ^TLine;
     849begin
     850  SetMainTextureByAge(-1);
     851  List.Font.Color := MainTexture.clMark;
     852  Fill(EmptyPicture.Canvas, 0, 0, 64, 64, (wMaintexture - 64) div 2,
     853    (hMaintexture - 64) div 2);
     854  for y := 0 to 63 do
     855  begin // darken texture for empty slot
     856    PictureLine := EmptyPicture.ScanLine[y];
     857    for x := 0 to 64 * 3 - 1 do
     858    begin
     859      i := integer(PictureLine[x]) - 28;
     860      if i < 0 then
     861        i := 0;
     862      PictureLine[x] := i;
     863    end
     864  end;
     865
     866  Difficulty[0] := Diff0;
     867
     868  SelectedAction := -1;
     869  if ShowTab = 3 then
     870    PreviewMap(StartLandMass); // avoid delay on first TabX change
     871  ChangeTab(ShowTab);
     872  Background.Enabled := false;
     873end;
     874
     875procedure TStartDlg.UnlistBackupFile(FileName: string);
     876var
     877  i: integer;
     878begin
     879  if FileName[1] <> '~' then
     880    FileName := '~' + FileName;
     881  i := FormerGames.Count - 1;
     882  while (i >= 0) and (AnsiCompareFileName(FormerGames[i], FileName) <> 0) do
     883    dec(i);
     884  if i >= 0 then
     885  begin
     886    FormerGames.Delete(i);
     887    if ListIndex[2] = i then
     888      ListIndex[2] := 0
     889  end
     890end;
     891
     892procedure TStartDlg.StartBtnClick(Sender: TObject);
     893var
     894  i, GameCount, MapCount: integer;
     895  FileName: string;
     896  Reg: TRegistry;
     897begin
     898  case Page of
     899    pgLoad:
     900      begin // load
     901        FileName := List.Items[List.ItemIndex];
     902        if LoadGame(DataDir + 'Saved\', FileName + '.cevo', LoadTurn, false)
     903        then
     904          UnlistBackupFile(FileName)
     905        else
     906          SimpleMessage(Phrases.Lookup('LOADERR'));
     907        SlotAvailable := -1;
     908      end;
     909
     910    pgStartRandom, pgStartMap:
     911      if bixView[0] >= 0 then
     912      begin
     913        if (Page = pgStartMap) and (nMapStartPositions = 0) and (AutoDiff > 0)
     914        then
     915        begin
     916          SimpleMessage(Phrases.Lookup('NOSTARTPOS'));
     917          exit
     918        end;
     919
     920        Reg := TRegistry.Create;
     921        Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start', true);
     922        try
     923          GameCount := Reg.ReadInteger('GameCount');
     924        except
     925          GameCount := 0;
     926        end;
     927
     928        if (AutoDiff < 0) and (bixView[0] = bixNoTerm) then
     929          FileName := 'Round' + IntToStr(GetCurrentProcessID())
     930        else
     931        begin
     932          inc(GameCount);
     933          FileName := Format(Phrases.Lookup('GAME'), [GameCount]);
     934        end;
     935
     936        // save settings and AI assignment
     937        if Page = pgStartRandom then
     938        begin
     939          Reg.WriteInteger('WorldSize', WorldSize);
     940          Reg.WriteInteger('LandMass', StartLandMass);
     941          if AutoDiff < 0 then
     942            for i := 0 to nPlOffered - 1 do
     943            begin
     944              if bixView[i] = -1 then
     945                Reg.WriteString('Control' + IntToStr(i), '')
     946              else
     947                Reg.WriteString('Control' + IntToStr(i),
     948                  Brain[bixView[i]].FileName);
     949              Reg.WriteInteger('Diff' + IntToStr(i), Difficulty[i]);
     950            end;
     951          Reg.WriteInteger('MultiControl', MultiControl);
     952        end;
     953
     954        if AutoDiff > 0 then
     955        begin
     956          Reg.WriteString('DefaultAI', Brain[bixDefault].FileName);
     957          SlotAvailable := 0; // bixView will be invalid hereafter
     958          bixView[0] := bixTerm;
     959          Difficulty[0] := PlayerAutoDiff[AutoDiff];
     960          for i := 1 to nPl - 1 do
     961            if (Page = pgStartRandom) and (i <= AutoEnemies) or
     962              (Page = pgStartMap) and (i < nMapStartPositions) then
     963            begin
     964              if AutoDiff = 1 then
     965                bixView[i] := bixBeginner
     966              else
     967                bixView[i] := bixDefault;
     968              Difficulty[i] := EnemyAutoDiff[AutoDiff];
     969            end
     970            else
     971              bixView[i] := -1;
     972        end
     973        else
     974        begin
     975          for i := 6 to 8 do
     976            if (bixView[0] <> bixNoTerm) and (MultiControl and (1 shl i) <> 0)
     977            then
     978            begin
     979              bixView[i + 3] := bixView[i];
     980              Difficulty[i + 3] := Difficulty[i];
     981              bixView[i + 6] := bixView[i];
     982              Difficulty[i + 6] := Difficulty[i];
     983            end
     984            else
     985            begin
     986              bixView[i + 3] := -1;
     987              bixView[i + 6] := -1;
     988            end
     989        end;
     990
     991        Reg.WriteInteger('AutoDiff', AutoDiff);
     992        Reg.WriteInteger('AutoEnemies', AutoEnemies);
     993        Reg.WriteInteger('MaxTurn', MaxTurn);
     994        Reg.WriteInteger('GameCount', GameCount);
     995        Reg.closekey;
     996        Reg.Free;
     997
     998        StartNewGame(DataDir + 'Saved\', FileName + '.cevo', MapFileName,
     999          lxpre[WorldSize], lypre[WorldSize], StartLandMass, MaxTurn);
     1000        UnlistBackupFile(FileName);
     1001      end;
     1002
     1003    pgEditMap:
     1004      EditMap(MapFileName, lxmax, lymax, StartLandMass);
     1005
     1006    pgEditRandom: // new map
     1007      begin
     1008        Reg := TRegistry.Create;
     1009        Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start', true);
     1010        try
     1011          MapCount := Reg.ReadInteger('MapCount');
     1012        except
     1013          MapCount := 0;
     1014        end;
     1015        inc(MapCount);
     1016        Reg.WriteInteger('MapCount', MapCount);
     1017        Reg.closekey;
     1018        Reg.Free;
     1019        MapFileName := Format(Phrases.Lookup('MAP'), [MapCount]) + '.cevo map';
     1020        EditMap(MapFileName, lxpre[WorldSize], lypre[WorldSize], StartLandMass);
     1021      end
     1022  end
     1023end;
     1024
     1025procedure TStartDlg.PaintInfo;
     1026
     1027  procedure PaintRandomMini(Brightness: integer);
     1028  type
     1029    TLine = array [0 .. lxmax * 2, 0 .. 2] of Byte;
     1030  var
     1031    i, x, y, xm, cm: integer;
     1032    MiniLine: ^TLine;
     1033    Map: ^TTileList;
     1034  begin
     1035    Map := PreviewMap(StartLandMass);
     1036    MiniWidth := lxpre[WorldSize];
     1037    MiniHeight := lypre[WorldSize];
     1038
     1039    Mini.PixelFormat := pf24bit;
     1040    Mini.width := MiniWidth * 2;
     1041    Mini.height := MiniHeight;
     1042    for y := 0 to MiniHeight - 1 do
     1043    begin
     1044      MiniLine := Mini.ScanLine[y];
     1045      for x := 0 to MiniWidth - 1 do
     1046        for i := 0 to 1 do
     1047        begin
     1048          xm := (x * 2 + i + y and 1) mod (MiniWidth * 2);
     1049          cm := MiniColors
     1050            [Map[x * lxmax div MiniWidth + lxmax *
     1051            ((y * (lymax - 1) + MiniHeight div 2) div (MiniHeight - 1))] and
     1052            fTerrain, i];
     1053          MiniLine[xm, 0] := cm shr 16 * Brightness div 3;
     1054          MiniLine[xm, 1] := cm shr 8 and $FF * Brightness div 3;
     1055          MiniLine[xm, 2] := cm and $FF * Brightness div 3;
     1056        end;
    4181057    end;
    419 if not invalidateTab0 then
    420   begin
    421   r1:=CreateRectRgn(0,0,6+36,3+38); // tab 0 icon
    422   CombineRgn(r0,r0,r1,RGN_DIFF);
    423   DeleteObject(r1);
    424   end;
    425 InvalidateRgn(Handle,r0,false);
    426 DeleteObject(r0);
    427 end;
    428 
    429 procedure TStartDlg.FormPaint(Sender:TObject);
    430 const
    431 TabNames: array[0..3] of integer=(0,11,3,4);
    432 
    433   procedure DrawAction(y,IconIndex: integer; HeaderItem, TextItem: string);
    434   begin
    435   Canvas.Font.Assign(UniFont[ftCaption]);
    436   Canvas.Font.Style:=Canvas.Font.Style+[fsUnderline];
    437   RisedTextOut(Canvas,xAction,y-3,Phrases2.Lookup(HeaderItem));
    438   Canvas.Font.Assign(UniFont[ftNormal]);
    439   BiColorTextOut(Canvas,Colors.Canvas.Pixels[clkAge0-1,cliDimmedText],
    440     $000000,xAction,y+21,Phrases2.Lookup(TextItem));
    441   BitBlt(LogoBuffer.Canvas.Handle,0,0,50,50,Canvas.Handle,xActionIcon-2,y-2,SRCCOPY);
    442   GlowFrame(LogoBuffer,8,8,34,34,$202020);
    443   BitBlt(Canvas.Handle,xActionIcon-2,y-2,50,50,LogoBuffer.Canvas.Handle,0,0,SRCCOPY);
    444   BitBlt(Canvas.Handle,xActionIcon,y,40,40,BigImp.Canvas.Handle,(IconIndex mod 7)*xSizeBig+8,
    445     (IconIndex div 7)*ySizeBig, SRCCOPY);
    446   RFrame(Canvas,xActionIcon-1,y-1,xActionIcon+40,y+40,$000000,$000000);
    447   end;
    448 
    449 var
    450 i,w,h,xMini,yMini,y:integer;
    451 s: string;
    452 begin
    453 PaintBackground(self,3,3,TabOffset+4*TabSize-4,TabHeight-3);
    454 PaintBackground(self,3,TabHeight+3,ClientWidth-6,ClientHeight-TabHeight-6);
    455 with Canvas do
    456   begin
    457   Brush.Color:=$000000;
    458   FillRect(Rect(0,1,ClientWidth,3));
    459   FillRect(Rect(TabOffset+4*TabSize+2,0,ClientWidth,TabHeight));
    460   Brush.Style:=bsClear;
    461   end;
    462 if Page in [pgStartRandom,pgStartMap] then
    463   begin
    464   Frame(Canvas,328,yMain+112-15,ClientWidth,Up2Btn.Top+38,
    465     MainTexture.clBevelShade,MainTexture.clBevelLight);
    466   if AutoDiff>0 then
    467     begin
    468     Frame(Canvas,-1{x0Brain-dxBrain},yMain+112-15{Up1Btn.Top-12}{y0Brain-dyBrain},x0Brain+dxBrain+64,
    469       Up2Btn.Top+38{y0Brain+dyBrain+64}, MainTexture.clBevelShade,MainTexture.clBevelLight);
    470     end
    471   end
    472 else if Page<>pgMain then
    473   Frame(Canvas,328,Up1Btn.Top-15,ClientWidth,Up2Btn.Top+38,
    474     MainTexture.clBevelShade,MainTexture.clBevelLight);
    475 Frame(Canvas,0,0,ClientWidth-1,ClientHeight-1,0,0);
    476 
    477 // draw tabs
    478 Frame(Canvas,2,2+2*integer(Tab<>0),TabOffset+(0+1)*TabSize-1,TabHeight,MainTexture.clBevelLight,MainTexture.clBevelShade);
    479 Frame(Canvas,1,1+2*integer(Tab<>0),TabOffset+(0+1)*TabSize,TabHeight,MainTexture.clBevelLight,MainTexture.clBevelShade);
    480 Canvas.Pixels[1,1+2*integer(Tab<>0)]:=MainTexture.clBevelShade;
    481 for i:=1 to 3 do
    482   begin
    483   Frame(Canvas,TabOffset+i*TabSize+2,2+2*integer(Tab<>i),TabOffset+(i+1)*TabSize-1,TabHeight,MainTexture.clBevelLight,MainTexture.clBevelShade);
    484   Frame(Canvas,TabOffset+i*TabSize+1,1+2*integer(Tab<>i),TabOffset+(i+1)*TabSize,TabHeight,MainTexture.clBevelLight,MainTexture.clBevelShade);
    485   Canvas.Pixels[TabOffset+i*TabSize+1,1+2*integer(Tab<>i)]:=MainTexture.clBevelShade;
    486   end;
    487 Canvas.Font.Assign(UniFont[ftNormal]);
    488 for i:=1 to 3 do
    489   begin
    490   s:=Phrases.Lookup('STARTCONTROLS',TabNames[i]);
    491   RisedTextOut(Canvas,TabOffset+i*TabSize+1+(TabSize-BiColorTextWidth(Canvas,s)) div 2,
    492     10+2*integer(Tab<>i),s);
    493   end;
    494 Frame(Canvas,TabOffset+4*TabSize+1,-1,ClientWidth,TabHeight,$000000,$000000);
    495 Frame(Canvas,1,TabHeight+1,ClientWidth-2,ClientHeight-2,MainTexture.clBevelLight,
    496   MainTexture.clBevelShade);
    497 Frame(Canvas,2,TabHeight+2,ClientWidth-3,ClientHeight-3,MainTexture.clBevelLight,
    498   MainTexture.clBevelShade);
    499 if Tab=0 then
    500   begin
    501   PaintBackground(self,3,TabHeight-1,TabSize-4-3+TabOffset+3,4);
    502   Canvas.Pixels[2,TabHeight]:=MainTexture.clBevelLight;
    503   end
    504 else
    505   begin
    506   PaintBackground(self,TabOffset+3+Tab*TabSize,TabHeight-1,TabSize-4,4);
    507   Canvas.Pixels[TabOffset+Tab*TabSize+2,TabHeight]:=MainTexture.clBevelLight;
    508   end;
    509 Canvas.Pixels[TabOffset+(Tab+1)*TabSize-1,TabHeight+1]:=MainTexture.clBevelShade;
    510 if Tab<3 then
    511   Frame(Canvas,TabOffset+(Tab+1)*TabSize+1,3,TabOffset+(Tab+1)*TabSize+2,TabHeight,
    512     MainTexture.clBevelShade,MainTexture.clBevelShade); // Tab shadow
    513 BitBlt(LogoBuffer.Canvas.Handle,0,0,36,36,Canvas.Handle,6,3+2*integer(Tab<>0),SRCCOPY);
    514 ImageOp_BCC(LogoBuffer,Templates,0,0,145,38,36,27,$BFBF20,$4040DF); // logo part 1
    515 ImageOp_BCC(LogoBuffer,Templates,10,27,155,38+27,26,9,$BFBF20,$4040DF); // logo part 2
    516 BitBlt(Canvas.Handle,6,3+2*integer(Tab<>0),36,36,LogoBuffer.Canvas.Handle,0,0,SRCCOPY);
    517 
    518 if page=pgMain then
    519   begin
    520   if SelectedAction>=0 then // mark selected action
    521     for i:=0 to (ClientWidth-2*ActionSideBorder) div wBuffer+1 do
    522       begin
    523       w:=ClientWidth-2*ActionSideBorder-i*wBuffer;
    524       if w>wBuffer then
    525         w:=wBuffer;
    526       h:=ActionPitch;
    527       if yAction+SelectedAction*ActionPitch-8+h>ClientHeight-ActionBottomBorder then
    528         h:=ClientHeight-ActionBottomBorder-(yAction+SelectedAction*ActionPitch-8);
    529       BitBlt(LogoBuffer.Canvas.Handle,0,0,w,h,Canvas.Handle,
    530         ActionSideBorder+i*wBuffer,yAction+SelectedAction*ActionPitch-8,SRCCOPY);
    531       MakeBlue(LogoBuffer,0,0,w,h);
    532       BitBlt(Canvas.Handle,ActionSideBorder+i*wBuffer,
    533         yAction+SelectedAction*ActionPitch-8,w,h,
    534         LogoBuffer.Canvas.Handle,0,0,SRCCOPY);
     1058  end;
     1059
     1060var
     1061  SaveMap: array [0 .. lxmax * lymax - 1] of Byte;
     1062
     1063  procedure PaintFileMini;
     1064  type
     1065    TLine = array [0 .. 99999999, 0 .. 2] of Byte;
     1066  var
     1067    i, x, y, xm, cm, Tile, OwnColor, EnemyColor: integer;
     1068    MiniLine, PrevMiniLine: ^TLine;
     1069  begin
     1070    OwnColor := GrExt[HGrSystem].Data.Canvas.Pixels[95, 67];
     1071    EnemyColor := GrExt[HGrSystem].Data.Canvas.Pixels[96, 67];
     1072    Mini.PixelFormat := pf24bit;
     1073    Mini.width := MiniWidth * 2;
     1074    Mini.height := MiniHeight;
     1075    if MiniMode = mmPicture then
     1076    begin
     1077      MiniLine := nil;
     1078      for y := 0 to MiniHeight - 1 do
     1079      begin
     1080        PrevMiniLine := MiniLine;
     1081        MiniLine := Mini.ScanLine[y];
     1082        for x := 0 to MiniWidth - 1 do
     1083          for i := 0 to 1 do
     1084          begin
     1085            xm := (x * 2 + i + y and 1) mod (MiniWidth * 2);
     1086            Tile := SaveMap[x + MiniWidth * y];
     1087            if Tile and fTerrain = fUNKNOWN then
     1088              cm := $000000
     1089            else if Tile and smCity <> 0 then
     1090            begin
     1091              if Tile and smOwned <> 0 then
     1092                cm := OwnColor
     1093              else
     1094                cm := EnemyColor;
     1095              if PrevMiniLine <> nil then
     1096              begin // 2x2 city dot covers two scanlines
     1097                PrevMiniLine[xm, 0] := cm shr 16;
     1098                PrevMiniLine[xm, 1] := cm shr 8 and $FF;
     1099                PrevMiniLine[xm, 2] := cm and $FF;
     1100              end
     1101            end
     1102            else if (i = 0) and (Tile and smUnit <> 0) then
     1103              if Tile and smOwned <> 0 then
     1104                cm := OwnColor
     1105              else
     1106                cm := EnemyColor
     1107            else
     1108              cm := MiniColors[Tile and fTerrain, i];
     1109            MiniLine[xm, 0] := cm shr 16;
     1110            MiniLine[xm, 1] := cm shr 8 and $FF;
     1111            MiniLine[xm, 2] := cm and $FF;
     1112          end;
     1113      end
     1114    end;
     1115  end;
     1116
     1117var
     1118  x, y, dummy, FileLandMass, lxFile, lyFile: integer;
     1119  LogFile, MapFile: file;
     1120  s: string[255];
     1121  MapRow: array [0 .. lxmax - 1] of Cardinal;
     1122
     1123begin
     1124  case Page of
     1125    pgStartRandom:
     1126      begin
     1127        MiniMode := mmPicture;
     1128        PaintRandomMini(3);
    5351129      end;
    536   y:=yAction;
    537   for i:=0 to nMainActions-1 do
    538     begin
    539     if i in ActionsOffered then
    540       case i of
    541         maConfig:
    542           DrawAction(y,25,'ACTIONHEADER_CONFIG','ACTION_CONFIG');
    543         maManual:
    544           DrawAction(y,19,'ACTIONHEADER_MANUAL','ACTION_MANUAL');
    545         maCredits:
    546           DrawAction(y,22,'ACTIONHEADER_CREDITS','ACTION_CREDITS');
    547         maAIDev:
    548           DrawAction(y,24,'ACTIONHEADER_AIDEV','ACTION_AIDEV');
    549         maWeb:
     1130
     1131    pgNoLoad:
     1132      begin
     1133        MiniWidth := lxpre[DefaultWorldSize];
     1134        MiniHeight := lypre[DefaultWorldSize];
     1135        MiniMode := mmNone;
     1136      end;
     1137
     1138    pgLoad:
     1139      begin
     1140        AssignFile(LogFile, DataDir + 'Saved\' + List.Items[List.ItemIndex]
     1141          + '.cevo');
     1142        try
     1143          Reset(LogFile, 4);
     1144          BlockRead(LogFile, s[1], 2); { file id }
     1145          BlockRead(LogFile, dummy, 1); { format id }
     1146          if dummy >= $000E01 then
     1147            BlockRead(LogFile, dummy, 1); { item stored since 0.14.1 }
     1148          BlockRead(LogFile, MiniWidth, 1);
     1149          BlockRead(LogFile, MiniHeight, 1);
     1150          BlockRead(LogFile, FileLandMass, 1);
     1151          if FileLandMass = 0 then
     1152            for y := 0 to MiniHeight - 1 do
     1153              BlockRead(LogFile, MapRow, MiniWidth);
     1154          BlockRead(LogFile, dummy, 1);
     1155          BlockRead(LogFile, dummy, 1);
     1156          BlockRead(LogFile, LastTurn, 1);
     1157          BlockRead(LogFile, SaveMap, 1);
     1158          if SaveMap[0] = $80 then
     1159            MiniMode := mmMultiPlayer
     1160          else
     1161            MiniMode := mmPicture;
     1162          if MiniMode = mmPicture then
     1163            BlockRead(LogFile, SaveMap[4], (MiniWidth * MiniHeight - 1) div 4);
     1164          CloseFile(LogFile);
     1165        except
     1166          CloseFile(LogFile);
     1167          LastTurn := 0;
     1168          MiniWidth := lxpre[DefaultWorldSize];
     1169          MiniHeight := lypre[DefaultWorldSize];
     1170          MiniMode := mmNone;
     1171        end;
     1172        // BookDate:=DateToStr(FileDateToDateTime(FileAge(FileName)));
     1173        PaintFileMini;
     1174        if not TurnValid then
     1175        begin
     1176          LoadTurn := LastTurn;
     1177          SmartInvalidate(xTurnSlider - 2, y0Mini + 61,
     1178            xTurnSlider + wTurnSlider + 2, yTurnSlider + 9);
     1179        end;
     1180        TurnValid := true;
     1181      end;
     1182
     1183    pgEditRandom:
     1184      begin
     1185        MapFileName := '';
     1186        MiniMode := mmPicture;
     1187        PaintRandomMini(4);
     1188      end;
     1189
     1190    pgStartMap, pgEditMap:
     1191      begin
     1192        MiniMode := mmPicture;
     1193        if Page = pgEditMap then
     1194          MapFileName := List.Items[List.ItemIndex] + '.cevo map';
     1195        if LoadGraphicFile(Mini, DataDir + 'Maps\' + Copy(MapFileName, 1,
     1196          Length(MapFileName) - 9), gfNoError) then
     1197        begin
     1198          if Mini.width div 2 > MaxWidthMapLogo then
     1199            Mini.width := MaxWidthMapLogo * 2;
     1200          if Mini.height > MaxHeightMapLogo then
     1201            Mini.height := MaxHeightMapLogo;
     1202          MiniWidth := Mini.width div 2;
     1203          MiniHeight := Mini.height;
     1204        end
     1205        else
     1206        begin
     1207          MiniMode := mmNone;
     1208          MiniWidth := MaxWidthMapLogo;
     1209          MiniHeight := MaxHeightMapLogo;
     1210        end;
     1211
     1212        AssignFile(MapFile, DataDir + 'Maps\' + MapFileName);
     1213        try
     1214          Reset(MapFile, 4);
     1215          BlockRead(MapFile, s[1], 2); { file id }
     1216          BlockRead(MapFile, x, 1); { format id }
     1217          BlockRead(MapFile, x, 1); // MaxTurn
     1218          BlockRead(MapFile, lxFile, 1);
     1219          BlockRead(MapFile, lyFile, 1);
     1220          nMapLandTiles := 0;
     1221          nMapStartPositions := 0;
     1222          for y := 0 to lyFile - 1 do
    5501223          begin
    551           Canvas.Font.Assign(UniFont[ftCaption]);
    552           //Canvas.Font.Style:=Canvas.Font.Style+[fsUnderline];
    553           RisedTextOut(Canvas,xActionIcon+99,y,Phrases2.Lookup('ACTIONHEADER_WEB'));
    554           Canvas.Font.Assign(UniFont[ftNormal]);
    555           BitBlt(LogoBuffer.Canvas.Handle,0,0,91,25,Canvas.Handle,xActionIcon,y+2,SRCCOPY);
    556           ImageOp_BCC(LogoBuffer,Templates,0,0,1,400,91,25,0,
    557             Colors.Canvas.Pixels[clkAge0-1,cliDimmedText]);
    558           BitBlt(Canvas.Handle,xActionIcon,y+2,91,25,LogoBuffer.Canvas.Handle,0,0,SRCCOPY);
     1224            BlockRead(MapFile, MapRow, lxFile);
     1225            for x := 0 to lxFile - 1 do
     1226            begin
     1227              if (MapRow[x] and fTerrain) in [fGrass, fPrairie, fTundra, fSwamp,
     1228                fForest, fHills] then
     1229                inc(nMapLandTiles);
     1230              if MapRow[x] and (fPrefStartPos or fStartPos) <> 0 then
     1231                inc(nMapStartPositions);
     1232            end
    5591233          end;
    560         end;
    561     inc(y,ActionPitch);
    562     end
    563   end
    564 else if Page in [pgStartRandom,pgStartMap] then
    565   begin
    566   DLine(Canvas,344,514,y0Mini+61+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    567   RisedTextOut(Canvas,344,y0Mini+61,Phrases.Lookup('STARTCONTROLS',10));
    568   s:=TurnToString(MaxTurn);
    569   RisedTextOut(Canvas,514-BiColorTextWidth(Canvas,s),y0Mini+61,s);
    570   s:=Phrases.Lookup('STARTCONTROLS',7);
    571   w:=Canvas.TextWidth(s);
    572   LoweredTextOut(Canvas,-2,MainTexture,x0Brain+32-w div 2,y0Brain+dyBrain+69,s);
    573 
    574   InitOrnament;
    575   if AutoDiff<0 then
    576     begin
    577     for i:=12 to 19 do if (i<13) or (i>17) then
    578       begin
    579       BitBlt(Canvas.Handle,9+i*27,yLogo-2,wOrna,hOrna,
    580         GrExt[HGrSystem2].Mask.Canvas.Handle,xOrna,yOrna,SRCAND);
    581       BitBlt(Canvas.Handle,9+i*27,yLogo-2,wOrna,hOrna,
    582         GrExt[HGrSystem2].Data.Canvas.Handle,xOrna,yOrna,SRCPAINT);
     1234          if nMapStartPositions > nPl then
     1235            nMapStartPositions := nPl;
     1236          CloseFile(MapFile);
     1237        except
     1238          CloseFile(MapFile);
     1239        end;
     1240        if Page = pgEditMap then
     1241          SmartInvalidate(x0Mini - 112, y0Mini + 61, x0Mini + 112, y0Mini + 91);
     1242      end
     1243  end;
     1244  SmartInvalidate(x0Mini - lxmax, y0Mini - lymax div 2,
     1245    x0Mini - lxmax + 2 * lxmax + 4, y0Mini - lymax div 2 + lymax + 4);
     1246end;
     1247
     1248procedure TStartDlg.BrainClick(Sender: TObject);
     1249var
     1250  i: integer;
     1251begin
     1252  // Play('BUTTON_UP');
     1253  if bixPopup < 0 then
     1254  begin // change default AI
     1255    bixDefault := TMenuItem(Sender).Tag;
     1256    SmartInvalidate(xDefault, yDefault, xDefault + 64, yDefault + 64);
     1257  end
     1258  else
     1259  begin
     1260    Brain[bixView[bixPopup]].Flags := Brain[bixView[bixPopup]].Flags and
     1261      not fUsed;
     1262    bixView[bixPopup] := TMenuItem(Sender).Tag;
     1263    DiffUpBtn[bixPopup].Visible := bixView[bixPopup] >= bixTerm;
     1264    DiffDownBtn[bixPopup].Visible := bixView[bixPopup] >= bixTerm;
     1265    if bixPopup in OfferMultiple then
     1266    begin
     1267      MultiBtn[bixPopup].Visible := bixView[bixPopup] >= bixTerm;
     1268      MultiBtn[bixPopup].ButtonIndex := 2 + (MultiControl shr bixPopup) and 1;
     1269    end;
     1270    Brain[bixView[bixPopup]].Flags := Brain[bixView[bixPopup]].Flags or fUsed;
     1271    if bixView[bixPopup] < bixTerm then
     1272      Difficulty[bixPopup] := 0 { supervisor }
     1273    else
     1274      Difficulty[bixPopup] := 2;
     1275    if (Page = pgStartRandom) and (bixPopup in OfferMultiple) and
     1276      (bixView[bixPopup] < 0) then
     1277      MultiControl := MultiControl and not(1 shl bixPopup);
     1278    if (bixPopup = 0) and (MapFileName <> '') then
     1279      ChangePage(Page);
     1280    if bixView[bixPopup] = bixNoTerm then
     1281    begin // turn all local players off
     1282      for i := 1 to nPlOffered - 1 do
     1283        if bixView[i] = bixTerm then
     1284        begin
     1285          bixView[i] := -1;
     1286          DiffUpBtn[i].Visible := false;
     1287          DiffUpBtn[i].Tag := 0;
     1288          DiffDownBtn[i].Visible := false;
     1289          DiffDownBtn[i].Tag := 0;
     1290          if i in OfferMultiple then
     1291          begin
     1292            MultiBtn[i].Visible := false;
     1293            MultiBtn[i].Tag := 0;
     1294          end;
     1295          SmartInvalidate(xBrain[i] - 31, yBrain[i] - 1, xBrain[i] + 64,
     1296            DiffUpBtn[i].top + 25);
     1297        end;
     1298      Brain[bixTerm].Flags := Brain[bixTerm].Flags and not fUsed;
     1299    end;
     1300    SmartInvalidate(xBrain[bixPopup] - 31, yBrain[bixPopup] - 1,
     1301      xBrain[bixPopup] + 64, DiffUpBtn[bixPopup].top + 25);
     1302  end
     1303end;
     1304
     1305procedure TStartDlg.InitPopup(PopupIndex: integer);
     1306var
     1307  i, FixedLines: integer;
     1308  m: TMenuItem;
     1309
     1310  procedure OfferBrain(Index: integer);
     1311  var
     1312    j: integer;
     1313  begin
     1314    m := TMenuItem.Create(PopupMenu1);
     1315    if Index < 0 then
     1316      m.Caption := Phrases.Lookup('NOMOD')
     1317    else
     1318      m.Caption := Brain[Index].Name;
     1319    m.Tag := Index;
     1320    m.OnClick := BrainClick;
     1321    j := FixedLines;
     1322    while (j < PopupMenu1.Items.Count) and
     1323      (StrIComp(pchar(m.Caption), pchar(PopupMenu1.Items[j].Caption)) > 0) do
     1324      inc(j);
     1325    m.RadioItem := true;
     1326    if bixPopup < 0 then
     1327      m.Checked := bixDefault = Index
     1328    else
     1329      m.Checked := bixView[bixPopup] = Index;
     1330    PopupMenu1.Items.Insert(j, m);
     1331  end;
     1332
     1333begin
     1334  bixPopup := PopupIndex;
     1335  EmptyMenu(PopupMenu1.Items);
     1336  if bixPopup < 0 then
     1337  begin // select default AI
     1338    FixedLines := 0;
     1339    if nBrain >= bixFirstAI + 2 then
     1340    begin
     1341      OfferBrain(bixRandom);
     1342      inc(FixedLines)
     1343    end;
     1344    for i := bixFirstAI to nBrain - 1 do // offer available AIs
     1345      if Brain[i].Flags and fMultiple <> 0 then
     1346        OfferBrain(i);
     1347  end
     1348  else
     1349  begin
     1350    FixedLines := 0;
     1351    if bixPopup > 0 then
     1352    begin
     1353      OfferBrain(-1);
     1354      inc(FixedLines);
     1355    end;
     1356    for i := bixTerm downto 0 do // offer game interfaces
     1357      if (bixPopup = 0) or (i = bixTerm) and (bixView[0] <> bixNoTerm) then
     1358      begin
     1359        OfferBrain(i);
     1360        inc(FixedLines);
    5831361      end;
    584     PaintLogo(Canvas,69+11*27,yLogo,MainTexture.clBevelLight,MainTexture.clBevelShade);
    585 
    586     for i:=0 to nPlOffered-1 do if 1 shl i and SlotAvailable<>0 then
    587       begin
    588       if bixView[i]>=0 then
    589         FrameImage(Canvas,BrainPicture[bixView[i]],xBrain[i],yBrain[i],64,64,0,0,true)
    590       else FrameImage(Canvas,EmptyPicture,xBrain[i],yBrain[i],64,64,0,0,true);
    591       if bixView[i]>=bixTerm then
     1362    if bixPopup > 0 then
     1363    begin
     1364      m := TMenuItem.Create(PopupMenu1);
     1365      m.Caption := '-';
     1366      PopupMenu1.Items.Add(m);
     1367      inc(FixedLines);
     1368      if nBrain >= bixFirstAI + 2 then
     1369      begin
     1370        OfferBrain(bixRandom);
     1371        inc(FixedLines);
     1372      end;
     1373      for i := bixFirstAI to nBrain - 1 do // offer available AIs
     1374        if (Brain[i].Flags and fMultiple <> 0) or (Brain[i].Flags and fUsed = 0)
     1375          or (i = bixView[bixPopup]) then
     1376          OfferBrain(i);
     1377    end;
     1378  end
     1379end;
     1380
     1381procedure TStartDlg.UpdateFormerGames;
     1382var
     1383  i: integer;
     1384  f: TSearchRec;
     1385begin
     1386  FormerGames.Clear;
     1387  if FindFirst(DataDir + 'Saved\*.cevo', $21, f) = 0 then
     1388    repeat
     1389      i := FormerGames.Count;
     1390      while (i > 0) and (f.Time < integer(FormerGames.Objects[i - 1])) do
     1391        dec(i);
     1392      FormerGames.InsertObject(i, Copy(f.Name, 1, Length(f.Name) - 5),
     1393        TObject(f.Time));
     1394    until FindNext(f) <> 0;
     1395  ListIndex[2] := FormerGames.Count - 1;
     1396  if (ShowTab = 2) and (FormerGames.Count > 0) then
     1397    ShowTab := 3;
     1398  TurnValid := false;
     1399end;
     1400
     1401procedure TStartDlg.UpdateMaps;
     1402var
     1403  f: TSearchRec;
     1404begin
     1405  Maps.Clear;
     1406  if FindFirst(DataDir + 'Maps\*.cevo map', $21, f) = 0 then
     1407    repeat
     1408      Maps.Add(Copy(f.Name, 1, Length(f.Name) - 9));
     1409    until FindNext(f) <> 0;
     1410  Maps.Sort;
     1411  Maps.Insert(0, Phrases.Lookup('RANMAP'));
     1412  ListIndex[0] := Maps.IndexOf(Copy(MapFileName, 1, Length(MapFileName) - 9));
     1413  if ListIndex[0] < 0 then
     1414    ListIndex[0] := 0;
     1415end;
     1416
     1417procedure TStartDlg.ChangePage(NewPage: integer);
     1418var
     1419  i, j, p1: integer;
     1420  s: string;
     1421  Reg: TRegistry;
     1422  invalidateTab0: boolean;
     1423begin
     1424  invalidateTab0 := (Page = pgMain) or (NewPage = pgMain);
     1425  Page := NewPage;
     1426  case Page of
     1427    pgStartRandom, pgStartMap:
     1428      begin
     1429        StartBtn.Caption := Phrases.Lookup('STARTCONTROLS', 1);
     1430        if Page = pgStartRandom then
     1431          i := nPlOffered
     1432        else
    5921433        begin
    593         BitBlt(Canvas.Handle,xBrain[i]-18,yBrain[i]+19,12,14,
    594           GrExt[HGrSystem].Data.Canvas.Handle,134+(Difficulty[i]-1)*13,28,SRCCOPY);
    595         Frame(Canvas,xBrain[i]-19,yBrain[i]+18,xBrain[i]-18+12,yBrain[i]+(19+14),
    596           $000000,$000000);
    597         RFrame(Canvas,DiffUpBtn[i].Left-1,DiffUpBtn[i].Top-1,DiffUpBtn[i].Left+12,
    598           DiffUpBtn[i].Top+24,MainTexture.clBevelShade,MainTexture.clBevelLight);
    599         with Canvas do
     1434          i := nMapStartPositions;
     1435          if i = 0 then
    6001436          begin
    601           Brush.Color:=$000000;
    602           FillRect(Rect(xBrain[i]-5,yBrain[i]+25,xBrain[i]-2,yBrain[i]+27));
    603           Brush.Style:=bsClear;
     1437            bixView[0] := bixSuper_Virtual;
     1438            Difficulty[0] := 0
    6041439          end;
    605         if i in OfferMultiple then
     1440          if bixView[0] < bixTerm then
     1441            inc(i);
     1442          if i > nPl then
     1443            i := nPl;
     1444          if i <= nPlOffered then
     1445            MultiControl := 0
     1446          else
     1447            MultiControl := InitMulti[i];
     1448        end;
     1449        if InitAlive[i] <> SlotAvailable then
     1450          if Page = pgStartRandom then
     1451          begin // restore AI assignment of last start
     1452            Reg := TRegistry.Create;
     1453            Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start', false);
     1454            for p1 := 0 to nPlOffered - 1 do
     1455            begin
     1456              bixView[p1] := -1;
     1457              s := Reg.ReadString('Control' + IntToStr(p1));
     1458              Difficulty[p1] := Reg.ReadInteger('Diff' + IntToStr(p1));
     1459              if s <> '' then
     1460                for j := 0 to nBrain - 1 do
     1461                  if AnsiCompareFileName(s, Brain[j].FileName) = 0 then
     1462                    bixView[p1] := j;
     1463            end;
     1464            MultiControl := Reg.ReadInteger('MultiControl');
     1465            Reg.closekey;
     1466            Reg.Free;
     1467          end
     1468          else
     1469            for p1 := 1 to nPl - 1 do
     1470              if 1 shl p1 and InitAlive[i] <> 0 then
     1471              begin
     1472                bixView[p1] := bixDefault;
     1473                Difficulty[p1] := 2;
     1474              end
     1475              else
     1476                bixView[p1] := -1;
     1477        SlotAvailable := InitAlive[i];
     1478        for i := 0 to nPlOffered - 1 do
     1479          if (AutoDiff < 0) and (bixView[i] >= bixTerm) then
    6061480          begin
    607           RFrame(Canvas,MultiBtn[i].Left-1,MultiBtn[i].Top-1,MultiBtn[i].Left+12,
    608             MultiBtn[i].Top+12,MainTexture.clBevelShade,MainTexture.clBevelLight);
    609           BitBlt(Canvas.Handle,xBrain[i]-31,yBrain[i],13,12,
    610             GrExt[HGrSystem].Data.Canvas.Handle,88,47,SRCCOPY);
     1481            DiffUpBtn[i].Tag := 768;
     1482            DiffDownBtn[i].Tag := 768;
    6111483          end
    612         end;
    613       if bixView[i]>=0 then
     1484          else
     1485          begin
     1486            DiffUpBtn[i].Tag := 0;
     1487            DiffDownBtn[i].Tag := 0;
     1488          end;
     1489        for i := 6 to 8 do
     1490          if (AutoDiff < 0) and (bixView[i] >= bixTerm) then
     1491          begin
     1492            MultiBtn[i].Tag := 768;
     1493            MultiBtn[i].ButtonIndex := 2 + (MultiControl shr i) and 1;
     1494            MultiBtn[i].Enabled := Page = pgStartRandom
     1495          end
     1496          else
     1497            MultiBtn[i].Tag := 0;
     1498        if (AutoDiff > 0) and (Page <> pgStartMap) then
    6141499        begin
    615         DiffUpBtn[i].Hint:=Format(Phrases.Lookup('STARTCONTROLS',9),
    616           [Brain[bixView[i]].Name]);
    617         DiffDownBtn[i].Hint:=DiffUpBtn[i].Hint;
     1500          AutoEnemyUpBtn.Tag := 768;
     1501          AutoEnemyDownBtn.Tag := 768;
     1502        end
     1503        else
     1504        begin
     1505          AutoEnemyUpBtn.Tag := 0;
     1506          AutoEnemyDownBtn.Tag := 0;
     1507        end;
     1508        if AutoDiff > 0 then
     1509        begin
     1510          AutoDiffUpBtn.Tag := 768;
     1511          AutoDiffDownBtn.Tag := 768;
     1512        end
     1513        else
     1514        begin
     1515          AutoDiffUpBtn.Tag := 0;
     1516          AutoDiffDownBtn.Tag := 0;
    6181517        end
    6191518      end;
    620     end
    621   else
    622     begin
    623     DLine(Canvas,24,198,yMain+140+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    624     RisedTextOut(Canvas,24{x0Brain+32-BiColorTextWidth(Canvas,s) div 2},yMain+140{y0Mini-77},
    625       Phrases.Lookup('STARTCONTROLS',15));
    626     if Page=pgStartRandom then s:=IntToStr(AutoEnemies)
    627     else if nMapStartPositions=0 then s:='0'
    628     else s:=IntToStr(nMapStartPositions-1);
    629     RisedTextOut(Canvas,198-BiColorTextWidth(Canvas,s),yMain+140,s);
    630     DLine(Canvas,24,xDefault-6,yMain+164+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    631     RisedTextOut(Canvas,24{x0Brain+32-BiColorTextWidth(Canvas,s) div 2},yMain+164{y0Mini-77},
    632       Phrases.Lookup('STARTCONTROLS',16));
    633     if AutoDiff=1 then
    634       FrameImage(Canvas,BrainPicture[bixBeginner],xDefault,yDefault,64,64,0,0,false)
    635     else FrameImage(Canvas,BrainPicture[bixDefault],xDefault,yDefault,64,64,0,0,true);
    636     DLine(Canvas,56,272,y0Mini+61+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    637     RisedTextOut(Canvas,56,y0Mini+61,Phrases.Lookup('STARTCONTROLS',14));
    638     s:=Phrases.Lookup('AUTODIFF',AutoDiff-1);
    639     RisedTextOut(Canvas,272-BiColorTextWidth(Canvas,s),y0Mini+61,s);
    640 
    641     for i:=0 to 19 do if (i<2) or (i>6) then
    642       begin
    643       BitBlt(Canvas.Handle,9+i*27,yLogo-2,wOrna,hOrna,
    644         GrExt[HGrSystem2].Mask.Canvas.Handle,xOrna,yOrna,SRCAND);
    645       BitBlt(Canvas.Handle,9+i*27,yLogo-2,wOrna,hOrna,
    646         GrExt[HGrSystem2].Data.Canvas.Handle,xOrna,yOrna,SRCPAINT);
     1519
     1520    pgNoLoad, pgLoad:
     1521      begin
     1522        StartBtn.Caption := Phrases.Lookup('STARTCONTROLS', 2);
     1523        RenameBtn.Hint := Phrases.Lookup('BTN_RENGAME');
     1524        DeleteBtn.Hint := Phrases.Lookup('BTN_DELGAME');
    6471525      end;
    648     PaintLogo(Canvas,69,yLogo,MainTexture.clBevelLight,MainTexture.clBevelShade);
    649     end
    650   end
    651 else if Page=pgLoad then
    652   begin
    653 //  RisedTextOut(Canvas,x0Mini+2-BiColorTextWidth(Canvas,BookDate) div 2,y0Mini-73,BookDate);
    654   if LastTurn>0 then
    655     begin
    656     PaintProgressBar(canvas,6,xTurnSlider,yTurnSlider,0,
    657       LoadTurn*wTurnSlider div LastTurn,wTurnSlider,MainTexture);
    658     Frame(canvas,xTurnSlider-2,yTurnSlider-2,xTurnSlider+wTurnSlider+1,
    659       yTurnSlider+8,$B0B0B0,$FFFFFF);
    660     RFrame(canvas,xTurnSlider-3,yTurnSlider-3,xTurnSlider+wTurnSlider+2,
    661       yTurnSlider+9,$FFFFFF,$B0B0B0);
    662     end
    663   else DLine(Canvas,344,514,y0Mini+61+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    664   RisedTextOut(Canvas,344,y0Mini+61,Phrases.Lookup('STARTCONTROLS',8));
    665   s:=TurnToString(LoadTurn);
    666   RisedTextOut(Canvas,514-BiColorTextWidth(Canvas,s),y0Mini+61,s);
    667   end
    668 else if Page=pgEditRandom then
    669   begin
    670   DLine(Canvas,344,514,y0Mini-77+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    671   RisedTextOut(Canvas,344,y0Mini-77,Phrases.Lookup('STARTCONTROLS',5));
    672   s:=IntToStr((lxpre[WorldSize]*lypre[WorldSize]*20 + DefaultWorldTiles div 2)
    673     div DefaultWorldTiles *5)+'%';
    674   RisedTextOut(Canvas,514-BiColorTextWidth(Canvas,s),y0Mini-77,s);
    675   DLine(Canvas,344,514,y0Mini+61+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    676   RisedTextOut(Canvas,344,y0Mini+61,Phrases.Lookup('STARTCONTROLS',6));
    677   s:=IntToStr(StartLandMass)+'%';
    678   RisedTextOut(Canvas,514-BiColorTextWidth(Canvas,s),y0Mini+61,s);
    679   end
    680 else if Page=pgEditMap then
    681   begin
    682   //DLine(Canvas,344,514,y0Mini+61+19,MainTexture.clBevelLight,MainTexture.clBevelShade);
    683   s:=Format(Phrases2.Lookup('MAPPROP'), [(nMapLandTiles*100+556) div 1112,
    684     // 1112 is typical for world with 100% size and default land mass
    685     nMapStartPositions]);
    686   RisedTextOut(Canvas,x0Mini-BiColorTextWidth(Canvas,s) div 2,y0Mini+61,s);
    687   end;
    688 
    689 if StartBtn.Visible then
    690   BtnFrame(Canvas,StartBtn.BoundsRect,MainTexture);
    691 if Up2Btn.Visible then
    692   RFrame(Canvas,Up2Btn.Left-1,Up2Btn.Top-1,Up2Btn.Left+12,
    693     Up2Btn.Top+24,MainTexture.clBevelShade,MainTexture.clBevelLight);
    694 if Up1Btn.Visible then
    695   RFrame(Canvas,Up1Btn.Left-1,Up1Btn.Top-1,Up1Btn.Left+12,
    696     Up1Btn.Top+24,MainTexture.clBevelShade,MainTexture.clBevelLight);
    697 if AutoDiffUpBtn.Visible then
    698   RFrame(Canvas,AutoDiffUpBtn.Left-1,AutoDiffUpBtn.Top-1,AutoDiffUpBtn.Left+12,
    699     AutoDiffUpBtn.Top+24,MainTexture.clBevelShade,MainTexture.clBevelLight);
    700 if AutoEnemyUpBtn.Visible then
    701   RFrame(Canvas,AutoEnemyUpBtn.Left-1,AutoEnemyUpBtn.Top-1,AutoEnemyUpBtn.Left+12,
    702     AutoEnemyUpBtn.Top+24,MainTexture.clBevelShade,MainTexture.clBevelLight);
    703 if CustomizeBtn.Visible then
    704   RFrame(Canvas,CustomizeBtn.Left-1,CustomizeBtn.Top-1,CustomizeBtn.Left+12,
    705     CustomizeBtn.Top+12,MainTexture.clBevelShade,MainTexture.clBevelLight);
    706 if List.Visible then
    707   EditFrame(Canvas,List.BoundsRect,MainTexture);
    708 if RenameBtn.Visible then
    709   BtnFrame(Canvas,RenameBtn.BoundsRect,MainTexture);
    710 if DeleteBtn.Visible then
    711   BtnFrame(Canvas,DeleteBtn.BoundsRect,MainTexture);
    712 if Page=pgLoad then
    713   BtnFrame(Canvas,ReplayBtn.BoundsRect,MainTexture);
    714 
    715 if not (Page in [pgMain,pgNoLoad]) then
    716   begin
    717   xMini:=x0Mini-MiniWidth;
    718   yMini:=y0Mini-MiniHeight div 2;
    719   Frame(Canvas,xMini,yMini,xMini+3+MiniWidth*2,yMini+3+MiniHeight,MainTexture.clBevelLight,
    720     MainTexture.clBevelShade);
    721   Frame(Canvas,xMini+1,yMini+1,xMini+2+MiniWidth*2,yMini+2+MiniHeight,MainTexture.clBevelShade,
    722     MainTexture.clBevelLight);
    723   end;
    724 s:='';
    725 if MiniMode=mmPicture then
    726   begin
    727   BitBlt(Canvas.Handle,xMini+2,yMini+2,MiniWidth*2,MiniHeight,Mini.Canvas.Handle,0,0,SRCCOPY);
    728   if page=pgStartRandom then s:=Phrases.Lookup('RANMAP')
    729   end
    730 else if MiniMode=mmMultiPlayer then s:=Phrases.Lookup('MPMAP')
    731 else if page=pgStartMap then s:=Copy(MapFileName,1,Length(MapFileName)-9)
    732 else if page=pgEditMap then s:=List.Items[List.ItemIndex]
    733 else if page=pgNoLoad then s:=Phrases.Lookup('NOGAMES');
    734 if s<>'' then
    735   RisedTextOut(Canvas,x0Mini+2-BiColorTextWidth(Canvas,s) div 2,y0Mini-8,s);
    736 end;
    737 
    738 procedure TStartDlg.FormShow(Sender:TObject);
    739 type
    740 TLine=array[0..99999999] of Byte;
    741 var
    742 i,x,y: integer;
    743 PictureLine: ^TLine;
    744 begin
    745 SetMainTextureByAge(-1);
    746 List.Font.Color:=MainTexture.clMark;
    747 Fill(EmptyPicture.Canvas,0,0,64,64,(wMaintexture-64) div 2,
    748   (hMaintexture-64) div 2);
    749 for y:=0 to 63 do
    750   begin // darken texture for empty slot
    751   PictureLine:=EmptyPicture.ScanLine[y];
    752   for x:=0 to 64*3-1 do
    753     begin
    754     i:=integer(PictureLine[x])-28;
    755     if i<0 then i:=0;
    756     PictureLine[x]:=i;
    757     end
    758   end;
    759 
    760 Difficulty[0]:=Diff0;
    761 
    762 SelectedAction:=-1;
    763 if ShowTab=3 then PreviewMap(StartLandMass); // avoid delay on first TabX change
    764 ChangeTab(ShowTab);
    765 Background.Enabled:=false;
    766 end;
    767 
    768 procedure TStartDlg.UnlistBackupFile(FileName: string);
    769 var
    770 i: integer;
    771 begin
    772 if FileName[1]<>'~' then FileName:='~'+FileName;
    773 i:=FormerGames.Count-1;
    774 while (i>=0) and (AnsiCompareFileName(FormerGames[i],FileName)<>0) do dec(i);
    775 if i>=0 then
    776   begin
    777   FormerGames.Delete(i);
    778   if ListIndex[2]=i then ListIndex[2]:=0
    779   end
    780 end;
    781 
    782 procedure TStartDlg.StartBtnClick(Sender:TObject);
    783 var
    784 i,GameCount,MapCount: integer;
    785 FileName: string;
    786 Reg: TRegistry;
    787 begin
    788 case Page of
    789   pgLoad:
    790     begin //load
    791     FileName:=List.Items[List.ItemIndex];
    792     if LoadGame(DataDir+'Saved\', FileName+'.cevo', LoadTurn,false) then
    793       UnlistBackupFile(FileName)
    794     else SimpleMessage(Phrases.Lookup('LOADERR'));
    795     SlotAvailable:=-1;
    796     end;
    797 
    798   pgStartRandom,pgStartMap: if bixView[0]>=0 then
    799     begin
    800     if (page=pgStartMap) and (nMapStartPositions=0) and (AutoDiff>0) then
    801       begin
    802       SimpleMessage(Phrases.Lookup('NOSTARTPOS'));
    803       exit
     1526
     1527    pgEditRandom, pgEditMap:
     1528      begin
     1529        StartBtn.Caption := Phrases.Lookup('STARTCONTROLS', 12);
     1530        RenameBtn.Hint := Phrases.Lookup('BTN_RENMAP');
     1531        DeleteBtn.Hint := Phrases.Lookup('BTN_DELMAP');
    8041532      end;
    805 
    806     Reg:=TRegistry.Create;
    807     Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start',true);
    808     try
    809       GameCount:=Reg.ReadInteger('GameCount');
    810     except
    811       GameCount:=0;
    812       end;
    813 
    814     if (AutoDiff<0) and (bixView[0]=bixNoTerm) then
    815       FileName:='Round'+IntToStr(GetCurrentProcessID())
     1533  end;
     1534
     1535  PaintInfo;
     1536  for i := 0 to ControlCount - 1 do
     1537    Controls[i].Visible := Controls[i].Tag and (256 shl Page) <> 0;
     1538  if Page = pgLoad then
     1539    ReplayBtn.Visible := MiniMode <> mmMultiPlayer;
     1540  List.Invalidate;
     1541  SmartInvalidate(0, 0, ClientWidth, ClientHeight, invalidateTab0);
     1542end;
     1543
     1544procedure TStartDlg.ChangeTab(NewTab: integer);
     1545begin
     1546  Tab := NewTab;
     1547  case Tab of
     1548    1:
     1549      List.Items.Assign(Maps);
     1550    3:
     1551      List.Items.Assign(FormerGames);
     1552  end;
     1553  if Tab <> 2 then
     1554    if ListIndex[Tab] >= 0 then
     1555      List.ItemIndex := ListIndex[Tab]
    8161556    else
    817       begin
    818       inc(GameCount);
    819       FileName:=Format(Phrases.Lookup('GAME'),[GameCount]);
    820       end;
    821 
    822     // save settings and AI assignment
    823     if page=pgStartRandom then
    824       begin
    825       Reg.WriteInteger('WorldSize',WorldSize);
    826       Reg.WriteInteger('LandMass',StartLandMass);
    827       if AutoDiff<0 then
    828         for i:=0 to nPlOffered-1 do
    829           begin
    830           if bixView[i]=-1 then Reg.WriteString('Control'+IntToStr(i),'')
    831           else Reg.WriteString('Control'+IntToStr(i),Brain[bixView[i]].FileName);
    832           Reg.WriteInteger('Diff'+IntToStr(i),Difficulty[i]);
    833           end;
    834       Reg.WriteInteger('MultiControl',MultiControl);
    835       end;
    836 
    837     if AutoDiff>0 then
    838       begin
    839       Reg.WriteString('DefaultAI',Brain[bixDefault].FileName);
    840       SlotAvailable:=0; // bixView will be invalid hereafter
    841       bixView[0]:=bixTerm;
    842       Difficulty[0]:=PlayerAutoDiff[AutoDiff];
    843       for i:=1 to nPl-1 do
    844         if (Page=pgStartRandom) and (i<=AutoEnemies)
    845           or (Page=pgStartMap) and (i<nMapStartPositions) then
    846           begin
    847           if AutoDiff=1 then
    848             bixView[i]:=bixBeginner
    849           else bixView[i]:=bixDefault;
    850           Difficulty[i]:=EnemyAutoDiff[AutoDiff];
    851           end
    852         else bixView[i]:=-1;
    853       end
    854     else
    855       begin
    856       for i:=6 to 8 do
    857         if (bixView[0]<>bixNoTerm) and (MultiControl and (1 shl i)<>0) then
    858           begin
    859           bixView[i+3]:=bixView[i];
    860           Difficulty[i+3]:=Difficulty[i];
    861           bixView[i+6]:=bixView[i];
    862           Difficulty[i+6]:=Difficulty[i];
    863           end
    864         else
    865           begin
    866           bixView[i+3]:=-1;
    867           bixView[i+6]:=-1;
    868           end
    869       end;
    870 
    871     Reg.WriteInteger('AutoDiff',AutoDiff);
    872     Reg.WriteInteger('AutoEnemies',AutoEnemies);
    873     Reg.WriteInteger('MaxTurn',MaxTurn);
    874     Reg.WriteInteger('GameCount',GameCount);
    875     Reg.closekey;
    876     Reg.Free;
    877 
    878     StartNewGame(DataDir+'Saved\', FileName+'.cevo', MapFileName,
    879       lxpre[WorldSize], lypre[WorldSize], StartLandMass, MaxTurn);
    880     UnlistBackupFile(FileName);
    881     end;
    882 
    883   pgEditMap: EditMap(MapFileName, lxmax, lymax, StartLandMass);
    884 
    885   pgEditRandom: // new map
    886     begin
    887     Reg:=TRegistry.Create;
    888     Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start',true);
    889     try
    890       MapCount:=Reg.ReadInteger('MapCount');
    891     except
    892       MapCount:=0;
    893       end;
    894     inc(MapCount);
    895     Reg.WriteInteger('MapCount',MapCount);
    896     Reg.closekey;
    897     Reg.Free;
    898     MapFileName:=Format(Phrases.Lookup('MAP'),[MapCount])+'.cevo map';
    899     EditMap(MapFileName, lxpre[WorldSize], lypre[WorldSize], StartLandMass);
    900     end
    901   end
    902 end;
    903 
    904 procedure TStartDlg.PaintInfo;
    905 
    906   procedure PaintRandomMini(Brightness: integer);
    907   type
    908   TLine=array[0..lxmax*2,0..2] of Byte;
    909   var
    910   i,x,y,xm,cm:integer;
    911   MiniLine:^TLine;
    912   Map: ^TTileList;
    913   begin
    914   Map:=PreviewMap(StartLandMass);
    915   MiniWidth:=lxpre[WorldSize]; MiniHeight:=lypre[WorldSize];
    916 
    917   Mini.PixelFormat:=pf24bit;
    918   Mini.Width:=MiniWidth*2;Mini.Height:=MiniHeight;
    919   for y:=0 to MiniHeight-1 do
    920     begin
    921     MiniLine:=Mini.ScanLine[y];
    922     for x:=0 to MiniWidth-1 do for i:=0 to 1 do
    923       begin
    924       xm:=(x*2+i+y and 1) mod (MiniWidth*2);
    925       cm:=MiniColors[Map[x*lxmax div MiniWidth
    926         +lxmax*((y*(lymax-1)+MiniHeight div 2) div (MiniHeight-1))] and fTerrain,i];
    927       MiniLine[xm,0]:=cm shr 16 *Brightness div 3;
    928       MiniLine[xm,1]:=cm shr 8 and $FF *Brightness div 3;
    929       MiniLine[xm,2]:=cm and $FF *Brightness div 3;
    930       end;
    931     end;
    932   end;
    933 
    934 var
    935 SaveMap: array[0..lxmax*lymax-1] of Byte;
    936 
    937   procedure PaintFileMini;
    938   type
    939   TLine=array[0..99999999,0..2] of Byte;
    940   var
    941   i,x,y,xm,cm,Tile,OwnColor,EnemyColor: integer;
    942   MiniLine,PrevMiniLine:^TLine;
    943   begin
    944   OwnColor:=GrExt[HGrSystem].Data.Canvas.Pixels[95,67];
    945   EnemyColor:=GrExt[HGrSystem].Data.Canvas.Pixels[96,67];
    946   Mini.PixelFormat:=pf24bit;
    947   Mini.Width:=MiniWidth*2;Mini.Height:=MiniHeight;
    948   if MiniMode=mmPicture then
    949     begin
    950     MiniLine:=nil;
    951     for y:=0 to MiniHeight-1 do
    952       begin
    953       PrevMiniLine:=MiniLine;
    954       MiniLine:=Mini.ScanLine[y];
    955       for x:=0 to MiniWidth-1 do for i:=0 to 1 do
    956         begin
    957         xm:=(x*2+i+y and 1) mod (MiniWidth*2);
    958         Tile:=SaveMap[x+MiniWidth*y];
    959         if Tile and fTerrain=fUNKNOWN then cm:=$000000
    960         else if Tile and smCity<>0 then
    961           begin
    962           if Tile and smOwned<>0 then cm:=OwnColor
    963           else cm:=EnemyColor;
    964           if PrevMiniLine<>nil then
    965             begin // 2x2 city dot covers two scanlines
    966             PrevMiniLine[xm,0]:=cm shr 16;
    967             PrevMiniLine[xm,1]:=cm shr 8 and $FF;
    968             PrevMiniLine[xm,2]:=cm and $FF;
    969             end
    970           end
    971         else if (i=0) and (Tile and smUnit<>0) then
    972           if Tile and smOwned<>0 then cm:=OwnColor
    973           else cm:=EnemyColor
    974         else cm:=MiniColors[Tile and fTerrain,i];
    975         MiniLine[xm,0]:=cm shr 16;
    976         MiniLine[xm,1]:=cm shr 8 and $FF;
    977         MiniLine[xm,2]:=cm and $FF;
    978         end;
    979       end
    980     end;
    981   end;
    982 
    983 var
    984 x,y,dummy, FileLandMass, lxFile, lyFile: integer;
    985 LogFile, MapFile: file;
    986 s: string[255];
    987 MapRow: array[0..lxmax-1] of Cardinal;
    988 
    989 begin
    990 case Page of
    991   pgStartRandom:
    992     begin
    993     MiniMode:=mmPicture;
    994     PaintRandomMini(3);
    995     end;
    996 
    997   pgNoLoad:
    998     begin
    999     MiniWidth:=lxpre[DefaultWorldSize]; MiniHeight:=lypre[DefaultWorldSize];
    1000     MiniMode:=mmNone;
    1001     end;
    1002 
    1003   pgLoad:
    1004     begin
    1005     AssignFile(LogFile,DataDir+'Saved\'+List.Items[List.ItemIndex]+'.cevo');
    1006     try
    1007       Reset(LogFile,4);
    1008       BlockRead(LogFile,s[1],2); {file id}
    1009       BlockRead(LogFile,dummy,1); {format id}
    1010       if dummy>=$000E01 then
    1011         BlockRead(LogFile,dummy,1); {item stored since 0.14.1}
    1012       BlockRead(LogFile,MiniWidth,1);
    1013       BlockRead(LogFile,MiniHeight,1);
    1014       BlockRead(LogFile,FileLandMass,1);
    1015       if FileLandMass=0 then
    1016         for y:=0 to MiniHeight-1 do BlockRead(LogFile,MapRow,MiniWidth);
    1017       BlockRead(LogFile,dummy,1);
    1018       BlockRead(LogFile,dummy,1);
    1019       BlockRead(LogFile,LastTurn,1);
    1020       BlockRead(LogFile,SaveMap,1);
    1021       if SaveMap[0]=$80 then MiniMode:=mmMultiPlayer
    1022       else MiniMode:=mmPicture;
    1023       if MiniMode=mmPicture then BlockRead(LogFile,SaveMap[4],(MiniWidth*MiniHeight-1) div 4);
    1024       CloseFile(LogFile);
    1025     except
    1026       CloseFile(LogFile);
    1027       LastTurn:=0;
    1028       MiniWidth:=lxpre[DefaultWorldSize]; MiniHeight:=lypre[DefaultWorldSize];
    1029       MiniMode:=mmNone;
    1030       end;
    1031     //BookDate:=DateToStr(FileDateToDateTime(FileAge(FileName)));
    1032     PaintFileMini;
    1033     if not TurnValid then
    1034       begin
    1035       LoadTurn:=LastTurn;
    1036       SmartInvalidate(xTurnSlider-2,y0Mini+61,xTurnSlider+wTurnSlider+2,yTurnSlider+9);
    1037       end;
    1038     TurnValid:=true;
    1039     end;
    1040 
    1041   pgEditRandom:
    1042     begin
    1043     MapFileName:='';
    1044     MiniMode:=mmPicture;
    1045     PaintRandomMini(4);
    1046     end;
    1047 
    1048   pgStartMap,pgEditMap:
    1049     begin
    1050     MiniMode:=mmPicture;
    1051     if Page=pgEditMap then MapFileName:=List.Items[List.ItemIndex]+'.cevo map';
    1052     if LoadGraphicFile(Mini, DataDir+'Maps\'+Copy(MapFileName,1,Length(MapFileName)-9), gfNoError) then
    1053       begin
    1054       if Mini.Width div 2>MaxWidthMapLogo then Mini.Width:=MaxWidthMapLogo*2;
    1055       if Mini.Height>MaxHeightMapLogo then Mini.Height:=MaxHeightMapLogo;
    1056       MiniWidth:=Mini.Width div 2;
    1057       MiniHeight:=Mini.Height;
    1058       end
    1059     else
    1060       begin
    1061       MiniMode:=mmNone;
    1062       MiniWidth:=MaxWidthMapLogo; MiniHeight:=MaxHeightMapLogo;
    1063       end;
    1064 
    1065     AssignFile(MapFile,DataDir+'Maps\'+MapFileName);
    1066     try
    1067       Reset(MapFile,4);
    1068       BlockRead(MapFile,s[1],2); {file id}
    1069       BlockRead(MapFile,x,1); {format id}
    1070       BlockRead(MapFile,x,1); //MaxTurn
    1071       BlockRead(MapFile,lxFile,1);
    1072       BlockRead(MapFile,lyFile,1);
    1073       nMapLandTiles:=0;
    1074       nMapStartPositions:=0;
    1075       for y:=0 to lyFile-1 do
    1076         begin
    1077         BlockRead(MapFile,MapRow,lxFile);
    1078         for x:=0 to lxFile-1 do
    1079           begin
    1080           if (MapRow[x] and fTerrain) in
    1081             [fGrass,fPrairie,fTundra,fSwamp,fForest,fHills] then
    1082             inc(nMapLandTiles);
    1083           if MapRow[x] and (fPrefStartPos or fStartPos)<>0 then
    1084             inc(nMapStartPositions);
    1085           end
    1086         end;
    1087       if nMapStartPositions>nPl then nMapStartPositions:=nPl;
    1088       CloseFile(MapFile);
    1089     except
    1090       CloseFile(MapFile);
    1091       end;
    1092     if Page=pgEditMap then
    1093       SmartInvalidate(x0Mini-112,y0Mini+61,x0Mini+112,y0Mini+91);
    1094     end
    1095   end;
    1096 SmartInvalidate(x0Mini-lxmax,y0Mini-lymax div 2,
    1097   x0Mini-lxmax+2*lxmax+4,y0Mini-lymax div 2+lymax+4);
    1098 end;
    1099 
    1100 procedure TStartDlg.BrainClick(Sender: TObject);
    1101 var
    1102 i: integer;
    1103 begin
    1104 //Play('BUTTON_UP');
    1105 if bixPopup<0 then
    1106   begin // change default AI
    1107   bixDefault:=TMenuItem(Sender).Tag;
    1108   SmartInvalidate(xDefault,yDefault,xDefault+64,yDefault+64);
    1109   end
    1110 else
    1111   begin
    1112   Brain[bixView[bixPopup]].Flags:=Brain[bixView[bixPopup]].Flags and not fUsed;
    1113   bixView[bixPopup]:=TMenuItem(Sender).Tag;
    1114   DiffUpBtn[bixPopup].Visible:= bixView[bixPopup]>=bixTerm;
    1115   DiffDownBtn[bixPopup].Visible:= bixView[bixPopup]>=bixTerm;
    1116   if bixPopup in OfferMultiple then
    1117     begin
    1118     MultiBtn[bixPopup].Visible:= bixView[bixPopup]>=bixTerm;
    1119     MultiBtn[bixPopup].ButtonIndex:=2+(MultiControl shr bixPopup) and 1;
    1120     end;
    1121   Brain[bixView[bixPopup]].Flags:=Brain[bixView[bixPopup]].Flags or fUsed;
    1122   if bixView[bixPopup]<bixTerm then Difficulty[bixPopup]:=0 {supervisor}
    1123   else Difficulty[bixPopup]:=2;
    1124   if (Page=pgStartRandom) and (bixPopup in OfferMultiple)
    1125     and (bixView[bixPopup]<0) then
    1126     MultiControl:=MultiControl and not (1 shl bixPopup);
    1127   if (bixPopup=0) and (MapFileName<>'') then ChangePage(Page);
    1128   if bixView[bixPopup]=bixNoTerm then
    1129     begin // turn all local players off
    1130     for i:=1 to nPlOffered-1 do if bixView[i]=bixTerm then
    1131       begin
    1132       bixView[i]:=-1;
    1133       DiffUpBtn[i].Visible:=false;
    1134       DiffUpBtn[i].Tag:=0;
    1135       DiffDownBtn[i].Visible:=false;
    1136       DiffDownBtn[i].Tag:=0;
    1137       if i in OfferMultiple then
    1138         begin
    1139         MultiBtn[i].Visible:=false;
    1140         MultiBtn[i].Tag:=0;
    1141         end;
    1142       SmartInvalidate(xBrain[i]-31,yBrain[i]-1,xBrain[i]+64,DiffUpBtn[i].Top+25);
    1143       end;
    1144     Brain[bixTerm].Flags:=Brain[bixTerm].Flags and not fUsed;
    1145     end;
    1146   SmartInvalidate(xBrain[bixPopup]-31,yBrain[bixPopup]-1,xBrain[bixPopup]+64,
    1147     DiffUpBtn[bixPopup].Top+25);
    1148   end
    1149 end;
    1150 
    1151 procedure TStartDlg.InitPopup(PopupIndex: integer);
    1152 var
    1153 i, FixedLines: integer;
    1154 m: TMenuItem;
    1155 
    1156   procedure OfferBrain(Index: integer);
    1157   var
    1158   j: integer;
    1159   begin
    1160   m:=TMenuItem.Create(PopupMenu1);
    1161   if Index<0 then m.Caption:=Phrases.Lookup('NOMOD')
    1162   else m.Caption:=Brain[Index].Name;
    1163   m.Tag:=Index;
    1164   m.OnClick:=BrainClick;
    1165   j:=FixedLines;
    1166   while (j<PopupMenu1.Items.Count) and (StrIComp(pchar(m.Caption),
    1167     pchar(PopupMenu1.Items[j].Caption))>0) do inc(j);
    1168   m.RadioItem:=true;
    1169   if bixPopup<0 then m.Checked:= bixDefault=Index
    1170   else m.Checked:= bixView[bixPopup]=Index;
    1171   PopupMenu1.Items.Insert(j,m);
    1172   end;
    1173 
    1174 begin
    1175 bixPopup:=PopupIndex;
    1176 EmptyMenu(PopupMenu1.Items);
    1177 if bixPopup<0 then
    1178   begin // select default AI
    1179   FixedLines:=0;
    1180   if nBrain>=bixFirstAI+2 then
    1181     begin OfferBrain(bixRandom); inc(FixedLines) end;
    1182   for i:=bixFirstAI to nBrain-1 do // offer available AIs
    1183     if Brain[i].Flags and fMultiple<>0 then
    1184       OfferBrain(i);
    1185   end
    1186 else
    1187   begin
    1188   FixedLines:=0;
    1189   if bixPopup>0 then begin OfferBrain(-1); inc(FixedLines); end;
    1190   for i:=bixTerm downto 0 do // offer game interfaces
    1191     if (bixPopup=0) or (i=bixTerm) and (bixView[0]<>bixNoTerm) then
    1192       begin OfferBrain(i); inc(FixedLines); end;
    1193   if bixPopup>0 then
    1194     begin
    1195     m:=TMenuItem.Create(PopupMenu1);
    1196     m.Caption:='-';
    1197     PopupMenu1.Items.Add(m);
    1198     inc(FixedLines);
    1199     if nBrain>=bixFirstAI+2 then
    1200       begin OfferBrain(bixRandom); inc(FixedLines); end;
    1201     for i:=bixFirstAI to nBrain-1 do // offer available AIs
    1202       if (Brain[i].Flags and fMultiple<>0) or (Brain[i].Flags and fUsed=0)
    1203         or (i=bixView[bixPopup]) then
    1204         OfferBrain(i);
    1205     end;
    1206   end
    1207 end;
    1208 
    1209 procedure TStartDlg.UpdateFormerGames;
    1210 var
    1211 i: integer;
    1212 f: TSearchRec;
    1213 begin
    1214 FormerGames.Clear;
    1215 if FindFirst(DataDir+'Saved\*.cevo',$21,f)=0 then
    1216   repeat
    1217     i:=FormerGames.Count;
    1218     while (i>0) and (f.Time<integer(FormerGames.Objects[i-1])) do
    1219       dec(i);
    1220     FormerGames.InsertObject(i,Copy(f.Name,1,Length(f.Name)-5),
    1221       TObject(f.Time));
    1222   until FindNext(f)<>0;
    1223 ListIndex[2]:=FormerGames.Count-1;
    1224 if (ShowTab=2) and (FormerGames.Count>0) then ShowTab:=3;
    1225 TurnValid:=false;
    1226 end;
    1227 
    1228 procedure TStartDlg.UpdateMaps;
    1229 var
    1230 f: TSearchRec;
    1231 begin
    1232 Maps.Clear;
    1233 if FindFirst(DataDir+'Maps\*.cevo map',$21,f)=0 then
    1234   repeat
    1235     Maps.Add(Copy(f.Name,1,Length(f.Name)-9));
    1236   until FindNext(f)<>0;
    1237 Maps.Sort;
    1238 Maps.Insert(0,Phrases.Lookup('RANMAP'));
    1239 ListIndex[0]:=Maps.IndexOf(Copy(MapFileName,1,Length(MapFileName)-9));
    1240 if ListIndex[0]<0 then ListIndex[0]:=0;
    1241 end;
    1242 
    1243 procedure TStartDlg.ChangePage(NewPage: integer);
    1244 var
    1245 i,j,p1: integer;
    1246 s: string;
    1247 Reg: TRegistry;
    1248 invalidateTab0: boolean;
    1249 begin
    1250 invalidateTab0:= (Page=pgMain) or (NewPage=pgMain);
    1251 Page:=NewPage;
    1252 case Page of
    1253   pgStartRandom, pgStartMap:
    1254     begin
    1255     StartBtn.Caption:=Phrases.Lookup('STARTCONTROLS',1);
    1256     if Page=pgStartRandom then i:=nPlOffered
    1257     else
    1258       begin
    1259       i:=nMapStartPositions;
    1260       if i=0 then begin bixView[0]:=bixSuper_Virtual; Difficulty[0]:=0 end;
    1261       if bixView[0]<bixTerm then inc(i);
    1262       if i>nPl then i:=nPl;
    1263       if i<=nPlOffered then MultiControl:=0
    1264       else MultiControl:=InitMulti[i];
    1265       end;
    1266     if InitAlive[i]<>SlotAvailable then
    1267       if Page=pgStartRandom then
    1268         begin // restore AI assignment of last start
    1269         Reg:=TRegistry.Create;
    1270         Reg.OpenKey('SOFTWARE\cevo\RegVer9\Start',false);
    1271         for p1:=0 to nPlOffered-1 do
    1272           begin
    1273           bixView[p1]:=-1;
    1274           s:=Reg.ReadString('Control'+IntToStr(p1));
    1275           Difficulty[p1]:=Reg.ReadInteger('Diff'+IntToStr(p1));
    1276           if s<>'' then
    1277             for j:=0 to nBrain-1 do
    1278               if AnsiCompareFileName(s,Brain[j].FileName)=0 then bixView[p1]:=j;
    1279           end;
    1280         MultiControl:=Reg.ReadInteger('MultiControl');
    1281         Reg.closekey;
    1282         Reg.Free;
    1283         end
     1557      List.ItemIndex := 0;
     1558  case Tab of
     1559    0:
     1560      ChangePage(pgMain);
     1561    1:
     1562      if List.ItemIndex = 0 then
     1563        ChangePage(pgEditRandom)
    12841564      else
    1285         for p1:=1 to nPl-1 do
    1286           if 1 shl p1 and InitAlive[i]<>0 then
    1287             begin bixView[p1]:=bixDefault; Difficulty[p1]:=2; end
    1288           else bixView[p1]:=-1;
    1289     SlotAvailable:=InitAlive[i];
    1290     for i:=0 to nPlOffered-1 do
    1291       if (AutoDiff<0) and (bixView[i]>=bixTerm) then
    1292         begin DiffUpBtn[i].Tag:=768; DiffDownBtn[i].Tag:=768; end
    1293       else begin DiffUpBtn[i].Tag:=0; DiffDownBtn[i].Tag:=0; end;
    1294     for i:=6 to 8 do
    1295       if (AutoDiff<0) and (bixView[i]>=bixTerm) then
    1296         begin
    1297         MultiBtn[i].Tag:=768;
    1298         MultiBtn[i].ButtonIndex:=2+(MultiControl shr i) and 1;
    1299         MultiBtn[i].Enabled:=Page=pgStartRandom
    1300         end
    1301       else MultiBtn[i].Tag:=0;
    1302     if (AutoDiff>0) and (Page<>pgStartMap) then
    1303       begin
    1304       AutoEnemyUpBtn.Tag:=768;
    1305       AutoEnemyDownBtn.Tag:=768;
    1306       end
    1307     else
    1308       begin
    1309       AutoEnemyUpBtn.Tag:=0;
    1310       AutoEnemyDownBtn.Tag:=0;
    1311       end;
    1312     if AutoDiff>0 then
    1313       begin
    1314       AutoDiffUpBtn.Tag:=768;
    1315       AutoDiffDownBtn.Tag:=768;
    1316       end
    1317     else
    1318       begin
    1319       AutoDiffUpBtn.Tag:=0;
    1320       AutoDiffDownBtn.Tag:=0;
    1321       end
    1322     end;
    1323 
    1324   pgNoLoad,pgLoad:
    1325     begin
    1326     StartBtn.Caption:=Phrases.Lookup('STARTCONTROLS',2);
    1327     RenameBtn.Hint:=Phrases.Lookup('BTN_RENGAME');
    1328     DeleteBtn.Hint:=Phrases.Lookup('BTN_DELGAME');
    1329     end;
    1330 
    1331   pgEditRandom,pgEditMap:
    1332     begin
    1333     StartBtn.Caption:=Phrases.Lookup('STARTCONTROLS',12);
    1334     RenameBtn.Hint:=Phrases.Lookup('BTN_RENMAP');
    1335     DeleteBtn.Hint:=Phrases.Lookup('BTN_DELMAP');
    1336     end;
    1337   end;
    1338 
    1339 PaintInfo;
    1340 for i:=0 to ControlCount-1 do
    1341   Controls[i].Visible:= Controls[i].Tag and (256 shl Page)<>0;
    1342 if Page=pgLoad then
    1343   ReplayBtn.Visible:= MiniMode<>mmMultiPlayer;
    1344 List.Invalidate;
    1345 SmartInvalidate(0,0,ClientWidth,ClientHeight,invalidateTab0);
    1346 end;
    1347 
    1348 procedure TStartDlg.ChangeTab(NewTab: integer);
    1349 begin
    1350 Tab:=NewTab;
    1351 case Tab of
    1352   1: List.Items.Assign(Maps);
    1353   3: List.Items.Assign(FormerGames);
    1354   end;
    1355 if Tab<>2 then
    1356   if ListIndex[Tab]>=0 then List.ItemIndex:=ListIndex[Tab]
    1357   else List.ItemIndex:=0;
    1358 case Tab of
    1359   0: ChangePage(pgMain);
    1360   1:
    1361     if List.ItemIndex=0 then ChangePage(pgEditRandom)
    1362     else ChangePage(pgEditMap);
    1363   2:
    1364     if MapFileName='' then ChangePage(pgStartRandom)
    1365     else ChangePage(pgStartMap);
    1366   3:
    1367     if FormerGames.Count=0 then ChangePage(pgNoLoad)
    1368     else ChangePage(pgLoad);
     1565        ChangePage(pgEditMap);
     1566    2:
     1567      if MapFileName = '' then
     1568        ChangePage(pgStartRandom)
     1569      else
     1570        ChangePage(pgStartMap);
     1571    3:
     1572      if FormerGames.Count = 0 then
     1573        ChangePage(pgNoLoad)
     1574      else
     1575        ChangePage(pgLoad);
    13691576  end;
    13701577end;
     
    13731580  Shift: TShiftState; x, y: integer);
    13741581var
    1375 i: integer;
    1376 begin
    1377 if (y<TabHeight+1) and (x-TabOffset<TabSize*4) and ((x-TabOffset) div TabSize<>Tab) then
    1378   begin
    1379 //  Play('BUTTON_DOWN');
    1380   ListIndex[Tab]:=List.ItemIndex;
    1381   ChangeTab((x-TabOffset) div TabSize);
    1382   end
    1383 else if page=pgMain then
    1384   begin
    1385   case SelectedAction of
    1386     maConfig:
    1387       begin
    1388       ShellExecute(Handle,'open',pchar(HomeDir+'Configurator.exe'),
    1389         pchar('-r"'+paramstr(0)+'"'),'',SW_SHOWNORMAL);
    1390       Close
    1391       end;
    1392     maManual: DirectHelp(cStartHelp);
    1393     maCredits: DirectHelp(cStartCredits);
    1394     maAIDev: ShellExecute(Handle,'open',
    1395       pchar(HomeDir+'AI Template\AI development manual.html'),'','',
    1396       SW_SHOWNORMAL);
    1397     maWeb:ShellExecute(Handle,'open','http://c-evo.org','','',SW_SHOWNORMAL)
     1582  i: integer;
     1583begin
     1584  if (y < TabHeight + 1) and (x - TabOffset < TabSize * 4) and
     1585    ((x - TabOffset) div TabSize <> Tab) then
     1586  begin
     1587    // Play('BUTTON_DOWN');
     1588    ListIndex[Tab] := List.ItemIndex;
     1589    ChangeTab((x - TabOffset) div TabSize);
     1590  end
     1591  else if Page = pgMain then
     1592  begin
     1593    case SelectedAction of
     1594      maConfig:
     1595        begin
     1596          ShellExecute(Handle, 'open', pchar(HomeDir + 'Configurator.exe'),
     1597            pchar('-r"' + ParamStr(0) + '"'), '', SW_SHOWNORMAL);
     1598          Close
     1599        end;
     1600      maManual:
     1601        DirectHelp(cStartHelp);
     1602      maCredits:
     1603        DirectHelp(cStartCredits);
     1604      maAIDev:
     1605        ShellExecute(Handle, 'open',
     1606          pchar(HomeDir + 'AI Template\AI development manual.html'), '', '',
     1607          SW_SHOWNORMAL);
     1608      maWeb:
     1609        ShellExecute(Handle, 'open', 'http://c-evo.org', '', '', SW_SHOWNORMAL)
    13981610    end;
    13991611  end
    1400 else if (AutoDiff<0) and ((page=pgStartRandom)
    1401   or (page=pgStartMap) and (nMapStartPositions>0)) then
    1402   begin
    1403   for i:=0 to nPlOffered-1 do
    1404     if (1 shl i and SlotAvailable<>0) and (x>=xBrain[i]) and (y>=yBrain[i])
    1405       and (x<xBrain[i]+64) and (y<yBrain[i]+64) then
    1406       begin
    1407       InitPopup(i);
    1408       if yBrain[i]>y0Brain then
    1409         PopupMenu1.Popup(Left+xBrain[i]+4,Top+yBrain[i]+60)
    1410       else PopupMenu1.Popup(Left+xBrain[i]+4,Top+yBrain[i]+4);
     1612  else if (AutoDiff < 0) and ((Page = pgStartRandom) or (Page = pgStartMap) and
     1613    (nMapStartPositions > 0)) then
     1614  begin
     1615    for i := 0 to nPlOffered - 1 do
     1616      if (1 shl i and SlotAvailable <> 0) and (x >= xBrain[i]) and
     1617        (y >= yBrain[i]) and (x < xBrain[i] + 64) and (y < yBrain[i] + 64) then
     1618      begin
     1619        InitPopup(i);
     1620        if yBrain[i] > y0Brain then
     1621          PopupMenu1.Popup(left + xBrain[i] + 4, top + yBrain[i] + 60)
     1622        else
     1623          PopupMenu1.Popup(left + xBrain[i] + 4, top + yBrain[i] + 4);
    14111624      end
    14121625  end
    1413 else if (AutoDiff>1) and ((page=pgStartRandom) or (page=pgStartMap))
    1414   and (x>=xDefault) and (y>=yDefault) and (x<xDefault+64) and (y<yDefault+64) then
    1415     if nBrain<bixFirstAI+2 then
     1626  else if (AutoDiff > 1) and ((Page = pgStartRandom) or (Page = pgStartMap)) and
     1627    (x >= xDefault) and (y >= yDefault) and (x < xDefault + 64) and
     1628    (y < yDefault + 64) then
     1629    if nBrain < bixFirstAI + 2 then
    14161630      SimpleMessage(Phrases.Lookup('NOALTAI'))
    14171631    else
    1418       begin
     1632    begin
    14191633      InitPopup(-1);
    1420       PopupMenu1.Popup(Left+xDefault+4,Top+yDefault+4);
     1634      PopupMenu1.Popup(left + xDefault + 4, top + yDefault + 4);
     1635    end
     1636  else if (Page = pgLoad) and (LastTurn > 0) and (y >= yTurnSlider) and
     1637    (y < yTurnSlider + 7) and (x >= xTurnSlider) and
     1638    (x <= xTurnSlider + wTurnSlider) then
     1639  begin
     1640    LoadTurn := LastTurn * (x - xTurnSlider) div wTurnSlider;
     1641    SmartInvalidate(xTurnSlider - 2, y0Mini + 61, xTurnSlider + wTurnSlider + 2,
     1642      yTurnSlider + 9);
     1643    Tracking := true
     1644  end
     1645end;
     1646
     1647procedure TStartDlg.Up2BtnClick(Sender: TObject);
     1648begin
     1649  case Page of
     1650    pgStartRandom, pgStartMap:
     1651      if MaxTurn < 1400 then
     1652      begin
     1653        inc(MaxTurn, 200);
     1654        SmartInvalidate(344, y0Mini + 61, 514, y0Mini + 82);
     1655      end;
     1656    pgLoad:
     1657      if LoadTurn < LastTurn then
     1658      begin
     1659        inc(LoadTurn);
     1660        SmartInvalidate(xTurnSlider - 2, y0Mini + 61, xTurnSlider + wTurnSlider
     1661          + 2, yTurnSlider + 9);
     1662      end;
     1663    pgEditRandom:
     1664      if StartLandMass < 96 then
     1665      begin
     1666        inc(StartLandMass, 5);
     1667        PaintInfo;
     1668        SmartInvalidate(344, y0Mini + 61, 514, y0Mini + 61 + 21);
     1669      end;
     1670  end
     1671end;
     1672
     1673procedure TStartDlg.Down2BtnClick(Sender: TObject);
     1674begin
     1675  case Page of
     1676    pgStartRandom, pgStartMap:
     1677      if MaxTurn > 400 then
     1678      begin
     1679        dec(MaxTurn, 200);
     1680        SmartInvalidate(344, y0Mini + 61, 514, y0Mini + 82);
     1681      end;
     1682    pgLoad:
     1683      if LoadTurn > 0 then
     1684      begin
     1685        dec(LoadTurn);
     1686        SmartInvalidate(xTurnSlider - 2, y0Mini + 61, xTurnSlider + wTurnSlider
     1687          + 2, yTurnSlider + 9);
     1688      end;
     1689    pgEditRandom:
     1690      if StartLandMass > 10 then
     1691      begin
     1692        dec(StartLandMass, 5);
     1693        PaintInfo;
     1694        SmartInvalidate(344, y0Mini + 61, 514, y0Mini + 61 + 21);
    14211695      end
    1422 else if (page=pgLoad) and (LastTurn>0) and (y>=yTurnSlider) and (y<yTurnSlider+7)
    1423   and (x>=xTurnSlider) and (x<=xTurnSlider+wTurnSlider) then
    1424     begin
    1425     LoadTurn:=LastTurn*(x-xTurnSlider) div wTurnSlider;
    1426     SmartInvalidate(xTurnSlider-2,y0Mini+61,xTurnSlider+wTurnSlider+2,yTurnSlider+9);
    1427     Tracking:=true
    1428     end
    1429 end;
    1430 
    1431 procedure TStartDlg.Up2BtnClick(Sender: TObject);
    1432 begin
    1433 case Page of
    1434   pgStartRandom,pgStartMap:
    1435     if MaxTurn<1400 then
    1436       begin
    1437       inc(MaxTurn,200);
    1438       SmartInvalidate(344,y0Mini+61,514,y0Mini+82);
     1696  end
     1697end;
     1698
     1699procedure TStartDlg.Up1BtnClick(Sender: TObject);
     1700begin
     1701  if WorldSize < nWorldSize - 1 then
     1702  begin
     1703    inc(WorldSize);
     1704    PaintInfo;
     1705    SmartInvalidate(344, y0Mini - 77, 510, y0Mini - 77 + 21);
     1706  end
     1707end;
     1708
     1709procedure TStartDlg.Down1BtnClick(Sender: TObject);
     1710begin
     1711  if WorldSize > 0 then
     1712  begin
     1713    dec(WorldSize);
     1714    PaintInfo;
     1715    SmartInvalidate(344, y0Mini - 77, 510, y0Mini - 77 + 21);
     1716  end
     1717end;
     1718
     1719procedure TStartDlg.FormClose(Sender: TObject; var Action: TCloseAction);
     1720begin
     1721  DirectDlg.Close
     1722end;
     1723
     1724procedure TStartDlg.ListClick(Sender: TObject);
     1725var
     1726  i: integer;
     1727begin
     1728  if (Tab = 1) and ((List.ItemIndex = 0) <> (Page = pgEditRandom)) then
     1729  begin
     1730    if List.ItemIndex = 0 then
     1731      Page := pgEditRandom
     1732    else
     1733      Page := pgEditMap;
     1734    for i := 0 to ControlCount - 1 do
     1735      Controls[i].Visible := Controls[i].Tag and (256 shl Page) <> 0;
     1736    SmartInvalidate(328, Up1Btn.top - 12, ClientWidth, Up2Btn.top + 35);
     1737  end;
     1738  if Page = pgLoad then
     1739    TurnValid := false;
     1740  PaintInfo;
     1741  if Page = pgLoad then
     1742    ReplayBtn.Visible := MiniMode <> mmMultiPlayer;
     1743end;
     1744
     1745procedure TStartDlg.RenameBtnClick(Sender: TObject);
     1746var
     1747  i: integer;
     1748  NewName: string;
     1749  f: file;
     1750  ok: boolean;
     1751begin
     1752  if List.ItemIndex >= 0 then
     1753  begin
     1754    if Page = pgLoad then
     1755      InputDlg.Caption := Phrases.Lookup('TITLE_BOOKNAME')
     1756    else
     1757      InputDlg.Caption := Phrases.Lookup('TITLE_MAPNAME');
     1758    InputDlg.EInput.Text := List.Items[List.ItemIndex];
     1759    InputDlg.CenterToRect(BoundsRect);
     1760    InputDlg.ShowModal;
     1761    NewName := InputDlg.EInput.Text;
     1762    while (NewName <> '') and (NewName[1] = '~') do
     1763      Delete(NewName, 1, 1);
     1764    if (InputDlg.ModalResult = mrOK) and (NewName <> '') and
     1765      (NewName <> List.Items[List.ItemIndex]) then
     1766    begin
     1767      for i := 1 to Length(NewName) do
     1768        if NewName[i] in ['\', '/', ':', '*', '?', '"', '<', '>', '|'] then
     1769        begin
     1770          SimpleMessage(Format(Phrases.Lookup('NOFILENAME'), [NewName[i]]));
     1771          exit
     1772        end;
     1773      if Page = pgLoad then
     1774        AssignFile(f, DataDir + 'Saved\' + List.Items[List.ItemIndex] + '.cevo')
     1775      else
     1776        AssignFile(f, DataDir + 'Maps\' + List.Items[List.ItemIndex] +
     1777          '.cevo map');
     1778      ok := true;
     1779      try
     1780        if Page = pgLoad then
     1781          Rename(f, DataDir + 'Saved\' + NewName + '.cevo')
     1782        else
     1783          Rename(f, DataDir + 'Maps\' + NewName + '.cevo map');
     1784      except
     1785        // Play('INVALID');
     1786        ok := false
    14391787      end;
    1440   pgLoad:
    1441     if LoadTurn<LastTurn then
    1442       begin
    1443       inc(LoadTurn);
    1444       SmartInvalidate(xTurnSlider-2,y0Mini+61,xTurnSlider+wTurnSlider+2,yTurnSlider+9);
    1445       end;
    1446   pgEditRandom:
    1447     if StartLandMass<96 then
    1448       begin
    1449       inc(StartLandMass,5);
    1450       PaintInfo;
    1451       SmartInvalidate(344,y0Mini+61,514,y0Mini+61+21);
    1452       end;
    1453   end
    1454 end;
    1455 
    1456 procedure TStartDlg.Down2BtnClick(Sender: TObject);
    1457 begin
    1458 case Page of
    1459   pgStartRandom,pgStartMap:
    1460     if MaxTurn>400 then
    1461       begin
    1462       dec(MaxTurn,200);
    1463       SmartInvalidate(344,y0Mini+61,514,y0Mini+82);
    1464       end;
    1465   pgLoad:
    1466     if LoadTurn>0 then
    1467       begin
    1468       dec(LoadTurn);
    1469       SmartInvalidate(xTurnSlider-2,y0Mini+61,xTurnSlider+wTurnSlider+2,yTurnSlider+9);
    1470       end;
    1471   pgEditRandom:
    1472     if StartLandMass>10 then
    1473       begin
    1474       dec(StartLandMass,5);
    1475       PaintInfo;
    1476       SmartInvalidate(344,y0Mini+61,514,y0Mini+61+21);
    1477       end
    1478   end
    1479 end;
    1480 
    1481 procedure TStartDlg.Up1BtnClick(Sender: TObject);
    1482 begin
    1483 if WorldSize<nWorldSize-1 then
    1484   begin
    1485   inc(WorldSize);
    1486   PaintInfo;
    1487   SmartInvalidate(344,y0Mini-77,510,y0Mini-77+21);
    1488   end
    1489 end;
    1490 
    1491 procedure TStartDlg.Down1BtnClick(Sender: TObject);
    1492 begin
    1493 if WorldSize>0 then
    1494   begin
    1495   dec(WorldSize);
    1496   PaintInfo;
    1497   SmartInvalidate(344,y0Mini-77,510,y0Mini-77+21);
    1498   end
    1499 end;
    1500 
    1501 procedure TStartDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    1502 begin
    1503 DirectDlg.Close
    1504 end;
    1505 
    1506 procedure TStartDlg.ListClick(Sender: TObject);
    1507 var
    1508 i: integer;
    1509 begin
    1510 if (Tab=1) and ((List.ItemIndex=0)<>(Page=pgEditRandom)) then
    1511   begin
    1512   if List.ItemIndex=0 then Page:=pgEditRandom
    1513   else Page:=pgEditMap;
    1514   for i:=0 to ControlCount-1 do
    1515     Controls[i].Visible:= Controls[i].Tag and (256 shl Page)<>0;
    1516   SmartInvalidate(328,Up1Btn.Top-12,ClientWidth,Up2Btn.Top+35);
    1517   end;
    1518 if Page=pgLoad then TurnValid:=false;
    1519 PaintInfo;
    1520 if Page=pgLoad then
    1521   ReplayBtn.Visible:= MiniMode<>mmMultiPlayer;
    1522 end;
    1523 
    1524 procedure TStartDlg.RenameBtnClick(Sender: TObject);
    1525 var
    1526 i: integer;
    1527 NewName: string;
    1528 f: file;
    1529 ok: boolean;
    1530 begin
    1531 if List.ItemIndex>=0 then
    1532   begin
    1533   if Page=pgLoad then InputDlg.Caption:=Phrases.Lookup('TITLE_BOOKNAME')
    1534   else InputDlg.Caption:=Phrases.Lookup('TITLE_MAPNAME');
    1535   InputDlg.EInput.Text:=List.Items[List.ItemIndex];
    1536   InputDlg.CenterToRect(BoundsRect);
    1537   InputDlg.ShowModal;
    1538   NewName:=InputDlg.EInput.Text;
    1539   while (NewName<>'') and (NewName[1]='~') do delete(NewName,1,1);
    1540   if (InputDlg.ModalResult=mrOK) and (NewName<>'')
    1541     and (NewName<>List.Items[List.ItemIndex]) then
    1542     begin
    1543     for i:=1 to Length(NewName) do
    1544       if NewName[i] in ['\','/',':','*','?','"','<','>','|'] then
    1545         begin
    1546         SimpleMessage(Format(Phrases.Lookup('NOFILENAME'),[NewName[i]]));
    1547         exit
    1548         end;
    1549     if Page=pgLoad then
    1550       AssignFile(f,DataDir+'Saved\'+List.Items[List.ItemIndex]+'.cevo')
    1551     else AssignFile(f,DataDir+'Maps\'+List.Items[List.ItemIndex]+'.cevo map');
    1552     ok:=true;
    1553     try
    1554       if Page=pgLoad then
    1555         Rename(f,DataDir+'Saved\'+NewName+'.cevo')
    1556       else Rename(f,DataDir+'Maps\'+NewName+'.cevo map');
    1557     except
    1558 //      Play('INVALID');
    1559       ok:=false
    1560       end;
    1561     if Page<>pgLoad then
    1562       try // rename map picture
    1563         AssignFile(f,DataDir+'Maps\'+List.Items[List.ItemIndex]+'.bmp');
    1564         Rename(f,DataDir+'Maps\'+NewName+'.bmp');
    1565       except
    1566         end;
    1567     if ok then
    1568       begin
    1569       if Page=pgLoad then
    1570         FormerGames[List.ItemIndex]:=NewName
    1571       else Maps[List.ItemIndex]:=NewName;
    1572       List.Items[List.ItemIndex]:=NewName;
    1573       if Page=pgEditMap then PaintInfo;
    1574       List.Invalidate;
     1788      if Page <> pgLoad then
     1789        try // rename map picture
     1790          AssignFile(f, DataDir + 'Maps\' + List.Items[List.ItemIndex]
     1791            + '.bmp');
     1792          Rename(f, DataDir + 'Maps\' + NewName + '.bmp');
     1793        except
     1794        end;
     1795      if ok then
     1796      begin
     1797        if Page = pgLoad then
     1798          FormerGames[List.ItemIndex] := NewName
     1799        else
     1800          Maps[List.ItemIndex] := NewName;
     1801        List.Items[List.ItemIndex] := NewName;
     1802        if Page = pgEditMap then
     1803          PaintInfo;
     1804        List.Invalidate;
    15751805      end
    15761806    end
     
    15801810procedure TStartDlg.DeleteBtnClick(Sender: TObject);
    15811811var
    1582 iDel: integer;
    1583 f: file;
    1584 begin
    1585 if List.ItemIndex>=0 then
    1586   begin
    1587   if Page=pgLoad then MessgDlg.MessgText:=Phrases.Lookup('DELETEQUERY')
    1588   else MessgDlg.MessgText:=Phrases.Lookup('MAPDELETEQUERY');
    1589   MessgDlg.Kind:=mkOKCancel;
    1590   MessgDlg.ShowModal;
    1591   if MessgDlg.ModalResult=mrOK then
    1592     begin
    1593     if Page=pgLoad then
    1594       AssignFile(f,DataDir+'Saved\'+List.Items[List.ItemIndex]+'.cevo')
    1595     else AssignFile(f,DataDir+'Maps\'+List.Items[List.ItemIndex]+'.cevo map');
    1596     Erase(f);
    1597     iDel:=List.ItemIndex;
    1598     if Page=pgLoad then FormerGames.Delete(iDel)
    1599     else Maps.Delete(iDel);
    1600     List.Items.Delete(iDel);
    1601     if List.Items.Count=0 then ChangePage(pgNoLoad)
     1812  iDel: integer;
     1813  f: file;
     1814begin
     1815  if List.ItemIndex >= 0 then
     1816  begin
     1817    if Page = pgLoad then
     1818      MessgDlg.MessgText := Phrases.Lookup('DELETEQUERY')
    16021819    else
    1603       begin
    1604       if iDel=0 then List.ItemIndex:=0
    1605       else List.ItemIndex:=iDel-1;
    1606       if (Page=pgEditMap) and (List.ItemIndex=0) then ChangePage(pgEditRandom)
     1820      MessgDlg.MessgText := Phrases.Lookup('MAPDELETEQUERY');
     1821    MessgDlg.Kind := mkOKCancel;
     1822    MessgDlg.ShowModal;
     1823    if MessgDlg.ModalResult = mrOK then
     1824    begin
     1825      if Page = pgLoad then
     1826        AssignFile(f, DataDir + 'Saved\' + List.Items[List.ItemIndex] + '.cevo')
    16071827      else
    1608         begin List.Invalidate;
    1609         if Page=pgLoad then TurnValid:=false;
    1610         PaintInfo;
    1611         if Page=pgLoad then
    1612           ReplayBtn.Visible:= MiniMode<>mmMultiPlayer;
     1828        AssignFile(f, DataDir + 'Maps\' + List.Items[List.ItemIndex] +
     1829          '.cevo map');
     1830      Erase(f);
     1831      iDel := List.ItemIndex;
     1832      if Page = pgLoad then
     1833        FormerGames.Delete(iDel)
     1834      else
     1835        Maps.Delete(iDel);
     1836      List.Items.Delete(iDel);
     1837      if List.Items.Count = 0 then
     1838        ChangePage(pgNoLoad)
     1839      else
     1840      begin
     1841        if iDel = 0 then
     1842          List.ItemIndex := 0
     1843        else
     1844          List.ItemIndex := iDel - 1;
     1845        if (Page = pgEditMap) and (List.ItemIndex = 0) then
     1846          ChangePage(pgEditRandom)
     1847        else
     1848        begin
     1849          List.Invalidate;
     1850          if Page = pgLoad then
     1851            TurnValid := false;
     1852          PaintInfo;
     1853          if Page = pgLoad then
     1854            ReplayBtn.Visible := MiniMode <> mmMultiPlayer;
    16131855        end;
    16141856      end
     
    16191861procedure TStartDlg.DiffBtnClick(Sender: TObject);
    16201862var
    1621 i: integer;
    1622 begin
    1623 for i:=0 to nPlOffered-1 do
    1624   if (Sender=DiffUpBtn[i]) and (Difficulty[i]<3)
    1625     or (Sender=DiffDownBtn[i]) and (Difficulty[i]>1) then
    1626     begin
    1627     if Sender=DiffUpBtn[i] then inc(Difficulty[i])
    1628     else dec(Difficulty[i]);
    1629     SmartInvalidate(xBrain[i]-18,yBrain[i]+19,xBrain[i]-18+12,yBrain[i]+(19+14));
     1863  i: integer;
     1864begin
     1865  for i := 0 to nPlOffered - 1 do
     1866    if (Sender = DiffUpBtn[i]) and (Difficulty[i] < 3) or
     1867      (Sender = DiffDownBtn[i]) and (Difficulty[i] > 1) then
     1868    begin
     1869      if Sender = DiffUpBtn[i] then
     1870        inc(Difficulty[i])
     1871      else
     1872        dec(Difficulty[i]);
     1873      SmartInvalidate(xBrain[i] - 18, yBrain[i] + 19, xBrain[i] - 18 + 12,
     1874        yBrain[i] + (19 + 14));
    16301875    end
    16311876end;
     
    16331878procedure TStartDlg.MultiBtnClick(Sender: TObject);
    16341879var
    1635 i: integer;
    1636 begin
    1637 for i:=6 to 8 do if Sender=MultiBtn[i] then
    1638   begin
    1639   MultiControl:=MultiControl xor (1 shl i);
    1640   TButtonC(Sender).ButtonIndex:=2+(MultiControl shr i) and 1;
    1641   end
     1880  i: integer;
     1881begin
     1882  for i := 6 to 8 do
     1883    if Sender = MultiBtn[i] then
     1884    begin
     1885      MultiControl := MultiControl xor (1 shl i);
     1886      TButtonC(Sender).ButtonIndex := 2 + (MultiControl shr i) and 1;
     1887    end
    16421888end;
    16431889
    16441890procedure TStartDlg.FormHide(Sender: TObject);
    16451891begin
    1646 Diff0:=Difficulty[0];
    1647 ListIndex[Tab]:=List.ItemIndex;
    1648 ShowTab:=Tab;
    1649 Background.Enabled:=true;
     1892  Diff0 := Difficulty[0];
     1893  ListIndex[Tab] := List.ItemIndex;
     1894  ShowTab := Tab;
     1895  Background.Enabled := true;
    16501896end;
    16511897
    16521898procedure TStartDlg.QuitBtnClick(Sender: TObject);
    16531899begin
    1654 Close
     1900  Close
    16551901end;
    16561902
     
    16581904  Shift: TShiftState);
    16591905begin
    1660 if (Shift=[]) and (Key=VK_F1) then DirectHelp(cStartHelp);
     1906  if (Shift = []) and (Key = VK_F1) then
     1907    DirectHelp(cStartHelp);
    16611908end;
    16621909
    16631910procedure TStartDlg.CustomizeBtnClick(Sender: TObject);
    16641911begin
    1665 AutoDiff:=-AutoDiff;
    1666 CustomizeBtn.ButtonIndex:=CustomizeBtn.ButtonIndex xor 1;
    1667 ChangePage(Page)
     1912  AutoDiff := -AutoDiff;
     1913  CustomizeBtn.ButtonIndex := CustomizeBtn.ButtonIndex xor 1;
     1914  ChangePage(Page)
    16681915end;
    16691916
    16701917procedure TStartDlg.AutoDiffUpBtnClick(Sender: TObject);
    16711918begin
    1672 if AutoDiff<5 then
    1673   begin
    1674   inc(AutoDiff);
    1675   SmartInvalidate(120,y0Mini+61,272,y0Mini+61+21);
    1676   SmartInvalidate(xDefault-2,yDefault-2,xDefault+64+2,yDefault+64+2);
     1919  if AutoDiff < 5 then
     1920  begin
     1921    inc(AutoDiff);
     1922    SmartInvalidate(120, y0Mini + 61, 272, y0Mini + 61 + 21);
     1923    SmartInvalidate(xDefault - 2, yDefault - 2, xDefault + 64 + 2,
     1924      yDefault + 64 + 2);
    16771925  end
    16781926end;
     
    16801928procedure TStartDlg.AutoDiffDownBtnClick(Sender: TObject);
    16811929begin
    1682 if AutoDiff>1 then
    1683   begin
    1684   dec(AutoDiff);
    1685   SmartInvalidate(120,y0Mini+61,272,y0Mini+61+21);
    1686   SmartInvalidate(xDefault-2,yDefault-2,xDefault+64+2,yDefault+64+2);
     1930  if AutoDiff > 1 then
     1931  begin
     1932    dec(AutoDiff);
     1933    SmartInvalidate(120, y0Mini + 61, 272, y0Mini + 61 + 21);
     1934    SmartInvalidate(xDefault - 2, yDefault - 2, xDefault + 64 + 2,
     1935      yDefault + 64 + 2);
    16871936  end
    16881937end;
    16891938
    16901939procedure TStartDlg.FormMouseUp(Sender: TObject; Button: TMouseButton;
    1691   Shift: TShiftState; X, Y: Integer);
    1692 begin
    1693 Tracking:=false
    1694 end;
    1695 
    1696 procedure TStartDlg.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
    1697   Y: Integer);
    1698 var
    1699 OldLoadTurn,NewSelectedAction: integer;
    1700 begin
    1701 if Tracking then
    1702   begin
    1703   x:=x-xTurnSlider;
    1704   if x<0 then x:=0
    1705   else if x>wTurnSlider then x:=wTurnSlider;
    1706   OldLoadTurn:=LoadTurn;
    1707   LoadTurn:=LastTurn*x div wTurnSlider;
    1708   if LoadTurn<OldLoadTurn then
    1709     begin
    1710     SmartInvalidate(xTurnSlider+LoadTurn*wTurnSlider div LastTurn,yTurnSlider,
    1711       xTurnSlider+OldLoadTurn*wTurnSlider div LastTurn+1,yTurnSlider+7);
    1712     SmartInvalidate(344,y0Mini+61,514,y0Mini+82);
     1940  Shift: TShiftState; x, y: integer);
     1941begin
     1942  Tracking := false
     1943end;
     1944
     1945procedure TStartDlg.FormMouseMove(Sender: TObject; Shift: TShiftState;
     1946  x, y: integer);
     1947var
     1948  OldLoadTurn, NewSelectedAction: integer;
     1949begin
     1950  if Tracking then
     1951  begin
     1952    x := x - xTurnSlider;
     1953    if x < 0 then
     1954      x := 0
     1955    else if x > wTurnSlider then
     1956      x := wTurnSlider;
     1957    OldLoadTurn := LoadTurn;
     1958    LoadTurn := LastTurn * x div wTurnSlider;
     1959    if LoadTurn < OldLoadTurn then
     1960    begin
     1961      SmartInvalidate(xTurnSlider + LoadTurn * wTurnSlider div LastTurn,
     1962        yTurnSlider, xTurnSlider + OldLoadTurn * wTurnSlider div LastTurn + 1,
     1963        yTurnSlider + 7);
     1964      SmartInvalidate(344, y0Mini + 61, 514, y0Mini + 82);
    17131965    end
    1714   else if LoadTurn>OldLoadTurn then
    1715     begin
    1716     SmartInvalidate(xTurnSlider+OldLoadTurn*wTurnSlider div LastTurn,yTurnSlider,
    1717       xTurnSlider+LoadTurn*wTurnSlider div LastTurn+1,yTurnSlider+7);
    1718     SmartInvalidate(344,y0Mini+61,514,y0Mini+82);
     1966    else if LoadTurn > OldLoadTurn then
     1967    begin
     1968      SmartInvalidate(xTurnSlider + OldLoadTurn * wTurnSlider div LastTurn,
     1969        yTurnSlider, xTurnSlider + LoadTurn * wTurnSlider div LastTurn + 1,
     1970        yTurnSlider + 7);
     1971      SmartInvalidate(344, y0Mini + 61, 514, y0Mini + 82);
    17191972    end;
    17201973  end
    1721 else if Page=pgMain then
    1722   begin
    1723   if (x>=ActionSideBorder) and (x<ClientWidth-ActionSideBorder)
    1724     and (y>=yAction-8) and (y<ClientHeight-ActionBottomBorder) then
    1725     begin
    1726     NewSelectedAction:=(y-(yAction-8)) div ActionPitch;
    1727     if not (NewSelectedAction in ActionsOffered) then
    1728       NewSelectedAction:=-1;
     1974  else if Page = pgMain then
     1975  begin
     1976    if (x >= ActionSideBorder) and (x < ClientWidth - ActionSideBorder) and
     1977      (y >= yAction - 8) and (y < ClientHeight - ActionBottomBorder) then
     1978    begin
     1979      NewSelectedAction := (y - (yAction - 8)) div ActionPitch;
     1980      if not(NewSelectedAction in ActionsOffered) then
     1981        NewSelectedAction := -1;
    17291982    end
    1730   else NewSelectedAction:=-1;
    1731   if NewSelectedAction<>SelectedAction then
    1732     begin
    1733     if SelectedAction>=0 then
    1734       SmartInvalidate(ActionSideBorder,yAction+SelectedAction*ActionPitch-8,
    1735         ClientWidth-ActionSideBorder,yAction+(SelectedAction+1)*ActionPitch-8);
    1736     SelectedAction:=NewSelectedAction;
    1737     if SelectedAction>=0 then
    1738       SmartInvalidate(ActionSideBorder,yAction+SelectedAction*ActionPitch-8,
    1739         ClientWidth-ActionSideBorder,yAction+(SelectedAction+1)*ActionPitch-8);
     1983    else
     1984      NewSelectedAction := -1;
     1985    if NewSelectedAction <> SelectedAction then
     1986    begin
     1987      if SelectedAction >= 0 then
     1988        SmartInvalidate(ActionSideBorder, yAction + SelectedAction * ActionPitch
     1989          - 8, ClientWidth - ActionSideBorder, yAction + (SelectedAction + 1) *
     1990          ActionPitch - 8);
     1991      SelectedAction := NewSelectedAction;
     1992      if SelectedAction >= 0 then
     1993        SmartInvalidate(ActionSideBorder, yAction + SelectedAction * ActionPitch
     1994          - 8, ClientWidth - ActionSideBorder, yAction + (SelectedAction + 1) *
     1995          ActionPitch - 8);
    17401996    end
    17411997  end
     
    17442000procedure TStartDlg.AutoEnemyUpBtnClick(Sender: TObject);
    17452001begin
    1746 if AutoEnemies<nPl-1 then
    1747   begin
    1748   inc(AutoEnemies);
    1749   SmartInvalidate(160,yMain+140,198,yMain+140+21);
     2002  if AutoEnemies < nPl - 1 then
     2003  begin
     2004    inc(AutoEnemies);
     2005    SmartInvalidate(160, yMain + 140, 198, yMain + 140 + 21);
    17502006  end
    17512007end;
     
    17532009procedure TStartDlg.AutoEnemyDownBtnClick(Sender: TObject);
    17542010begin
    1755 if AutoEnemies>0 then
    1756   begin
    1757   dec(AutoEnemies);
    1758   SmartInvalidate(160,yMain+140,198,yMain+140+21);
     2011  if AutoEnemies > 0 then
     2012  begin
     2013    dec(AutoEnemies);
     2014    SmartInvalidate(160, yMain + 140, 198, yMain + 140 + 21);
    17592015  end
    17602016end;
     
    17622018procedure TStartDlg.ReplayBtnClick(Sender: TObject);
    17632019begin
    1764 LoadGame(DataDir+'Saved\', List.Items[List.ItemIndex]+'.cevo', LastTurn, true);
    1765 SlotAvailable:=-1;
     2020  LoadGame(DataDir + 'Saved\', List.Items[List.ItemIndex] + '.cevo',
     2021    LastTurn, true);
     2022  SlotAvailable := -1;
    17662023end;
    17672024
    17682025end.
    1769 
  • trunk/StringTables.pas

    r5 r6  
    11{$INCLUDE switches}
    2 
    32unit StringTables;
    43
     
    65
    76const
    8 MaxCount=4000;
     7  MaxCount = 4000;
    98
    109type
    11 TCharList=array[0..9999999] of AnsiChar;
    12 
    13 TStringTable=class
    14   constructor Create;
    15   destructor Destroy; override;
    16   function LoadFromFile(const FileName: String): boolean;
    17   function GetHandle(const Item: AnsiString): integer;
    18   function LookupByHandle(Handle: integer; Index: integer =-1): string;
    19   function Lookup(const Item: string; Index: integer =-1): string;
    20   function Search(const Content: string; var Handle, Index: integer): boolean;
    21 protected
    22   Count: integer;
    23   Data: ^TCharList;
    24   Lines: array[0..MaxCount-1] of PAnsiChar;
    25   end;
    26 
     10  TCharList = array [0 .. 9999999] of AnsiChar;
     11
     12  TStringTable = class
     13    constructor Create;
     14    destructor Destroy; override;
     15    function LoadFromFile(const FileName: String): boolean;
     16    function GetHandle(const Item: AnsiString): integer;
     17    function LookupByHandle(Handle: integer; Index: integer = -1): string;
     18    function Lookup(const Item: string; Index: integer = -1): string;
     19    function Search(const Content: string; var Handle, Index: integer): boolean;
     20  protected
     21    Count: integer;
     22    Data: ^TCharList;
     23    Lines: array [0 .. MaxCount - 1] of PAnsiChar;
     24  end;
    2725
    2826implementation
    2927
    3028uses
    31 Classes,SysUtils;
    32 
     29  Classes, SysUtils;
    3330
    3431constructor TStringTable.Create;
    3532begin
    36 Data:=nil;
     33  Data := nil;
    3734end;
    3835
    3936destructor TStringTable.Destroy;
    4037begin
    41 if Data<>nil then FreeMem(Data);
    42 end;
    43 
    44 function TStringTable.LoadFromFile(const FileName:string): boolean;
    45 var
    46 nData, i: integer;
    47 f: TFileStream;
    48 begin
    49 if Data<>nil then FreeMem(Data);
    50 try
    51   f:=TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
    52 except
    53   result:=false;
    54   exit;
    55   end;
    56 result:=true;
    57 nData:=f.Size;
    58 GetMem(Data,nData+1);
    59 f.read(Data^,nData);
    60 f.Free;
    61 i:=0;
    62 Count:=0;
    63 while (i<nData) and (Count<MaxCount) do
    64   begin
    65   Lines[Count]:=@Data[i];
    66   while (i<nData) and (Data[i]<>#13) do inc(i);
    67   Data[i]:=#0;
    68   inc(i,2);
    69   inc(Count);
     38  if Data <> nil then
     39    FreeMem(Data);
     40end;
     41
     42function TStringTable.LoadFromFile(const FileName: string): boolean;
     43var
     44  nData, i: integer;
     45  f: TFileStream;
     46begin
     47  if Data <> nil then
     48    FreeMem(Data);
     49  try
     50    f := TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
     51  except
     52    result := false;
     53    exit;
     54  end;
     55  result := true;
     56  nData := f.Size;
     57  GetMem(Data, nData + 1);
     58  f.read(Data^, nData);
     59  f.Free;
     60  i := 0;
     61  Count := 0;
     62  while (i < nData) and (Count < MaxCount) do
     63  begin
     64    Lines[Count] := @Data[i];
     65    while (i < nData) and (Data[i] <> #13) do
     66      inc(i);
     67    Data[i] := #0;
     68    inc(i, 2);
     69    inc(Count);
    7070  end;
    7171end;
     
    7373function TStringTable.GetHandle(const Item: AnsiString): integer;
    7474var
    75 i,l: integer;
    76 begin
    77 l:=Length(Item);
    78 i:=Count-1;
    79 while (i>=0) and ((Lines[i][0]<>'#')
    80   or (StrLComp(Lines[i]+1,@Item[1],l)<>0)
    81   or (Lines[i][l+1]<>#0) and (Lines[i][l+1]<>' ')) do
    82   dec(i);
    83 result:=i
    84 end;
    85 
    86 function TStringTable.LookupByHandle(Handle: Integer; Index: integer): string;
    87 var
    88 s: string;
    89 begin
    90 if Index<0 then
    91   if Handle<0 then begin result:=''; exit end
    92   else
    93     begin
    94     if pos(' ',Lines[Handle])=0 then s:=''
    95     else s:=copy(Lines[Handle],pos(' ',Lines[Handle])+1,MaxInt);
    96     while (Handle+1<Count) and (Lines[Handle+1][0]<>'#') do
     75  i, l: integer;
     76begin
     77  l := Length(Item);
     78  i := Count - 1;
     79  while (i >= 0) and ((Lines[i][0] <> '#') or (StrLComp(Lines[i] + 1, @Item[1],
     80    l) <> 0) or (Lines[i][l + 1] <> #0) and (Lines[i][l + 1] <> ' ')) do
     81    dec(i);
     82  result := i
     83end;
     84
     85function TStringTable.LookupByHandle(Handle: integer; Index: integer): string;
     86var
     87  s: string;
     88begin
     89  if Index < 0 then
     90    if Handle < 0 then
     91    begin
     92      result := '';
     93      exit
     94    end
     95    else
     96    begin
     97      if pos(' ', Lines[Handle]) = 0 then
     98        s := ''
     99      else
     100        s := copy(Lines[Handle], pos(' ', Lines[Handle]) + 1, MaxInt);
     101      while (Handle + 1 < Count) and (Lines[Handle + 1][0] <> '#') do
    97102      begin
    98       inc(Handle);
    99       if (Lines[Handle][0]<>#0) and (Lines[Handle][0]<>'''') then
     103        inc(Handle);
     104        if (Lines[Handle][0] <> #0) and (Lines[Handle][0] <> '''') then
    100105        begin
    101         if (s<>'') and (s[Length(s)]<>'\') then s:=s+' ';
    102         s:=s+Lines[Handle];
     106          if (s <> '') and (s[Length(s)] <> '\') then
     107            s := s + ' ';
     108          s := s + Lines[Handle];
    103109        end
    104110      end;
    105     result:=s
     111      result := s
    106112    end
    107 else if Handle+Index+1>=Count then begin result:=''; exit end
    108 else result:=Lines[Handle+Index+1];
    109 while (result<>'') and ((result[1]=' ') or (result[1]=#9)) do
    110   Delete(result,1,1);
    111 while (result<>'')
    112   and ((result[Length(result)]=' ') or (result[Length(result)]=#9)) do
    113   Delete(result,Length(result),1);
    114 if result='' then result:='*'; 
     113  else if Handle + Index + 1 >= Count then
     114  begin
     115    result := '';
     116    exit
     117  end
     118  else
     119    result := Lines[Handle + Index + 1];
     120  while (result <> '') and ((result[1] = ' ') or (result[1] = #9)) do
     121    Delete(result, 1, 1);
     122  while (result <> '') and ((result[Length(result)] = ' ') or
     123    (result[Length(result)] = #9)) do
     124    Delete(result, Length(result), 1);
     125  if result = '' then
     126    result := '*';
    115127end;
    116128
    117129function TStringTable.Lookup(const Item: string; Index: integer): string;
    118130var
    119 Handle: integer;
    120 begin
    121 Handle:=Gethandle(Item);
    122 if Handle>=0 then result:=LookupByHandle(Handle, Index)
    123 else result:='';
    124 if result='' then
    125   if Index<0 then result:=Format('[%s]',[Item])
    126   else result:=Format('[%s %d]',[Item,Index])
    127 end;
    128 
    129 {might become necessary for 1.3
    130 
    131 function TStringTable.Lookup(const Fallback: TStringTable; const Item: string; Index: integer): string;
    132 var
    133 Handle: integer;
    134 begin
    135 Handle:=Gethandle(Item);
    136 if Handle>=0 then result:=LookupByHandle(Handle, Index)
    137 else result:='';
    138 if result='' then
     131  Handle: integer;
     132begin
     133  Handle := GetHandle(Item);
     134  if Handle >= 0 then
     135    result := LookupByHandle(Handle, Index)
     136  else
     137    result := '';
     138  if result = '' then
     139    if Index < 0 then
     140      result := Format('[%s]', [Item])
     141    else
     142      result := Format('[%s %d]', [Item, Index])
     143end;
     144
     145{ might become necessary for 1.3
     146
     147  function TStringTable.Lookup(const Fallback: TStringTable; const Item: string; Index: integer): string;
     148  var
     149  Handle: integer;
     150  begin
     151  Handle:=Gethandle(Item);
     152  if Handle>=0 then result:=LookupByHandle(Handle, Index)
     153  else result:='';
     154  if result='' then
    139155  result:=Fallback.Lookup(Item, Index);
    140 end;
    141 
    142 function TStringTable.TryLookup(const Item: string; Index: integer): string;
    143 var
    144 Handle: integer;
    145 begin
    146 Handle:=Gethandle(Item);
    147 if Handle>=0 then result:=LookupByHandle(Handle, Index)
    148 else result:='';
    149 end;}
    150 
    151 function TStringTable.Search(const Content: string; var Handle, Index: integer): boolean;
    152 var
    153 h,i: integer;
    154 UContent: string;
    155 begin
    156 UContent:=UpperCase(Content);
    157 h:=Handle;
    158 if h<0 then i:=0
    159 else i:=Index+1;
    160 repeat
    161   if h+i+1>=Count then
    162     begin result:=false; exit end;
    163   if Lines[h+i+1][0]='#' then
    164     begin h:=h+i+1; i:=-1 end;
    165   if (h>=0) and not (Lines[h+i+1][0] in ['#',':',';'])
    166     and (pos(UContent, UpperCase(Lines[h+i+1]))>0) then
    167     begin Index:=i; Handle:=h; result:=true; exit end;
    168   inc(i);
    169 until false;
     156  end;
     157
     158  function TStringTable.TryLookup(const Item: string; Index: integer): string;
     159  var
     160  Handle: integer;
     161  begin
     162  Handle:=Gethandle(Item);
     163  if Handle>=0 then result:=LookupByHandle(Handle, Index)
     164  else result:='';
     165  end; }
     166
     167function TStringTable.Search(const Content: string;
     168  var Handle, Index: integer): boolean;
     169var
     170  h, i: integer;
     171  UContent: string;
     172begin
     173  UContent := UpperCase(Content);
     174  h := Handle;
     175  if h < 0 then
     176    i := 0
     177  else
     178    i := Index + 1;
     179  repeat
     180    if h + i + 1 >= Count then
     181    begin
     182      result := false;
     183      exit
     184    end;
     185    if Lines[h + i + 1][0] = '#' then
     186    begin
     187      h := h + i + 1;
     188      i := -1
     189    end;
     190    if (h >= 0) and not(Lines[h + i + 1][0] in ['#', ':', ';']) and
     191      (pos(UContent, UpperCase(Lines[h + i + 1])) > 0) then
     192    begin
     193      Index := i;
     194      Handle := h;
     195      result := true;
     196      exit
     197    end;
     198    inc(i);
     199  until false;
    170200end;
    171201
    172202end.
    173 
  • trunk/UnitProcessing.pas

    r2 r6  
    55
    66uses
    7 Protocol, Database;
     7  Protocol, Database;
    88
    99type
    10 TMoveType = (mtInvalid, mtMove, mtCapture, mtSpyMission, mtAttack, mtBombard, mtExpel);
    11 
    12 TMoveInfo=record
    13   MoveType: TMoveType;
    14   Cost,
    15   ToMaster,
    16   EndHealth,
    17   Defender,
    18   Dcix,
    19   Duix,
    20   EndHealthDef: integer;
    21   MountainDelay: boolean;
     10  TMoveType = (mtInvalid, mtMove, mtCapture, mtSpyMission, mtAttack,
     11    mtBombard, mtExpel);
     12
     13  TMoveInfo = record
     14    MoveType: TMoveType;
     15    Cost, ToMaster, EndHealth, Defender, Dcix, Duix, EndHealthDef: integer;
     16    MountainDelay: boolean;
    2217  end;
    2318
    2419var
    25 uixSelectedTransport: integer;
    26 Worked: array[0..nPl-1] of integer; {settler work statistics}
    27 
    28 
    29 //Moving/Combat
     20  uixSelectedTransport: integer;
     21  Worked: array [0 .. nPl - 1] of integer; { settler work statistics }
     22
     23  // Moving/Combat
    3024function HostileDamage(p, mix, Loc, MP: integer): integer;
    31 function CalculateMove(p,uix,ToLoc,MoveLength: integer; TestOnly: boolean;
     25function CalculateMove(p, uix, ToLoc, MoveLength: integer; TestOnly: boolean;
    3226  var MoveInfo: TMoveInfo): integer;
    3327function GetBattleForecast(Loc: integer; var BattleForecast: TBattleForecast;
    34   var Duix,Dcix,AStr,DStr,ABaseDamage,DBaseDamage: integer): integer;
    35 function LoadUnit(p,uix: integer; TestOnly: boolean): integer;
    36 function UnloadUnit(p,uix: integer; TestOnly: boolean): integer;
    37 procedure Recover(p,uix: integer);
    38 function GetMoveAdvice(p,uix: integer; var a: TMoveAdviceData): integer;
    39 function CanPlaneReturn(p,uix: integer; PlaneReturnData: TPlaneReturnData): boolean;
     28  var Duix, Dcix, AStr, DStr, ABaseDamage, DBaseDamage: integer): integer;
     29function LoadUnit(p, uix: integer; TestOnly: boolean): integer;
     30function UnloadUnit(p, uix: integer; TestOnly: boolean): integer;
     31procedure Recover(p, uix: integer);
     32function GetMoveAdvice(p, uix: integer; var a: TMoveAdviceData): integer;
     33function CanPlaneReturn(p, uix: integer;
     34  PlaneReturnData: TPlaneReturnData): boolean;
    4035
    4136// Terrain Improvement
    42 function StartJob(p,uix,NewJob: integer; TestOnly: boolean): integer;
    43 function Work(p,uix: integer): boolean;
    44 function GetJobProgress(p,Loc: integer; var JobProgressData: TJobProgressData): integer;
     37function StartJob(p, uix, NewJob: integer; TestOnly: boolean): integer;
     38function Work(p, uix: integer): boolean;
     39function GetJobProgress(p, Loc: integer;
     40  var JobProgressData: TJobProgressData): integer;
    4541
    4642// Start/End Game
     
    4844procedure ReleaseGame;
    4945
    50 
    5146implementation
    5247
    5348uses
    54 IPQ;
     49  IPQ;
    5550
    5651const
    57 eMountains=$6000FFFF; // additional return code for server internal use
    58 
    59 // tile control flags
    60 coKnown=$02; coTrue=$04;
    61 
    62 ContraJobs: array[0..nJob-1] of Set of 0..nJob-1=
    63 ([],                             //jNone
    64 [jCity],                         //jRoad
    65 [jCity],                         //jRR
    66 [jCity,jTrans],                  //jClear
    67 [jCity,jFarm,jAfforest,jMine,jBase,jFort],         //jIrr
    68 [jCity,jIrr,jAfforest,jMine,jBase,jFort],         //jFarm
    69 [jCity,jIrr,jFarm,jTrans],       //jAfforest
    70 [jCity,jTrans,jIrr,jFarm,jBase,jFort],       //jMine
    71 [jCity,jTrans],                  //jCanal
    72 [jCity,jClear,jAfforest,jMine,jCanal], //jTrans
    73 [jCity,jIrr,jFarm,jMine,jBase],                         //jFort
    74 [jCity],                         //jPoll
    75 [jCity,jIrr,jFarm,jMine,jFort],                         //jBase
    76 [jCity],                         //jPillage
    77 [jRoad..jPillage]);              //jCity
     52  eMountains = $6000FFFF; // additional return code for server internal use
     53
     54  // tile control flags
     55  coKnown = $02;
     56  coTrue = $04;
     57
     58  ContraJobs: array [0 .. nJob - 1] of Set of 0 .. nJob - 1 = ([], // jNone
     59    [jCity], // jRoad
     60    [jCity], // jRR
     61    [jCity, jTrans], // jClear
     62    [jCity, jFarm, jAfforest, jMine, jBase, jFort], // jIrr
     63    [jCity, jIrr, jAfforest, jMine, jBase, jFort], // jFarm
     64    [jCity, jIrr, jFarm, jTrans], // jAfforest
     65    [jCity, jTrans, jIrr, jFarm, jBase, jFort], // jMine
     66    [jCity, jTrans], // jCanal
     67    [jCity, jClear, jAfforest, jMine, jCanal], // jTrans
     68    [jCity, jIrr, jFarm, jMine, jBase], // jFort
     69    [jCity], // jPoll
     70    [jCity, jIrr, jFarm, jMine, jFort], // jBase
     71    [jCity], // jPillage
     72    [jRoad .. jPillage]); // jCity
    7873
    7974type
    80 TToWorkList = array[0..INFIN,0..nJob-1] of word;
    81 
    82 var
    83 ToWork: ^TToWorkList; {work left for each tile and job}
    84 
    85 
    86 {
    87                             Moving/Combat
    88  ____________________________________________________________________
    89 }
     75  TToWorkList = array [0 .. INFIN, 0 .. nJob - 1] of word;
     76
     77var
     78  ToWork: ^TToWorkList; { work left for each tile and job }
     79
     80  {
     81    Moving/Combat
     82    ____________________________________________________________________
     83  }
    9084function HostileDamage(p, mix, Loc, MP: integer): integer;
    9185var
    92 Tile: integer;
    93 begin
    94 Tile:=RealMap[Loc];
    95 if (RW[p].Model[mix].Domain>=dSea)
    96   or (RW[p].Model[mix].Kind=mkSettler) and (RW[p].Model[mix].Speed>=200)
    97   or (Tile and (fCity or fRiver or fCanal)<>0)
    98   or (Tile and fTerImp=tiBase)
    99   or (GWonder[woGardens].EffectiveOwner=p) then
    100   result:=0
    101 else if (Tile and fTerrain=fDesert)
    102   and (Tile and fSpecial<>fSpecial1{Oasis}) then
    103   begin
    104   assert((Tile and fTerImp<>tiIrrigation) and (Tile and fTerImp<>tiFarm));
    105   result:=(DesertThurst*MP-1) div RW[p].Model[mix].Speed +1
     86  Tile: integer;
     87begin
     88  Tile := RealMap[Loc];
     89  if (RW[p].Model[mix].Domain >= dSea) or (RW[p].Model[mix].Kind = mkSettler)
     90    and (RW[p].Model[mix].Speed >= 200) or
     91    (Tile and (fCity or fRiver or fCanal) <> 0) or (Tile and fTerImp = tiBase)
     92    or (GWonder[woGardens].EffectiveOwner = p) then
     93    result := 0
     94  else if (Tile and fTerrain = fDesert) and
     95    (Tile and fSpecial <> fSpecial1 { Oasis } ) then
     96  begin
     97    assert((Tile and fTerImp <> tiIrrigation) and (Tile and fTerImp <> tiFarm));
     98    result := (DesertThurst * MP - 1) div RW[p].Model[mix].Speed + 1
    10699  end
    107 else if Tile and fTerrain=fArctic then
    108   begin
    109   assert((Tile and fTerImp<>tiIrrigation) and (Tile and fTerImp<>tiFarm));
    110   result:=(ArcticThurst*MP-1) div RW[p].Model[mix].Speed +1
     100  else if Tile and fTerrain = fArctic then
     101  begin
     102    assert((Tile and fTerImp <> tiIrrigation) and (Tile and fTerImp <> tiFarm));
     103    result := (ArcticThurst * MP - 1) div RW[p].Model[mix].Speed + 1
    111104  end
    112 else result:=0
     105  else
     106    result := 0
    113107end;
    114108
    115 function Controlled(p,Loc: integer; IsDest: boolean): integer;
    116 {whether tile at Loc is in control zone of enemy unit
    117 returns combination of tile control flags}
    118 var
    119 Loc1,V8: integer;
    120 Adjacent: TVicinity8Loc;
    121 begin
    122 result:=0;
    123 if IsDest and (Occupant[Loc]=p) and (ZoCMap[Loc]>0) then exit;
     109function Controlled(p, Loc: integer; IsDest: boolean): integer;
     110{ whether tile at Loc is in control zone of enemy unit
     111  returns combination of tile control flags }
     112var
     113  Loc1, V8: integer;
     114  Adjacent: TVicinity8Loc;
     115begin
     116  result := 0;
     117  if IsDest and (Occupant[Loc] = p) and (ZoCMap[Loc] > 0) then
     118    exit;
    124119  // destination tile, not controlled if already occupied
    125120
    126 if (RealMap[Loc] and fCity=0)
    127   or (integer(RealMap[Loc] shr 27)<>p) and (ServerVersion[p]>=$000EF0) then
     121  if (RealMap[Loc] and fCity = 0) or (integer(RealMap[Loc] shr 27) <> p) and
     122    (ServerVersion[p] >= $000EF0) then
    128123  begin // not own city
    129   V8_to_Loc(Loc,Adjacent);
    130   for V8:=0 to 7 do
    131     begin
    132     Loc1:=Adjacent[V8];
    133     if (Loc1>=0) and (Loc1<MapSize)
    134       and (ZoCMap[Loc1]>0)
    135       and (Occupant[Loc1]>=0) and (Occupant[Loc1]<>p)
    136       and (RW[p].Treaty[Occupant[Loc1]]<trAlliance) then
    137       if ObserveLevel[Loc1] and (3 shl (p*2))>0 then
     124    V8_to_Loc(Loc, Adjacent);
     125    for V8 := 0 to 7 do
     126    begin
     127      Loc1 := Adjacent[V8];
     128      if (Loc1 >= 0) and (Loc1 < MapSize) and (ZoCMap[Loc1] > 0) and
     129        (Occupant[Loc1] >= 0) and (Occupant[Loc1] <> p) and
     130        (RW[p].Treaty[Occupant[Loc1]] < trAlliance) then
     131        if ObserveLevel[Loc1] and (3 shl (p * 2)) > 0 then
    138132        begin // p observes tile
    139         result:=coKnown or coTrue;
    140         exit
    141         end
    142       else result:=coTrue; // p does not observe tile
     133          result := coKnown or coTrue;
     134          exit
     135        end
     136        else
     137          result := coTrue; // p does not observe tile
    143138    end;
    144139  end
    145140end;
    146141
    147 function GetMoveCost(p,mix,FromLoc,ToLoc,MoveLength: integer; var MoveCost: integer): integer;
     142function GetMoveCost(p, mix, FromLoc, ToLoc, MoveLength: integer;
     143  var MoveCost: integer): integer;
    148144// MoveLength - 2 for short move, 3 for long move
    149145var
    150 FromTile,ToTile: integer;
    151 begin
    152 result:=eOK;
    153 FromTile:=RealMap[FromLoc];
    154 ToTile:=RealMap[ToLoc];
    155 with RW[p].Model[mix] do
    156   begin
    157   case Domain of
    158     dGround:
    159       if (ToTile and fTerrain>=fGrass) then {domain ok}
    160 //          if (Flags and mdCivil<>0) and (ToTile and fDeadLands<>0) then result:=eEerie
    161 //          else
    162           begin {valid move}
    163           if (FromTile and (fRR or fCity)<>0)
    164             and (ToTile and (fRR or fCity)<>0) then
    165             if GWonder[woShinkansen].EffectiveOwner=p then MoveCost:=0
    166             else MoveCost:=Speed*(4*1311) shr 17 //move along railroad
    167           else if (FromTile and (fRoad or fRR or fCity)<>0)
    168             and (ToTile and (fRoad or fRR or fCity)<>0)
    169             or (FromTile and ToTile and (fRiver or fCanal)<>0)
    170             or (Cap[mcAlpine]>0) then
    171             //move along road, river or canal
    172             if Cap[mcOver]>0 then MoveCost:=40
    173             else MoveCost:=20
    174           else if Cap[mcOver]>0 then result:=eNoRoad
    175           else case Terrain[ToTile and fTerrain].MoveCost of
    176             1: MoveCost:=50; // plain terrain
    177             2:
    178               begin
    179               assert(Speed-150<=600);
    180               MoveCost:=50+(Speed-150)*13 shr 7; // heavy terrain
    181               end;
    182             3:
    183               begin
    184               MoveCost:=Speed;
    185               result:=eMountains;
    186               exit
    187               end;
     146  FromTile, ToTile: integer;
     147begin
     148  result := eOK;
     149  FromTile := RealMap[FromLoc];
     150  ToTile := RealMap[ToLoc];
     151  with RW[p].Model[mix] do
     152  begin
     153    case Domain of
     154      dGround:
     155        if (ToTile and fTerrain >= fGrass) then { domain ok }
     156        // if (Flags and mdCivil<>0) and (ToTile and fDeadLands<>0) then result:=eEerie
     157        // else
     158        begin { valid move }
     159          if (FromTile and (fRR or fCity) <> 0) and
     160            (ToTile and (fRR or fCity) <> 0) then
     161            if GWonder[woShinkansen].EffectiveOwner = p then
     162              MoveCost := 0
     163            else
     164              MoveCost := Speed * (4 * 1311) shr 17 // move along railroad
     165          else if (FromTile and (fRoad or fRR or fCity) <> 0) and
     166            (ToTile and (fRoad or fRR or fCity) <> 0) or
     167            (FromTile and ToTile and (fRiver or fCanal) <> 0) or
     168            (Cap[mcAlpine] > 0) then
     169            // move along road, river or canal
     170            if Cap[mcOver] > 0 then
     171              MoveCost := 40
     172            else
     173              MoveCost := 20
     174          else if Cap[mcOver] > 0 then
     175            result := eNoRoad
     176          else
     177            case Terrain[ToTile and fTerrain].MoveCost of
     178              1:
     179                MoveCost := 50; // plain terrain
     180              2:
     181                begin
     182                  assert(Speed - 150 <= 600);
     183                  MoveCost := 50 + (Speed - 150) * 13 shr 7; // heavy terrain
     184                end;
     185              3:
     186                begin
     187                  MoveCost := Speed;
     188                  result := eMountains;
     189                  exit
     190                end;
    188191            end;
    189           MoveCost:=MoveCost*MoveLength;
    190           end
    191       else result:=eDomainMismatch;
    192 
    193     dSea:
    194       if (ToTile and (fCity or fCanal)<>0)
    195         or (ToTile and fTerrain<fGrass) then {domain ok}
    196         if (ToTile and fTerrain<>fOcean) or (Cap[mcNav]>0) then
    197           MoveCost:=50*MoveLength {valid move}
    198         else result:=eNoNav {navigation required for open sea}
    199       else result:=eDomainMismatch;
    200 
    201     dAir:
    202       MoveCost:=50*MoveLength; {always valid move}
     192          MoveCost := MoveCost * MoveLength;
     193        end
     194        else
     195          result := eDomainMismatch;
     196
     197      dSea:
     198        if (ToTile and (fCity or fCanal) <> 0) or (ToTile and fTerrain < fGrass)
     199        then { domain ok }
     200          if (ToTile and fTerrain <> fOcean) or (Cap[mcNav] > 0) then
     201            MoveCost := 50 * MoveLength { valid move }
     202          else
     203            result := eNoNav { navigation required for open sea }
     204        else
     205          result := eDomainMismatch;
     206
     207      dAir:
     208        MoveCost := 50 * MoveLength; { always valid move }
    203209    end
    204210  end
    205211end;
    206212
    207 function CalculateMove(p,uix,ToLoc,MoveLength: integer; TestOnly: boolean;
     213function CalculateMove(p, uix, ToLoc, MoveLength: integer; TestOnly: boolean;
    208214  var MoveInfo: TMoveInfo): integer;
    209215var
    210 uix1,p1,FromLoc,DestControlled,AStr,DStr,ABaseDamage,DBaseDamage: integer;
    211 PModel: ^TModel;
    212 BattleForecast: TBattleForecast;
    213 begin
    214 with RW[p],Un[uix] do
    215   begin
    216   PModel:=@Model[mix];
    217   FromLoc:=Loc;
    218 
    219   BattleForecast.pAtt:=p;
    220   BattleForecast.mixAtt:=mix;
    221   BattleForecast.HealthAtt:=Health;
    222   BattleForecast.ExpAtt:=Exp;
    223   BattleForecast.FlagsAtt:=Flags;
    224   BattleForecast.Movement:=Movement;
    225   result:=GetBattleForecast(ToLoc,BattleForecast,MoveInfo.Duix,MoveInfo.Dcix,AStr,DStr,ABaseDamage,DBaseDamage);
    226 
    227   if result=eHiddenUnit then
    228     if TestOnly then result:=eOK // behave just like unit was moving
    229     else if Mode>moLoading_Fast then
    230       Map[ToLoc]:=Map[ToLoc] or fHiddenUnit;
    231   if result=eStealthUnit then
    232     if TestOnly then result:=eOK // behave just like unit was moving
    233     else if Mode>moLoading_Fast then
    234       Map[ToLoc]:=Map[ToLoc] or fStealthUnit;
    235   if result<rExecuted then exit;
    236 
    237   case result of
    238     eOk: MoveInfo.MoveType:=mtMove;
    239     eExpelled: MoveInfo.MoveType:=mtExpel;
    240     else MoveInfo.MoveType:=mtAttack;
    241     end;
    242 
    243   if MoveInfo.MoveType=mtMove then
    244     begin
    245     if Mode=moPlaying then
    246       begin
    247       p1:=RealMap[ToLoc] shr 27;
    248       if (p1<nPl) and (p1<>p)
    249         and ((RealMap[Loc] shr 27<>Cardinal(p1))
    250         and (PModel.Kind<>mkDiplomat)
    251         and (Treaty[p1]>=trPeace) and (Treaty[p1]<trAlliance)
    252         or (RealMap[ToLoc] and fCity<>0) and (Treaty[p1]>=trPeace)) then
    253         begin result:=eTreaty; exit end; // keep peace treaty!
     216  uix1, p1, FromLoc, DestControlled, AStr, DStr, ABaseDamage,
     217    DBaseDamage: integer;
     218  PModel: ^TModel;
     219  BattleForecast: TBattleForecast;
     220begin
     221  with RW[p], Un[uix] do
     222  begin
     223    PModel := @Model[mix];
     224    FromLoc := Loc;
     225
     226    BattleForecast.pAtt := p;
     227    BattleForecast.mixAtt := mix;
     228    BattleForecast.HealthAtt := Health;
     229    BattleForecast.ExpAtt := Exp;
     230    BattleForecast.FlagsAtt := Flags;
     231    BattleForecast.Movement := Movement;
     232    result := GetBattleForecast(ToLoc, BattleForecast, MoveInfo.Duix,
     233      MoveInfo.Dcix, AStr, DStr, ABaseDamage, DBaseDamage);
     234
     235    if result = eHiddenUnit then
     236      if TestOnly then
     237        result := eOK // behave just like unit was moving
     238      else if Mode > moLoading_Fast then
     239        Map[ToLoc] := Map[ToLoc] or fHiddenUnit;
     240    if result = eStealthUnit then
     241      if TestOnly then
     242        result := eOK // behave just like unit was moving
     243      else if Mode > moLoading_Fast then
     244        Map[ToLoc] := Map[ToLoc] or fStealthUnit;
     245    if result < rExecuted then
     246      exit;
     247
     248    case result of
     249      eOK:
     250        MoveInfo.MoveType := mtMove;
     251      eExpelled:
     252        MoveInfo.MoveType := mtExpel;
     253    else
     254      MoveInfo.MoveType := mtAttack;
     255    end;
     256
     257    if MoveInfo.MoveType = mtMove then
     258    begin
     259      if Mode = moPlaying then
     260      begin
     261        p1 := RealMap[ToLoc] shr 27;
     262        if (p1 < nPl) and (p1 <> p) and
     263          ((RealMap[Loc] shr 27 <> Cardinal(p1)) and (PModel.Kind <> mkDiplomat)
     264          and (Treaty[p1] >= trPeace) and (Treaty[p1] < trAlliance) or
     265          (RealMap[ToLoc] and fCity <> 0) and (Treaty[p1] >= trPeace)) then
     266        begin
     267          result := eTreaty;
     268          exit
     269        end; // keep peace treaty!
    254270      end;
    255     if (RealMap[ToLoc] and fCity<>0)
    256       and (RealMap[ToLoc] shr 27<>Cardinal(p)) then // empty enemy city
    257       if PModel.Kind=mkDiplomat then
    258         begin
    259         MoveInfo.MoveType:=mtSpyMission;
    260         end
    261       else if PModel.Domain=dGround then
    262         begin
    263         if PModel.Flags and mdCivil<>0 then
    264           begin result:=eNoCapturer; exit end;
    265         MoveInfo.MoveType:=mtCapture;
    266         end
     271      if (RealMap[ToLoc] and fCity <> 0) and
     272        (RealMap[ToLoc] shr 27 <> Cardinal(p)) then // empty enemy city
     273        if PModel.Kind = mkDiplomat then
     274        begin
     275          MoveInfo.MoveType := mtSpyMission;
     276        end
     277        else if PModel.Domain = dGround then
     278        begin
     279          if PModel.Flags and mdCivil <> 0 then
     280          begin
     281            result := eNoCapturer;
     282            exit
     283          end;
     284          MoveInfo.MoveType := mtCapture;
     285        end
     286        else
     287        begin
     288          if (PModel.Domain = dSea) and (PModel.Cap[mcArtillery] = 0) then
     289          begin
     290            result := eDomainMismatch;
     291            exit
     292          end
     293          else if (PModel.Attack = 0) and
     294            not((PModel.Cap[mcBombs] > 0) and (Flags and unBombsLoaded <> 0))
     295          then
     296          begin
     297            result := eNoBombarder;
     298            exit
     299          end
     300          else if Movement < 100 then
     301          begin
     302            result := eNoTime_Bombard;
     303            exit
     304          end;
     305          MoveInfo.MoveType := mtBombard;
     306          result := eBombarded;
     307        end
     308    end;
     309
     310    MoveInfo.MountainDelay := false;
     311    if MoveInfo.MoveType in [mtAttack, mtBombard, mtExpel] then
     312    begin
     313      if (Master >= 0) or (PModel.Domain = dSea) and
     314        (RealMap[Loc] and fTerrain >= fGrass) or (PModel.Domain = dAir) and
     315        ((RealMap[Loc] and fCity <> 0) or (RealMap[Loc] and fTerImp = tiBase))
     316      then
     317      begin
     318        result := eViolation;
     319        exit
     320      end;
     321      if MoveInfo.MoveType = mtBombard then
     322      begin
     323        MoveInfo.EndHealth := Health;
     324        MoveInfo.EndHealthDef := -1;
     325      end
    267326      else
    268         begin
    269         if (PModel.Domain=dSea) and (PModel.Cap[mcArtillery]=0) then
    270           begin result:=eDomainMismatch; exit end
    271         else if (PModel.Attack=0)
    272           and not ((PModel.Cap[mcBombs]>0) and (Flags and unBombsLoaded<>0)) then
    273           begin result:=eNoBombarder; exit end
    274         else if Movement<100 then
    275           begin result:=eNoTime_Bombard; exit end;
    276         MoveInfo.MoveType:=mtBombard;
    277         result:=eBombarded;
    278         end
    279     end;
    280 
    281   MoveInfo.MountainDelay:=false;
    282   if MoveInfo.MoveType in [mtAttack,mtBombard,mtExpel] then
    283     begin
    284     if (Master>=0)
    285       or (PModel.Domain=dSea) and (RealMap[Loc] and fTerrain>=fGrass)
    286       or (PModel.Domain=dAir) and ((RealMap[Loc] and fCity<>0)
    287         or (RealMap[Loc] and fTerImp=tiBase)) then
    288       begin result:=eViolation; exit end;
    289     if MoveInfo.MoveType=mtBombard then
    290       begin
    291       MoveInfo.EndHealth:=Health;
    292       MoveInfo.EndHealthDef:=-1;
    293       end
    294     else
    295       begin
    296       MoveInfo.EndHealth:=BattleForecast.EndHealthAtt;
    297       MoveInfo.EndHealthDef:=BattleForecast.EndHealthDef;
     327      begin
     328        MoveInfo.EndHealth := BattleForecast.EndHealthAtt;
     329        MoveInfo.EndHealthDef := BattleForecast.EndHealthDef;
    298330      end
    299331    end
    300   else // if MoveInfo.MoveType in [mtMove,mtCapture,mtSpyMission] then
    301     begin
    302     if (Master>=0) and (PModel.Domain<dSea) then
     332    else // if MoveInfo.MoveType in [mtMove,mtCapture,mtSpyMission] then
     333    begin
     334      if (Master >= 0) and (PModel.Domain < dSea) then
    303335      begin // transport unload
    304       MoveInfo.Cost:=PModel.Speed;
    305       if RealMap[ToLoc] and fTerrain<fGrass then
    306         result:=eDomainMismatch;
     336        MoveInfo.Cost := PModel.Speed;
     337        if RealMap[ToLoc] and fTerrain < fGrass then
     338          result := eDomainMismatch;
    307339      end
    308     else
    309       begin
    310       result:=GetMoveCost(p,mix,FromLoc,ToLoc,MoveLength,MoveInfo.Cost);
    311       if result=eMountains then
    312         begin result:=eOk; MoveInfo.MountainDelay:=true end;
     340      else
     341      begin
     342        result := GetMoveCost(p, mix, FromLoc, ToLoc, MoveLength,
     343          MoveInfo.Cost);
     344        if result = eMountains then
     345        begin
     346          result := eOK;
     347          MoveInfo.MountainDelay := true
     348        end;
    313349      end;
    314     if (result>=rExecuted) and (MoveInfo.MoveType=mtSpyMission) then
    315       result:=eMissionDone;
    316 
    317     MoveInfo.ToMaster:=-1;
    318     if (result=eDomainMismatch) and (PModel.Domain<dSea)
    319       and (PModel.Cap[mcOver]=0) then
    320       begin
    321       for uix1:=0 to nUn-1 do with Un[uix1] do // check load to transport
    322         if (Loc=ToLoc)
    323           and (TroopLoad<Model[mix].MTrans*Model[mix].Cap[mcSeaTrans]) then
    324           begin
    325           result:=eLoaded;
    326           MoveInfo.Cost:=PModel.Speed;
    327           MoveInfo.ToMaster:=uix1;
    328           if (uixSelectedTransport>=0) and (uix1=uixSelectedTransport) then
    329             Break;
    330           end;
     350      if (result >= rExecuted) and (MoveInfo.MoveType = mtSpyMission) then
     351        result := eMissionDone;
     352
     353      MoveInfo.ToMaster := -1;
     354      if (result = eDomainMismatch) and (PModel.Domain < dSea) and
     355        (PModel.Cap[mcOver] = 0) then
     356      begin
     357        for uix1 := 0 to nUn - 1 do
     358          with Un[uix1] do // check load to transport
     359            if (Loc = ToLoc) and
     360              (TroopLoad < Model[mix].MTrans * Model[mix].Cap[mcSeaTrans]) then
     361            begin
     362              result := eLoaded;
     363              MoveInfo.Cost := PModel.Speed;
     364              MoveInfo.ToMaster := uix1;
     365              if (uixSelectedTransport >= 0) and (uix1 = uixSelectedTransport)
     366              then
     367                Break;
     368            end;
    331369      end
    332     else if (PModel.Domain=dAir) and (PModel.Cap[mcAirTrans]=0)
    333       and (RealMap[ToLoc] and fCity=0) and (RealMap[ToLoc] and fTerImp<>tiBase) then
    334       begin
    335       for uix1:=0 to nUn-1 do with Un[uix1] do
    336         if (Loc=ToLoc)
    337           and (AirLoad<Model[mix].MTrans*Model[mix].Cap[mcCarrier]) then
    338           begin// load plane to ship
    339           result:=eLoaded;
    340           MoveInfo.ToMaster:=uix1;
    341           if (uixSelectedTransport>=0) and (uix1=uixSelectedTransport) then
    342             Break;
    343           end
     370      else if (PModel.Domain = dAir) and (PModel.Cap[mcAirTrans] = 0) and
     371        (RealMap[ToLoc] and fCity = 0) and (RealMap[ToLoc] and fTerImp <> tiBase)
     372      then
     373      begin
     374        for uix1 := 0 to nUn - 1 do
     375          with Un[uix1] do
     376            if (Loc = ToLoc) and
     377              (AirLoad < Model[mix].MTrans * Model[mix].Cap[mcCarrier]) then
     378            begin // load plane to ship
     379              result := eLoaded;
     380              MoveInfo.ToMaster := uix1;
     381              if (uixSelectedTransport >= 0) and (uix1 = uixSelectedTransport)
     382              then
     383                Break;
     384            end
    344385      end;
    345     if result<rExecuted then exit;
    346 
    347     if (Master<0) and (MoveInfo.ToMaster<0) then
    348       MoveInfo.EndHealth:=Health-HostileDamage(p,mix,ToLoc,MoveInfo.Cost)
    349     else MoveInfo.EndHealth:=Health;
    350 
    351     if (Mode=moPlaying)
    352       and (PModel.Flags and mdZOC<>0)
    353       and (Master<0) and (MoveInfo.ToMaster<0)
    354       and (Controlled(p,FromLoc,false)>=coTrue) then
    355       begin
    356       DestControlled:=Controlled(p,ToLoc,true);
    357       if DestControlled>=coTrue+coKnown then
    358         begin result:=eZOC; exit end
    359       else if not TestOnly and (DestControlled>=coTrue) then
    360         begin result:=eZOC_EnemySpotted; exit end
     386      if result < rExecuted then
     387        exit;
     388
     389      if (Master < 0) and (MoveInfo.ToMaster < 0) then
     390        MoveInfo.EndHealth := Health - HostileDamage(p, mix, ToLoc,
     391          MoveInfo.Cost)
     392      else
     393        MoveInfo.EndHealth := Health;
     394
     395      if (Mode = moPlaying) and (PModel.Flags and mdZOC <> 0) and (Master < 0)
     396        and (MoveInfo.ToMaster < 0) and (Controlled(p, FromLoc, false) >= coTrue)
     397      then
     398      begin
     399        DestControlled := Controlled(p, ToLoc, true);
     400        if DestControlled >= coTrue + coKnown then
     401        begin
     402          result := eZOC;
     403          exit
     404        end
     405        else if not TestOnly and (DestControlled >= coTrue) then
     406        begin
     407          result := eZOC_EnemySpotted;
     408          exit
     409        end
    361410      end;
    362     if (Movement=0) and (ServerVersion[p]>=$0100F1) or (MoveInfo.Cost>Movement) then
    363       if (Master>=0) or (MoveInfo.ToMaster>=0) then
    364         begin result:=eNoTime_Load; exit end
    365       else begin result:=eNoTime_Move; exit end;
    366     if (MoveInfo.EndHealth<=0) or (MoveInfo.MoveType=mtSpyMission) then
    367       result:=result or rUnitRemoved; // spy mission or victim of HostileDamage
     411      if (Movement = 0) and (ServerVersion[p] >= $0100F1) or
     412        (MoveInfo.Cost > Movement) then
     413        if (Master >= 0) or (MoveInfo.ToMaster >= 0) then
     414        begin
     415          result := eNoTime_Load;
     416          exit
     417        end
     418        else
     419        begin
     420          result := eNoTime_Move;
     421          exit
     422        end;
     423      if (MoveInfo.EndHealth <= 0) or (MoveInfo.MoveType = mtSpyMission) then
     424        result := result or rUnitRemoved;
     425      // spy mission or victim of HostileDamage
    368426
    369427    end; // if MoveInfo.MoveType in [mtMove,mtCapture,mtSpyMission]
    370428
    371   if MoveInfo.MoveType in [mtAttack,mtExpel] then
    372     MoveInfo.Defender:=Occupant[ToLoc]
    373   else if RealMap[ToLoc] and fCity<>0 then
     429    if MoveInfo.MoveType in [mtAttack, mtExpel] then
     430      MoveInfo.Defender := Occupant[ToLoc]
     431    else if RealMap[ToLoc] and fCity <> 0 then
    374432    begin // MoveInfo.Dcix not set yet
    375     MoveInfo.Defender:=RealMap[ToLoc] shr 27;
    376     SearchCity(ToLoc,MoveInfo.Defender,MoveInfo.Dcix);
     433      MoveInfo.Defender := RealMap[ToLoc] shr 27;
     434      SearchCity(ToLoc, MoveInfo.Defender, MoveInfo.Dcix);
    377435    end
    378436  end
    379 end; //CalculateMove
     437end; // CalculateMove
    380438
    381439function GetBattleForecast(Loc: integer; var BattleForecast: TBattleForecast;
    382   var Duix,Dcix,AStr,DStr,ABaseDamage,DBaseDamage: integer): integer;
    383 var
    384 Time,Defender,ABon,DBon,DCnt,MultiDamage: integer;
    385 PModel,DModel: ^TModel;
    386 begin
    387 with BattleForecast do
    388   begin
    389   Defender:=Occupant[Loc];
    390   if (Defender<0) or (Defender=pAtt) then
    391     begin result:=eOK; exit end; // no attack, simple move
    392 
    393   PModel:=@RW[pAtt].Model[mixAtt];
    394   Strongest(Loc,Duix,DStr,DBon,DCnt); {get defense strength and bonus}
    395   if (PModel.Kind=mkDiplomat) and (RealMap[Loc] and fCity<>0) then
     440  var Duix, Dcix, AStr, DStr, ABaseDamage, DBaseDamage: integer): integer;
     441var
     442  Time, Defender, ABon, DBon, DCnt, MultiDamage: integer;
     443  PModel, DModel: ^TModel;
     444begin
     445  with BattleForecast do
     446  begin
     447    Defender := Occupant[Loc];
     448    if (Defender < 0) or (Defender = pAtt) then
     449    begin
     450      result := eOK;
     451      exit
     452    end; // no attack, simple move
     453
     454    PModel := @RW[pAtt].Model[mixAtt];
     455    Strongest(Loc, Duix, DStr, DBon, DCnt); { get defense strength and bonus }
     456    if (PModel.Kind = mkDiplomat) and (RealMap[Loc] and fCity <> 0) then
    396457    begin // spy mission -- return as if move was possible
    397     EndHealthAtt:=HealthAtt;
    398     EndHealthDef:=RW[Defender].Un[Duix].Health;
    399     result:=eOk;
    400     exit
    401     end;
    402 
    403   DModel:=@RW[Defender].Model[RW[Defender].Un[Duix].mix];
    404   if (RealMap[Loc] and fCity=0) and (RealMap[Loc] and fTerImp<>tiBase) then
    405     begin
    406     if (DModel.Cap[mcSub]>0)
    407       and (RealMap[Loc] and fTerrain<fGrass)
    408       and (ObserveLevel[Loc] shr (2*pAtt) and 3<lObserveAll) then
    409       begin result:=eHiddenUnit; exit; end; //attacking submarine not allowed
    410     if (DModel.Cap[mcStealth]>0)
    411       and (ObserveLevel[Loc] shr (2*pAtt) and 3<>lObserveSuper) then
    412       begin result:=eStealthUnit; exit; end; //attacking stealth aircraft not allowed
    413     if (DModel.Domain=dAir) and (DModel.Kind<>mkSpecial_Glider)
    414       and (PModel.Domain<>dAir) then
    415       begin result:=eDomainMismatch; exit end; //can't attack plane
    416     end;
    417   if ((PModel.Cap[mcArtillery]=0)
    418     or ((ServerVersion[pAtt]>=$010200) and (RealMap[Loc] and fTerrain<fGrass)
    419       and (DModel.Cap[mcSub]>0))) // ground units can't attack submarines
    420     and ((PModel.Domain=dGround) and (RealMap[Loc] and fTerrain<fGrass)
    421       or (PModel.Domain=dSea) and (RealMap[Loc] and fTerrain>=fGrass)) then
    422     begin result:=eDomainMismatch; exit end;
    423   if (PModel.Attack=0)
    424     and not ((PModel.Cap[mcBombs]>0) and (FlagsAtt and unBombsLoaded<>0)
    425     and (DModel.Domain<dAir)) then
    426     begin result:=eInvalid; exit end;
    427 
    428   if Movement=0 then
    429     begin result:=eNoTime_Attack; exit end;
    430 
    431   {$IFOPT O-}assert(InvalidTreatyMap=0);{$ENDIF}
    432   if RW[pAtt].Treaty[Defender]>=trPeace then
    433     begin
    434     if (PModel.Domain<>dAir)
    435       and (PModel.Attack>0) and (integer(RealMap[Loc] shr 27)=pAtt) then
    436       if Movement>=100 then
     458      EndHealthAtt := HealthAtt;
     459      EndHealthDef := RW[Defender].Un[Duix].Health;
     460      result := eOK;
     461      exit
     462    end;
     463
     464    DModel := @RW[Defender].Model[RW[Defender].Un[Duix].mix];
     465    if (RealMap[Loc] and fCity = 0) and (RealMap[Loc] and fTerImp <> tiBase)
     466    then
     467    begin
     468      if (DModel.Cap[mcSub] > 0) and (RealMap[Loc] and fTerrain < fGrass) and
     469        (ObserveLevel[Loc] shr (2 * pAtt) and 3 < lObserveAll) then
     470      begin
     471        result := eHiddenUnit;
     472        exit;
     473      end; // attacking submarine not allowed
     474      if (DModel.Cap[mcStealth] > 0) and
     475        (ObserveLevel[Loc] shr (2 * pAtt) and 3 <> lObserveSuper) then
     476      begin
     477        result := eStealthUnit;
     478        exit;
     479      end; // attacking stealth aircraft not allowed
     480      if (DModel.Domain = dAir) and (DModel.Kind <> mkSpecial_Glider) and
     481        (PModel.Domain <> dAir) then
     482      begin
     483        result := eDomainMismatch;
     484        exit
     485      end; // can't attack plane
     486    end;
     487    if ((PModel.Cap[mcArtillery] = 0) or ((ServerVersion[pAtt] >= $010200) and
     488      (RealMap[Loc] and fTerrain < fGrass) and (DModel.Cap[mcSub] > 0)))
     489    // ground units can't attack submarines
     490      and ((PModel.Domain = dGround) and (RealMap[Loc] and fTerrain < fGrass) or
     491      (PModel.Domain = dSea) and (RealMap[Loc] and fTerrain >= fGrass)) then
     492    begin
     493      result := eDomainMismatch;
     494      exit
     495    end;
     496    if (PModel.Attack = 0) and not((PModel.Cap[mcBombs] > 0) and
     497      (FlagsAtt and unBombsLoaded <> 0) and (DModel.Domain < dAir)) then
     498    begin
     499      result := eInvalid;
     500      exit
     501    end;
     502
     503    if Movement = 0 then
     504    begin
     505      result := eNoTime_Attack;
     506      exit
     507    end;
     508
     509{$IFOPT O-}assert(InvalidTreatyMap = 0); {$ENDIF}
     510    if RW[pAtt].Treaty[Defender] >= trPeace then
     511    begin
     512      if (PModel.Domain <> dAir) and (PModel.Attack > 0) and
     513        (integer(RealMap[Loc] shr 27) = pAtt) then
     514        if Movement >= 100 then
    437515        begin // expel friendly unit
    438         EndHealthDef:=RW[Defender].Un[Duix].Health;
    439         EndHealthAtt:=HealthAtt;
    440         result:=eExpelled
    441         end
    442       else result:=eNoTime_Expel
    443     else result:=eTreaty;
    444     exit;
    445     end;
    446 
    447   // calculate defender strength
    448   if RealMap[Loc] and fCity<>0 then
     516          EndHealthDef := RW[Defender].Un[Duix].Health;
     517          EndHealthAtt := HealthAtt;
     518          result := eExpelled
     519        end
     520        else
     521          result := eNoTime_Expel
     522      else
     523        result := eTreaty;
     524      exit;
     525    end;
     526
     527    // calculate defender strength
     528    if RealMap[Loc] and fCity <> 0 then
    449529    begin // consider city improvements
    450     SearchCity(Loc,Defender,Dcix);
    451     if (PModel.Domain<dSea) and (PModel.Cap[mcArtillery]=0)
    452       and ((RW[Defender].City[Dcix].Built[imWalls]=1)
    453       or (Continent[RW[Defender].City[Dcix].Loc]=GrWallContinent[Defender])) then
    454       inc(DBon,8)
    455     else if (PModel.Domain=dSea)
    456       and (RW[Defender].City[Dcix].Built[imCoastalFort]=1) then
    457       inc(DBon,4)
    458     else if (PModel.Domain=dAir)
    459       and (RW[Defender].City[Dcix].Built[imMissileBat]=1) then
    460       inc(DBon,4);
    461     if RW[Defender].City[Dcix].Built[imBunker]=1 then
    462       inc(DBon,4)
    463     end;
    464   if (PModel.Domain=dAir) and (DModel.Cap[mcAirDef]>0) then
    465     inc(DBon,4);
    466   DStr:=DModel.Defense*DBon*100;
    467   if (DModel.Domain=dAir) and ((RealMap[Loc] and fCity<>0)
    468     or (RealMap[Loc] and fTerImp=tiBase)) then
    469     DStr:=0;
    470   if (DModel.Domain=dSea) and (RealMap[Loc] and fTerrain>=fGrass) then
    471     DStr:=DStr shr 1;
    472 
    473   // calculate attacker strength
    474   if PModel.Cap[mcWill]>0 then Time:=100
    475   else begin Time:=Movement; if Time>100 then Time:=100; end;
    476   ABon:=4+ExpAtt div ExpCost;
    477   AStr:=PModel.Attack;
    478   if (FlagsAtt and unBombsLoaded<>0) and (DModel.Domain<dAir) then // use bombs
    479     AStr:=AStr+PModel.Cap[mcBombs]*PModel.MStrength*2;
    480   AStr:=Time*AStr*ABon;
    481 
    482   // calculate base damage for defender
    483   if DStr=0 then
    484     DBaseDamage:=RW[Defender].Un[Duix].Health
    485   else
    486     begin
    487     DBaseDamage:=HealthAtt*AStr div DStr;
    488     if DBaseDamage=0 then
    489       DBaseDamage:=1;
    490     if DBaseDamage>RW[Defender].Un[Duix].Health then
    491       DBaseDamage:=RW[Defender].Un[Duix].Health
    492     end;
    493 
    494   // calculate base damage for attacker
    495   if AStr=0 then
    496     ABaseDamage:=HealthAtt
    497   else
    498     begin
    499     ABaseDamage:=RW[Defender].Un[Duix].Health*DStr div AStr;
    500     if ABaseDamage=0 then
    501       ABaseDamage:=1;
    502     if ABaseDamage>HealthAtt then
    503       ABaseDamage:=HealthAtt
    504     end;
    505 
    506   // calculate final damage for defender
    507   MultiDamage:=2;
    508   if (ABaseDamage=HealthAtt) and (PModel.Cap[mcFanatic]>0)
    509     and not (RW[pAtt].Government in [gRepublic,gDemocracy,gFuture]) then
    510     MultiDamage:=MultiDamage*2; // fanatic attacker died
    511   EndHealthDef:=RW[Defender].Un[Duix].Health-MultiDamage*DBaseDamage div 2;
    512   if EndHealthDef<0 then EndHealthDef:=0;
    513 
    514   // calculate final damage for attacker
    515   MultiDamage:=2;
    516   if DBaseDamage=RW[Defender].Un[Duix].Health then
    517     begin
    518     if (DModel.Cap[mcFanatic]>0)
    519       and not (RW[Defender].Government in [gRepublic,gDemocracy,gFuture]) then
    520       MultiDamage:=MultiDamage*2; // fanatic defender died
    521     if PModel.Cap[mcFirst]>0 then
    522       MultiDamage:=MultiDamage shr 1; // first strike unit wins
    523     end;
    524   Time:=Movement; if Time>100 then Time:=100;
    525   EndHealthAtt:=HealthAtt-MultiDamage*ABaseDamage div 2-HostileDamage(pAtt,mixAtt,Loc,Time);
    526   if EndHealthAtt<0 then EndHealthAtt:=0;
    527 
    528   if EndHealthDef>0 then result:=eLost
    529   else if EndHealthAtt>0 then result:=eWon
    530   else result:=eBloody
     530      SearchCity(Loc, Defender, Dcix);
     531      if (PModel.Domain < dSea) and (PModel.Cap[mcArtillery] = 0) and
     532        ((RW[Defender].City[Dcix].Built[imWalls] = 1) or
     533        (Continent[RW[Defender].City[Dcix].Loc] = GrWallContinent[Defender]))
     534      then
     535        inc(DBon, 8)
     536      else if (PModel.Domain = dSea) and
     537        (RW[Defender].City[Dcix].Built[imCoastalFort] = 1) then
     538        inc(DBon, 4)
     539      else if (PModel.Domain = dAir) and
     540        (RW[Defender].City[Dcix].Built[imMissileBat] = 1) then
     541        inc(DBon, 4);
     542      if RW[Defender].City[Dcix].Built[imBunker] = 1 then
     543        inc(DBon, 4)
     544    end;
     545    if (PModel.Domain = dAir) and (DModel.Cap[mcAirDef] > 0) then
     546      inc(DBon, 4);
     547    DStr := DModel.Defense * DBon * 100;
     548    if (DModel.Domain = dAir) and ((RealMap[Loc] and fCity <> 0) or
     549      (RealMap[Loc] and fTerImp = tiBase)) then
     550      DStr := 0;
     551    if (DModel.Domain = dSea) and (RealMap[Loc] and fTerrain >= fGrass) then
     552      DStr := DStr shr 1;
     553
     554    // calculate attacker strength
     555    if PModel.Cap[mcWill] > 0 then
     556      Time := 100
     557    else
     558    begin
     559      Time := Movement;
     560      if Time > 100 then
     561        Time := 100;
     562    end;
     563    ABon := 4 + ExpAtt div ExpCost;
     564    AStr := PModel.Attack;
     565    if (FlagsAtt and unBombsLoaded <> 0) and (DModel.Domain < dAir) then
     566    // use bombs
     567      AStr := AStr + PModel.Cap[mcBombs] * PModel.MStrength * 2;
     568    AStr := Time * AStr * ABon;
     569
     570    // calculate base damage for defender
     571    if DStr = 0 then
     572      DBaseDamage := RW[Defender].Un[Duix].Health
     573    else
     574    begin
     575      DBaseDamage := HealthAtt * AStr div DStr;
     576      if DBaseDamage = 0 then
     577        DBaseDamage := 1;
     578      if DBaseDamage > RW[Defender].Un[Duix].Health then
     579        DBaseDamage := RW[Defender].Un[Duix].Health
     580    end;
     581
     582    // calculate base damage for attacker
     583    if AStr = 0 then
     584      ABaseDamage := HealthAtt
     585    else
     586    begin
     587      ABaseDamage := RW[Defender].Un[Duix].Health * DStr div AStr;
     588      if ABaseDamage = 0 then
     589        ABaseDamage := 1;
     590      if ABaseDamage > HealthAtt then
     591        ABaseDamage := HealthAtt
     592    end;
     593
     594    // calculate final damage for defender
     595    MultiDamage := 2;
     596    if (ABaseDamage = HealthAtt) and (PModel.Cap[mcFanatic] > 0) and
     597      not(RW[pAtt].Government in [gRepublic, gDemocracy, gFuture]) then
     598      MultiDamage := MultiDamage * 2; // fanatic attacker died
     599    EndHealthDef := RW[Defender].Un[Duix].Health - MultiDamage *
     600      DBaseDamage div 2;
     601    if EndHealthDef < 0 then
     602      EndHealthDef := 0;
     603
     604    // calculate final damage for attacker
     605    MultiDamage := 2;
     606    if DBaseDamage = RW[Defender].Un[Duix].Health then
     607    begin
     608      if (DModel.Cap[mcFanatic] > 0) and
     609        not(RW[Defender].Government in [gRepublic, gDemocracy, gFuture]) then
     610        MultiDamage := MultiDamage * 2; // fanatic defender died
     611      if PModel.Cap[mcFirst] > 0 then
     612        MultiDamage := MultiDamage shr 1; // first strike unit wins
     613    end;
     614    Time := Movement;
     615    if Time > 100 then
     616      Time := 100;
     617    EndHealthAtt := HealthAtt - MultiDamage * ABaseDamage div 2 -
     618      HostileDamage(pAtt, mixAtt, Loc, Time);
     619    if EndHealthAtt < 0 then
     620      EndHealthAtt := 0;
     621
     622    if EndHealthDef > 0 then
     623      result := eLost
     624    else if EndHealthAtt > 0 then
     625      result := eWon
     626    else
     627      result := eBloody
    531628  end
    532 end; //GetBattleForecast
    533 
    534 function LoadUnit(p,uix: integer; TestOnly: boolean): integer;
    535 var
    536 uix1,d,Cost,ToMaster: integer;
    537 begin
    538 result:=eOk;
    539 with RW[p].Un[uix] do
    540   begin
    541   d:=RW[p].Model[mix].Domain;
    542   if (Master>=0) or (d=dSea)
    543     or (RW[p].Model[mix].Cap[mcAirTrans]
    544     +RW[p].Model[mix].Cap[mcOver]>0) then
    545     result:=eViolation
    546   else
    547     begin
    548     ToMaster:=-1;
    549     for uix1:=0 to RW[p].nUn-1 do if RW[p].Un[uix1].Loc=Loc then
    550       with RW[p].Un[uix1], RW[p].Model[mix] do
    551         if (d<dSea) and (TroopLoad<MTrans*(Cap[mcSeaTrans]+Cap[mcAirTrans]))
    552           or (d=dAir) and (AirLoad<MTrans*Cap[mcCarrier]) then
    553           begin {load onto unit uix1}
    554           if (uixSelectedTransport<0) or (uix1=uixSelectedTransport) then
    555             begin ToMaster:=uix1; Break end
    556           else if ToMaster<0 then
    557             ToMaster:=uix1;
    558           end;
    559     if ToMaster<0 then result:=eNoLoadCapacity
     629end; // GetBattleForecast
     630
     631function LoadUnit(p, uix: integer; TestOnly: boolean): integer;
     632var
     633  uix1, d, Cost, ToMaster: integer;
     634begin
     635  result := eOK;
     636  with RW[p].Un[uix] do
     637  begin
     638    d := RW[p].Model[mix].Domain;
     639    if (Master >= 0) or (d = dSea) or
     640      (RW[p].Model[mix].Cap[mcAirTrans] + RW[p].Model[mix].Cap[mcOver] > 0) then
     641      result := eViolation
    560642    else
    561       begin
    562       if d=dAir then Cost:=100
    563       else Cost:=RW[p].Model[mix].Speed;
    564       if Movement<Cost then result:=eNoTime_Load
    565       else if not TestOnly then
    566         begin
    567         FreeUnit(p,uix);
    568         dec(Movement,Cost);
    569         if d=dAir then inc(RW[p].Un[ToMaster].AirLoad)
    570         else inc(RW[p].Un[ToMaster].TroopLoad);
    571         Master:=ToMaster;
    572         UpdateUnitMap(Loc);
     643    begin
     644      ToMaster := -1;
     645      for uix1 := 0 to RW[p].nUn - 1 do
     646        if RW[p].Un[uix1].Loc = Loc then
     647          with RW[p].Un[uix1], RW[p].Model[mix] do
     648            if (d < dSea) and
     649              (TroopLoad < MTrans * (Cap[mcSeaTrans] + Cap[mcAirTrans])) or
     650              (d = dAir) and (AirLoad < MTrans * Cap[mcCarrier]) then
     651            begin { load onto unit uix1 }
     652              if (uixSelectedTransport < 0) or (uix1 = uixSelectedTransport)
     653              then
     654              begin
     655                ToMaster := uix1;
     656                Break
     657              end
     658              else if ToMaster < 0 then
     659                ToMaster := uix1;
     660            end;
     661      if ToMaster < 0 then
     662        result := eNoLoadCapacity
     663      else
     664      begin
     665        if d = dAir then
     666          Cost := 100
     667        else
     668          Cost := RW[p].Model[mix].Speed;
     669        if Movement < Cost then
     670          result := eNoTime_Load
     671        else if not TestOnly then
     672        begin
     673          FreeUnit(p, uix);
     674          dec(Movement, Cost);
     675          if d = dAir then
     676            inc(RW[p].Un[ToMaster].AirLoad)
     677          else
     678            inc(RW[p].Un[ToMaster].TroopLoad);
     679          Master := ToMaster;
     680          UpdateUnitMap(Loc);
    573681        end
    574682      end
     
    577685end;
    578686
    579 function UnloadUnit(p,uix: integer; TestOnly: boolean): integer;
    580 var
    581 Cost: integer;
    582 begin
    583 result:=eOk;
    584 with RW[p].Un[uix] do
    585   if Master<0 then result:=eNotChanged
    586   else if (RW[p].Model[mix].Domain<dSea)
    587     and (RealMap[Loc] and fTerrain<fGrass) then result:=eDomainMismatch
    588 //    else if (RW[p].Model[mix].Domain<dSea)
    589 //      and (RW[p].Model[mix].Flags and mdCivil<>0)
    590 //      and (RealMap[Loc] and fDeadLands<>0) then result:=eEerie
    591   else
    592     begin
    593     if RW[p].Model[mix].Domain=dAir then Cost:=100
    594     else Cost:=RW[p].Model[mix].Speed;
    595     if Movement<Cost then result:=eNoTime_Load
    596     else if not TestOnly then
    597       begin
    598       dec(Movement,Cost);
    599       if RW[p].Model[mix].Domain=dAir then
    600         dec(RW[p].Un[Master].AirLoad)
     687function UnloadUnit(p, uix: integer; TestOnly: boolean): integer;
     688var
     689  Cost: integer;
     690begin
     691  result := eOK;
     692  with RW[p].Un[uix] do
     693    if Master < 0 then
     694      result := eNotChanged
     695    else if (RW[p].Model[mix].Domain < dSea) and
     696      (RealMap[Loc] and fTerrain < fGrass) then
     697      result := eDomainMismatch
     698      // else if (RW[p].Model[mix].Domain<dSea)
     699      // and (RW[p].Model[mix].Flags and mdCivil<>0)
     700      // and (RealMap[Loc] and fDeadLands<>0) then result:=eEerie
     701    else
     702    begin
     703      if RW[p].Model[mix].Domain = dAir then
     704        Cost := 100
    601705      else
    602         begin
    603         dec(RW[p].Un[Master].TroopLoad);
    604 //            Movement:=0 // no more movement after unload
     706        Cost := RW[p].Model[mix].Speed;
     707      if Movement < Cost then
     708        result := eNoTime_Load
     709      else if not TestOnly then
     710      begin
     711        dec(Movement, Cost);
     712        if RW[p].Model[mix].Domain = dAir then
     713          dec(RW[p].Un[Master].AirLoad)
     714        else
     715        begin
     716          dec(RW[p].Un[Master].TroopLoad);
     717          // Movement:=0 // no more movement after unload
    605718        end;
    606       Master:=-1;
    607       PlaceUnit(p,uix);
    608       UpdateUnitMap(Loc);
     719        Master := -1;
     720        PlaceUnit(p, uix);
     721        UpdateUnitMap(Loc);
    609722      end;
    610723    end
    611724end;
    612725
    613 procedure Recover(p,uix: integer);
    614 var
    615 cix,Recovery: integer;
    616 begin
    617 with RW[p],Un[uix] do
    618   begin
    619   if (Master>=0) and (Model[Un[Master].mix].Cap[mcSupplyShip]>0) then
    620     Recovery:=FastRecovery {hospital ship}
    621   else if RealMap[Loc] and fTerImp=tiBase then
    622     Recovery:=CityRecovery
    623   else if RealMap[Loc] and fCity<>0 then
    624     begin {unit in city}
    625     cix:=nCity-1;
    626     while (cix>=0) and (City[cix].Loc<>Loc) do dec(cix);
    627     if City[cix].Flags and chDisorder<>0 then
    628       Recovery:=NoCityRecovery
    629     else if (Model[mix].Domain=dGround)
    630         and (City[cix].Built[imBarracks]+City[cix].Built[imElite]>0)
    631       or (Model[mix].Domain=dSea) and (City[cix].Built[imDockyard]=1)
    632       or (Model[mix].Domain=dAir) and (City[cix].Built[imAirport]=1) then
    633       Recovery:=FastRecovery {city has baracks/shipyard/airport}
    634     else Recovery:=CityRecovery
     726procedure Recover(p, uix: integer);
     727var
     728  cix, Recovery: integer;
     729begin
     730  with RW[p], Un[uix] do
     731  begin
     732    if (Master >= 0) and (Model[Un[Master].mix].Cap[mcSupplyShip] > 0) then
     733      Recovery := FastRecovery { hospital ship }
     734    else if RealMap[Loc] and fTerImp = tiBase then
     735      Recovery := CityRecovery
     736    else if RealMap[Loc] and fCity <> 0 then
     737    begin { unit in city }
     738      cix := nCity - 1;
     739      while (cix >= 0) and (City[cix].Loc <> Loc) do
     740        dec(cix);
     741      if City[cix].Flags and chDisorder <> 0 then
     742        Recovery := NoCityRecovery
     743      else if (Model[mix].Domain = dGround) and
     744        (City[cix].Built[imBarracks] + City[cix].Built[imElite] > 0) or
     745        (Model[mix].Domain = dSea) and (City[cix].Built[imDockyard] = 1) or
     746        (Model[mix].Domain = dAir) and (City[cix].Built[imAirport] = 1) then
     747        Recovery := FastRecovery { city has baracks/shipyard/airport }
     748      else
     749        Recovery := CityRecovery
    635750    end
    636   else if (RealMap[Loc] and fTerrain>=fGrass) and (Model[mix].Domain<>dAir) then
    637     Recovery:=NoCityRecovery
    638   else Recovery:=0;
    639 
    640   Recovery:=Recovery*Movement div Model[mix].Speed; {recovery depends on movement unused}
    641   if Recovery>Health then Recovery:=Health; // health max. doubled each turn
    642   if Recovery>100-Health then Recovery:=100-Health;
    643   inc(Health,Recovery);
     751    else if (RealMap[Loc] and fTerrain >= fGrass) and (Model[mix].Domain <> dAir)
     752    then
     753      Recovery := NoCityRecovery
     754    else
     755      Recovery := 0;
     756
     757    Recovery := Recovery * Movement div Model[mix].Speed;
     758    { recovery depends on movement unused }
     759    if Recovery > Health then
     760      Recovery := Health; // health max. doubled each turn
     761    if Recovery > 100 - Health then
     762      Recovery := 100 - Health;
     763    inc(Health, Recovery);
    644764  end;
    645765end;
    646766
    647 function GetMoveAdvice(p,uix: integer; var a: TMoveAdviceData): integer;
     767function GetMoveAdvice(p, uix: integer; var a: TMoveAdviceData): integer;
    648768const
    649 //domains
    650 gmaAir=0; gmaSea=1; gmaGround_NoZoC=2; gmaGround_ZoC=3;
    651 //flags
    652 gmaNav=4; gmaOver=4; gmaAlpine=8;
    653 var
    654 i,FromLoc,EndLoc,T,T1,maxmov,initmov,Loc,Loc1,FromTile,ToTile,V8,
    655   MoveInfo,HeavyCost,RailCost,MoveCost,AddDamage,MaxDamage,MovementLeft: integer;
    656 Map: ^TTileList;
    657 Q: TIPQ;
    658 Adjacent: TVicinity8Loc;
    659 From: array[0..lxmax*lymax-1] of integer;
    660 Time: array[0..lxmax*lymax-1] of integer;
    661 Damage: array[0..lxmax*lymax-1] of integer;
    662 MountainDelay, Resistant: boolean;
    663 //  tt,tt0: int64;
    664 begin
    665 //  QueryPerformanceCounter(tt0);
    666 
    667 MaxDamage:=RW[p].Un[uix].Health-1;
    668 if MaxDamage>a.MaxHostile_MovementLeft then
    669   if a.MaxHostile_MovementLeft>=0 then
    670     MaxDamage:=a.MaxHostile_MovementLeft
    671   else MaxDamage:=0;
    672 
    673 Map:=@(RW[p].Map^);
    674 if (a.ToLoc<>maNextCity) and ((a.ToLoc<0) or (a.ToLoc>=MapSize)) then
    675   begin result:=eInvalid; exit end;
    676 if (a.ToLoc<>maNextCity) and (Map[a.ToLoc] and fTerrain=fUNKNOWN) then
    677   begin result:=eNoWay; exit end;
    678 
    679 with RW[p].Model[RW[p].Un[uix].mix] do
    680   case Domain of
    681     dGround:
    682       if (a.ToLoc<>maNextCity) and (Map[a.ToLoc] and fTerrain=fOcean) then
    683         begin result:=eDomainMismatch; exit end
    684       else
    685         begin
    686         if Flags and mdZOC<>0 then MoveInfo:=gmaGround_ZoC
    687         else MoveInfo:=gmaGround_NoZoC;
    688         if Cap[mcOver]>0 then inc(MoveInfo,gmaOver);
    689         if Cap[mcAlpine]>0 then inc(MoveInfo,gmaAlpine);
    690         HeavyCost:=50+(Speed-150)*13 shr 7;
    691         if GWonder[woShinkansen].EffectiveOwner=p then RailCost:=0
    692         else RailCost:=Speed*(4*1311) shr 17;
    693         maxmov:=Speed;
    694         initmov:=0;
    695         Resistant:= (GWonder[woGardens].EffectiveOwner=p) or
    696           (Kind=mkSettler) and (Speed>=200);
     769  // domains
     770  gmaAir = 0;
     771  gmaSea = 1;
     772  gmaGround_NoZoC = 2;
     773  gmaGround_ZoC = 3;
     774  // flags
     775  gmaNav = 4;
     776  gmaOver = 4;
     777  gmaAlpine = 8;
     778var
     779  i, FromLoc, EndLoc, T, T1, maxmov, initmov, Loc, Loc1, FromTile, ToTile, V8,
     780    MoveInfo, HeavyCost, RailCost, MoveCost, AddDamage, MaxDamage,
     781    MovementLeft: integer;
     782  Map: ^TTileList;
     783  Q: TIPQ;
     784  Adjacent: TVicinity8Loc;
     785  From: array [0 .. lxmax * lymax - 1] of integer;
     786  Time: array [0 .. lxmax * lymax - 1] of integer;
     787  Damage: array [0 .. lxmax * lymax - 1] of integer;
     788  MountainDelay, Resistant: boolean;
     789  // tt,tt0: int64;
     790begin
     791  // QueryPerformanceCounter(tt0);
     792
     793  MaxDamage := RW[p].Un[uix].Health - 1;
     794  if MaxDamage > a.MaxHostile_MovementLeft then
     795    if a.MaxHostile_MovementLeft >= 0 then
     796      MaxDamage := a.MaxHostile_MovementLeft
     797    else
     798      MaxDamage := 0;
     799
     800  Map := @(RW[p].Map^);
     801  if (a.ToLoc <> maNextCity) and ((a.ToLoc < 0) or (a.ToLoc >= MapSize)) then
     802  begin
     803    result := eInvalid;
     804    exit
     805  end;
     806  if (a.ToLoc <> maNextCity) and (Map[a.ToLoc] and fTerrain = fUNKNOWN) then
     807  begin
     808    result := eNoWay;
     809    exit
     810  end;
     811
     812  with RW[p].Model[RW[p].Un[uix].mix] do
     813    case Domain of
     814      dGround:
     815        if (a.ToLoc <> maNextCity) and (Map[a.ToLoc] and fTerrain = fOcean) then
     816        begin
     817          result := eDomainMismatch;
     818          exit
     819        end
     820        else
     821        begin
     822          if Flags and mdZOC <> 0 then
     823            MoveInfo := gmaGround_ZoC
     824          else
     825            MoveInfo := gmaGround_NoZoC;
     826          if Cap[mcOver] > 0 then
     827            inc(MoveInfo, gmaOver);
     828          if Cap[mcAlpine] > 0 then
     829            inc(MoveInfo, gmaAlpine);
     830          HeavyCost := 50 + (Speed - 150) * 13 shr 7;
     831          if GWonder[woShinkansen].EffectiveOwner = p then
     832            RailCost := 0
     833          else
     834            RailCost := Speed * (4 * 1311) shr 17;
     835          maxmov := Speed;
     836          initmov := 0;
     837          Resistant := (GWonder[woGardens].EffectiveOwner = p) or
     838            (Kind = mkSettler) and (Speed >= 200);
    697839        end;
    698     dSea:
    699       if (a.ToLoc<>maNextCity) and (Map[a.ToLoc] and fTerrain>=fGrass)
    700         and (Map[a.ToLoc] and (fCity or fUnit or fCanal)=0) then
    701         begin result:=eDomainMismatch; exit end
    702       else
    703         begin
    704         MoveInfo:=gmaSea;
    705         if Cap[mcNav]>0 then inc(MoveInfo,gmaNav);
    706         maxmov:=UnitSpeed(p,RW[p].Un[uix].mix,100);
    707         initmov:=maxmov-UnitSpeed(p,RW[p].Un[uix].mix,
    708           RW[p].Un[uix].Health);
     840      dSea:
     841        if (a.ToLoc <> maNextCity) and (Map[a.ToLoc] and fTerrain >= fGrass) and
     842          (Map[a.ToLoc] and (fCity or fUnit or fCanal) = 0) then
     843        begin
     844          result := eDomainMismatch;
     845          exit
     846        end
     847        else
     848        begin
     849          MoveInfo := gmaSea;
     850          if Cap[mcNav] > 0 then
     851            inc(MoveInfo, gmaNav);
     852          maxmov := UnitSpeed(p, RW[p].Un[uix].mix, 100);
     853          initmov := maxmov - UnitSpeed(p, RW[p].Un[uix].mix,
     854            RW[p].Un[uix].Health);
    709855        end;
    710     dAir:
    711       begin
    712       MoveInfo:=gmaAir;
    713       maxmov:=Speed;
    714       initmov:=0;
    715       end
    716     end;
    717 
    718 FromLoc:=RW[p].Un[uix].Loc;
    719 FillChar(Time,SizeOf(Time),255); {-1}
    720 Damage[FromLoc]:=0;
    721 Q:=TIPQ.Create(MapSize);
    722 Q.Put(FromLoc,(maxmov-RW[p].Un[uix].Movement) shl 8);
    723 while Q.Get(Loc,T) do
    724   begin
    725   Time[Loc]:=T;
    726   if T>=(a.MoreTurns+1) shl 20 then begin Loc:=-1; Break end;
    727   FromTile:=Map[Loc];
    728   if (Loc=a.ToLoc) or (a.ToLoc=maNextCity) and (FromTile and fCity<>0) then
    729     Break;
    730   if T and $FFF00=$FFF00 then inc(T,$100000); // indicates mountain delay
    731   V8_to_Loc(Loc,Adjacent);
    732   for V8:=0 to 7 do
    733     begin
    734     Loc1:=Adjacent[V8];
    735     if (Loc1>=0) and (Loc1<MapSize) and (Time[Loc1]<0) then
    736       begin
    737       ToTile:=Map[Loc1];
    738       if (Loc1=a.ToLoc) and (ToTile and (fUnit or fOwned)=fUnit)
    739         and not ((MoveInfo and 3=gmaSea) and (FromTile and fTerrain>=fGrass))
    740         and not ((MoveInfo and 3=gmaAir) and ((FromTile and fCity<>0)
    741           or (FromTile and fTerImp=tiBase))) then
     856      dAir:
     857        begin
     858          MoveInfo := gmaAir;
     859          maxmov := Speed;
     860          initmov := 0;
     861        end
     862    end;
     863
     864  FromLoc := RW[p].Un[uix].Loc;
     865  FillChar(Time, SizeOf(Time), 255); { -1 }
     866  Damage[FromLoc] := 0;
     867  Q := TIPQ.Create(MapSize);
     868  Q.Put(FromLoc, (maxmov - RW[p].Un[uix].Movement) shl 8);
     869  while Q.Get(Loc, T) do
     870  begin
     871    Time[Loc] := T;
     872    if T >= (a.MoreTurns + 1) shl 20 then
     873    begin
     874      Loc := -1;
     875      Break
     876    end;
     877    FromTile := Map[Loc];
     878    if (Loc = a.ToLoc) or (a.ToLoc = maNextCity) and (FromTile and fCity <> 0)
     879    then
     880      Break;
     881    if T and $FFF00 = $FFF00 then
     882      inc(T, $100000); // indicates mountain delay
     883    V8_to_Loc(Loc, Adjacent);
     884    for V8 := 0 to 7 do
     885    begin
     886      Loc1 := Adjacent[V8];
     887      if (Loc1 >= 0) and (Loc1 < MapSize) and (Time[Loc1] < 0) then
     888      begin
     889        ToTile := Map[Loc1];
     890        if (Loc1 = a.ToLoc) and (ToTile and (fUnit or fOwned) = fUnit) and
     891          not((MoveInfo and 3 = gmaSea) and (FromTile and fTerrain >= fGrass))
     892          and not((MoveInfo and 3 = gmaAir) and ((FromTile and fCity <> 0) or
     893          (FromTile and fTerImp = tiBase))) then
    742894        begin // attack position found
    743         if Q.Put(Loc1,T+1) then From[Loc1]:=Loc;
    744         end
    745       else if (ToTile and fTerrain<>fUNKNOWN)
    746         and ((Loc1=a.ToLoc) or (ToTile and (fCity or fOwned)<>fCity)) // don't move through enemy cities
    747         and ((Loc1=a.ToLoc) or (ToTile and (fUnit or fOwned)<>fUnit)) // way is blocked
    748         and (ToTile and not FromTile and fPeace=0)
    749         and ((MoveInfo and 3<gmaGround_ZoC)
    750           or (ToTile and FromTile and fInEnemyZoc=0)
    751           or (ToTile and fOwnZoCUnit<>0)
    752           or (FromTile and fCity<>0)
    753           or (ToTile and (fCity or fOwned)=fCity or fOwned)) then
    754         begin
    755         // calculate move cost, must be identic to GetMoveCost function
    756         AddDamage:=0;
    757         MountainDelay:=false;
    758         case MoveInfo of
    759 
    760           gmaAir:
    761             MoveCost:=50; {always valid move}
    762 
    763           gmaSea:
    764             if (ToTile and (fCity or fCanal)<>0)
    765               or (ToTile and fTerrain=fShore) then {domain ok}
    766               MoveCost:=50 {valid move}
    767             else MoveCost:=-1;
    768 
    769           gmaSea+gmaNav:
    770             if (ToTile and (fCity or fCanal)<>0)
    771               or (ToTile and fTerrain<fGrass) then {domain ok}
    772               MoveCost:=50 {valid move}
    773             else MoveCost:=-1;
     895          if Q.Put(Loc1, T + 1) then
     896            From[Loc1] := Loc;
     897        end
     898        else if (ToTile and fTerrain <> fUNKNOWN) and
     899          ((Loc1 = a.ToLoc) or (ToTile and (fCity or fOwned) <> fCity))
     900        // don't move through enemy cities
     901          and ((Loc1 = a.ToLoc) or (ToTile and (fUnit or fOwned) <> fUnit))
     902        // way is blocked
     903          and (ToTile and not FromTile and fPeace = 0) and
     904          ((MoveInfo and 3 < gmaGround_ZoC) or (ToTile and FromTile and
     905          fInEnemyZoc = 0) or (ToTile and fOwnZoCUnit <> 0) or
     906          (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or
     907          fOwned)) then
     908        begin
     909          // calculate move cost, must be identic to GetMoveCost function
     910          AddDamage := 0;
     911          MountainDelay := false;
     912          case MoveInfo of
     913
     914            gmaAir:
     915              MoveCost := 50; { always valid move }
     916
     917            gmaSea:
     918              if (ToTile and (fCity or fCanal) <> 0) or
     919                (ToTile and fTerrain = fShore) then { domain ok }
     920                MoveCost := 50 { valid move }
     921              else
     922                MoveCost := -1;
     923
     924            gmaSea + gmaNav:
     925              if (ToTile and (fCity or fCanal) <> 0) or
     926                (ToTile and fTerrain < fGrass) then { domain ok }
     927                MoveCost := 50 { valid move }
     928              else
     929                MoveCost := -1;
    774930
    775931          else // ground unit
    776             if (ToTile and fTerrain>=fGrass) then {domain ok}
    777               begin {valid move}
    778               if (FromTile and (fRR or fCity)<>0)
    779                 and (ToTile and (fRR or fCity)<>0) then
    780                 MoveCost:=RailCost //move along railroad
    781               else if (FromTile and (fRoad or fRR or fCity)<>0)
    782                 and (ToTile and (fRoad or fRR or fCity)<>0)
    783                 or (FromTile and ToTile and (fRiver or fCanal)<>0)
    784                 or (MoveInfo and gmaAlpine<>0) then
    785                 //move along road, river or canal
    786                 if MoveInfo and gmaOver<>0 then MoveCost:=40
    787                 else MoveCost:=20
    788               else if MoveInfo and gmaOver<>0 then MoveCost:=-1
    789               else case Terrain[ToTile and fTerrain].MoveCost of
    790                 1: MoveCost:=50; // plain terrain
    791                 2: MoveCost:=HeavyCost; // heavy terrain
    792                 3:
    793                   begin
    794                   MoveCost:=maxmov;
    795                   MountainDelay:=true;
    796                   end;
     932            if (ToTile and fTerrain >= fGrass) then { domain ok }
     933            begin { valid move }
     934              if (FromTile and (fRR or fCity) <> 0) and
     935                (ToTile and (fRR or fCity) <> 0) then
     936                MoveCost := RailCost // move along railroad
     937              else if (FromTile and (fRoad or fRR or fCity) <> 0) and
     938                (ToTile and (fRoad or fRR or fCity) <> 0) or
     939                (FromTile and ToTile and (fRiver or fCanal) <> 0) or
     940                (MoveInfo and gmaAlpine <> 0) then
     941                // move along road, river or canal
     942                if MoveInfo and gmaOver <> 0 then
     943                  MoveCost := 40
     944                else
     945                  MoveCost := 20
     946              else if MoveInfo and gmaOver <> 0 then
     947                MoveCost := -1
     948              else
     949                case Terrain[ToTile and fTerrain].MoveCost of
     950                  1:
     951                    MoveCost := 50; // plain terrain
     952                  2:
     953                    MoveCost := HeavyCost; // heavy terrain
     954                  3:
     955                    begin
     956                      MoveCost := maxmov;
     957                      MountainDelay := true;
     958                    end;
    797959                end;
    798960
    799961              // calculate HostileDamage
    800               if not resistant and (ToTile and fTerImp<>tiBase) then
    801                 if ToTile and (fTerrain or fCity or fRiver or fCanal or fSpecial1{Oasis})=fDesert then
    802                   begin
    803                   if V8 and 1<>0 then
    804                     AddDamage:=((DesertThurst*3)*MoveCost-1) div maxmov +1
    805                   else AddDamage:=((DesertThurst*2)*MoveCost-1) div maxmov +1
    806                   end
    807                 else if ToTile and (fTerrain or fCity or fRiver or fCanal)=fArctic then
    808                   begin
    809                   if V8 and 1<>0 then
    810                     AddDamage:=((ArcticThurst*3)*MoveCost-1) div maxmov +1
    811                   else AddDamage:=((ArcticThurst*2)*MoveCost-1) div maxmov +1
    812                   end;
    813               end
    814             else MoveCost:=-1;
     962              if not Resistant and (ToTile and fTerImp <> tiBase) then
     963                if ToTile and (fTerrain or fCity or fRiver or fCanal or
     964                  fSpecial1 { Oasis } ) = fDesert then
     965                begin
     966                  if V8 and 1 <> 0 then
     967                    AddDamage := ((DesertThurst * 3) * MoveCost - 1)
     968                      div maxmov + 1
     969                  else
     970                    AddDamage := ((DesertThurst * 2) * MoveCost - 1)
     971                      div maxmov + 1
     972                end
     973                else if ToTile and (fTerrain or fCity or fRiver or fCanal) = fArctic
     974                then
     975                begin
     976                  if V8 and 1 <> 0 then
     977                    AddDamage := ((ArcticThurst * 3) * MoveCost - 1)
     978                      div maxmov + 1
     979                  else
     980                    AddDamage := ((ArcticThurst * 2) * MoveCost - 1)
     981                      div maxmov + 1
     982                end;
     983            end
     984            else
     985              MoveCost := -1;
    815986
    816987          end;
    817988
    818         if (MoveCost>0) and not MountainDelay then
    819           if V8 and 1<>0 then inc(MoveCost,MoveCost*2)
    820           else inc(MoveCost,MoveCost);
    821 
    822         if (MoveInfo and 2<>0) // ground unit, check transport load/unload
    823           and ((MoveCost<0)
    824             and (ToTile and (fUnit or fOwned)=fUnit or fOwned) // assume ship/airplane is transport -- load!
    825             or (MoveCost>=0) and (FromTile and fTerrain<fGrass)) then
    826           MoveCost:=maxmov; // transport load or unload
    827 
    828         if MoveCost>=0 then
    829           begin {valid move}
    830           MovementLeft:=maxmov-T shr 8 and $FFF-MoveCost;
    831           if (MovementLeft<0) or ((MoveCost=0) and (MovementLeft=0)) then
     989          if (MoveCost > 0) and not MountainDelay then
     990            if V8 and 1 <> 0 then
     991              inc(MoveCost, MoveCost * 2)
     992            else
     993              inc(MoveCost, MoveCost);
     994
     995          if (MoveInfo and 2 <> 0) // ground unit, check transport load/unload
     996            and ((MoveCost < 0) and (ToTile and (fUnit or fOwned) = fUnit or
     997            fOwned) // assume ship/airplane is transport -- load!
     998            or (MoveCost >= 0) and (FromTile and fTerrain < fGrass)) then
     999            MoveCost := maxmov; // transport load or unload
     1000
     1001          if MoveCost >= 0 then
     1002          begin { valid move }
     1003            MovementLeft := maxmov - T shr 8 and $FFF - MoveCost;
     1004            if (MovementLeft < 0) or ((MoveCost = 0) and (MovementLeft = 0))
     1005            then
    8321006            begin // must wait for next turn
    833             // calculate HostileDamage
    834             if (MoveInfo and 2<>0){ground unit}
    835               and not resistant and (FromTile and fTerImp<>tiBase) then
    836               if FromTile and (fTerrain or fCity or fRiver or fCanal or fSpecial1{Oasis})=fDesert then
    837                 inc(AddDamage, (DesertThurst*(maxmov-T shr 8 and $FFF)-1) div maxmov +1)
    838               else if FromTile and (fTerrain or fCity or fRiver or fCanal)=fArctic then
    839                 inc(AddDamage, (ArcticThurst*(maxmov-T shr 8 and $FFF)-1) div maxmov +1);
    840 
    841             T1:=T and $7FF000FF +$100000+(initmov+MoveCost) shl 8;
     1007              // calculate HostileDamage
     1008              if (MoveInfo and 2 <> 0) { ground unit }
     1009                and not Resistant and (FromTile and fTerImp <> tiBase) then
     1010                if FromTile and (fTerrain or fCity or fRiver or fCanal or
     1011                  fSpecial1 { Oasis } ) = fDesert then
     1012                  inc(AddDamage, (DesertThurst * (maxmov - T shr 8 and $FFF) -
     1013                    1) div maxmov + 1)
     1014                else if FromTile and (fTerrain or fCity or fRiver or fCanal) = fArctic
     1015                then
     1016                  inc(AddDamage, (ArcticThurst * (maxmov - T shr 8 and $FFF) -
     1017                    1) div maxmov + 1);
     1018
     1019              T1 := T and $7FF000FF + $100000 + (initmov + MoveCost) shl 8;
    8421020            end
    843           else T1:=T+MoveCost shl 8+1;
    844           if MountainDelay then T1:=T1 or $FFF00;
    845           if (Damage[Loc]+AddDamage<=MaxDamage) and (T1 and $FF<$FF) then
    846             if Q.Put(Loc1,T1) then
     1021            else
     1022              T1 := T + MoveCost shl 8 + 1;
     1023            if MountainDelay then
     1024              T1 := T1 or $FFF00;
     1025            if (Damage[Loc] + AddDamage <= MaxDamage) and (T1 and $FF < $FF)
     1026            then
     1027              if Q.Put(Loc1, T1) then
    8471028              begin
    848               From[Loc1]:=Loc;
    849               Damage[Loc1]:=Damage[Loc]+AddDamage;
     1029                From[Loc1] := Loc;
     1030                Damage[Loc1] := Damage[Loc] + AddDamage;
    8501031              end
    8511032          end
     
    8541035    end
    8551036  end;
    856 Q.Free;
    857 if (Loc=a.ToLoc) or (a.ToLoc=maNextCity) and (Loc>=0)
    858   and (Map[Loc] and fCity<>0) then
    859   begin
    860   a.MoreTurns:=T shr 20;
    861   EndLoc:=Loc;
    862   a.nStep:=0;
    863   while Loc<>FromLoc do
    864     begin
    865     if Time[Loc]<$100000 then inc(a.nStep);
    866     Loc:=From[Loc];
    867     end;
    868   Loc:=EndLoc;
    869   i:=a.nStep;
    870   while Loc<>FromLoc do
    871     begin
    872     if Time[Loc]<$100000 then
    873       begin
    874       dec(i);
    875       if i<25 then
    876         begin
    877         a.dx[i]:=((Loc mod lx *2 +Loc div lx and 1)
    878           -(From[Loc] mod lx *2 +From[Loc] div lx and 1)+3*lx) mod (2*lx) -lx;
    879         a.dy[i]:=Loc div lx-From[Loc] div lx;
     1037  Q.Free;
     1038  if (Loc = a.ToLoc) or (a.ToLoc = maNextCity) and (Loc >= 0) and
     1039    (Map[Loc] and fCity <> 0) then
     1040  begin
     1041    a.MoreTurns := T shr 20;
     1042    EndLoc := Loc;
     1043    a.nStep := 0;
     1044    while Loc <> FromLoc do
     1045    begin
     1046      if Time[Loc] < $100000 then
     1047        inc(a.nStep);
     1048      Loc := From[Loc];
     1049    end;
     1050    Loc := EndLoc;
     1051    i := a.nStep;
     1052    while Loc <> FromLoc do
     1053    begin
     1054      if Time[Loc] < $100000 then
     1055      begin
     1056        dec(i);
     1057        if i < 25 then
     1058        begin
     1059          a.dx[i] := ((Loc mod lx * 2 + Loc div lx and 1) -
     1060            (From[Loc] mod lx * 2 + From[Loc] div lx and 1) + 3 * lx)
     1061            mod (2 * lx) - lx;
     1062          a.dy[i] := Loc div lx - From[Loc] div lx;
    8801063        end
    8811064      end;
    882     Loc:=From[Loc];
    883     end;
    884   a.MaxHostile_MovementLeft:=maxmov-Time[EndLoc] shr 8 and $FFF;
    885   if a.nStep>25 then a.nStep:=25;
    886   result:=eOK
     1065      Loc := From[Loc];
     1066    end;
     1067    a.MaxHostile_MovementLeft := maxmov - Time[EndLoc] shr 8 and $FFF;
     1068    if a.nStep > 25 then
     1069      a.nStep := 25;
     1070    result := eOK
    8871071  end
    888 else result:=eNoWay;
    889 
    890 //  QueryPerformanceCounter(tt);{time in s is: (tt-tt0)/PerfFreq}
     1072  else
     1073    result := eNoWay;
     1074
     1075  // QueryPerformanceCounter(tt);{time in s is: (tt-tt0)/PerfFreq}
    8911076end; // GetMoveAdvice
    8921077
    893 function CanPlaneReturn(p,uix: integer; PlaneReturnData: TPlaneReturnData): boolean;
     1078function CanPlaneReturn(p, uix: integer;
     1079  PlaneReturnData: TPlaneReturnData): boolean;
    8941080const
    895 mfEnd=1; mfReached=2;
    896 var
    897 uix1,T,T1,Loc,Loc1,FromTile,ToTile,V8,MoveCost,maxmov: integer;
    898 Map: ^TTileList;
    899 Q: TIPQ;
    900 Adjacent: TVicinity8Loc;
    901 MapFlags: array[0..lxmax*lymax-1] of byte;
    902 begin
    903 Map:=@(RW[p].Map^);
    904 
    905 // calculate possible return points
    906 FillChar(MapFlags,SizeOf(MapFlags),0);
    907 if RW[p].Model[RW[p].Un[uix].mix].Kind=mkSpecial_Glider then
    908   begin
    909   for Loc:=0 to MapSize-1 do
    910     if Map[Loc] and fTerrain>=fGrass then
    911       MapFlags[Loc]:=MapFlags[Loc] or mfEnd;
     1081  mfEnd = 1;
     1082  mfReached = 2;
     1083var
     1084  uix1, T, T1, Loc, Loc1, FromTile, ToTile, V8, MoveCost, maxmov: integer;
     1085  Map: ^TTileList;
     1086  Q: TIPQ;
     1087  Adjacent: TVicinity8Loc;
     1088  MapFlags: array [0 .. lxmax * lymax - 1] of byte;
     1089begin
     1090  Map := @(RW[p].Map^);
     1091
     1092  // calculate possible return points
     1093  FillChar(MapFlags, SizeOf(MapFlags), 0);
     1094  if RW[p].Model[RW[p].Un[uix].mix].Kind = mkSpecial_Glider then
     1095  begin
     1096    for Loc := 0 to MapSize - 1 do
     1097      if Map[Loc] and fTerrain >= fGrass then
     1098        MapFlags[Loc] := MapFlags[Loc] or mfEnd;
    9121099  end
    913 else
    914   begin
    915   for Loc:=0 to MapSize-1 do
    916     if (Map[Loc] and (fCity or fOwned)=fCity or fOwned)
    917       or (Map[Loc] and fTerImp=tiBase) and (Map[Loc] and fObserved<>0)
    918         and (Map[Loc] and (fUnit or fOwned)<>fUnit) then
    919       MapFlags[Loc]:=MapFlags[Loc] or mfEnd;
    920   if RW[p].Model[RW[p].Un[uix].mix].Cap[mcAirTrans]=0 then // plane can land on carriers
    921     for uix1:=0 to RW[p].nUn-1 do
    922       with RW[p].Un[uix1], RW[p].Model[mix] do
    923         if AirLoad<MTrans*Cap[mcCarrier] then
    924           MapFlags[Loc]:=MapFlags[Loc] or mfEnd;
     1100  else
     1101  begin
     1102    for Loc := 0 to MapSize - 1 do
     1103      if (Map[Loc] and (fCity or fOwned) = fCity or fOwned) or
     1104        (Map[Loc] and fTerImp = tiBase) and (Map[Loc] and fObserved <> 0) and
     1105        (Map[Loc] and (fUnit or fOwned) <> fUnit) then
     1106        MapFlags[Loc] := MapFlags[Loc] or mfEnd;
     1107    if RW[p].Model[RW[p].Un[uix].mix].Cap[mcAirTrans] = 0 then
     1108    // plane can land on carriers
     1109      for uix1 := 0 to RW[p].nUn - 1 do
     1110        with RW[p].Un[uix1], RW[p].Model[mix] do
     1111          if AirLoad < MTrans * Cap[mcCarrier] then
     1112            MapFlags[Loc] := MapFlags[Loc] or mfEnd;
    9251113  end;
    9261114
    927 with RW[p].Un[uix] do
    928   begin
    929   if Master>=0 then // can return to same carrier, even if full now
    930     MapFlags[Loc]:=MapFlags[Loc] or mfEnd;
    931   maxmov:=RW[p].Model[mix].Speed;
     1115  with RW[p].Un[uix] do
     1116  begin
     1117    if Master >= 0 then // can return to same carrier, even if full now
     1118      MapFlags[Loc] := MapFlags[Loc] or mfEnd;
     1119    maxmov := RW[p].Model[mix].Speed;
    9321120  end;
    9331121
    934 result:=false;
    935 Q:=TIPQ.Create(MapSize);
    936 Q.Put(PlaneReturnData.Loc,(maxmov-PlaneReturnData.Movement) shl 8);
    937 while Q.Get(Loc,T) do
    938   begin
    939   MapFlags[Loc]:=MapFlags[Loc] or mfReached;
    940   if T>=(PlaneReturnData.Fuel+1) shl 20 then
    941     begin result:=false; break end;
    942   if MapFlags[Loc] and mfEnd<>0 then
    943     begin result:=true; break end;
    944   FromTile:=Map[Loc];
    945   V8_to_Loc(Loc,Adjacent);
    946   for V8:=0 to 7 do
    947     begin
    948     Loc1:=Adjacent[V8];
    949     if (Loc1>=0) and (Loc1<MapSize) and (MapFlags[Loc1] and mfReached=0) then
    950       begin
    951       ToTile:=Map[Loc1];
    952       if (ToTile and fTerrain<>fUNKNOWN)
    953         and (ToTile and (fCity or fOwned)<>fCity) // don't move through enemy cities
    954         and (ToTile and (fUnit or fOwned)<>fUnit) // way is blocked
    955         and (ToTile and not FromTile and fPeace=0) then
    956         begin
    957         if V8 and 1<>0 then MoveCost:=150
    958         else MoveCost:=100;
    959         if MoveCost+T shr 8 and $FFF>maxmov then // must wait for next turn
    960           T1:=T and $7FF000FF +$100000+MoveCost shl 8
    961         else T1:=T+MoveCost shl 8;
    962         Q.Put(Loc1,T1);
     1122  result := false;
     1123  Q := TIPQ.Create(MapSize);
     1124  Q.Put(PlaneReturnData.Loc, (maxmov - PlaneReturnData.Movement) shl 8);
     1125  while Q.Get(Loc, T) do
     1126  begin
     1127    MapFlags[Loc] := MapFlags[Loc] or mfReached;
     1128    if T >= (PlaneReturnData.Fuel + 1) shl 20 then
     1129    begin
     1130      result := false;
     1131      Break
     1132    end;
     1133    if MapFlags[Loc] and mfEnd <> 0 then
     1134    begin
     1135      result := true;
     1136      Break
     1137    end;
     1138    FromTile := Map[Loc];
     1139    V8_to_Loc(Loc, Adjacent);
     1140    for V8 := 0 to 7 do
     1141    begin
     1142      Loc1 := Adjacent[V8];
     1143      if (Loc1 >= 0) and (Loc1 < MapSize) and (MapFlags[Loc1] and mfReached = 0)
     1144      then
     1145      begin
     1146        ToTile := Map[Loc1];
     1147        if (ToTile and fTerrain <> fUNKNOWN) and
     1148          (ToTile and (fCity or fOwned) <> fCity)
     1149        // don't move through enemy cities
     1150          and (ToTile and (fUnit or fOwned) <> fUnit) // way is blocked
     1151          and (ToTile and not FromTile and fPeace = 0) then
     1152        begin
     1153          if V8 and 1 <> 0 then
     1154            MoveCost := 150
     1155          else
     1156            MoveCost := 100;
     1157          if MoveCost + T shr 8 and $FFF > maxmov then
     1158          // must wait for next turn
     1159            T1 := T and $7FF000FF + $100000 + MoveCost shl 8
     1160          else
     1161            T1 := T + MoveCost shl 8;
     1162          Q.Put(Loc1, T1);
    9631163        end
    9641164      end
    9651165    end
    9661166  end;
    967 Q.Free;
     1167  Q.Free;
    9681168end; // CanPlaneReturn
    9691169
    9701170{
    971                           Terrain Improvement
    972  ____________________________________________________________________
     1171  Terrain Improvement
     1172  ____________________________________________________________________
    9731173}
    974 function CalculateJobWork(p,Loc,Job: integer; var JobWork: integer): integer;
    975 var
    976 TerrType: integer;
    977 begin
    978 result:=eOK;
    979 TerrType:=RealMap[Loc] and fTerrain;
    980 with Terrain[TerrType] do case Job of
    981   jCity:
    982     if RealMap[Loc] and fCity<>0 then result:=eInvalid
    983     else if IrrEff=0 then result:=eNoCityTerrain
    984     else JobWork:=CityWork;
    985   jRoad:
    986     if RealMap[Loc] and (fRoad or fRR)=0 then
    987       begin
    988       JobWork:=MoveCost*RoadWork;
    989       if RealMap[Loc] and fRiver<>0 then
    990         if RW[p].Tech[adBridgeBuilding]>=tsApplicable then
    991           inc(JobWork,RoadBridgeWork) {across river}
    992         else result:=eNoBridgeBuilding
    993       end
    994     else result:=eInvalid;
    995   jRR:
    996     if RealMap[Loc] and fRoad=0 then result:=eNoPreq
    997     else if RealMap[Loc] and fRR<>0 then result:=eInvalid
    998     else
    999       begin
    1000       JobWork:=MoveCost*RRWork;
    1001       if RealMap[Loc] and fRiver<>0 then
    1002         inc(JobWork,RRBridgeWork); {across river}
    1003       end;
    1004   jClear:
    1005     if (TerrType=fDesert)
    1006       and (GWonder[woGardens].EffectiveOwner<>p) then
    1007       result:=eInvalid
    1008     else if ClearTerrain>=0 then
    1009       JobWork:=IrrClearWork
    1010     else result:=eInvalid;
    1011   jIrr:
    1012     begin
    1013     JobWork:=IrrClearWork;
    1014     if (IrrEff=0)
    1015       or (RealMap[Loc] and fTerImp=tiIrrigation)
    1016       or (RealMap[Loc] and fTerImp=tiFarm) then
    1017       result:=eInvalid
    1018     end;
    1019   jFarm:
    1020     if RealMap[Loc] and fTerImp<>tiIrrigation then result:=eNoPreq
    1021     else
    1022       begin
    1023       JobWork:=IrrClearWork*FarmWork;
    1024       if (JobWork<=0) or (RealMap[Loc] and fTerImp=tiFarm) then
    1025         result:=eInvalid
    1026       end;
    1027   jAfforest:
    1028     if AfforestTerrain>=0 then
    1029       JobWork:=MineAfforestWork
    1030     else result:=eInvalid;
    1031   jMine:
    1032     begin
    1033     JobWork:=MineAfforestWork;
    1034     if (MineEff=0)
    1035       or (RealMap[Loc] and fTerImp=tiMine) then
    1036       result:=eInvalid
    1037     end;
    1038   jFort:
    1039     if RealMap[Loc] and fTerImp<>tiFort then
    1040       JobWork:=MoveCost*FortWork
    1041     else result:=eInvalid;
    1042   jCanal:
    1043     if (RealMap[Loc] and fCanal=0) and (TerrType in TerrType_Canalable) then
    1044       JobWork:=CanalWork
    1045     else result:=eInvalid;
    1046   jTrans:
    1047     begin
    1048     JobWork:=TransWork;
    1049     if JobWork<=0 then result:=eInvalid
    1050     end;
    1051   jPoll:
    1052     if RealMap[Loc] and fPoll<>0 then JobWork:=PollWork
    1053     else result:=eInvalid;
    1054   jBase:
    1055     if RealMap[Loc] and fTerImp<>tiBase then
    1056       JobWork:=MoveCost*BaseWork
    1057     else result:=eInvalid;
    1058   jPillage:
    1059     if RealMap[Loc] and (fRoad or fRR or fCanal or fTerImp)<>0 then
    1060       JobWork:=PillageWork
    1061     else result:=eInvalid;
    1062   end;
    1063 end; //CalculateJobWork
    1064 
    1065 function StartJob(p,uix,NewJob: integer; TestOnly: boolean): integer;
    1066 var
    1067 JobWork, Loc0, p1, uix1, TerrType: integer;
    1068 begin
    1069 {$IFOPT O-}assert(1 shl p and InvalidTreatyMap=0);{$ENDIF}
    1070 result:=eOK;
    1071 with RW[p].Un[uix] do
    1072   begin
    1073   if NewJob=Job then
    1074     begin result:=eNotChanged; exit end;
    1075   if NewJob=jNone then
    1076     begin if not TestOnly then Job:=jNone; exit end;
    1077   Loc0:=Loc;
    1078   if (RealMap[Loc0] and fDeadLands<>0) and (NewJob<>jRoad) and (NewJob<>jRR) then
    1079     begin result:=eDeadLands; exit end;
    1080   TerrType:=RealMap[Loc0] and fTerrain;
    1081   if (RealMap[Loc0] and fCity<>0) or (TerrType<fGrass)
    1082     or (Master>=0)
    1083     or not ((NewJob=jPillage) and (RW[p].Model[mix].Domain=dGround)
    1084       or (RW[p].Model[mix].Kind=mkSettler)
    1085       or (NewJob<>jCity) and (RW[p].Model[mix].Kind=mkSlaves)
    1086         and (GWonder[woPyramids].EffectiveOwner>=0)) then
    1087     begin result:=eInvalid; exit end;
    1088   if (JobPreq[NewJob]<>preNone)
    1089     and (RW[p].Tech[JobPreq[NewJob]]<tsApplicable) then
    1090     begin result:=eNoPreq; exit end;
    1091 
    1092   result:=CalculateJobWork(p,Loc0,NewJob,JobWork);
    1093   if (Mode=moPlaying) and (result=eOk) and (NewJob<>jPoll) then
     1174function CalculateJobWork(p, Loc, Job: integer; var JobWork: integer): integer;
     1175var
     1176  TerrType: integer;
     1177begin
     1178  result := eOK;
     1179  TerrType := RealMap[Loc] and fTerrain;
     1180  with Terrain[TerrType] do
     1181    case Job of
     1182      jCity:
     1183        if RealMap[Loc] and fCity <> 0 then
     1184          result := eInvalid
     1185        else if IrrEff = 0 then
     1186          result := eNoCityTerrain
     1187        else
     1188          JobWork := CityWork;
     1189      jRoad:
     1190        if RealMap[Loc] and (fRoad or fRR) = 0 then
     1191        begin
     1192          JobWork := MoveCost * RoadWork;
     1193          if RealMap[Loc] and fRiver <> 0 then
     1194            if RW[p].Tech[adBridgeBuilding] >= tsApplicable then
     1195              inc(JobWork, RoadBridgeWork) { across river }
     1196            else
     1197              result := eNoBridgeBuilding
     1198        end
     1199        else
     1200          result := eInvalid;
     1201      jRR:
     1202        if RealMap[Loc] and fRoad = 0 then
     1203          result := eNoPreq
     1204        else if RealMap[Loc] and fRR <> 0 then
     1205          result := eInvalid
     1206        else
     1207        begin
     1208          JobWork := MoveCost * RRWork;
     1209          if RealMap[Loc] and fRiver <> 0 then
     1210            inc(JobWork, RRBridgeWork); { across river }
     1211        end;
     1212      jClear:
     1213        if (TerrType = fDesert) and (GWonder[woGardens].EffectiveOwner <> p)
     1214        then
     1215          result := eInvalid
     1216        else if ClearTerrain >= 0 then
     1217          JobWork := IrrClearWork
     1218        else
     1219          result := eInvalid;
     1220      jIrr:
     1221        begin
     1222          JobWork := IrrClearWork;
     1223          if (IrrEff = 0) or (RealMap[Loc] and fTerImp = tiIrrigation) or
     1224            (RealMap[Loc] and fTerImp = tiFarm) then
     1225            result := eInvalid
     1226        end;
     1227      jFarm:
     1228        if RealMap[Loc] and fTerImp <> tiIrrigation then
     1229          result := eNoPreq
     1230        else
     1231        begin
     1232          JobWork := IrrClearWork * FarmWork;
     1233          if (JobWork <= 0) or (RealMap[Loc] and fTerImp = tiFarm) then
     1234            result := eInvalid
     1235        end;
     1236      jAfforest:
     1237        if AfforestTerrain >= 0 then
     1238          JobWork := MineAfforestWork
     1239        else
     1240          result := eInvalid;
     1241      jMine:
     1242        begin
     1243          JobWork := MineAfforestWork;
     1244          if (MineEff = 0) or (RealMap[Loc] and fTerImp = tiMine) then
     1245            result := eInvalid
     1246        end;
     1247      jFort:
     1248        if RealMap[Loc] and fTerImp <> tiFort then
     1249          JobWork := MoveCost * FortWork
     1250        else
     1251          result := eInvalid;
     1252      jCanal:
     1253        if (RealMap[Loc] and fCanal = 0) and (TerrType in TerrType_Canalable)
     1254        then
     1255          JobWork := CanalWork
     1256        else
     1257          result := eInvalid;
     1258      jTrans:
     1259        begin
     1260          JobWork := TransWork;
     1261          if JobWork <= 0 then
     1262            result := eInvalid
     1263        end;
     1264      jPoll:
     1265        if RealMap[Loc] and fPoll <> 0 then
     1266          JobWork := PollWork
     1267        else
     1268          result := eInvalid;
     1269      jBase:
     1270        if RealMap[Loc] and fTerImp <> tiBase then
     1271          JobWork := MoveCost * BaseWork
     1272        else
     1273          result := eInvalid;
     1274      jPillage:
     1275        if RealMap[Loc] and (fRoad or fRR or fCanal or fTerImp) <> 0 then
     1276          JobWork := PillageWork
     1277        else
     1278          result := eInvalid;
     1279    end;
     1280end; // CalculateJobWork
     1281
     1282function StartJob(p, uix, NewJob: integer; TestOnly: boolean): integer;
     1283var
     1284  JobWork, Loc0, p1, uix1, TerrType: integer;
     1285begin
     1286{$IFOPT O-}assert(1 shl p and InvalidTreatyMap = 0); {$ENDIF}
     1287  result := eOK;
     1288  with RW[p].Un[uix] do
     1289  begin
     1290    if NewJob = Job then
     1291    begin
     1292      result := eNotChanged;
     1293      exit
     1294    end;
     1295    if NewJob = jNone then
     1296    begin
     1297      if not TestOnly then
     1298        Job := jNone;
     1299      exit
     1300    end;
     1301    Loc0 := Loc;
     1302    if (RealMap[Loc0] and fDeadLands <> 0) and (NewJob <> jRoad) and
     1303      (NewJob <> jRR) then
     1304    begin
     1305      result := eDeadLands;
     1306      exit
     1307    end;
     1308    TerrType := RealMap[Loc0] and fTerrain;
     1309    if (RealMap[Loc0] and fCity <> 0) or (TerrType < fGrass) or (Master >= 0) or
     1310      not((NewJob = jPillage) and (RW[p].Model[mix].Domain = dGround) or
     1311      (RW[p].Model[mix].Kind = mkSettler) or (NewJob <> jCity) and
     1312      (RW[p].Model[mix].Kind = mkSlaves) and (GWonder[woPyramids].EffectiveOwner
     1313      >= 0)) then
     1314    begin
     1315      result := eInvalid;
     1316      exit
     1317    end;
     1318    if (JobPreq[NewJob] <> preNone) and
     1319      (RW[p].Tech[JobPreq[NewJob]] < tsApplicable) then
     1320    begin
     1321      result := eNoPreq;
     1322      exit
     1323    end;
     1324
     1325    result := CalculateJobWork(p, Loc0, NewJob, JobWork);
     1326    if (Mode = moPlaying) and (result = eOK) and (NewJob <> jPoll) then
    10941327    begin // not allowed in territory of friendly nation
    1095     p1:=RealMap[Loc0] shr 27; // owner of territory
    1096     if (p1<nPl) and (p1<>p) and (RW[p].Treaty[p1]>=trPeace) then
    1097       result:=eTreaty; // keep peace treaty!
    1098     end;
    1099   if TestOnly or (result<rExecuted) then exit;
    1100 
    1101   if (ToWork[Loc0,NewJob]=0) or (ToWork[Loc0,NewJob]>JobWork) then
    1102     ToWork[Loc0,NewJob]:=JobWork;
    1103   Job:=NewJob;
    1104   Flags:=Flags and not unFortified;
    1105   for uix1:=0 to RW[p].nUn-1 do
    1106     if (RW[p].Un[uix1].Loc=Loc)
    1107       and (RW[p].Un[uix1].Job in ContraJobs[NewJob]) then
    1108       RW[p].Un[uix1].Job:=jNone; // stop contradictive jobs
    1109   if ServerVersion[p]<$000EF0 then
    1110     if Work(p,uix) then result:=eJobDone;
    1111   if (NewJob=jCity) and (result=eJobDone) then
    1112     begin
    1113     RemoveUnit_UpdateMap(p,uix);
    1114     result:=eCity
     1328      p1 := RealMap[Loc0] shr 27; // owner of territory
     1329      if (p1 < nPl) and (p1 <> p) and (RW[p].Treaty[p1] >= trPeace) then
     1330        result := eTreaty; // keep peace treaty!
     1331    end;
     1332    if TestOnly or (result < rExecuted) then
     1333      exit;
     1334
     1335    if (ToWork[Loc0, NewJob] = 0) or (ToWork[Loc0, NewJob] > JobWork) then
     1336      ToWork[Loc0, NewJob] := JobWork;
     1337    Job := NewJob;
     1338    Flags := Flags and not unFortified;
     1339    for uix1 := 0 to RW[p].nUn - 1 do
     1340      if (RW[p].Un[uix1].Loc = Loc) and
     1341        (RW[p].Un[uix1].Job in ContraJobs[NewJob]) then
     1342        RW[p].Un[uix1].Job := jNone; // stop contradictive jobs
     1343    if ServerVersion[p] < $000EF0 then
     1344      if Work(p, uix) then
     1345        result := eJobDone;
     1346    if (NewJob = jCity) and (result = eJobDone) then
     1347    begin
     1348      RemoveUnit_UpdateMap(p, uix);
     1349      result := eCity
    11151350    end
    1116   else if Health<=0 then
     1351    else if Health <= 0 then
    11171352    begin // victim of HostileDamage
    1118     RemoveUnit_UpdateMap(p,uix);
    1119     result:=result or rUnitRemoved;
    1120     end;
    1121   if Mode>moLoading_Fast then
    1122     begin
    1123     if result=eCity then
    1124       begin
    1125       ObserveLevel[Loc0]:=ObserveLevel[Loc0] and not (3 shl (2*p));
    1126       Discover21(Loc0,p,lObserveUnhidden,true,true);
    1127 //        CheckContact;
     1353      RemoveUnit_UpdateMap(p, uix);
     1354      result := result or rUnitRemoved;
     1355    end;
     1356    if Mode > moLoading_Fast then
     1357    begin
     1358      if result = eCity then
     1359      begin
     1360        ObserveLevel[Loc0] := ObserveLevel[Loc0] and not(3 shl (2 * p));
     1361        Discover21(Loc0, p, lObserveUnhidden, true, true);
     1362        // CheckContact;
    11281363      end
    11291364    end
    11301365  end; // with
    1131 end; //StartJob
    1132 
    1133 function Work(p,uix: integer): boolean;
    1134 var
    1135 uix1,j0: integer;
    1136 begin
    1137 result:=false;
    1138 with RW[p].Un[uix] do if Movement>=100 then
    1139   begin
    1140   assert(ToWork[Loc,Job]<$FFFF); // should have been set by StartJob
    1141   if Job>=jRoad then
    1142     if integer(Movement)>=integer(ToWork[Loc,Job]) then {work complete}
    1143       begin
    1144       result:=true;
    1145       if Job<>jIrr then
    1146         Health:=Health-HostileDamage(p,mix,Loc,ToWork[Loc,Job]);
    1147       dec(Movement,ToWork[Loc,Job]);
    1148       if not (Job in [jCity,jPillage,jPoll]) then
    1149         inc(Worked[p],ToWork[Loc,Job]);
    1150       if Job=jCity then
    1151         begin // found new city
    1152         FoundCity(p,Loc);
    1153         inc(Founded[p]);
    1154         with RW[p].City[RW[p].nCity-1] do
    1155           begin
    1156           ID:=p shl 12+Founded[p]-1;
    1157           Flags:=chFounded;
    1158           end;
    1159         if Mode=moPlaying then
    1160           begin
    1161           LogCheckBorders(p,RW[p].nCity-1);
    1162           RecalcPeaceMap(p);
    1163           end;
    1164         {$IFOPT O-}if Mode<moPlaying then InvalidTreatyMap:=not(1 shl p);{$ENDIF}
    1165           // territory should not be considered for the rest of the command
    1166           // execution, because during loading a game it's incorrect before
    1167           // subsequent sIntExpandTerritory is processed
    1168         RW[p].Un[uix].Health:=0; // causes unit to be removed later
    1169         end
    1170       else CompleteJob(p,Loc,Job);
    1171       ToWork[Loc,Job]:=0;
    1172       j0:=Job;
    1173       for uix1:=0 to RW[p].nUn-1 do
    1174         if (RW[p].Un[uix1].Loc=Loc) and (RW[p].Un[uix1].Job=j0) then
    1175           RW[p].Un[uix1].Job:=jNone
    1176       end
     1366end; // StartJob
     1367
     1368function Work(p, uix: integer): boolean;
     1369var
     1370  uix1, j0: integer;
     1371begin
     1372  result := false;
     1373  with RW[p].Un[uix] do
     1374    if Movement >= 100 then
     1375    begin
     1376      assert(ToWork[Loc, Job] < $FFFF); // should have been set by StartJob
     1377      if Job >= jRoad then
     1378        if integer(Movement) >= integer(ToWork[Loc, Job]) then { work complete }
     1379        begin
     1380          result := true;
     1381          if Job <> jIrr then
     1382            Health := Health - HostileDamage(p, mix, Loc, ToWork[Loc, Job]);
     1383          dec(Movement, ToWork[Loc, Job]);
     1384          if not(Job in [jCity, jPillage, jPoll]) then
     1385            inc(Worked[p], ToWork[Loc, Job]);
     1386          if Job = jCity then
     1387          begin // found new city
     1388            FoundCity(p, Loc);
     1389            inc(Founded[p]);
     1390            with RW[p].City[RW[p].nCity - 1] do
     1391            begin
     1392              ID := p shl 12 + Founded[p] - 1;
     1393              Flags := chFounded;
     1394            end;
     1395            if Mode = moPlaying then
     1396            begin
     1397              LogCheckBorders(p, RW[p].nCity - 1);
     1398              RecalcPeaceMap(p);
     1399            end;
     1400{$IFOPT O-} if Mode < moPlaying then
     1401              InvalidTreatyMap := not(1 shl p); {$ENDIF}
     1402            // territory should not be considered for the rest of the command
     1403            // execution, because during loading a game it's incorrect before
     1404            // subsequent sIntExpandTerritory is processed
     1405            RW[p].Un[uix].Health := 0; // causes unit to be removed later
     1406          end
     1407          else
     1408            CompleteJob(p, Loc, Job);
     1409          ToWork[Loc, Job] := 0;
     1410          j0 := Job;
     1411          for uix1 := 0 to RW[p].nUn - 1 do
     1412            if (RW[p].Un[uix1].Loc = Loc) and (RW[p].Un[uix1].Job = j0) then
     1413              RW[p].Un[uix1].Job := jNone
     1414        end
     1415        else
     1416        begin
     1417          dec(ToWork[Loc, Job], Movement);
     1418          if not(Job in [jCity, jPillage, jPoll]) then
     1419            inc(Worked[p], Movement);
     1420          Health := Health - HostileDamage(p, mix, Loc, Movement);
     1421          Movement := 0;
     1422        end
     1423    end
     1424end; // work
     1425
     1426function GetJobProgress(p, Loc: integer;
     1427  var JobProgressData: TJobProgressData): integer;
     1428var
     1429  Job, JobResult, uix: integer;
     1430begin
     1431  for Job := 0 to nJob - 1 do
     1432  begin
     1433    JobResult := CalculateJobWork(p, Loc, Job, JobProgressData[Job].Required);
     1434    if JobResult = eOK then
     1435    begin
     1436      if ToWork[Loc, Job] = $FFFF then // not calculated yet
     1437        JobProgressData[Job].Done := 0
     1438      else
     1439        JobProgressData[Job].Done := JobProgressData[Job].Required -
     1440          ToWork[Loc, Job]
     1441    end
    11771442    else
    1178       begin
    1179       dec(ToWork[Loc,Job],Movement);
    1180       if not (Job in [jCity,jPillage,jPoll]) then
    1181         inc(Worked[p],Movement);
    1182       Health:=Health-HostileDamage(p,mix,Loc,Movement);
    1183       Movement:=0;
    1184       end
    1185   end
    1186 end; // work
    1187 
    1188 function GetJobProgress(p,Loc: integer; var JobProgressData: TJobProgressData): integer;
    1189 var
    1190 Job,JobResult,uix: integer;
    1191 begin
    1192 for Job:=0 to nJob-1 do
    1193   begin
    1194   JobResult:=CalculateJobWork(p,Loc,Job,JobProgressData[Job].Required);
    1195   if JobResult=eOk then
    1196     begin
    1197     if ToWork[Loc,Job]=$FFFF then // not calculated yet
    1198       JobProgressData[Job].Done:=0
    1199     else JobProgressData[Job].Done:=JobProgressData[Job].Required-ToWork[Loc,Job]
    1200     end
    1201   else
    1202     begin
    1203     JobProgressData[Job].Required:=0;
    1204     JobProgressData[Job].Done:=0;
    1205     end;
    1206   JobProgressData[Job].NextTurnPlus:=0;
     1443    begin
     1444      JobProgressData[Job].Required := 0;
     1445      JobProgressData[Job].Done := 0;
     1446    end;
     1447    JobProgressData[Job].NextTurnPlus := 0;
    12071448  end;
    1208 for uix:=0 to RW[p].nUn-1 do
    1209   if (RW[p].Un[uix].Loc=Loc) and (RW[p].Un[uix].Movement>=100) then
    1210     inc(JobProgressData[RW[p].Un[uix].Job].NextTurnPlus, RW[p].Un[uix].Movement);
    1211 result:=eOk;
     1449  for uix := 0 to RW[p].nUn - 1 do
     1450    if (RW[p].Un[uix].Loc = Loc) and (RW[p].Un[uix].Movement >= 100) then
     1451      inc(JobProgressData[RW[p].Un[uix].Job].NextTurnPlus,
     1452        RW[p].Un[uix].Movement);
     1453  result := eOK;
    12121454end;
    12131455
    1214 
    12151456{
    1216                              Start/End Game
    1217  ____________________________________________________________________
     1457  Start/End Game
     1458  ____________________________________________________________________
    12181459}
    12191460procedure InitGame;
    12201461begin
    1221 GetMem(ToWork,2*MapSize*nJob);
    1222 FillChar(ToWork^,2*MapSize*nJob,$FF);
     1462  GetMem(ToWork, 2 * MapSize * nJob);
     1463  FillChar(ToWork^, 2 * MapSize * nJob, $FF);
    12231464end;
    12241465
    12251466procedure ReleaseGame;
    12261467begin
    1227 FreeMem(ToWork);
     1468  FreeMem(ToWork);
    12281469end;
    12291470
    12301471end.
    1231 
Note: See TracChangeset for help on using the changeset viewer.