Changeset 6 for trunk/UnitProcessing.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/UnitProcessing.pas

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