hedgewars/uAIMisc.pas
branchwebgl
changeset 9127 e350500c4edb
parent 8833 c13ebed437cb
parent 9080 9b42757d7e71
child 9136 78f087fd3e5b
equal deleted inserted replaced
8860:bde641cf53c8 9127:e350500c4edb
     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-2013 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  *
    28       afErasesLand = $00000002;
    28       afErasesLand = $00000002;
    29       afSetSkip    = $00000004;
    29       afSetSkip    = $00000004;
    30 
    30 
    31       BadTurn = Low(LongInt) div 4;
    31       BadTurn = Low(LongInt) div 4;
    32 
    32 
    33 type TTarget = record
    33 type TTarget = record // starting to look more and more like a gear
    34     Point: TPoint;
    34     Point: TPoint;
    35     Score: LongInt;
    35     Score, Radius: LongInt;
    36     skip, matters: boolean;
    36     State: LongWord;
       
    37     Density: real;
       
    38     skip, matters, dead: boolean;
       
    39     Kind: TGearType;
    37     end;
    40     end;
    38 TTargets = record
    41 TTargets = record
    39     Count: Longword;
    42     Count: Longword;
    40     ar: array[0..Pred(cMaxHHs)] of TTarget;
    43     ar: array[0..Pred(256)] of TTarget;
       
    44     reset: boolean;
    41     end;
    45     end;
    42 TJumpType = (jmpNone, jmpHJump, jmpLJump);
    46 TJumpType = (jmpNone, jmpHJump, jmpLJump);
    43 TGoInfo = record
    47 TGoInfo = record
    44     Ticks: Longword;
    48     Ticks: Longword;
    45     FallPix: Longword;
    49     FallPix: Longword;
    63 
    67 
    64 procedure initModule;
    68 procedure initModule;
    65 procedure freeModule;
    69 procedure freeModule;
    66 
    70 
    67 procedure FillTargets;
    71 procedure FillTargets;
       
    72 procedure ResetTargets; inline;
    68 procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
    73 procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
    69 procedure FillBonuses(isAfterAttack: boolean);
    74 procedure FillBonuses(isAfterAttack: boolean);
    70 procedure AwareOfExplosion(x, y, r: LongInt); inline;
    75 procedure AwareOfExplosion(x, y, r: LongInt); inline;
    71 
    76 
    72 function  RatePlace(Gear: PGear): LongInt;
    77 function  RatePlace(Gear: PGear): LongInt;
    73 function  TestColl(x, y, r: LongInt): boolean; inline;
    78 function  TestColl(x, y, r: LongInt): boolean; inline;
    74 function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
    79 function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
    75 function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
    80 function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
    76 function  TraceShoveFall(x, y, dX, dY: Real): LongInt;
       
    77 function TestCollWithLand(x, y, r: LongInt): boolean; inline;
       
    78 
    81 
    79 function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
    82 function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
    80 function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
    83 function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
    81 function  RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
    84 function  RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
       
    85 function  RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
    82 function  RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
    86 function  RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
    83 function  RateHammer(Me: PGear): LongInt;
    87 function  RateHammer(Me: PGear): LongInt;
    84 
    88 
    85 function  HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
    89 function  HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
    86 function  AIrndSign(num: LongInt): LongInt;
    90 function  AIrndSign(num: LongInt): LongInt;
    92 
    96 
    93     walkbonuses: Twalkbonuses;
    97     walkbonuses: Twalkbonuses;
    94 
    98 
    95 const KillScore = 200;
    99 const KillScore = 200;
    96 var friendlyfactor: LongInt = 300;
   100 var friendlyfactor: LongInt = 300;
       
   101 var dmgMod: real = 1.0;
    97 
   102 
    98 implementation
   103 implementation
    99 uses uCollisions, uVariables, uUtils, uLandTexture, uGearsUtils;
   104 uses uCollisions, uVariables, uUtils, uLandTexture, uGearsUtils;
   100 
   105 
   101 var 
   106 var
   102     KnownExplosion: record
   107     KnownExplosion: record
   103         X, Y, Radius: LongInt
   108         X, Y, Radius: LongInt
   104         end = (X: 0; Y: 0; Radius: 0);
   109         end = (X: 0; Y: 0; Radius: 0);
   105 
   110 
       
   111 procedure ResetTargets; inline;
       
   112 var i: LongWord;
       
   113 begin
       
   114 if Targets.reset then
       
   115     for i:= 0 to Targets.Count do
       
   116         Targets.ar[i].dead:= false;
       
   117 Targets.reset:= false;
       
   118 end;
   106 procedure FillTargets;
   119 procedure FillTargets;
   107 var i, t: Longword;
   120 var i, t: Longword;
   108     f, e: LongInt;
   121     f, e: LongInt;
       
   122     Gear: PGear;
   109 begin
   123 begin
   110 Targets.Count:= 0;
   124 Targets.Count:= 0;
       
   125 Targets.reset:= false;
   111 f:= 0;
   126 f:= 0;
   112 e:= 0;
   127 e:= 0;
   113 for t:= 0 to Pred(TeamsCount) do
   128 Gear:= GearsList;
   114     with TeamsArray[t]^ do
   129 while Gear <> nil do
   115         if not hasGone then
   130     begin
   116             begin
   131     if  (((Gear^.Kind = gtHedgehog) and
   117             for i:= 0 to cMaxHHIndex do
   132             (Gear <> ThinkingHH) and
   118                 if (Hedgehogs[i].Gear <> nil)
   133             (Gear^.Health > Gear^.Damage) and
   119                 and (Hedgehogs[i].Gear <> ThinkingHH)
   134             not(Gear^.Hedgehog^.Team^.hasgone)) or
   120                 and (Hedgehogs[i].Gear^.Health > Hedgehogs[i].Gear^.Damage) 
   135         ((Gear^.Kind = gtExplosives) and
   121                     then
   136             (Gear^.Health > Gear^.Damage)) or
   122                     begin
   137         ((Gear^.Kind = gtMine) and
   123                     with Targets.ar[Targets.Count], Hedgehogs[i] do
   138             (Gear^.Health = 0) and
   124                         begin
   139              (Gear^.Damage < 35))
   125                         skip:= false;
   140              )  and 
   126                         matters:= (Hedgehogs[i].Gear^.AIHints and aihDoesntMatter) = 0;
   141         (Targets.Count < 256) then
   127                         
   142         begin
   128                         Point.X:= hwRound(Gear^.X);
   143         with Targets.ar[Targets.Count] do
   129                         Point.Y:= hwRound(Gear^.Y);
   144             begin
   130                         if Clan <> CurrentTeam^.Clan then
   145             skip:= false;
   131                             begin
   146             dead:= false;
   132                             Score:= Gear^.Health - Gear^.Damage;
   147             Kind:= Gear^.Kind;
   133                             inc(e)
   148             Radius:= Gear^.Radius;
   134                             end else
   149             Density:= hwFloat2Float(Gear^.Density)/3;
   135                             begin
   150             State:= Gear^.State;
   136                             Score:= Gear^.Damage - Gear^.Health;
   151             matters:= (Gear^.AIHints and aihDoesntMatter) = 0;
   137                             inc(f)
   152 
   138                             end
   153             Point.X:= hwRound(Gear^.X);
   139                         end;
   154             Point.Y:= hwRound(Gear^.Y);
   140                     inc(Targets.Count)
   155             if (Gear^.Kind = gtHedgehog) then
       
   156                 begin
       
   157                 if (Gear^.Hedgehog^.Team^.Clan = CurrentTeam^.Clan) then
       
   158                     begin
       
   159                     Score:= Gear^.Damage - Gear^.Health;
       
   160                     inc(f)
       
   161                     end
       
   162                 else 
       
   163                     begin
       
   164                     Score:= Gear^.Health - Gear^.Damage;
       
   165                     inc(e)
   141                     end;
   166                     end;
   142             end;
   167                 end
       
   168             else if Gear^.Kind = gtExplosives then
       
   169                 Score:= Gear^.Health - Gear^.Damage
       
   170             else if Gear^.Kind = gtMine then 
       
   171                 Score:= max(0,35-Gear^.Damage);
       
   172             end;
       
   173         inc(Targets.Count)
       
   174         end;
       
   175     Gear:= Gear^.NextGear
       
   176     end;
   143 
   177 
   144 if e > f then friendlyfactor:= 300 + (e - f) * 30
   178 if e > f then friendlyfactor:= 300 + (e - f) * 30
   145 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e))
   179 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e))
   146 end;
   180 end;
   147 
   181 
   183             gtCase:
   217             gtCase:
   184                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y) + 3, 37, 25);
   218                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y) + 3, 37, 25);
   185             gtFlame:
   219             gtFlame:
   186                 if (Gear^.State and gsttmpFlag) <> 0 then
   220                 if (Gear^.State and gsttmpFlag) <> 0 then
   187                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 20, -50);
   221                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 20, -50);
   188 // avoid mines unless they are very likely to be duds, or are duds. also avoid if they are about to blow 
   222 // avoid mines unless they are very likely to be duds, or are duds. also avoid if they are about to blow
   189             gtMine:
   223             gtMine:
   190                 if ((Gear^.State and gstAttacking) = 0) and (((cMineDudPercent < 90) and (Gear^.Health <> 0))
   224                 if ((Gear^.State and gstAttacking) = 0) and (((cMineDudPercent < 90) and (Gear^.Health <> 0))
   191                 or (isAfterAttack and (Gear^.Health = 0) and (Gear^.Damage > 30))) then
   225                 or (isAfterAttack and (Gear^.Health = 0) and (Gear^.Damage > 30))) then
   192                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -50)
   226                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -50)
   193                 else if (Gear^.State and gstAttacking) <> 0 then
   227                 else if (Gear^.State and gstAttacking) <> 0 then
   194                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 100, -50); // mine is on
   228                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 100, -50); // mine is on
   195                     
   229 
   196             gtExplosives:
   230             gtExplosives:
   197             if isAfterAttack then
   231             if isAfterAttack then
   198                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 75, -60 + Gear^.Health);
   232                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 75, -60 + Gear^.Health);
   199                 
   233 
   200             gtSMine:
   234             gtSMine:
   201                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -30);
   235                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -30);
   202                 
   236 
   203             gtDynamite:
   237             gtDynamite:
   204                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 150, -75);
   238                 AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 150, -75);
   205                 
   239 
   206             gtHedgehog:
   240             gtHedgehog:
   207                 begin
   241                 begin
   208                 if Gear^.Damage >= Gear^.Health then
   242                 if Gear^.Damage >= Gear^.Health then
   209                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 60, -25)
   243                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 60, -25)
   210                 else
   244                 else
   258                 inc(rate, Score * (Radius - r))
   292                 inc(rate, Score * (Radius - r))
   259         end;
   293         end;
   260     RatePlace:= rate;
   294     RatePlace:= rate;
   261 end;
   295 end;
   262 
   296 
       
   297 function CheckBounds(x, y, r: Longint): boolean; inline;
       
   298 begin
       
   299     CheckBounds := (((x-r) and LAND_WIDTH_MASK) = 0) and
       
   300         (((x+r) and LAND_WIDTH_MASK) = 0) and
       
   301         (((y-r) and LAND_HEIGHT_MASK) = 0) and
       
   302         (((y+r) and LAND_HEIGHT_MASK) = 0);
       
   303 end;
       
   304 
       
   305 
       
   306 function TestCollWithEverything(x, y, r: LongInt): boolean; inline;
       
   307 begin
       
   308     if not CheckBounds(x, y, r) then
       
   309         exit(false);
       
   310 
       
   311     if (Land[y-r, x-r] <> 0) or
       
   312        (Land[y+r, x-r] <> 0) or
       
   313        (Land[y-r, x+r] <> 0) or
       
   314        (Land[y+r, x+r] <> 0) then
       
   315        exit(true);
       
   316 
       
   317     TestCollWithEverything := false;
       
   318 end;
       
   319 
       
   320 function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
       
   321 begin
       
   322     if not CheckBounds(x, y, r) then
       
   323         exit(false);
       
   324 
       
   325     if (Land[y-r, x-r] > lfAllObjMask) or
       
   326        (Land[y+r, x-r] > lfAllObjMask) or
       
   327        (Land[y-r, x-r] > lfAllObjMask) or
       
   328        (Land[y+r, x+r] > lfAllObjMask) then
       
   329        exit(true);
       
   330 
       
   331     TestCollExcludingObjects:= false;
       
   332 end;
       
   333 
       
   334 function TestColl(x, y, r: LongInt): boolean; inline;
       
   335 begin
       
   336     if not CheckBounds(x, y, r) then
       
   337         exit(false);
       
   338 
       
   339     if (Land[y-r, x-r] and lfNotCurrentMask <> 0) or
       
   340        (Land[y+r, x-r] and lfNotCurrentMask <> 0) or
       
   341        (Land[y+r, x-r] and lfNotCurrentMask <> 0) or
       
   342        (Land[y+r, x+r] and lfNotCurrentMask <> 0) then
       
   343        exit(true);
       
   344 
       
   345     TestColl:= false;
       
   346 end;
       
   347 
       
   348 
   263 // Wrapper to test various approaches.  If it works reasonably, will just replace.
   349 // Wrapper to test various approaches.  If it works reasonably, will just replace.
   264 // Right now, converting to hwFloat is a tad inefficient since the x/y were hwFloat to begin with...
   350 // Right now, converting to hwFloat is a tad inefficient since the x/y were hwFloat to begin with...
   265 function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
   351 function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
   266 var MeX, MeY: LongInt;
   352 var MeX, MeY: LongInt;
   267 begin
   353 begin
   268     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
   354     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
   269     begin
   355     begin
   270         MeX:= hwRound(Me^.X);
   356         MeX:= hwRound(Me^.X);
   271         MeY:= hwRound(Me^.Y);
   357         MeY:= hwRound(Me^.Y);
   272         // We are still inside the hog. Skip radius test
   358         // We are still inside the hog. Skip radius test
   273         if ((((x-MeX)*(x-MeX)) + ((y-MeY)*(y-MeY))) < 256) and (Land[y, x] <= lfAllObjMask) and ((Land[y, x] and lfObjMask) < 2) then
   359         if ((sqr(x-MeX) + sqr(y-MeY)) < 256) and (Land[y, x] and lfObjMask = 0) then
   274             exit(false);
   360             exit(false);
   275     end;
   361     end;
   276     TestCollExcludingMe:= TestColl(x, y, r)
   362     TestCollExcludingMe:= TestCollWithEverything(x, y, r)
   277 end;
   363 end;
   278 
   364 
   279 function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
   365 
   280 var b: boolean;
   366 
   281 begin
   367 function TraceFall(eX, eY: LongInt; var x, y: Real; dX, dY: Real; r: LongWord; Target: TTarget): LongInt;
   282     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > lfAllObjMask);
       
   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] > lfAllObjMask);
       
   287     if b then
       
   288         exit(true);
       
   289     
       
   290     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > lfAllObjMask);
       
   291     if b then
       
   292         exit(true);
       
   293     
       
   294     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > lfAllObjMask);
       
   295     if b then
       
   296         exit(true);
       
   297     
       
   298     TestCollExcludingObjects:= false;
       
   299 end;
       
   300 
       
   301 function TestColl(x, y, r: LongInt): boolean; inline;
       
   302 var b: boolean;
       
   303 begin
       
   304     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] and lfNotCurrentMask <> 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 lfNotCurrentMask <> 0);
       
   309     if b then
       
   310         exit(true);
       
   311     
       
   312     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] and lfNotCurrentMask <> 0);
       
   313     if b then
       
   314         exit(true);
       
   315     
       
   316     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] and lfNotCurrentMask <> 0);
       
   317     if b then
       
   318         exit(true);
       
   319     
       
   320     TestColl:= false;
       
   321 end;
       
   322 
       
   323 function TestCollWithLand(x, y, r: LongInt): boolean; inline;
       
   324 var b: boolean;
       
   325 begin
       
   326     b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > lfAllObjMask);
       
   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] > lfAllObjMask);
       
   331     if b then
       
   332         exit(true);
       
   333         
       
   334     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > lfAllObjMask);
       
   335     if b then
       
   336         exit(true);
       
   337         
       
   338     b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > lfAllObjMask);
       
   339     if b then
       
   340         exit(true);
       
   341 
       
   342     TestCollWithLand:= false;
       
   343 end;
       
   344 
       
   345 function TraceFall(eX, eY: LongInt; x, y, dX, dY: Real; r: LongWord): LongInt;
       
   346 var skipLandCheck: boolean;
   368 var skipLandCheck: boolean;
   347     rCorner: real;
   369     rCorner, dxdy, odX, odY: real;
   348     dmg: LongInt;
   370     dmg: LongInt;
   349 begin
   371 begin
       
   372     odX:= dX;
       
   373     odY:= dY;
   350     skipLandCheck:= true;
   374     skipLandCheck:= true;
   351     if x - eX < 0 then dX:= -dX;
       
   352     if y - eY < 0 then dY:= -dY;
       
   353     // ok. attempt approximate search for an unbroken trajectory into water.  if it continues far enough, assume out of map
   375     // ok. attempt approximate search for an unbroken trajectory into water.  if it continues far enough, assume out of map
   354     rCorner:= r * 0.75;
   376     rCorner:= r * 0.75;
   355     while true do
   377     while true do
   356     begin
   378         begin
   357         x:= x + dX;
   379         x:= x + dX;
   358         y:= y + dY;
   380         y:= y + dY;
   359         dY:= dY + cGravityf;
   381         dY:= dY + cGravityf;
   360         skipLandCheck:= skipLandCheck and (r <> 0) and (abs(eX-x) + abs(eY-y) < r) and ((abs(eX-x) < rCorner) or (abs(eY-y) < rCorner));
   382         skipLandCheck:= skipLandCheck and (r <> 0) and (abs(eX-x) + abs(eY-y) < r) and ((abs(eX-x) < rCorner) or (abs(eY-y) < rCorner));
   361         if (not skipLandCheck) and TestCollWithLand(trunc(x), trunc(y), cHHRadius) then
   383         if not skipLandCheck and TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then
   362         begin
   384             with Target do
   363             if 0.4 < dY then
   385                 begin
   364             begin
   386                 if (Kind = gtHedgehog) and (0.4 < dY) then
   365                 dmg := 1 + trunc((abs(dY) - 0.4) * 70);
   387                     begin
   366                 if dmg >= 1 then
   388                     dmg := 1 + trunc((dY - 0.4) * 70);
   367                     exit(dmg);
   389                     exit(dmg)
   368             end;
   390                     end
       
   391                 else 
       
   392                     begin
       
   393                     dxdy:= abs(dX)+abs(dY);
       
   394                     if ((Kind = gtMine) and (dxdy > 0.35)) or 
       
   395                        ((Kind = gtExplosives) and 
       
   396                             (((State and gstTmpFlag <> 0) and (dxdy > 0.35)) or
       
   397                              ((State and gstTmpFlag = 0) and 
       
   398                                 ((abs(odX) > 0.15) or ((abs(odY) > 0.15) and 
       
   399                                 (abs(odX) > 0.02))) and (dxdy > 0.35)))) then
       
   400                         begin
       
   401                         dmg := trunc(dxdy * 25);
       
   402                         exit(dmg)
       
   403                         end
       
   404                     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
       
   405                         begin
       
   406                         dmg := trunc(dy * 70);
       
   407                         exit(dmg)
       
   408                         end
       
   409                     end;
   369             exit(0)
   410             exit(0)
   370         end;
   411             end;
   371         if (y > cWaterLine) or (x > 4096) or (x < 0) then
   412         if (y > cWaterLine) or (x > rightX) or (x < leftX) then exit(-1)
   372             exit(-1);
   413         end
   373     end;
   414 end;
   374 end;
   415 
   375 
   416 function TraceShoveFall(var x, y: Real; dX, dY: Real; Target: TTarget): LongInt;
   376 function TraceShoveFall(x, y, dX, dY: Real): LongInt;
       
   377 var dmg: LongInt;
   417 var dmg: LongInt;
   378 begin
   418     dxdy, odX, odY: real;
       
   419 begin
       
   420     odX:= dX;
       
   421     odY:= dY;
   379 //v:= random($FFFFFFFF);
   422 //v:= random($FFFFFFFF);
   380     while true do
   423     while true do
   381     begin
   424         begin
   382         x:= x + dX;
   425         x:= x + dX;
   383         y:= y + dY;
   426         y:= y + dY;
   384         dY:= dY + cGravityf;
   427         dY:= dY + cGravityf;
   385 
   428 
   386 {        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then 
   429 {        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then
   387             begin
   430             begin
   388             LandPixels[trunc(y), trunc(x)]:= v;
   431             LandPixels[trunc(y), trunc(x)]:= v;
   389             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
   432             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
   390             end;}
   433             end;}
   391 
   434 
   392 
   435         if TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then
   393         // consider adding dX/dY calc here for fall damage
   436             with Target do
   394         if TestCollExcludingObjects(trunc(x), trunc(y), cHHRadius) then
   437                 begin
   395         begin
   438                 if (Kind = gtHedgehog) and (0.4 < dY) then
   396             if 0.4 < dY then
   439                     begin
   397             begin
   440                     dmg := trunc((dY - 0.4) * 70);
   398                 dmg := 1 + trunc((abs(dY) - 0.4) * 70);
       
   399                 if dmg >= 1 then
       
   400                     exit(dmg);
   441                     exit(dmg);
   401             end;
   442                     end
       
   443                 else 
       
   444                     begin
       
   445                     dxdy:= abs(dX)+abs(dY);
       
   446                     if ((Kind = gtMine) and (dxdy > 0.4)) or 
       
   447                        ((Kind = gtExplosives) and 
       
   448                             (((State and gstTmpFlag <> 0) and (dxdy > 0.4)) 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 * 50);
       
   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;
   402             exit(0)
   462             exit(0)
   403         end;
   463         end;
   404         if (y > cWaterLine) or (x > 4096) or (x < 0) then
   464         if (y > cWaterLine) or (x > rightX) or (x < leftX) then
   405             // returning -1 for drowning so it can be considered in the Rate routine
   465             // returning -1 for drowning so it can be considered in the Rate routine
   406             exit(-1)
   466             exit(-1)
   407     end;
   467     end;
   408 end;
   468 end;
   409 
   469 
   410 function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt;
   470 function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
   411 begin
   471 begin
   412     RateExplosion:= RateExplosion(Me, x, y, r, 0);
   472     RateExplosion:= RealRateExplosion(Me, x, y, r, 0);
   413 end;
   473     ResetTargets;
   414 
   474 end;
   415 function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
   475 function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
   416 var i, fallDmg, dmg, dmgBase, rate, erasure: LongInt;
   476 begin
   417     dX, dY, dmgMod: real;
   477     RateExplosion:= RealRateExplosion(Me, x, y, r, Flags);
       
   478     ResetTargets;
       
   479 end;
       
   480 
       
   481 function RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
       
   482 var i, fallDmg, dmg, dmgBase, rate, subrate, erasure: LongInt;
       
   483     pX, pY, dX, dY: real;
   418     hadSkips: boolean;
   484     hadSkips: boolean;
   419 begin
   485 begin
   420 fallDmg:= 0;
   486 fallDmg:= 0;
   421 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
       
   422 rate:= 0;
   487 rate:= 0;
   423 // add our virtual position
   488 // add our virtual position
   424 with Targets.ar[Targets.Count] do
   489 with Targets.ar[Targets.Count] do
   425     begin
   490     begin
   426     Point.x:= hwRound(Me^.X);
   491     Point.x:= hwRound(Me^.X);
   427     Point.y:= hwRound(Me^.Y);
   492     Point.y:= hwRound(Me^.Y);
   428     skip:= false;
   493     skip:= false;
   429     matters:= true;
   494     matters:= true;
       
   495     Kind:= gtHedgehog;
       
   496     Density:= 1;
       
   497     Radius:= cHHRadius;
   430     Score:= - ThinkingHH^.Health
   498     Score:= - ThinkingHH^.Health
   431     end;
   499     end;
   432 // rate explosion
   500 // rate explosion
   433 dmgBase:= r + cHHRadius div 2;
       
   434 
   501 
   435 if (Flags and afErasesLand <> 0) and (GameFlags and gfSolidLand = 0) then erasure:= r
   502 if (Flags and afErasesLand <> 0) and (GameFlags and gfSolidLand = 0) then erasure:= r
   436 else erasure:= 0;
   503 else erasure:= 0;
   437 
   504 
   438 hadSkips:= false;
   505 hadSkips:= false;
   439 
   506 
   440 for i:= 0 to Targets.Count do
   507 for i:= 0 to Targets.Count do
   441     with Targets.ar[i] do
   508     if not Targets.ar[i].dead then
   442       if not matters then hadSkips:= true
   509         with Targets.ar[i] do
   443         else
   510           if not matters then hadSkips:= true
   444         begin
   511             else
   445         dmg:= 0;
   512             begin
   446         if abs(Point.x - x) + abs(Point.y - y) < dmgBase then
   513             dmg:= 0;
   447             dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r));
   514             dmgBase:= r + Radius div 2;
   448 
   515             if abs(Point.x - x) + abs(Point.y - y) < dmgBase then
   449         if dmg > 0 then
   516                 dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r));
   450             begin
   517 
   451             if (Flags and afTrackFall <> 0) and (dmg < abs(Score)) then
   518             if dmg > 0 then
   452                 begin
   519                 begin
   453                 dX:= 0.005 * dmg + 0.01;
   520                 pX:= Point.x;
   454                 dY:= dX;
   521                 pY:= Point.y;
   455                 if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and 
   522                 fallDmg:= 0;
   456                    (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
   523                 if (Flags and afTrackFall <> 0) and (Score > 0) and (dmg < Score) then
   457                      fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod)
   524                     begin
   458                 else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod)
   525                     dX:= (0.005 * dmg + 0.01) / Density;
   459                 end;
   526                     dY:= dX;
   460             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   527                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and 
   461                 if Score > 0 then
   528                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
   462                     inc(rate, (KillScore + Score div 10) * 1024)   // Add a bit of a bonus for bigger hog drownings
   529                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
   463                 else
   530                         dX:= 0;
   464                     dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
   531 
   465             else if (dmg+fallDmg) >= abs(Score) then
   532                     if pX - x < 0 then dX:= -dX;
   466                 if Score > 0 then
   533                     if pY - y < 0 then dY:= -dY;
   467                     inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
   534 
   468                 else
   535                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
   469                     dec(rate, KillScore * friendlyfactor div 100 * 1024)
   536                        (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
   470             else
   537                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
   471                 if Score > 0 then
   538                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
   472                     inc(rate, (dmg + fallDmg) * 1024)
   539                     end;
   473                 else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
   540                 if Kind = gtHedgehog then
   474             end;
   541                     begin
   475         end;
   542                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   543                         begin
       
   544                         if Score > 0 then
       
   545                             inc(rate, (KillScore + Score div 10) * 1024)   // Add a bit of a bonus for bigger hog drownings
       
   546                         else
       
   547                             dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
       
   548                         end
       
   549                     else if (dmg+fallDmg) >= abs(Score) then
       
   550                         begin
       
   551                         dead:= true;
       
   552                         Targets.reset:= true;
       
   553                         if dX < 0.035 then
       
   554                             begin
       
   555                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or (Flags and afTrackFall));
       
   556                             if abs(subrate) > 2000 then inc(Rate,subrate)
       
   557                             end;
       
   558                         if Score > 0 then
       
   559                              inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
       
   560                         else dec(rate, KillScore * friendlyfactor div 100 * 1024)
       
   561                         end
       
   562                     else
       
   563                         begin
       
   564                         if Score > 0 then
       
   565                              inc(rate, (dmg + fallDmg) * 1024)
       
   566                         else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
       
   567                         end
       
   568                     end
       
   569                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   570                     begin
       
   571                     dead:= true;
       
   572                     Targets.reset:= true;
       
   573                     if Kind = gtExplosives then
       
   574                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall))
       
   575                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall));
       
   576                     if abs(subrate) > 2000 then inc(Rate,subrate);
       
   577                     end
       
   578                 end
       
   579             end;
   476 
   580 
   477 if hadSkips and (rate = 0) then
   581 if hadSkips and (rate = 0) then
   478     RateExplosion:= BadTurn
   582     RealRateExplosion:= BadTurn
   479     else
   583     else
   480     RateExplosion:= rate;
   584     RealRateExplosion:= rate;
   481 end;
   585 end;
   482 
   586 
   483 function RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
   587 function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
   484 var i, fallDmg, dmg, rate: LongInt;
   588 var i, fallDmg, dmg, rate, subrate: LongInt;
   485     dX, dY, dmgMod: real;
   589     dX, dY, pX, pY: real;
   486 begin
   590 begin
   487 fallDmg:= 0;
   591 fallDmg:= 0;
   488 dX:= gdX * 0.01 * kick;
   592 dX:= gdX * 0.01 * kick;
   489 dY:= gdY * 0.01 * kick;
   593 dY:= gdY * 0.01 * kick;
   490 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
       
   491 rate:= 0;
   594 rate:= 0;
   492 for i:= 0 to Pred(Targets.Count) do
   595 for i:= 0 to Pred(Targets.Count) do
   493     with Targets.ar[i] do
   596     with Targets.ar[i] do
   494       if skip then 
   597         if skip then
   495         if (Flags and afSetSkip = 0) then skip:= false else {still skip}
   598             begin
   496       else if matters then
   599             if Flags and afSetSkip = 0 then skip:= false
   497         begin
   600             end
   498         dmg:= 0;
   601         else if matters then
   499         if abs(Point.x - x) + abs(Point.y - y) < r then
   602             begin
   500             dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   603             dmg:= 0;
   501 
   604             if abs(Point.x - x) + abs(Point.y - y) < r then
   502         if dmg > 0 then
   605                 dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   503             begin
   606 
   504             if (Flags and afSetSkip <> 0) then skip:= true;
   607             if dmg > 0 then
   505             if (Flags and afTrackFall <> 0) and (Score > 0) then 
   608                 begin
   506                 fallDmg:= trunc(TraceShoveFall(Point.x, Point.y - 2, dX, dY) * dmgMod);
   609                 pX:= Point.x;
   507             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   610                 pY:= Point.y-2;
   508                 if Score > 0 then
   611                 fallDmg:= 0;
   509                     inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
   612                 if (Flags and afSetSkip <> 0) then skip:= true;
   510                 else
   613                 if not(dead) and (Flags and afTrackFall <> 0) and (Score > 0) and (power < Score) then
   511                     dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
   614                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and 
   512             else if power+fallDmg >= abs(Score) then
   615                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
   513                 if Score > 0 then
   616                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
   514                     inc(rate, KillScore)
   617                         fallDmg:= trunc(TraceShoveFall(pX, pY, 0, dY, Targets.ar[i]) * dmgMod)
   515                 else
   618                     else
   516                     dec(rate, KillScore * friendlyfactor div 100)
   619                         fallDmg:= trunc(TraceShoveFall(pX, pY, dX, dY, Targets.ar[i]) * dmgMod);
   517             else
   620                 if Kind = gtHedgehog then
   518                 if Score > 0 then
   621                     begin
   519                     inc(rate, power+fallDmg)
   622                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   520                 else
   623                         begin
   521                     dec(rate, (power+fallDmg) * friendlyfactor div 100)
   624                         if Score > 0 then
   522             end;
   625                             inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
   523         end;
   626                         else
   524 RateShove:= rate * 1024
   627                             dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   628                         end
       
   629                     else if power+fallDmg >= abs(Score) then
       
   630                         begin
       
   631                         dead:= true;
       
   632                         Targets.reset:= true;
       
   633                         if dX < 0.035 then
       
   634                             begin
       
   635                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall);
       
   636                             if abs(subrate) > 2000 then inc(Rate,subrate div 1024)
       
   637                             end;
       
   638                         if Score > 0 then
       
   639                             inc(rate, KillScore)
       
   640                         else
       
   641                             dec(rate, KillScore * friendlyfactor div 100)
       
   642                         end
       
   643                     else
       
   644                         begin
       
   645                         if Score > 0 then
       
   646                             inc(rate, power+fallDmg)
       
   647                         else
       
   648                             dec(rate, (power+fallDmg) * friendlyfactor div 100)
       
   649                         end
       
   650                     end
       
   651                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   652                     begin
       
   653                     dead:= true;
       
   654                     Targets.reset:= true;
       
   655                     if Kind = gtExplosives then
       
   656                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall))
       
   657                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall));
       
   658                     if abs(subrate) > 2000 then inc(Rate,subrate div 1024);
       
   659                     end
       
   660                 end
       
   661             end;
       
   662 RateShove:= rate * 1024;
       
   663 ResetTargets
   525 end;
   664 end;
   526 
   665 
   527 function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
   666 function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
   528 var i, dmg, fallDmg, baseDmg, rate, erasure: LongInt;
   667 var i, dmg, fallDmg, baseDmg, rate, subrate, erasure: LongInt;
   529     dX, dY, dmgMod: real;
   668     pX, pY, dX, dY: real;
   530     hadSkips: boolean;
   669     hadSkips: boolean;
   531 begin
   670 begin
   532 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
       
   533 rate:= 0;
   671 rate:= 0;
   534 gdX:= gdX * 0.01;
   672 gdX:= gdX * 0.01;
   535 gdY:= gdX * 0.01;
   673 gdY:= gdX * 0.01;
   536 // add our virtual position
   674 // add our virtual position
   537 with Targets.ar[Targets.Count] do
   675 with Targets.ar[Targets.Count] do
   538     begin
   676     begin
   539     Point.x:= hwRound(Me^.X);
   677     Point.x:= hwRound(Me^.X);
   540     Point.y:= hwRound(Me^.Y);
   678     Point.y:= hwRound(Me^.Y);
   541     skip:= false;
   679     skip:= false;
   542     matters:= true;
   680     matters:= true;
       
   681     Kind:= gtHedgehog;
       
   682     Density:= 1;
       
   683     Radius:= cHHRadius;
   543     Score:= - ThinkingHH^.Health
   684     Score:= - ThinkingHH^.Health
   544     end;
   685     end;
   545 // rate shot
   686 // rate shot
   546 baseDmg:= cHHRadius + cShotgunRadius + 4;
   687 baseDmg:= cHHRadius + cShotgunRadius + 4;
   547 
   688 
   549 else erasure:= 0;
   690 else erasure:= 0;
   550 
   691 
   551 hadSkips:= false;
   692 hadSkips:= false;
   552 
   693 
   553 for i:= 0 to Targets.Count do
   694 for i:= 0 to Targets.Count do
   554     with Targets.ar[i] do
   695     if not Targets.ar[i].dead then
   555       if not matters then hadSkips:= true
   696         with Targets.ar[i] do
   556         else
   697           if not matters then hadSkips:= true
   557         begin
       
   558         dmg:= 0;
       
   559         if abs(Point.x - x) + abs(Point.y - y) < baseDmg then
       
   560             begin
       
   561             dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25);
       
   562             dmg:= trunc(dmg * dmgMod);
       
   563             end;
       
   564         if dmg > 0 then
       
   565             begin
       
   566             dX:= gdX * dmg;
       
   567             dY:= gdY * dmg;
       
   568             if dX < 0 then dX:= dX - 0.01
       
   569             else dX:= dX + 0.01;
       
   570             if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and 
       
   571                (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
       
   572                  fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod)
       
   573             else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod);
       
   574             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   575                 if Score > 0 then
       
   576                     inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
       
   577                 else
       
   578                     dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   579             else if (dmg+fallDmg) >= abs(Score) then
       
   580                 if Score > 0 then
       
   581                     inc(rate, KillScore)
       
   582                 else
       
   583                     dec(rate, KillScore * friendlyfactor div 100)
       
   584             else
   698             else
   585                 if Score > 0 then
   699             begin
   586                     inc(rate, dmg+fallDmg)
   700             dmg:= 0;
   587             else
   701             if abs(Point.x - x) + abs(Point.y - y) < baseDmg then
   588                 dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
   702                 begin
   589             end;
   703                 dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25);
   590         end;
   704                 dmg:= trunc(dmg * dmgMod);
       
   705                 end;
       
   706             if dmg > 0 then
       
   707                 begin
       
   708                 if not(dead) and (Score > 0) and (dmg < Score) then
       
   709                     begin
       
   710                     pX:= Point.x;
       
   711                     pY:= Point.y;
       
   712                     dX:= gdX * dmg / Density;
       
   713                     dY:= gdY * dmg / Density;
       
   714                     if dX < 0 then dX:= dX - 0.01
       
   715                     else dX:= dX + 0.01;
       
   716                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and 
       
   717                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
       
   718                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then 
       
   719                        dX:= 0;
       
   720                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
       
   721                        (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
       
   722                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
       
   723                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
       
   724                     end;
       
   725                 if Kind = gtHedgehog then
       
   726                     begin
       
   727                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   728                         begin
       
   729                         if Score > 0 then
       
   730                             inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
       
   731                         else
       
   732                             dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   733                         end
       
   734                     else if (dmg+fallDmg) >= abs(Score) then
       
   735                         begin
       
   736                         dead:= true;
       
   737                         Targets.reset:= true;
       
   738                         if dX < 0.035 then
       
   739                             begin
       
   740                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall);
       
   741                             if abs(subrate) > 2000 then inc(Rate,subrate div 1024)
       
   742                             end;
       
   743                         if Score > 0 then
       
   744                             inc(rate, KillScore)
       
   745                         else
       
   746                             dec(rate, KillScore * friendlyfactor div 100)
       
   747                         end
       
   748                     else if Score > 0 then
       
   749                          inc(rate, dmg+fallDmg)
       
   750                     else dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
       
   751                     end
       
   752                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   753                     begin
       
   754                     dead:= true;
       
   755                     Targets.reset:= true;
       
   756                     if Kind = gtExplosives then
       
   757                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or afTrackFall)
       
   758                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or afTrackFall);
       
   759                     if abs(subrate) > 2000 then inc(Rate,subrate div 1024);
       
   760                     end
       
   761                 end
       
   762             end;
   591 
   763 
   592 if hadSkips and (rate = 0) then
   764 if hadSkips and (rate = 0) then
   593     RateShotgun:= BadTurn
   765     RateShotgun:= BadTurn
   594     else
   766     else
   595     RateShotgun:= rate * 1024;
   767     RateShotgun:= rate * 1024;
       
   768     ResetTargets;
   596 end;
   769 end;
   597 
   770 
   598 function RateHammer(Me: PGear): LongInt;
   771 function RateHammer(Me: PGear): LongInt;
   599 var x, y, i, r, rate: LongInt;
   772 var x, y, i, r, rate: LongInt;
   600 begin
   773 begin
   603 y:= hwRound(Me^.Y);
   776 y:= hwRound(Me^.Y);
   604 rate:= 0;
   777 rate:= 0;
   605 
   778 
   606 for i:= 0 to Pred(Targets.Count) do
   779 for i:= 0 to Pred(Targets.Count) do
   607     with Targets.ar[i] do
   780     with Targets.ar[i] do
   608       if matters then
       
   609          // hammer hit radius is 8, shift is 10
   781          // hammer hit radius is 8, shift is 10
   610         if abs(Point.x - x) + abs(Point.y - y) < 18 then
   782       if matters and (Kind = gtHedgehog) and (abs(Point.x - x) + abs(Point.y - y) < 18) then
   611             begin
   783             begin
   612             r:= trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   784             r:= trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   613 
   785 
   614             if r <= 18 then
   786             if r <= 18 then
   615                 if Score > 0 then 
   787                 if Score > 0 then
   616                     inc(rate, Score div 3)
   788                     inc(rate, Score div 3)
   617                 else
   789                 else
   618                     inc(rate, Score div 3 * friendlyfactor div 100)
   790                     inc(rate, Score div 3 * friendlyfactor div 100)
   619             end;
   791             end;
   620 RateHammer:= rate * 1024;
   792 RateHammer:= rate * 1024;
   628 GoInfo.JumpType:= jmpNone;
   800 GoInfo.JumpType:= jmpNone;
   629 bX:= hwRound(Gear^.X);
   801 bX:= hwRound(Gear^.X);
   630 bY:= hwRound(Gear^.Y);
   802 bY:= hwRound(Gear^.Y);
   631 case JumpType of
   803 case JumpType of
   632     jmpNone: exit(false);
   804     jmpNone: exit(false);
   633     
   805 
   634     jmpHJump:
   806     jmpHJump:
   635         if TestCollisionYwithGear(Gear, -1) = 0 then
   807         if TestCollisionYwithGear(Gear, -1) = 0 then
   636         begin
   808         begin
   637             Gear^.dY:= -_0_2;
   809             Gear^.dY:= -_0_2;
   638             SetLittle(Gear^.dX);
   810             SetLittle(Gear^.dX);
   639             Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
   811             Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
   640         end
   812         end
   641     else
   813     else
   642         exit(false);
   814         exit(false);
   643         
   815 
   644     jmpLJump:
   816     jmpLJump:
   645         begin
   817         begin
   646             if TestCollisionYwithGear(Gear, -1) <> 0 then
   818             if TestCollisionYwithGear(Gear, -1) <> 0 then
   647                 if not TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) then
   819                 if not TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) then
   648                     Gear^.Y:= Gear^.Y - int2hwFloat(2)
   820                     Gear^.Y:= Gear^.Y - int2hwFloat(2)
   660             exit(false)
   832             exit(false)
   661         end
   833         end
   662 end;
   834 end;
   663 
   835 
   664 repeat
   836 repeat
   665         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then 
   837         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
   666             begin
   838             begin
   667             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color;
   839             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color;
   668             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   840             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   669             end;}
   841             end;}
   670             
   842 
   671     if not (hwRound(Gear^.Y) + cHHRadius < cWaterLine) then
   843     if not (hwRound(Gear^.Y) + cHHRadius < cWaterLine) then
   672         exit(false);
   844         exit(false);
   673     if (Gear^.State and gstMoving) <> 0 then
   845     if (Gear^.State and gstMoving) <> 0 then
   674     begin
   846     begin
   675         if (GoInfo.Ticks = 350) then
   847         if (GoInfo.Ticks = 350) then
   723 GoInfo.Ticks:= 0;
   895 GoInfo.Ticks:= 0;
   724 GoInfo.FallPix:= 0;
   896 GoInfo.FallPix:= 0;
   725 GoInfo.JumpType:= jmpNone;
   897 GoInfo.JumpType:= jmpNone;
   726 tY:= hwRound(Gear^.Y);
   898 tY:= hwRound(Gear^.Y);
   727 repeat
   899 repeat
   728         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then 
   900         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
   729             begin
   901             begin
   730             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
   902             LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
   731             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   903             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
   732             end;}
   904             end;}
   733 
   905 
   737         begin
   909         begin
   738         if AltGear^.Hedgehog^.BotLevel < 4 then
   910         if AltGear^.Hedgehog^.BotLevel < 4 then
   739             AddWalkBonus(pX, tY, 250, -40);
   911             AddWalkBonus(pX, tY, 250, -40);
   740         exit(false)
   912         exit(false)
   741         end;
   913         end;
   742         
   914 
   743     // hog is falling    
   915     // hog is falling
   744     if (Gear^.State and gstMoving) <> 0 then
   916     if (Gear^.State and gstMoving) <> 0 then
   745         begin
   917         begin
   746         inc(GoInfo.Ticks);
   918         inc(GoInfo.Ticks);
   747         Gear^.dY:= Gear^.dY + cGravity;
   919         Gear^.dY:= Gear^.dY + cGravity;
   748         if Gear^.dY > _0_4 then
   920         if Gear^.dY > _0_4 then
   749             begin
   921             begin
   750             GoInfo.FallPix:= 0;
   922             GoInfo.FallPix:= 0;
   751             // try ljump instead of fall with damage
   923             // try ljump instead of fall with damage
   752             HHJump(AltGear, jmpLJump, GoInfo); 
   924             HHJump(AltGear, jmpLJump, GoInfo);
   753             if AltGear^.Hedgehog^.BotLevel < 4 then
   925             if AltGear^.Hedgehog^.BotLevel < 4 then
   754                 AddWalkBonus(pX, tY, 175, -20);
   926                 AddWalkBonus(pX, tY, 175, -20);
   755             exit(false)
   927             exit(false)
   756             end;
   928             end;
   757         Gear^.Y:= Gear^.Y + Gear^.dY;
   929         Gear^.Y:= Gear^.Y + Gear^.dY;