hedgewars/uAIMisc.pas
branchhedgeroid
changeset 15515 7030706266df
parent 15394 7718bdf60d45
equal deleted inserted replaced
7861:bc7b6aa5d67a 15515:7030706266df
     1 (*
     1 (*
     2  * Hedgewars, a free turn based strategy game
     2  * Hedgewars, a free turn based strategy game
     3  * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
     3  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
     4  *
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation; version 2 of the License
     7  * the Free Software Foundation; version 2 of the License
     8  *
     8  *
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    12  * GNU General Public License for more details.
    13  *
    13  *
    14  * You should have received a copy of the GNU General Public License
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program; if not, write to the Free Software
    15  * along with this program; if not, write to the Free Software
    16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
    16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    17  *)
    17  *)
    18 
    18 
    19 {$INCLUDE "options.inc"}
    19 {$INCLUDE "options.inc"}
    20 
    20 
    21 unit uAIMisc;
    21 unit uAIMisc;
    26 
    26 
    27       afTrackFall  = $00000001;
    27       afTrackFall  = $00000001;
    28       afErasesLand = $00000002;
    28       afErasesLand = $00000002;
    29       afSetSkip    = $00000004;
    29       afSetSkip    = $00000004;
    30 
    30 
    31 
    31       BadTurn = Low(LongInt) div 4;
    32 type TTarget = record
    32 
       
    33 type TTarget = record // starting to look more and more like a gear
    33     Point: TPoint;
    34     Point: TPoint;
    34     Score: LongInt;
    35     Score, Radius: LongInt;
    35     skip: boolean;
    36     State: LongWord;
       
    37     Density: real;
       
    38     skip, matters, dead: boolean;
       
    39     Kind: TGearType;
    36     end;
    40     end;
    37 TTargets = record
    41 TTargets = record
    38     Count: Longword;
    42     Count: Longword;
    39     ar: array[0..Pred(cMaxHHs)] of TTarget;
    43     ar: array[0..Pred(256)] of TTarget;
       
    44     reset: boolean;
    40     end;
    45     end;
    41 TJumpType = (jmpNone, jmpHJump, jmpLJump);
    46 TJumpType = (jmpNone, jmpHJump, jmpLJump);
    42 TGoInfo = record
    47 TGoInfo = record
    43     Ticks: Longword;
    48     Ticks: Longword;
    44     FallPix: Longword;
    49     FallPix: Longword;
    46     end;
    51     end;
    47 TBonus = record
    52 TBonus = record
    48     X, Y: LongInt;
    53     X, Y: LongInt;
    49     Radius: LongInt;
    54     Radius: LongInt;
    50     Score: LongInt;
    55     Score: LongInt;
    51     end;
    56      end;
       
    57 
       
    58 TBonuses = record
       
    59           activity: boolean;
       
    60           Count : LongInt;
       
    61           ar    : array[0..Pred(MAXBONUS)] of TBonus;
       
    62        end;
       
    63 
       
    64 Twalkbonuses =  record
       
    65         Count: Longword;
       
    66         ar: array[0..Pred(MAXBONUS div 8)] of TBonus;  // don't use too many
       
    67         end;
    52 
    68 
    53 procedure initModule;
    69 procedure initModule;
    54 procedure freeModule;
    70 procedure freeModule;
    55 
    71 
    56 procedure FillTargets;
    72 procedure FillTargets;
       
    73 procedure ResetTargets; inline;
    57 procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
    74 procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
    58 procedure FillBonuses(isAfterAttack: boolean);
    75 procedure FillBonuses(isAfterAttack: boolean);
    59 procedure AwareOfExplosion(x, y, r: LongInt); inline;
    76 procedure AwareOfExplosion(x, y, r: LongInt); inline;
    60 
    77 
    61 function  RatePlace(Gear: PGear): LongInt;
    78 function  RatePlace(Gear: PGear): LongInt;
       
    79 function  CheckWrap(x: real): real; inline;
    62 function  TestColl(x, y, r: LongInt): boolean; inline;
    80 function  TestColl(x, y, r: LongInt): boolean; inline;
    63 function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
    81 function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
    64 function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
    82 function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
    65 function  TraceShoveFall(x, y, dX, dY: Real): LongInt;
       
    66 
    83 
    67 function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
    84 function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
    68 function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
    85 function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
    69 function  RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
    86 function  RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
       
    87 function  RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
    70 function  RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
    88 function  RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
    71 function  RateHammer(Me: PGear): LongInt;
    89 function  RateHammer(Me: PGear): LongInt;
    72 
    90 
    73 function  HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
    91 function  HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
    74 function  AIrndSign(num: LongInt): LongInt;
    92 function  AIrndSign(num: LongInt): LongInt; inline;
       
    93 function  AIrndOffset(targ: TTarget; Level: LongWord): LongInt; inline;
    75 
    94 
    76 var ThinkingHH: PGear;
    95 var ThinkingHH: PGear;
    77     Targets: TTargets;
    96     Targets: TTargets;
    78 
    97 
    79     bonuses: record
    98     bonuses: TBonuses;
    80         Count: Longword;
    99 
    81         ar: array[0..Pred(MAXBONUS)] of TBonus;
   100     walkbonuses: Twalkbonuses;
    82         end;
       
    83 
       
    84     walkbonuses: record
       
    85         Count: Longword;
       
    86         ar: array[0..Pred(MAXBONUS div 8)] of TBonus;  // don't use too many
       
    87         end;
       
    88 
   101 
    89 const KillScore = 200;
   102 const KillScore = 200;
    90 var friendlyfactor: LongInt = 300;
   103 var friendlyfactor: LongInt = 300;
       
   104 var dmgMod: real = 1.0;
    91 
   105 
    92 implementation
   106 implementation
    93 uses uCollisions, uVariables, uUtils, uLandTexture, uGearsUtils;
   107 uses uCollisions, uVariables, uUtils, uGearsUtils;
    94 
   108 
    95 var 
   109 var
    96     KnownExplosion: record
   110     KnownExplosion: record
    97         X, Y, Radius: LongInt
   111         X, Y, Radius: LongInt
    98         end = (X: 0; Y: 0; Radius: 0);
   112         end = (X: 0; Y: 0; Radius: 0);
    99 
   113 
       
   114 procedure ResetTargets; inline;
       
   115 var i: LongWord;
       
   116 begin
       
   117 if Targets.reset then
       
   118     for i:= 0 to Targets.Count do
       
   119         Targets.ar[i].dead:= false;
       
   120 Targets.reset:= false;
       
   121 end;
   100 procedure FillTargets;
   122 procedure FillTargets;
   101 var i, t: Longword;
   123 var //i, t: Longword;
   102     f, e: LongInt;
   124     f, e: LongInt;
       
   125     Gear: PGear;
   103 begin
   126 begin
   104 Targets.Count:= 0;
   127 Targets.Count:= 0;
       
   128 Targets.reset:= false;
   105 f:= 0;
   129 f:= 0;
   106 e:= 0;
   130 e:= 0;
   107 for t:= 0 to Pred(TeamsCount) do
   131 Gear:= GearsList;
   108     with TeamsArray[t]^ do
   132 while Gear <> nil do
   109         if not hasGone then
   133     begin
   110             begin
   134     if  (((Gear^.Kind = gtHedgehog) and
   111             for i:= 0 to cMaxHHIndex do
   135             (Gear <> ThinkingHH) and
   112                 if (Hedgehogs[i].Gear <> nil)
   136             (Gear^.Health > Gear^.Damage) and
   113                 and (Hedgehogs[i].Gear <> ThinkingHH) 
   137             (not Gear^.Hedgehog^.Team^.hasgone)) or
   114                 and (Hedgehogs[i].Gear^.Health > Hedgehogs[i].Gear^.Damage) 
   138         ((Gear^.Kind = gtExplosives) and
   115                     then
   139             (Gear^.Health > Gear^.Damage)) or
   116                     begin
   140         ((Gear^.Kind = gtMine) and
   117                     with Targets.ar[Targets.Count], Hedgehogs[i] do
   141             (Gear^.Health = 0) and
   118                         begin
   142              (Gear^.Damage < 35))
   119                         skip:= false;
   143              )  and
   120                         Point.X:= hwRound(Gear^.X);
   144         (Targets.Count < 256) then
   121                         Point.Y:= hwRound(Gear^.Y);
   145         begin
   122                         if Clan <> CurrentTeam^.Clan then
   146         with Targets.ar[Targets.Count] do
   123                             begin
   147             begin
   124                             Score:= Gear^.Health - Gear^.Damage;
   148             skip:= false;
   125                             inc(e)
   149             dead:= false;
   126                             end else
   150             Kind:= Gear^.Kind;
   127                             begin
   151             Radius:= Gear^.Radius;
   128                             Score:= Gear^.Damage - Gear^.Health;
   152             Density:= hwFloat2Float(Gear^.Density)/3;
   129                             inc(f)
   153             State:= Gear^.State;
   130                             end
   154             matters:= (Gear^.AIHints and aihDoesntMatter) = 0;
   131                         end;
   155 
   132                     inc(Targets.Count)
   156             Point.X:= hwRound(Gear^.X);
       
   157             Point.Y:= hwRound(Gear^.Y);
       
   158             if (Gear^.Kind = gtHedgehog) then
       
   159                 begin
       
   160                 if (Gear^.Hedgehog^.Team^.Clan = CurrentTeam^.Clan) then
       
   161                     begin
       
   162                     Score:= Gear^.Damage - Gear^.Health;
       
   163                     inc(f)
       
   164                     end
       
   165                 else
       
   166                     begin
       
   167                     Score:= Gear^.Health - Gear^.Damage;
       
   168                     inc(e)
   133                     end;
   169                     end;
       
   170                 end
       
   171             else if Gear^.Kind = gtExplosives then
       
   172                 Score:= Gear^.Health - Gear^.Damage
       
   173             else if Gear^.Kind = gtMine then
       
   174                 Score:= max(0,35-Gear^.Damage);
   134             end;
   175             end;
       
   176         inc(Targets.Count)
       
   177         end;
       
   178     Gear:= Gear^.NextGear
       
   179     end;
   135 
   180 
   136 if e > f then friendlyfactor:= 300 + (e - f) * 30
   181 if e > f then friendlyfactor:= 300 + (e - f) * 30
   137 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e))
   182 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e))
   138 end;
   183 end;
   139 
   184 
   165 var Gear: PGear;
   210 var Gear: PGear;
   166     MyClan: PClan;
   211     MyClan: PClan;
   167     i: Longint;
   212     i: Longint;
   168 begin
   213 begin
   169 bonuses.Count:= 0;
   214 bonuses.Count:= 0;
       
   215 bonuses.activity:= false;
   170 MyClan:= ThinkingHH^.Hedgehog^.Team^.Clan;
   216 MyClan:= ThinkingHH^.Hedgehog^.Team^.Clan;
   171 Gear:= GearsList;
   217 Gear:= GearsList;
   172 while Gear <> nil do
   218 while Gear <> nil do
   173     begin
   219     begin
   174         case Gear^.Kind of
   220         case Gear^.Kind of
       
   221             gtGrenade
       
   222             , gtClusterBomb
       
   223             , gtGasBomb
       
   224             , gtShell
       
   225             , gtAirAttack
       
   226             , gtMortar
       
   227             , gtWatermelon
       
   228             , gtDrill
       
   229             , gtAirBomb
       
   230             , gtCluster
       
   231             , gtMelonPiece
       
   232             , gtBee
       
   233             , gtMolotov: bonuses.activity:= true;
   175             gtCase:
   234             gtCase:
   176                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y) + 3, 37, 25);
   235                 if (Gear^.AIHints and aihDoesntMatter) = 0 then
       
   236                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y) + 3, 37, 25);
   177             gtFlame:
   237             gtFlame:
   178                 if (Gear^.State and gsttmpFlag) <> 0 then
   238                 if (Gear^.State and gsttmpFlag) <> 0 then
   179                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 20, -50);
   239                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 20, -50);
   180 // avoid mines unless they are very likely to be duds, or are duds. also avoid if they are about to blow 
   240 // avoid mines unless they are very likely to be duds, or are duds. also avoid if they are about to blow
   181             gtMine:
   241             gtMine: begin
   182                 if ((Gear^.State and gstAttacking) = 0) and (((cMineDudPercent < 90) and (Gear^.Health <> 0))
   242                 if (Gear^.State and gstMoving) <> 0 then bonuses.activity:= true;
       
   243 
       
   244                 if ((Gear^.State and gstAttacking) = 0) and (((cMineDudPercent < 90) or ((Gear^.State and gstWait) <> 0) and (Gear^.Health <> 0))
   183                 or (isAfterAttack and (Gear^.Health = 0) and (Gear^.Damage > 30))) then
   245                 or (isAfterAttack and (Gear^.Health = 0) and (Gear^.Damage > 30))) then
   184                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -50)
   246                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -50)
   185                 else if (Gear^.State and gstAttacking) <> 0 then
   247                 else if (Gear^.State and gstAttacking) <> 0 then
   186                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 100, -50); // mine is on
   248                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 100, -50); // mine is on
   187                     
   249                 end;
       
   250             gtAirMine: if ((Gear^.State and gstFrozen) = 0) then AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), gear^.Angle+5, -30);
       
   251 
   188             gtExplosives:
   252             gtExplosives:
   189             if isAfterAttack then
   253                 begin
   190                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 75, -60 + Gear^.Health);
   254                 //if (Gear^.State and gstMoving) <> 0 then bonuses.activity:= true;
   191                 
   255 
   192             gtSMine:
   256                 if isAfterAttack then
       
   257                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 75, -60 + Gear^.Health);
       
   258                 end;
       
   259 
       
   260             gtSMine: begin
       
   261                 if (Gear^.State and (gstMoving or gstAttacking)) <> 0 then bonuses.activity:= true;
       
   262 
   193                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -30);
   263                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -30);
   194                 
   264                 end;
       
   265 
   195             gtDynamite:
   266             gtDynamite:
       
   267                 begin
       
   268                 bonuses.activity:= true;
   196                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 150, -75);
   269                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 150, -75);
   197                 
   270                 end;
       
   271 
   198             gtHedgehog:
   272             gtHedgehog:
   199                 begin
   273                 begin
       
   274                 if (ThinkingHH <> Gear)
       
   275                     and (((Gear^.State and (gstMoving or gstDrowning or gstHHDeath)) <> 0)
       
   276                         or (Gear^.Health = 0)
       
   277                         or (Gear^.Damage >= Gear^.Health))
       
   278                     then begin
       
   279                     bonuses.activity:= true;
       
   280                     end;
       
   281 
   200                 if Gear^.Damage >= Gear^.Health then
   282                 if Gear^.Damage >= Gear^.Health then
   201                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 60, -25)
   283                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 60, -25)
   202                 else
   284                 else
   203                     if isAfterAttack
   285                     if isAfterAttack
   204                       and (ThinkingHH^.Hedgehog <> Gear^.Hedgehog)
   286                       and (ThinkingHH^.Hedgehog <> Gear^.Hedgehog)
   209                             AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 100, 3)
   291                             AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 100, 3)
   210                 end;
   292                 end;
   211             end;
   293             end;
   212     Gear:= Gear^.NextGear
   294     Gear:= Gear^.NextGear
   213     end;
   295     end;
       
   296 
   214 if isAfterAttack and (KnownExplosion.Radius > 0) then
   297 if isAfterAttack and (KnownExplosion.Radius > 0) then
   215     with KnownExplosion do
   298     with KnownExplosion do
   216         AddBonus(X, Y, Radius + 10, -Radius);
   299         AddBonus(X, Y, Radius + 10, -Radius);
   217 if isAfterAttack then
   300 if isAfterAttack then
   218     begin
   301     begin
   250                 inc(rate, Score * (Radius - r))
   333                 inc(rate, Score * (Radius - r))
   251         end;
   334         end;
   252     RatePlace:= rate;
   335     RatePlace:= rate;
   253 end;
   336 end;
   254 
   337 
       
   338 function CheckWrap(x: real): real; inline;
       
   339 begin
       
   340     if WorldEdge = weWrap then
       
   341         if (x < leftX) then
       
   342              x:= x + (rightX - leftX)
       
   343         else if x > rightX then    
       
   344              x:= x - (rightX - leftX);
       
   345     CheckWrap:= x;
       
   346 end;
       
   347 
       
   348 function CheckBounds(x, y, r: Longint): boolean; inline;
       
   349 begin
       
   350     CheckBounds := (((x-r) and LAND_WIDTH_MASK) = 0) and
       
   351         (((x+r) and LAND_WIDTH_MASK) = 0) and
       
   352         (((y-r) and LAND_HEIGHT_MASK) = 0) and
       
   353         (((y+r) and LAND_HEIGHT_MASK) = 0);
       
   354 end;
       
   355 
       
   356 
       
   357 function TestCollWithEverything(x, y, r: LongInt): boolean; inline;
       
   358 begin
       
   359     if not CheckBounds(x, y, r) then
       
   360         exit(false);
       
   361 
       
   362     if (Land[y-r, x-r] <> 0) or
       
   363        (Land[y+r, x-r] <> 0) or
       
   364        (Land[y-r, x+r] <> 0) or
       
   365        (Land[y+r, x+r] <> 0) then
       
   366        exit(true);
       
   367 
       
   368     TestCollWithEverything := false;
       
   369 end;
       
   370 
       
   371 function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
       
   372 begin
       
   373     if not CheckBounds(x, y, r) then
       
   374         exit(false);
       
   375 
       
   376     if (Land[y-r, x-r] > lfAllObjMask) or
       
   377        (Land[y+r, x-r] > lfAllObjMask) or
       
   378        (Land[y-r, x-r] > lfAllObjMask) or
       
   379        (Land[y+r, x+r] > lfAllObjMask) then
       
   380        exit(true);
       
   381 
       
   382     TestCollExcludingObjects:= false;
       
   383 end;
       
   384 
       
   385 function TestColl(x, y, r: LongInt): boolean; inline;
       
   386 begin
       
   387     if not CheckBounds(x, y, r) then
       
   388         exit(false);
       
   389 
       
   390     if (Land[y-r, x-r] and lfNotCurHogCrate <> 0) or
       
   391        (Land[y+r, x-r] and lfNotCurHogCrate <> 0) or
       
   392        (Land[y+r, x-r] and lfNotCurHogCrate <> 0) or
       
   393        (Land[y+r, x+r] and lfNotCurHogCrate <> 0) then
       
   394        exit(true);
       
   395 
       
   396     TestColl:= false;
       
   397 end;
       
   398 
       
   399 
   255 // Wrapper to test various approaches.  If it works reasonably, will just replace.
   400 // Wrapper to test various approaches.  If it works reasonably, will just replace.
   256 // Right now, converting to hwFloat is a tad inefficient since the x/y were hwFloat to begin with...
   401 // Right now, converting to hwFloat is a tad inefficient since the x/y were hwFloat to begin with...
   257 function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
   402 function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
   258 var MeX, MeY: LongInt;
   403 var MeX, MeY: LongInt;
   259 begin
   404 begin
   260     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
   405     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
   261     begin
   406     begin
   262         MeX:= hwRound(Me^.X);
   407         MeX:= hwRound(Me^.X);
   263         MeY:= hwRound(Me^.Y);
   408         MeY:= hwRound(Me^.Y);
   264         // We are still inside the hog. Skip radius test
   409         // We are still inside the hog. Skip radius test
   265         if ((((x-MeX)*(x-MeX)) + ((y-MeY)*(y-MeY))) < 256) and ((Land[y, x] and $FF00) = 0) then
   410         if ((sqr(x-MeX) + sqr(y-MeY)) < 256) and (Land[y, x] and lfObjMask = 0) then
   266             exit(false);
   411             exit(false);
   267     end;
   412     end;
   268     TestCollExcludingMe:= TestColl(x, y, r)
   413     TestCollExcludingMe:= TestCollWithEverything(x, y, r)
   269 end;
   414 end;
   270 
   415 
   271 function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
   416 
   272 var b: boolean;
   417 
   273 begin
   418 function TraceFall(eX, eY: LongInt; var x, y: Real; dX, dY: Real; r: LongWord; Target: TTarget): LongInt;
   274     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] and $FF00 <> 0);
       
   275     if b then
       
   276         exit(true);
       
   277     
       
   278     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] and $FF00 <> 0);
       
   279     if b then
       
   280         exit(true);
       
   281     
       
   282     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] and $FF00 <> 0);
       
   283     if b then
       
   284         exit(true);
       
   285     
       
   286     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] and $FF00 <> 0);
       
   287     if b then
       
   288         exit(true);
       
   289     
       
   290     TestCollExcludingObjects:= false;
       
   291 end;
       
   292 
       
   293 function TestColl(x, y, r: LongInt): boolean; inline;
       
   294 var b: boolean;
       
   295 begin
       
   296     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] and $FF7F <> 0);
       
   297     if b then
       
   298         exit(true);
       
   299     
       
   300     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] and $FF7F <> 0);
       
   301     if b then
       
   302         exit(true);
       
   303     
       
   304     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] and $FF7F <> 0);
       
   305     if b then
       
   306         exit(true);
       
   307     
       
   308     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] and $FF7F <> 0);
       
   309     if b then
       
   310         exit(true);
       
   311     
       
   312     TestColl:= false;
       
   313 end;
       
   314 
       
   315 function TestCollWithLand(x, y, r: LongInt): boolean; inline;
       
   316 var b: boolean;
       
   317 begin
       
   318     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > 255);
       
   319     if b then
       
   320         exit(true);
       
   321         
       
   322     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] > 255);
       
   323     if b then
       
   324         exit(true);
       
   325         
       
   326     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > 255);
       
   327     if b then
       
   328         exit(true);
       
   329         
       
   330     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > 255);
       
   331     if b then
       
   332         exit(true);
       
   333 
       
   334     TestCollWithLand:= false;
       
   335 end;
       
   336 
       
   337 function TraceFall(eX, eY: LongInt; x, y, dX, dY: Real; r: LongWord): LongInt;
       
   338 var skipLandCheck: boolean;
   419 var skipLandCheck: boolean;
   339     rCorner: real;
   420     rCorner, dxdy, odX, odY: real;
   340     dmg: LongInt;
   421     dmg: LongInt;
   341 begin
   422 begin
       
   423     odX:= dX;
       
   424     odY:= dY;
   342     skipLandCheck:= true;
   425     skipLandCheck:= true;
   343     if x - eX < 0 then dX:= -dX;
       
   344     if y - eY < 0 then dY:= -dY;
       
   345     // ok. attempt approximate search for an unbroken trajectory into water.  if it continues far enough, assume out of map
   426     // ok. attempt approximate search for an unbroken trajectory into water.  if it continues far enough, assume out of map
   346     rCorner:= r * 0.75;
   427     rCorner:= r * 0.75;
   347     while true do
   428     while true do
   348     begin
   429         begin
       
   430         x:= CheckWrap(x);
   349         x:= x + dX;
   431         x:= x + dX;
   350         y:= y + dY;
   432         y:= y + dY;
   351         dY:= dY + cGravityf;
   433         dY:= dY + cGravityf;
   352         skipLandCheck:= skipLandCheck and (r <> 0) and (abs(eX-x) + abs(eY-y) < r) and ((abs(eX-x) < rCorner) or (abs(eY-y) < rCorner));
   434         skipLandCheck:= skipLandCheck and (r <> 0) and (abs(eX-x) + abs(eY-y) < r) and ((abs(eX-x) < rCorner) or (abs(eY-y) < rCorner));
   353         if not skipLandCheck and TestCollWithLand(trunc(x), trunc(y), cHHRadius) then
   435         if not skipLandCheck and TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then
   354         begin
   436             with Target do
   355             if 0.4 < dY then
   437                 begin
   356             begin
   438                 if (Kind = gtHedgehog) and (0.4 < dY) then
   357                 dmg := 1 + trunc((abs(dY) - 0.4) * 70);
   439                     begin
   358                 if dmg >= 1 then
   440                     dmg := 1 + trunc((dY - 0.4) * 70);
   359                     exit(dmg);
   441                     exit(dmg)
       
   442                     end
       
   443                 else
       
   444                     begin
       
   445                     dxdy:= abs(dX)+abs(dY);
       
   446                     if ((Kind = gtMine) and (dxdy > 0.35)) or
       
   447                        ((Kind = gtExplosives) and
       
   448                             (((State and gstTmpFlag <> 0) and (dxdy > 0.35)) or
       
   449                              ((State and gstTmpFlag = 0) and
       
   450                                 ((abs(odX) > 0.15) or ((abs(odY) > 0.15) and
       
   451                                 (abs(odX) > 0.02))) and (dxdy > 0.35)))) then
       
   452                         begin
       
   453                         dmg := trunc(dxdy * 25);
       
   454                         exit(dmg)
       
   455                         end
       
   456                     else if (Kind = gtExplosives) and (not(abs(odX) > 0.15) or ((abs(odY) > 0.15) and (abs(odX) > 0.02))) and (dY > 0.2) then
       
   457                         begin
       
   458                         dmg := trunc(dy * 70);
       
   459                         exit(dmg)
       
   460                         end
       
   461                     end;
       
   462             exit(0)
   360             end;
   463             end;
   361             exit(0)
   464         if CheckCoordInWater(round(x), round(y)) then exit(-1)
   362         end;
   465         end
   363         if (y > cWaterLine) or (x > 4096) or (x < 0) then
   466 end;
   364             exit(-1);
   467 
   365     end;
   468 function TraceShoveFall(var x, y: Real; dX, dY: Real; Target: TTarget): LongInt;
   366 end;
       
   367 
       
   368 function TraceShoveFall(x, y, dX, dY: Real): LongInt;
       
   369 var dmg: LongInt;
   469 var dmg: LongInt;
   370 begin
   470     dxdy, odX, odY: real;
       
   471 begin
       
   472     odX:= dX;
       
   473     odY:= dY;
   371 //v:= random($FFFFFFFF);
   474 //v:= random($FFFFFFFF);
   372     while true do
   475     while true do
   373     begin
   476         begin
       
   477         x:= CheckWrap(x);
   374         x:= x + dX;
   478         x:= x + dX;
   375         y:= y + dY;
   479         y:= y + dY;
   376         dY:= dY + cGravityf;
   480         dY:= dY + cGravityf;
   377 
   481 
   378 {        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then 
   482 {        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then
   379             begin
   483             begin
   380             LandPixels[trunc(y), trunc(x)]:= v;
   484             LandPixels[trunc(y), trunc(x)]:= v;
   381             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
   485             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
   382             end;}
   486             end;}
   383 
   487 
   384 
   488         if TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then
   385         // consider adding dX/dY calc here for fall damage
   489             with Target do
   386         if TestCollExcludingObjects(trunc(x), trunc(y), cHHRadius) then
   490                 begin
   387         begin
   491                 if (Kind = gtHedgehog) and (0.4 < dY) then
   388             if 0.4 < dY then
   492                     begin
   389             begin
   493                     dmg := trunc((dY - 0.4) * 70);
   390                 dmg := 1 + trunc((abs(dY) - 0.4) * 70);
       
   391                 if dmg >= 1 then
       
   392                     exit(dmg);
   494                     exit(dmg);
   393             end;
   495                     end
       
   496                 else
       
   497                     begin
       
   498                     dxdy:= abs(dX)+abs(dY);
       
   499                     if ((Kind = gtMine) and (dxdy > 0.4)) or
       
   500                        ((Kind = gtExplosives) and
       
   501                             (((State and gstTmpFlag <> 0) and (dxdy > 0.4)) or
       
   502                              ((State and gstTmpFlag = 0) and
       
   503                                 ((abs(odX) > 0.15) or ((abs(odY) > 0.15) and
       
   504                                 (abs(odX) > 0.02))) and (dxdy > 0.35)))) then
       
   505                         begin
       
   506                         dmg := trunc(dxdy * 50);
       
   507                         exit(dmg)
       
   508                         end
       
   509                     else if (Kind = gtExplosives) and (not(abs(odX) > 0.15) or ((abs(odY) > 0.15) and (abs(odX) > 0.02))) and (dY > 0.2) then
       
   510                         begin
       
   511                         dmg := trunc(dy * 70);
       
   512                         exit(dmg)
       
   513                         end
       
   514                     end;
   394             exit(0)
   515             exit(0)
   395         end;
   516         end;
   396         if (y > cWaterLine) or (x > 4096) or (x < 0) then
   517         if CheckCoordInWater(round(x), round(y)) then
   397             // returning -1 for drowning so it can be considered in the Rate routine
   518             // returning -1 for drowning so it can be considered in the Rate routine
   398             exit(-1)
   519             exit(-1)
   399     end;
   520     end;
   400 end;
   521 end;
   401 
   522 
   402 function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt;
   523 function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
   403 begin
   524 begin
   404     RateExplosion:= RateExplosion(Me, x, y, r, 0);
   525     RateExplosion:= RealRateExplosion(Me, x, y, r, 0);
   405 end;
   526     ResetTargets;
   406 
   527 end;
   407 function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
   528 function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
   408 var i, fallDmg, dmg, dmgBase, rate, erasure: LongInt;
   529 begin
   409     dX, dY, dmgMod: real;
   530     RateExplosion:= RealRateExplosion(Me, x, y, r, Flags);
   410 begin
   531     ResetTargets;
       
   532 end;
       
   533 
       
   534 function RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
       
   535 var i, fallDmg, dmg, dmgBase, rate, subrate, erasure: LongInt;
       
   536     pX, pY, dX, dY: real;
       
   537     hadSkips: boolean;
       
   538 begin
       
   539 x:= round(CheckWrap(real(x)));
   411 fallDmg:= 0;
   540 fallDmg:= 0;
   412 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
       
   413 rate:= 0;
   541 rate:= 0;
   414 // add our virtual position
   542 // add our virtual position
   415 with Targets.ar[Targets.Count] do
   543 with Targets.ar[Targets.Count] do
   416     begin
   544     begin
   417     Point.x:= hwRound(Me^.X);
   545     Point.x:= hwRound(Me^.X);
   418     Point.y:= hwRound(Me^.Y);
   546     Point.y:= hwRound(Me^.Y);
       
   547     skip:= false;
       
   548     matters:= true;
       
   549     Kind:= gtHedgehog;
       
   550     Density:= 1;
       
   551     Radius:= cHHRadius;
   419     Score:= - ThinkingHH^.Health
   552     Score:= - ThinkingHH^.Health
   420     end;
   553     end;
   421 // rate explosion
   554 // rate explosion
   422 dmgBase:= r + cHHRadius div 2;
   555 
   423 if (Flags and afErasesLand <> 0) and (GameFlags and gfSolidLand = 0) then erasure:= r
   556 if (Flags and afErasesLand <> 0) and (GameFlags and gfSolidLand = 0) then erasure:= r
   424 else erasure:= 0;
   557 else erasure:= 0;
       
   558 
       
   559 hadSkips:= false;
       
   560 
   425 for i:= 0 to Targets.Count do
   561 for i:= 0 to Targets.Count do
   426     with Targets.ar[i] do
   562     if not Targets.ar[i].dead then
   427         begin
   563         with Targets.ar[i] do
   428         dmg:= 0;
   564           if not matters then hadSkips:= true
   429         if abs(Point.x - x) + abs(Point.y - y) < dmgBase then
   565             else
   430             dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r));
   566             begin
   431 
   567             dmg:= 0;
   432         if dmg > 0 then
   568             dmgBase:= r + Radius div 2;
   433             begin
   569             if abs(Point.x - x) + abs(Point.y - y) < dmgBase then
   434             if (Flags and afTrackFall <> 0) and (dmg < abs(Score)) then
   570                 dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r));
       
   571 
       
   572             if dmg > 0 then
   435                 begin
   573                 begin
   436                 dX:= 0.005 * dmg + 0.01;
   574                 pX:= Point.x;
   437                 dY:= dX;
   575                 pY:= Point.y;
   438                 if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and 
   576                 fallDmg:= 0;
   439                    (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
   577                 dX:= 0;
   440                      fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod)
   578                 if (Flags and afTrackFall <> 0) and (Score > 0) and (dmg < Score) then
   441                 else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod)
   579                     begin
   442                 end;
   580                     dX:= (0.005 * dmg + 0.01) / Density;
   443             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   581                     dY:= dX;
   444                 if Score > 0 then
   582                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and
   445                     inc(rate, (KillScore + Score div 10) * 1024)   // Add a bit of a bonus for bigger hog drownings
   583                        (((abs(dY) >= 0.15) and (abs(dX) < 0.02)) or
   446                 else
   584                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
   447                     dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
   585                         dX:= 0;
   448             else if (dmg+fallDmg) >= abs(Score) then
   586 
   449                 if Score > 0 then
   587                     if pX - x < 0 then dX:= -dX;
   450                     inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
   588                     if pY - y < 0 then dY:= -dY;
   451                 else
   589 
   452                     dec(rate, KillScore * friendlyfactor div 100 * 1024)
   590                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
   453             else
   591                        (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
   454                 if Score > 0 then
   592                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
   455                     inc(rate, (dmg + fallDmg) * 1024)
   593                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
   456                 else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
   594                     end;
       
   595                 if Kind = gtHedgehog then
       
   596                     begin
       
   597                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   598                         begin
       
   599                         if Score > 0 then
       
   600                             inc(rate, (KillScore + Score div 10) * 1024)   // Add a bit of a bonus for bigger hog drownings
       
   601                         else
       
   602                             dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
       
   603                         end
       
   604                     else if (dmg+fallDmg) >= abs(Score) then
       
   605                         begin
       
   606                         dead:= true;
       
   607                         Targets.reset:= true;
       
   608                         if dX < 0.035 then
       
   609                             begin
       
   610                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or (Flags and afTrackFall));
       
   611                             if abs(subrate) > 2000 then inc(Rate,subrate)
       
   612                             end;
       
   613                         if Score > 0 then
       
   614                              inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
       
   615                         else dec(rate, KillScore * friendlyfactor div 100 * 1024)
       
   616                         end
       
   617                     else
       
   618                         begin
       
   619                         if Score > 0 then
       
   620                              inc(rate, (dmg + fallDmg) * 1024)
       
   621                         else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
       
   622                         end
       
   623                     end
       
   624                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   625                     begin
       
   626                     dead:= true;
       
   627                     Targets.reset:= true;
       
   628                     if Kind = gtExplosives then
       
   629                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall))
       
   630                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall));
       
   631                     if abs(subrate) > 2000 then inc(Rate,subrate);
       
   632                     end
       
   633                 end
   457             end;
   634             end;
   458         end;
   635 
   459 RateExplosion:= rate;
   636 if hadSkips and (rate <= 0) then
   460 end;
   637     RealRateExplosion:= BadTurn
   461 
   638 else
   462 function RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
   639     RealRateExplosion:= rate;
   463 var i, fallDmg, dmg, rate: LongInt;
   640 end;
   464     dX, dY, dmgMod: real;
   641 
       
   642 function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
       
   643 var i, fallDmg, dmg, rate, subrate: LongInt;
       
   644     dX, dY, pX, pY: real;
       
   645     hadSkips: boolean;
   465 begin
   646 begin
   466 fallDmg:= 0;
   647 fallDmg:= 0;
   467 dX:= gdX * 0.01 * kick;
   648 dX:= gdX * 0.01 * kick;
   468 dY:= gdY * 0.01 * kick;
   649 dY:= gdY * 0.01 * kick;
   469 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
       
   470 rate:= 0;
   650 rate:= 0;
       
   651 hadSkips:= false;
   471 for i:= 0 to Pred(Targets.Count) do
   652 for i:= 0 to Pred(Targets.Count) do
   472     with Targets.ar[i] do
   653     with Targets.ar[i] do
   473       if skip then 
   654         if skip then
   474         if (Flags and afSetSkip = 0) then skip:= false else {still skip}
   655             begin
   475       else  
   656             if Flags and afSetSkip = 0 then skip:= false
   476         begin
   657             end
   477         dmg:= 0;
   658         else if matters then
   478         if abs(Point.x - x) + abs(Point.y - y) < r then
   659             begin
   479             dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   660             dmg:= 0;
   480 
   661             if abs(Point.x - x) + abs(Point.y - y) < r then
   481         if dmg > 0 then
   662                 dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   482             begin
   663 
   483             if (Flags and afSetSkip <> 0) then skip:= true;
   664             if dmg > 0 then
   484             if (Flags and afTrackFall <> 0) and (Score > 0) then 
   665                 begin
   485                 fallDmg:= trunc(TraceShoveFall(Point.x, Point.y - 2, dX, dY) * dmgMod);
   666                 pX:= Point.x;
   486             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   667                 pY:= Point.y-2;
   487                 if Score > 0 then
   668                 fallDmg:= 0;
   488                     inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
   669                 if (Flags and afSetSkip <> 0) then skip:= true;
   489                 else
   670                 if (not dead) and (Flags and afTrackFall <> 0) and (Score > 0) and (power < Score) then
   490                     dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
   671                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and
   491             else if power+fallDmg >= abs(Score) then
   672                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
   492                 if Score > 0 then
   673                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
   493                     inc(rate, KillScore)
   674                         fallDmg:= trunc(TraceShoveFall(pX, pY, 0, dY, Targets.ar[i]) * dmgMod)
   494                 else
   675                     else
   495                     dec(rate, KillScore * friendlyfactor div 100)
   676                         fallDmg:= trunc(TraceShoveFall(pX, pY, dX, dY, Targets.ar[i]) * dmgMod);
   496             else
   677                 if Kind = gtHedgehog then
   497                 if Score > 0 then
   678                     begin
   498                     inc(rate, power+fallDmg)
   679                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   499                 else
   680                         begin
   500                     dec(rate, (power+fallDmg) * friendlyfactor div 100)
   681                         if Score > 0 then
   501             end;
   682                             inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
   502         end;
   683                         else
   503 RateShove:= rate * 1024
   684                             dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   685                         end
       
   686                     else if power+fallDmg >= abs(Score) then
       
   687                         begin
       
   688                         dead:= true;
       
   689                         Targets.reset:= true;
       
   690                         if dX < 0.035 then
       
   691                             begin
       
   692                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall);
       
   693                             if abs(subrate) > 2000 then inc(Rate,subrate div 1024)
       
   694                             end;
       
   695                         if Score > 0 then
       
   696                             inc(rate, KillScore)
       
   697                         else
       
   698                             dec(rate, KillScore * friendlyfactor div 100)
       
   699                         end
       
   700                     else
       
   701                         begin
       
   702                         if Score > 0 then
       
   703                             inc(rate, power+fallDmg)
       
   704                         else
       
   705                             dec(rate, (power+fallDmg) * friendlyfactor div 100)
       
   706                         end
       
   707                     end
       
   708                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   709                     begin
       
   710                     dead:= true;
       
   711                     Targets.reset:= true;
       
   712                     if Kind = gtExplosives then
       
   713                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall))
       
   714                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall));
       
   715                     if abs(subrate) > 2000 then inc(Rate,subrate div 1024);
       
   716                     end
       
   717                 end
       
   718             end
       
   719         else
       
   720             hadSkips:= true;
       
   721 
       
   722 if hadSkips and (rate <= 0) then
       
   723     RateShove:= BadTurn
       
   724 else
       
   725     RateShove:= rate * 1024;
       
   726 ResetTargets
   504 end;
   727 end;
   505 
   728 
   506 function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
   729 function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
   507 var i, dmg, fallDmg, baseDmg, rate, erasure: LongInt;
   730 var i, dmg, fallDmg, baseDmg, rate, subrate, erasure: LongInt;
   508     dX, dY, dmgMod: real;
   731     pX, pY, dX, dY: real;
   509 begin
   732     hadSkips: boolean;
   510 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
   733 begin
   511 rate:= 0;
   734 rate:= 0;
   512 gdX:= gdX * 0.01;
   735 gdX:= gdX * 0.01;
   513 gdY:= gdX * 0.01;
   736 gdY:= gdX * 0.01;
   514 // add our virtual position
   737 // add our virtual position
   515 with Targets.ar[Targets.Count] do
   738 with Targets.ar[Targets.Count] do
   516     begin
   739     begin
   517     Point.x:= hwRound(Me^.X);
   740     Point.x:= hwRound(Me^.X);
   518     Point.y:= hwRound(Me^.Y);
   741     Point.y:= hwRound(Me^.Y);
       
   742     skip:= false;
       
   743     matters:= true;
       
   744     Kind:= gtHedgehog;
       
   745     Density:= 1;
       
   746     Radius:= cHHRadius;
   519     Score:= - ThinkingHH^.Health
   747     Score:= - ThinkingHH^.Health
   520     end;
   748     end;
   521 // rate shot
   749 // rate shot
   522 baseDmg:= cHHRadius + cShotgunRadius + 4;
   750 baseDmg:= cHHRadius + cShotgunRadius + 4;
       
   751 
   523 if GameFlags and gfSolidLand = 0 then erasure:= cShotgunRadius
   752 if GameFlags and gfSolidLand = 0 then erasure:= cShotgunRadius
   524 else erasure:= 0;
   753 else erasure:= 0;
       
   754 
       
   755 hadSkips:= false;
       
   756 
   525 for i:= 0 to Targets.Count do
   757 for i:= 0 to Targets.Count do
   526     with Targets.ar[i] do
   758     if not Targets.ar[i].dead then
   527         begin
   759         with Targets.ar[i] do
   528         dmg:= 0;
   760           if not matters then hadSkips:= true
   529         if abs(Point.x - x) + abs(Point.y - y) < baseDmg then
   761             else
   530             begin
   762             begin
   531             dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25);
   763             dmg:= 0;
   532             dmg:= trunc(dmg * dmgMod);
   764             if abs(Point.x - x) + abs(Point.y - y) < baseDmg then
       
   765                 begin
       
   766                 dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25);
       
   767                 dmg:= trunc(dmg * dmgMod);
       
   768                 end;
       
   769             if dmg > 0 then
       
   770                 begin
       
   771                 fallDmg:= 0;
       
   772                 pX:= Point.x;
       
   773                 pY:= Point.y;
       
   774                 if (not dead) and (Score > 0) and (dmg < Score) then
       
   775                     begin
       
   776                     dX:= gdX * dmg / Density;
       
   777                     dY:= gdY * dmg / Density;
       
   778                     if dX < 0 then dX:= dX - 0.01
       
   779                     else dX:= dX + 0.01;
       
   780                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and
       
   781                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
       
   782                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
       
   783                        dX:= 0;
       
   784                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
       
   785                        (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
       
   786                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
       
   787                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
       
   788                     end;
       
   789                 if Kind = gtHedgehog then
       
   790                     begin
       
   791                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   792                         begin
       
   793                         if Score > 0 then
       
   794                             inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
       
   795                         else
       
   796                             dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   797                         end
       
   798                     else if (dmg+fallDmg) >= abs(Score) then
       
   799                         begin
       
   800                         dead:= true;
       
   801                         Targets.reset:= true;
       
   802                         if abs(gdX) < 0.035 then
       
   803                             begin
       
   804                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall);
       
   805                             if abs(subrate) > 2000 then inc(Rate,subrate div 1024)
       
   806                             end;
       
   807                         if Score > 0 then
       
   808                             inc(rate, KillScore)
       
   809                         else
       
   810                             dec(rate, KillScore * friendlyfactor div 100)
       
   811                         end
       
   812                     else if Score > 0 then
       
   813                          inc(rate, dmg+fallDmg)
       
   814                     else dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
       
   815                     end
       
   816                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   817                     begin
       
   818                     dead:= true;
       
   819                     Targets.reset:= true;
       
   820                     if Kind = gtExplosives then
       
   821                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or afTrackFall)
       
   822                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or afTrackFall);
       
   823                     if abs(subrate) > 2000 then inc(Rate,subrate div 1024);
       
   824                     end
       
   825                 end
   533             end;
   826             end;
   534         if dmg > 0 then
   827 
   535             begin
   828 if hadSkips and (rate <= 0) then
   536             dX:= gdX * dmg;
   829     RateShotgun:= BadTurn
   537             dY:= gdY * dmg;
   830 else
   538             if dX < 0 then dX:= dX - 0.01
   831     RateShotgun:= rate * 1024;
   539             else dX:= dX + 0.01;
   832 ResetTargets;
   540             if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and 
       
   541                (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
       
   542                  fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod)
       
   543             else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod);
       
   544             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   545                 if Score > 0 then
       
   546                     inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
       
   547                 else
       
   548                     dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   549             else if (dmg+fallDmg) >= abs(Score) then
       
   550                 if Score > 0 then
       
   551                     inc(rate, KillScore)
       
   552                 else
       
   553                     dec(rate, KillScore * friendlyfactor div 100)
       
   554             else
       
   555                 if Score > 0 then
       
   556                     inc(rate, dmg+fallDmg)
       
   557             else
       
   558                 dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
       
   559             end;
       
   560         end;        
       
   561 RateShotgun:= rate * 1024;
       
   562 end;
   833 end;
   563 
   834 
   564 function RateHammer(Me: PGear): LongInt;
   835 function RateHammer(Me: PGear): LongInt;
   565 var x, y, i, r, rate: LongInt;
   836 var x, y, i, r, rate: LongInt;
       
   837     hadSkips: boolean;
   566 begin
   838 begin
   567 // hammer hit shift against attecker hog is 10
   839 // hammer hit shift against attecker hog is 10
   568 x:= hwRound(Me^.X) + hwSign(Me^.dX) * 10;
   840 x:= hwRound(Me^.X) + hwSign(Me^.dX) * 10;
   569 y:= hwRound(Me^.Y);
   841 y:= hwRound(Me^.Y);
   570 rate:= 0;
   842 rate:= 0;
   571 
   843 hadSkips:= false;
   572 for i:= 0 to Pred(Targets.Count) do
   844 for i:= 0 to Pred(Targets.Count) do
   573     with Targets.ar[i] do
   845     with Targets.ar[i] do
   574          // hammer hit radius is 8, shift is 10
   846          // hammer hit radius is 8, shift is 10
   575         if abs(Point.x - x) + abs(Point.y - y) < 18 then
   847       if (not matters) then
       
   848           hadSkips:= true
       
   849       else if matters and (Kind = gtHedgehog) and (abs(Point.x - x) + abs(Point.y - y) < 18) then
   576             begin
   850             begin
   577             r:= trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   851             r:= trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   578 
   852 
   579             if r <= 18 then
   853             if r <= 18 then
   580                 if Score > 0 then 
   854                 if Score > 0 then
   581                     inc(rate, Score div 3)
   855                     inc(rate, Score div 3)
   582                 else
   856                 else
   583                     inc(rate, Score div 3 * friendlyfactor div 100)
   857                     inc(rate, Score div 3 * friendlyfactor div 100)
   584             end;
   858             end;
   585 RateHammer:= rate * 1024;
   859 
       
   860 if hadSkips and (rate <= 0) then
       
   861     RateHammer:= BadTurn
       
   862 else
       
   863     RateHammer:= rate * 1024;
   586 end;
   864 end;
   587 
   865 
   588 function HHJump(Gear: PGear; JumpType: TJumpType; var GoInfo: TGoInfo): boolean;
   866 function HHJump(Gear: PGear; JumpType: TJumpType; var GoInfo: TGoInfo): boolean;
   589 var bX, bY: LongInt;
   867 var bX, bY: LongInt;
   590 begin
   868 begin
   593 GoInfo.JumpType:= jmpNone;
   871 GoInfo.JumpType:= jmpNone;
   594 bX:= hwRound(Gear^.X);
   872 bX:= hwRound(Gear^.X);
   595 bY:= hwRound(Gear^.Y);
   873 bY:= hwRound(Gear^.Y);
   596 case JumpType of
   874 case JumpType of
   597     jmpNone: exit(false);
   875     jmpNone: exit(false);
   598     
   876 
   599     jmpHJump:
   877     jmpHJump:
   600         if TestCollisionYwithGear(Gear, -1) = 0 then
   878         if TestCollisionYwithGear(Gear, -1) = 0 then
   601         begin
   879         begin
   602             Gear^.dY:= -_0_2;
   880             Gear^.dY:= -_0_2;
   603             SetLittle(Gear^.dX);
   881             SetLittle(Gear^.dX);
   604             Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
   882             Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
   605         end
   883         end
   606     else
   884     else
   607         exit(false);
   885         exit(false);
   608         
   886 
   609     jmpLJump:
   887     jmpLJump:
   610         begin
   888         begin
   611             if TestCollisionYwithGear(Gear, -1) <> 0 then
   889             if TestCollisionYwithGear(Gear, -1) <> 0 then
   612                 if not TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) then
   890                 if TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) = 0 then
   613                     Gear^.Y:= Gear^.Y - int2hwFloat(2)
   891                     Gear^.Y:= Gear^.Y - int2hwFloat(2)
   614                 else
   892                 else
   615                     if not TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) then
   893                     if TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) = 0 then
   616                         Gear^.Y:= Gear^.Y - _1;
   894                         Gear^.Y:= Gear^.Y - _1;
   617             if not (TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) or
   895             if (TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) = 0) and
   618                (TestCollisionYwithGear(Gear, -1) <> 0)) then
   896                (TestCollisionYwithGear(Gear, -1) = 0) then
   619             begin
   897             begin
   620                 Gear^.dY:= -_0_15;
   898                 Gear^.dY:= -_0_15;
   621                 Gear^.dX:= SignAs(_0_15, Gear^.dX);
   899                 Gear^.dX:= SignAs(_0_15, Gear^.dX);
   622                 Gear^.State:= Gear^.State or gstMoving or gstHHJumping
   900                 Gear^.State:= Gear^.State or gstMoving or gstHHJumping
   623             end
   901             end
   625             exit(false)
   903             exit(false)
   626         end
   904         end
   627 end;
   905 end;
   628 
   906 
   629 repeat
   907 repeat
   630         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then 
   908         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
   631             begin
   909             begin
   632             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color;
   910             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color;
   633             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   911             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   634             end;}
   912             end;}
   635             
   913 
   636     if not (hwRound(Gear^.Y) + cHHRadius < cWaterLine) then
   914     if CheckCoordInWater(hwRound(Gear^.X), hwRound(Gear^.Y) + cHHRadius) then
   637         exit(false);
   915         exit(false);
   638     if (Gear^.State and gstMoving) <> 0 then
   916     if (Gear^.State and gstMoving) <> 0 then
   639     begin
   917     begin
   640         if (GoInfo.Ticks = 350) then
   918         if (GoInfo.Ticks = 350) then
   641             if (not (hwAbs(Gear^.dX) > cLittle)) and (Gear^.dY < -_0_02) then
   919             if (not (hwAbs(Gear^.dX) > cLittle)) and (Gear^.dY < -_0_02) then
   642             begin
   920             begin
   643                 Gear^.dY:= -_0_25;
   921                 Gear^.dY:= -_0_25;
   644                 Gear^.dX:= SignAs(_0_02, Gear^.dX)
   922                 Gear^.dX:= SignAs(_0_02, Gear^.dX)
   645             end;
   923             end;
   646         if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then SetLittle(Gear^.dX);
   924         if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) <> 0 then SetLittle(Gear^.dX);
   647             Gear^.X:= Gear^.X + Gear^.dX;
   925             Gear^.X:= Gear^.X + Gear^.dX;
   648         inc(GoInfo.Ticks);
   926         inc(GoInfo.Ticks);
   649         Gear^.dY:= Gear^.dY + cGravity;
   927         Gear^.dY:= Gear^.dY + cGravity;
   650         if Gear^.dY > _0_4 then
   928         if Gear^.dY > _0_4 then
   651             exit(false);
   929             exit(false);
   680 
   958 
   681 function HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
   959 function HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
   682 var pX, pY, tY: LongInt;
   960 var pX, pY, tY: LongInt;
   683 begin
   961 begin
   684 HHGo:= false;
   962 HHGo:= false;
   685 Gear^.CollisionMask:= $FF7F;
   963 Gear^.CollisionMask:= lfNotCurHogCrate;
       
   964 
       
   965 Gear^.dX.isNegative:= (Gear^.Message and gmLeft) <> 0;
       
   966 
   686 AltGear^:= Gear^;
   967 AltGear^:= Gear^;
   687 
   968 
   688 GoInfo.Ticks:= 0;
   969 GoInfo.Ticks:= 0;
   689 GoInfo.FallPix:= 0;
   970 GoInfo.FallPix:= 0;
   690 GoInfo.JumpType:= jmpNone;
   971 GoInfo.JumpType:= jmpNone;
   691 tY:= hwRound(Gear^.Y);
   972 tY:= hwRound(Gear^.Y);
   692 repeat
   973 repeat
   693         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then 
   974         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
   694             begin
   975             begin
   695             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
   976             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
   696             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   977             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   697             end;}
   978             end;}
   698 
   979 
   699     pX:= hwRound(Gear^.X);
   980     pX:= hwRound(Gear^.X);
   700     pY:= hwRound(Gear^.Y);
   981     pY:= hwRound(Gear^.Y);
   701     if pY + cHHRadius >= cWaterLine then
   982     if CheckCoordInWater(pX, pY + cHHRadius) then
   702         begin
   983         begin
   703         if AltGear^.Hedgehog^.BotLevel < 4 then
   984         if AltGear^.Hedgehog^.BotLevel < 4 then
   704             AddWalkBonus(pX, tY, 250, -40);
   985             AddWalkBonus(pX, tY, 250, -40);
   705         exit(false)
   986         exit(false)
   706         end;
   987         end;
   707         
   988 
   708     // hog is falling    
   989     // hog is falling
   709     if (Gear^.State and gstMoving) <> 0 then
   990     if (Gear^.State and gstMoving) <> 0 then
   710         begin
   991         begin
   711         inc(GoInfo.Ticks);
   992         inc(GoInfo.Ticks);
   712         Gear^.dY:= Gear^.dY + cGravity;
   993         Gear^.dY:= Gear^.dY + cGravity;
   713         if Gear^.dY > _0_4 then
   994         if Gear^.dY > _0_4 then
   714             begin
   995             begin
   715             GoInfo.FallPix:= 0;
   996             GoInfo.FallPix:= 0;
   716             // try ljump instead of fall with damage
   997             // try ljump instead of fall with damage
   717             HHJump(AltGear, jmpLJump, GoInfo); 
   998             HHJump(AltGear, jmpLJump, GoInfo);
   718             if AltGear^.Hedgehog^.BotLevel < 4 then
   999             if AltGear^.Hedgehog^.BotLevel < 4 then
   719                 AddWalkBonus(pX, tY, 175, -20);
  1000                 AddWalkBonus(pX, tY, 175, -20);
   720             exit(false)
  1001             exit(false)
   721             end;
  1002             end;
   722         Gear^.Y:= Gear^.Y + Gear^.dY;
  1003         Gear^.Y:= Gear^.Y + Gear^.dY;
   732             exit(true)
  1013             exit(true)
   733             end;
  1014             end;
   734         continue
  1015         continue
   735         end;
  1016         end;
   736 
  1017 
   737         // usual walk
  1018     // usual walk
   738         if (Gear^.Message and gmLeft) <> 0 then
  1019     Gear^.dX:= SignAs(cLittle, Gear^.dX);
   739             Gear^.dX:= -cLittle
  1020 
   740         else
  1021     if MakeHedgehogsStep(Gear) then
   741             if (Gear^.Message and gmRight) <> 0 then
  1022         inc(GoInfo.Ticks, cHHStepTicks);
   742                 Gear^.dX:=  cLittle
  1023 
   743             else
  1024     // we have moved for 1 px
   744                 exit(false);
  1025     if (pX <> hwRound(Gear^.X)) and ((Gear^.State and gstMoving) = 0) then
   745 
  1026         exit(true)
   746         if MakeHedgehogsStep(Gear) then
       
   747             inc(GoInfo.Ticks, cHHStepTicks);
       
   748 
       
   749         // we have moved for 1 px
       
   750         if (pX <> hwRound(Gear^.X)) and ((Gear^.State and gstMoving) = 0) then
       
   751             exit(true)
       
   752 until (pX = hwRound(Gear^.X)) and (pY = hwRound(Gear^.Y)) and ((Gear^.State and gstMoving) = 0);
  1027 until (pX = hwRound(Gear^.X)) and (pY = hwRound(Gear^.Y)) and ((Gear^.State and gstMoving) = 0);
   753 
  1028 
   754 HHJump(AltGear, jmpHJump, GoInfo);
  1029 HHJump(AltGear, jmpHJump, GoInfo);
   755 end;
  1030 end;
   756 
  1031 
   757 function AIrndSign(num: LongInt): LongInt;
  1032 function AIrndSign(num: LongInt): LongInt; inline;
   758 begin
  1033 begin
   759 if random(2) = 0 then
  1034 if random(2) = 0 then
   760     AIrndSign:=   num
  1035     AIrndSign:=   num
   761 else
  1036 else
   762     AIrndSign:= - num
  1037     AIrndSign:= - num
   763 end;
  1038 end;
   764 
  1039 
       
  1040 function AIrndOffset(targ: TTarget; Level: LongWord): LongInt; inline;
       
  1041 begin
       
  1042 if Level <> 1 then exit(0);
       
  1043 // at present level 2 doesn't track falls on most things
       
  1044 //if Level = 2 then exit(round(targ.Radius*(random(5)-2)/2));
       
  1045 AIrndOffset := targ.Radius*(random(7)-3)*2
       
  1046 end;
       
  1047 
   765 procedure initModule;
  1048 procedure initModule;
   766 begin
  1049 begin
   767     friendlyfactor:= 300;
  1050     friendlyfactor:= 300;
   768     KnownExplosion.X:= 0;
  1051     KnownExplosion.X:= 0;
   769     KnownExplosion.Y:= 0;
  1052     KnownExplosion.Y:= 0;