Changeset 6 for trunk/GameServer.pas


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

Legend:

Unmodified
Added
Removed
  • trunk/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 
Note: See TracChangeset for help on using the changeset viewer.