hedgewars/uAIMisc.pas
branch0.9.19
changeset 9081 7f04ad2cda54
parent 8972 f01b836d702e
child 9080 9b42757d7e71
equal deleted inserted replaced
8932:e095e3023682 9081:7f04ad2cda54
    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;
    53 
    57 
    54 procedure initModule;
    58 procedure initModule;
    55 procedure freeModule;
    59 procedure freeModule;
    56 
    60 
    57 procedure FillTargets;
    61 procedure FillTargets;
       
    62 procedure ResetTargets; inline;
    58 procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
    63 procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
    59 procedure FillBonuses(isAfterAttack: boolean);
    64 procedure FillBonuses(isAfterAttack: boolean);
    60 procedure AwareOfExplosion(x, y, r: LongInt); inline;
    65 procedure AwareOfExplosion(x, y, r: LongInt); inline;
    61 
    66 
    62 function  RatePlace(Gear: PGear): LongInt;
    67 function  RatePlace(Gear: PGear): LongInt;
    63 function  TestColl(x, y, r: LongInt): boolean; inline;
    68 function  TestColl(x, y, r: LongInt): boolean; inline;
    64 function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
    69 function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
    65 function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
    70 function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
    66 function  TraceShoveFall(x, y, dX, dY: Real): LongInt;
       
    67 
    71 
    68 function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
    72 function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
    69 function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
    73 function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
    70 function  RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
    74 function  RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
       
    75 function  RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
    71 function  RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
    76 function  RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
    72 function  RateHammer(Me: PGear): LongInt;
    77 function  RateHammer(Me: PGear): LongInt;
    73 
    78 
    74 function  HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
    79 function  HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
    75 function  AIrndSign(num: LongInt): LongInt;
    80 function  AIrndSign(num: LongInt): LongInt;
    97 var
   102 var
    98     KnownExplosion: record
   103     KnownExplosion: record
    99         X, Y, Radius: LongInt
   104         X, Y, Radius: LongInt
   100         end = (X: 0; Y: 0; Radius: 0);
   105         end = (X: 0; Y: 0; Radius: 0);
   101 
   106 
       
   107 procedure ResetTargets; inline;
       
   108 var i: LongWord;
       
   109 begin
       
   110 if Targets.reset then
       
   111     for i:= 0 to Targets.Count do
       
   112         Targets.ar[i].dead:= false;
       
   113 Targets.reset:= false;
       
   114 end;
   102 procedure FillTargets;
   115 procedure FillTargets;
   103 var i, t: Longword;
   116 var i, t: Longword;
   104     f, e: LongInt;
   117     f, e: LongInt;
       
   118     Gear: PGear;
   105 begin
   119 begin
   106 Targets.Count:= 0;
   120 Targets.Count:= 0;
       
   121 Targets.reset:= false;
   107 f:= 0;
   122 f:= 0;
   108 e:= 0;
   123 e:= 0;
   109 for t:= 0 to Pred(TeamsCount) do
   124 Gear:= GearsList;
   110     with TeamsArray[t]^ do
   125 while Gear <> nil do
   111         if not hasGone then
   126     begin
   112             begin
   127     if  (((Gear^.Kind = gtHedgehog) and
   113             for i:= 0 to cMaxHHIndex do
   128             (Gear <> ThinkingHH) and
   114                 if (Hedgehogs[i].Gear <> nil)
   129             (Gear^.Health > Gear^.Damage) and
   115                 and (Hedgehogs[i].Gear <> ThinkingHH)
   130             not(Gear^.Hedgehog^.Team^.hasgone)) or
   116                 and (Hedgehogs[i].Gear^.Health > Hedgehogs[i].Gear^.Damage)
   131         ((Gear^.Kind = gtExplosives) and
   117                     then
   132             (Gear^.Health > Gear^.Damage)) or
   118                     begin
   133         ((Gear^.Kind = gtMine) and
   119                     with Targets.ar[Targets.Count], Hedgehogs[i] do
   134             (Gear^.Health = 0) and
   120                         begin
   135              (Gear^.Damage < 35))
   121                         skip:= false;
   136              )  and 
   122                         matters:= (Hedgehogs[i].Gear^.AIHints and aihDoesntMatter) = 0;
   137         (Targets.Count < 256) then
   123 
   138         begin
   124                         Point.X:= hwRound(Gear^.X);
   139         with Targets.ar[Targets.Count] do
   125                         Point.Y:= hwRound(Gear^.Y);
   140             begin
   126                         if Clan <> CurrentTeam^.Clan then
   141             skip:= false;
   127                             begin
   142             dead:= false;
   128                             Score:= Gear^.Health - Gear^.Damage;
   143             Kind:= Gear^.Kind;
   129                             inc(e)
   144             Radius:= Gear^.Radius;
   130                             end else
   145             Density:= hwFloat2Float(Gear^.Density)/3;
   131                             begin
   146             State:= Gear^.State;
   132                             Score:= Gear^.Damage - Gear^.Health;
   147             matters:= (Gear^.AIHints and aihDoesntMatter) = 0;
   133                             inc(f)
   148 
   134                             end
   149             Point.X:= hwRound(Gear^.X);
   135                         end;
   150             Point.Y:= hwRound(Gear^.Y);
   136                     inc(Targets.Count)
   151             if (Gear^.Kind = gtHedgehog) then
       
   152                 begin
       
   153                 if (Gear^.Hedgehog^.Team^.Clan = CurrentTeam^.Clan) then
       
   154                     begin
       
   155                     Score:= Gear^.Damage - Gear^.Health;
       
   156                     inc(f)
       
   157                     end
       
   158                 else 
       
   159                     begin
       
   160                     Score:= Gear^.Health - Gear^.Damage;
       
   161                     inc(e)
   137                     end;
   162                     end;
   138             end;
   163                 end
       
   164             else if Gear^.Kind = gtExplosives then
       
   165                 Score:= Gear^.Health - Gear^.Damage
       
   166             else if Gear^.Kind = gtMine then 
       
   167                 Score:= max(0,35-Gear^.Damage);
       
   168             end;
       
   169         inc(Targets.Count)
       
   170         end;
       
   171     Gear:= Gear^.NextGear
       
   172     end;
   139 
   173 
   140 if e > f then friendlyfactor:= 300 + (e - f) * 30
   174 if e > f then friendlyfactor:= 300 + (e - f) * 30
   141 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e))
   175 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e))
   142 end;
   176 end;
   143 
   177 
   284     if not CheckBounds(x, y, r) then
   318     if not CheckBounds(x, y, r) then
   285         exit(false);
   319         exit(false);
   286 
   320 
   287     if (Land[y-r, x-r] > lfAllObjMask) or
   321     if (Land[y-r, x-r] > lfAllObjMask) or
   288        (Land[y+r, x-r] > lfAllObjMask) or
   322        (Land[y+r, x-r] > lfAllObjMask) or
   289        (Land[y-r, x+r] > lfAllObjMask) or
   323        (Land[y-r, x-r] > lfAllObjMask) or
   290        (Land[y+r, x+r] > lfAllObjMask) then
   324        (Land[y+r, x+r] > lfAllObjMask) then
   291        exit(true);
   325        exit(true);
   292 
   326 
   293     TestCollExcludingObjects:= false;
   327     TestCollExcludingObjects:= false;
   294 end;
   328 end;
   298     if not CheckBounds(x, y, r) then
   332     if not CheckBounds(x, y, r) then
   299         exit(false);
   333         exit(false);
   300 
   334 
   301     if (Land[y-r, x-r] and lfNotCurrentMask <> 0) or
   335     if (Land[y-r, x-r] and lfNotCurrentMask <> 0) or
   302        (Land[y+r, x-r] and lfNotCurrentMask <> 0) or
   336        (Land[y+r, x-r] and lfNotCurrentMask <> 0) or
   303        (Land[y-r, x+r] and lfNotCurrentMask <> 0) or
   337        (Land[y+r, x-r] and lfNotCurrentMask <> 0) or
   304        (Land[y+r, x+r] and lfNotCurrentMask <> 0) then
   338        (Land[y+r, x+r] and lfNotCurrentMask <> 0) then
   305        exit(true);
   339        exit(true);
   306 
   340 
   307     TestColl:= false;
   341     TestColl:= false;
   308 end;
   342 end;
   324     TestCollExcludingMe:= TestCollWithEverything(x, y, r)
   358     TestCollExcludingMe:= TestCollWithEverything(x, y, r)
   325 end;
   359 end;
   326 
   360 
   327 
   361 
   328 
   362 
   329 function TraceFall(eX, eY: LongInt; x, y, dX, dY: Real; r: LongWord): LongInt;
   363 function TraceFall(eX, eY: LongInt; var x, y: Real; dX, dY: Real; r: LongWord; Target: TTarget): LongInt;
   330 var skipLandCheck: boolean;
   364 var skipLandCheck: boolean;
   331     rCorner: real;
   365     rCorner, dxdy, odX, odY: real;
   332     dmg: LongInt;
   366     dmg: LongInt;
   333 begin
   367 begin
       
   368     odX:= dX;
       
   369     odY:= dY;
   334     skipLandCheck:= true;
   370     skipLandCheck:= true;
   335     if x - eX < 0 then dX:= -dX;
       
   336     if y - eY < 0 then dY:= -dY;
       
   337     // ok. attempt approximate search for an unbroken trajectory into water.  if it continues far enough, assume out of map
   371     // ok. attempt approximate search for an unbroken trajectory into water.  if it continues far enough, assume out of map
   338     rCorner:= r * 0.75;
   372     rCorner:= r * 0.75;
   339     while true do
   373     while true do
   340     begin
   374         begin
   341         x:= x + dX;
   375         x:= x + dX;
   342         y:= y + dY;
   376         y:= y + dY;
   343         dY:= dY + cGravityf;
   377         dY:= dY + cGravityf;
   344         skipLandCheck:= skipLandCheck and (r <> 0) and (abs(eX-x) + abs(eY-y) < r) and ((abs(eX-x) < rCorner) or (abs(eY-y) < rCorner));
   378         skipLandCheck:= skipLandCheck and (r <> 0) and (abs(eX-x) + abs(eY-y) < r) and ((abs(eX-x) < rCorner) or (abs(eY-y) < rCorner));
   345         if not skipLandCheck and TestCollExcludingObjects(trunc(x), trunc(y), cHHRadius) then
   379         if not skipLandCheck and TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then
   346         begin
   380             with Target do
   347             if 0.4 < dY then
   381                 begin
   348             begin
   382                 if (Kind = gtHedgehog) and (0.4 < dY) then
   349                 dmg := 1 + trunc((abs(dY) - 0.4) * 70);
   383                     begin
   350                 if dmg >= 1 then
   384                     dmg := 1 + trunc((dY - 0.4) * 70);
   351                     exit(dmg);
   385                     exit(dmg)
   352             end;
   386                     end
       
   387                 else 
       
   388                     begin
       
   389                     dxdy:= abs(dX)+abs(dY);
       
   390                     if ((Kind = gtMine) and (dxdy > 0.35)) or 
       
   391                        ((Kind = gtExplosives) and 
       
   392                             (((State and gstTmpFlag <> 0) and (dxdy > 0.35)) or
       
   393                              ((State and gstTmpFlag = 0) and 
       
   394                                 ((abs(odX) > 0.15) or ((abs(odY) > 0.15) and 
       
   395                                 (abs(odX) > 0.02))) and (dxdy > 0.35)))) then
       
   396                         begin
       
   397                         dmg := trunc(dxdy * 25);
       
   398                         exit(dmg)
       
   399                         end
       
   400                     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
       
   401                         begin
       
   402                         dmg := trunc(dy * 70);
       
   403                         exit(dmg)
       
   404                         end
       
   405                     end;
   353             exit(0)
   406             exit(0)
   354         end;
   407             end;
   355         if (y > cWaterLine) or (x > 4096) or (x < 0) then
   408         if (y > cWaterLine) or (x > rightX) or (x < leftX) then exit(-1)
   356             exit(-1);
   409         end
   357     end;
   410 end;
   358 end;
   411 
   359 
   412 function TraceShoveFall(var x, y: Real; dX, dY: Real; Target: TTarget): LongInt;
   360 function TraceShoveFall(x, y, dX, dY: Real): LongInt;
       
   361 var dmg: LongInt;
   413 var dmg: LongInt;
   362 begin
   414     dxdy, odX, odY: real;
       
   415 begin
       
   416     odX:= dX;
       
   417     odY:= dY;
   363 //v:= random($FFFFFFFF);
   418 //v:= random($FFFFFFFF);
   364     while true do
   419     while true do
   365     begin
   420         begin
   366         x:= x + dX;
   421         x:= x + dX;
   367         y:= y + dY;
   422         y:= y + dY;
   368         dY:= dY + cGravityf;
   423         dY:= dY + cGravityf;
   369 
   424 
   370 {        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then
   425 {        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then
   371             begin
   426             begin
   372             LandPixels[trunc(y), trunc(x)]:= v;
   427             LandPixels[trunc(y), trunc(x)]:= v;
   373             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
   428             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
   374             end;}
   429             end;}
   375 
   430 
   376 
   431         if TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then
   377         // consider adding dX/dY calc here for fall damage
   432             with Target do
   378         if TestCollExcludingObjects(trunc(x), trunc(y), cHHRadius) then
   433                 begin
   379         begin
   434                 if (Kind = gtHedgehog) and (0.4 < dY) then
   380             if 0.4 < dY then
   435                     begin
   381             begin
   436                     dmg := trunc((dY - 0.4) * 70);
   382                 dmg := 1 + trunc((abs(dY) - 0.4) * 70);
       
   383                 if dmg >= 1 then
       
   384                     exit(dmg);
   437                     exit(dmg);
   385             end;
   438                     end
       
   439                 else 
       
   440                     begin
       
   441                     dxdy:= abs(dX)+abs(dY);
       
   442                     if ((Kind = gtMine) and (dxdy > 0.4)) or 
       
   443                        ((Kind = gtExplosives) and 
       
   444                             (((State and gstTmpFlag <> 0) and (dxdy > 0.4)) or
       
   445                              ((State and gstTmpFlag = 0) and 
       
   446                                 ((abs(odX) > 0.15) or ((abs(odY) > 0.15) and 
       
   447                                 (abs(odX) > 0.02))) and (dxdy > 0.35)))) then
       
   448                         begin
       
   449                         dmg := trunc(dxdy * 50);
       
   450                         exit(dmg)
       
   451                         end
       
   452                     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
       
   453                         begin
       
   454                         dmg := trunc(dy * 70);
       
   455                         exit(dmg)
       
   456                         end
       
   457                     end;
   386             exit(0)
   458             exit(0)
   387         end;
   459         end;
   388         if (y > cWaterLine) or (x > 4096) or (x < 0) then
   460         if (y > cWaterLine) or (x > rightX) or (x < leftX) then
   389             // returning -1 for drowning so it can be considered in the Rate routine
   461             // returning -1 for drowning so it can be considered in the Rate routine
   390             exit(-1)
   462             exit(-1)
   391     end;
   463     end;
   392 end;
   464 end;
   393 
   465 
   394 function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt;
   466 function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
   395 begin
   467 begin
   396     RateExplosion:= RateExplosion(Me, x, y, r, 0);
   468     RateExplosion:= RealRateExplosion(Me, x, y, r, 0);
   397 end;
   469     ResetTargets;
   398 
   470 end;
   399 function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
   471 function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
   400 var i, fallDmg, dmg, dmgBase, rate, erasure: LongInt;
   472 begin
   401     dX, dY: real;
   473     RateExplosion:= RealRateExplosion(Me, x, y, r, Flags);
       
   474     ResetTargets;
       
   475 end;
       
   476 
       
   477 function RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
       
   478 var i, fallDmg, dmg, dmgBase, rate, subrate, erasure: LongInt;
       
   479     pX, pY, dX, dY: real;
   402     hadSkips: boolean;
   480     hadSkips: boolean;
   403 begin
   481 begin
   404 fallDmg:= 0;
   482 fallDmg:= 0;
   405 rate:= 0;
   483 rate:= 0;
   406 // add our virtual position
   484 // add our virtual position
   408     begin
   486     begin
   409     Point.x:= hwRound(Me^.X);
   487     Point.x:= hwRound(Me^.X);
   410     Point.y:= hwRound(Me^.Y);
   488     Point.y:= hwRound(Me^.Y);
   411     skip:= false;
   489     skip:= false;
   412     matters:= true;
   490     matters:= true;
       
   491     Kind:= gtHedgehog;
       
   492     Density:= 1;
       
   493     Radius:= cHHRadius;
   413     Score:= - ThinkingHH^.Health
   494     Score:= - ThinkingHH^.Health
   414     end;
   495     end;
   415 // rate explosion
   496 // rate explosion
   416 dmgBase:= r + cHHRadius div 2;
       
   417 
   497 
   418 if (Flags and afErasesLand <> 0) and (GameFlags and gfSolidLand = 0) then erasure:= r
   498 if (Flags and afErasesLand <> 0) and (GameFlags and gfSolidLand = 0) then erasure:= r
   419 else erasure:= 0;
   499 else erasure:= 0;
   420 
   500 
   421 hadSkips:= false;
   501 hadSkips:= false;
   422 
   502 
   423 for i:= 0 to Targets.Count do
   503 for i:= 0 to Targets.Count do
   424     with Targets.ar[i] do
   504     if not Targets.ar[i].dead then
   425       if not matters then hadSkips:= true
   505         with Targets.ar[i] do
   426         else
   506           if not matters then hadSkips:= true
   427         begin
   507             else
   428         dmg:= 0;
   508             begin
   429         if abs(Point.x - x) + abs(Point.y - y) < dmgBase then
   509             dmg:= 0;
   430             dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r));
   510             dmgBase:= r + Radius div 2;
   431 
   511             if abs(Point.x - x) + abs(Point.y - y) < dmgBase then
   432         if dmg > 0 then
   512                 dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r));
   433             begin
   513 
   434             if (Flags and afTrackFall <> 0) and (dmg < abs(Score)) then
   514             if dmg > 0 then
   435                 begin
   515                 begin
   436                 dX:= 0.005 * dmg + 0.01;
   516                 pX:= Point.x;
   437                 dY:= dX;
   517                 pY:= Point.y;
   438                 if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
   518                 fallDmg:= 0;
   439                    (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
   519                 if (Flags and afTrackFall <> 0) and (Score > 0) and (dmg < Score) then
   440                      fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod)
   520                     begin
   441                 else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod)
   521                     dX:= (0.005 * dmg + 0.01) / Density;
   442                 end;
   522                     dY:= dX;
   443             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   523                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and 
   444                 if Score > 0 then
   524                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
   445                     inc(rate, (KillScore + Score div 10) * 1024)   // Add a bit of a bonus for bigger hog drownings
   525                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
   446                 else
   526                         dX:= 0;
   447                     dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
   527 
   448             else if (dmg+fallDmg) >= abs(Score) then
   528                     if pX - x < 0 then dX:= -dX;
   449                 if Score > 0 then
   529                     if pY - y < 0 then dY:= -dY;
   450                     inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
   530 
   451                 else
   531                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
   452                     dec(rate, KillScore * friendlyfactor div 100 * 1024)
   532                        (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
   453             else
   533                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
   454                 if Score > 0 then
   534                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
   455                     inc(rate, (dmg + fallDmg) * 1024)
   535                     end;
   456                 else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
   536                 if Kind = gtHedgehog then
   457             end;
   537                     begin
   458         end;
   538                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   539                         begin
       
   540                         if Score > 0 then
       
   541                             inc(rate, (KillScore + Score div 10) * 1024)   // Add a bit of a bonus for bigger hog drownings
       
   542                         else
       
   543                             dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
       
   544                         end
       
   545                     else if (dmg+fallDmg) >= abs(Score) then
       
   546                         begin
       
   547                         dead:= true;
       
   548                         Targets.reset:= true;
       
   549                         if dX < 0.035 then
       
   550                             begin
       
   551                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or (Flags and afTrackFall));
       
   552                             if abs(subrate) > 2000 then inc(Rate,subrate)
       
   553                             end;
       
   554                         if Score > 0 then
       
   555                              inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
       
   556                         else dec(rate, KillScore * friendlyfactor div 100 * 1024)
       
   557                         end
       
   558                     else
       
   559                         begin
       
   560                         if Score > 0 then
       
   561                              inc(rate, (dmg + fallDmg) * 1024)
       
   562                         else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
       
   563                         end
       
   564                     end
       
   565                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   566                     begin
       
   567                     dead:= true;
       
   568                     Targets.reset:= true;
       
   569                     if Kind = gtExplosives then
       
   570                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall))
       
   571                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall));
       
   572                     if abs(subrate) > 2000 then inc(Rate,subrate);
       
   573                     end
       
   574                 end
       
   575             end;
   459 
   576 
   460 if hadSkips and (rate = 0) then
   577 if hadSkips and (rate = 0) then
   461     RateExplosion:= BadTurn
   578     RealRateExplosion:= BadTurn
   462     else
   579     else
   463     RateExplosion:= rate;
   580     RealRateExplosion:= rate;
   464 end;
   581 end;
   465 
   582 
   466 function RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
   583 function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
   467 var i, fallDmg, dmg, rate: LongInt;
   584 var i, fallDmg, dmg, rate, subrate: LongInt;
   468     dX, dY: real;
   585     dX, dY, pX, pY: real;
   469 begin
   586 begin
   470 fallDmg:= 0;
   587 fallDmg:= 0;
   471 dX:= gdX * 0.01 * kick;
   588 dX:= gdX * 0.01 * kick;
   472 dY:= gdY * 0.01 * kick;
   589 dY:= gdY * 0.01 * kick;
   473 rate:= 0;
   590 rate:= 0;
   474 for i:= 0 to Pred(Targets.Count) do
   591 for i:= 0 to Pred(Targets.Count) do
   475     with Targets.ar[i] do
   592     with Targets.ar[i] do
   476       if skip then
   593         if skip then
   477         if (Flags and afSetSkip = 0) then skip:= false else {still skip}
   594             begin
   478       else if matters then
   595             if Flags and afSetSkip = 0 then skip:= false
   479         begin
   596             end
   480         dmg:= 0;
   597         else if matters then
   481         if abs(Point.x - x) + abs(Point.y - y) < r then
   598             begin
   482             dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   599             dmg:= 0;
   483 
   600             if abs(Point.x - x) + abs(Point.y - y) < r then
   484         if dmg > 0 then
   601                 dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   485             begin
   602 
   486             if (Flags and afSetSkip <> 0) then skip:= true;
   603             if dmg > 0 then
   487             if (Flags and afTrackFall <> 0) and (Score > 0) then
   604                 begin
   488                 fallDmg:= trunc(TraceShoveFall(Point.x, Point.y - 2, dX, dY) * dmgMod);
   605                 pX:= Point.x;
   489             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   606                 pY:= Point.y-2;
   490                 if Score > 0 then
   607                 fallDmg:= 0;
   491                     inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
   608                 if (Flags and afSetSkip <> 0) then skip:= true;
   492                 else
   609                 if not(dead) and (Flags and afTrackFall <> 0) and (Score > 0) and (power < Score) then
   493                     dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
   610                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and 
   494             else if power+fallDmg >= abs(Score) then
   611                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
   495                 if Score > 0 then
   612                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
   496                     inc(rate, KillScore)
   613                         fallDmg:= trunc(TraceShoveFall(pX, pY, 0, dY, Targets.ar[i]) * dmgMod)
   497                 else
   614                     else
   498                     dec(rate, KillScore * friendlyfactor div 100)
   615                         fallDmg:= trunc(TraceShoveFall(pX, pY, dX, dY, Targets.ar[i]) * dmgMod);
   499             else
   616                 if Kind = gtHedgehog then
   500                 if Score > 0 then
   617                     begin
   501                     inc(rate, power+fallDmg)
   618                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
   502                 else
   619                         begin
   503                     dec(rate, (power+fallDmg) * friendlyfactor div 100)
   620                         if Score > 0 then
   504             end;
   621                             inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
   505         end;
   622                         else
   506 RateShove:= rate * 1024
   623                             dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   624                         end
       
   625                     else if power+fallDmg >= abs(Score) then
       
   626                         begin
       
   627                         dead:= true;
       
   628                         Targets.reset:= true;
       
   629                         if dX < 0.035 then
       
   630                             begin
       
   631                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall);
       
   632                             if abs(subrate) > 2000 then inc(Rate,subrate div 1024)
       
   633                             end;
       
   634                         if Score > 0 then
       
   635                             inc(rate, KillScore)
       
   636                         else
       
   637                             dec(rate, KillScore * friendlyfactor div 100)
       
   638                         end
       
   639                     else
       
   640                         begin
       
   641                         if Score > 0 then
       
   642                             inc(rate, power+fallDmg)
       
   643                         else
       
   644                             dec(rate, (power+fallDmg) * friendlyfactor div 100)
       
   645                         end
       
   646                     end
       
   647                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   648                     begin
       
   649                     dead:= true;
       
   650                     Targets.reset:= true;
       
   651                     if Kind = gtExplosives then
       
   652                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall))
       
   653                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall));
       
   654                     if abs(subrate) > 2000 then inc(Rate,subrate div 1024);
       
   655                     end
       
   656                 end
       
   657             end;
       
   658 RateShove:= rate * 1024;
       
   659 ResetTargets
   507 end;
   660 end;
   508 
   661 
   509 function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
   662 function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
   510 var i, dmg, fallDmg, baseDmg, rate, erasure: LongInt;
   663 var i, dmg, fallDmg, baseDmg, rate, subrate, erasure: LongInt;
   511     dX, dY: real;
   664     pX, pY, dX, dY: real;
   512     hadSkips: boolean;
   665     hadSkips: boolean;
   513 begin
   666 begin
   514 rate:= 0;
   667 rate:= 0;
   515 gdX:= gdX * 0.01;
   668 gdX:= gdX * 0.01;
   516 gdY:= gdX * 0.01;
   669 gdY:= gdX * 0.01;
   519     begin
   672     begin
   520     Point.x:= hwRound(Me^.X);
   673     Point.x:= hwRound(Me^.X);
   521     Point.y:= hwRound(Me^.Y);
   674     Point.y:= hwRound(Me^.Y);
   522     skip:= false;
   675     skip:= false;
   523     matters:= true;
   676     matters:= true;
       
   677     Kind:= gtHedgehog;
       
   678     Density:= 1;
       
   679     Radius:= cHHRadius;
   524     Score:= - ThinkingHH^.Health
   680     Score:= - ThinkingHH^.Health
   525     end;
   681     end;
   526 // rate shot
   682 // rate shot
   527 baseDmg:= cHHRadius + cShotgunRadius + 4;
   683 baseDmg:= cHHRadius + cShotgunRadius + 4;
   528 
   684 
   530 else erasure:= 0;
   686 else erasure:= 0;
   531 
   687 
   532 hadSkips:= false;
   688 hadSkips:= false;
   533 
   689 
   534 for i:= 0 to Targets.Count do
   690 for i:= 0 to Targets.Count do
   535     with Targets.ar[i] do
   691     if not Targets.ar[i].dead then
   536       if not matters then hadSkips:= true
   692         with Targets.ar[i] do
   537         else
   693           if not matters then hadSkips:= true
   538         begin
       
   539         dmg:= 0;
       
   540         if abs(Point.x - x) + abs(Point.y - y) < baseDmg then
       
   541             begin
       
   542             dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25);
       
   543             dmg:= trunc(dmg * dmgMod);
       
   544             end;
       
   545         if dmg > 0 then
       
   546             begin
       
   547             dX:= gdX * dmg;
       
   548             dY:= gdY * dmg;
       
   549             if dX < 0 then dX:= dX - 0.01
       
   550             else dX:= dX + 0.01;
       
   551             if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
       
   552                (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
       
   553                  fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod)
       
   554             else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod);
       
   555             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   556                 if Score > 0 then
       
   557                     inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
       
   558                 else
       
   559                     dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   560             else if (dmg+fallDmg) >= abs(Score) then
       
   561                 if Score > 0 then
       
   562                     inc(rate, KillScore)
       
   563                 else
       
   564                     dec(rate, KillScore * friendlyfactor div 100)
       
   565             else
   694             else
   566                 if Score > 0 then
   695             begin
   567                     inc(rate, dmg+fallDmg)
   696             dmg:= 0;
   568             else
   697             if abs(Point.x - x) + abs(Point.y - y) < baseDmg then
   569                 dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
   698                 begin
   570             end;
   699                 dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25);
   571         end;
   700                 dmg:= trunc(dmg * dmgMod);
       
   701                 end;
       
   702             if dmg > 0 then
       
   703                 begin
       
   704                 if not(dead) and (Score > 0) and (dmg < Score) then
       
   705                     begin
       
   706                     pX:= Point.x;
       
   707                     pY:= Point.y;
       
   708                     dX:= gdX * dmg / Density;
       
   709                     dY:= gdY * dmg / Density;
       
   710                     if dX < 0 then dX:= dX - 0.01
       
   711                     else dX:= dX + 0.01;
       
   712                     if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and 
       
   713                        (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or
       
   714                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then 
       
   715                        dX:= 0;
       
   716                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
       
   717                        (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
       
   718                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
       
   719                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
       
   720                     end;
       
   721                 if Kind = gtHedgehog then
       
   722                     begin
       
   723                     if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
       
   724                         begin
       
   725                         if Score > 0 then
       
   726                             inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
       
   727                         else
       
   728                             dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
       
   729                         end
       
   730                     else if (dmg+fallDmg) >= abs(Score) then
       
   731                         begin
       
   732                         dead:= true;
       
   733                         Targets.reset:= true;
       
   734                         if dX < 0.035 then
       
   735                             begin
       
   736                             subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall);
       
   737                             if abs(subrate) > 2000 then inc(Rate,subrate div 1024)
       
   738                             end;
       
   739                         if Score > 0 then
       
   740                             inc(rate, KillScore)
       
   741                         else
       
   742                             dec(rate, KillScore * friendlyfactor div 100)
       
   743                         end
       
   744                     else if Score > 0 then
       
   745                          inc(rate, dmg+fallDmg)
       
   746                     else dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
       
   747                     end
       
   748                 else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then
       
   749                     begin
       
   750                     dead:= true;
       
   751                     Targets.reset:= true;
       
   752                     if Kind = gtExplosives then
       
   753                          subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or afTrackFall)
       
   754                     else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or afTrackFall);
       
   755                     if abs(subrate) > 2000 then inc(Rate,subrate div 1024);
       
   756                     end
       
   757                 end
       
   758             end;
   572 
   759 
   573 if hadSkips and (rate = 0) then
   760 if hadSkips and (rate = 0) then
   574     RateShotgun:= BadTurn
   761     RateShotgun:= BadTurn
   575     else
   762     else
   576     RateShotgun:= rate * 1024;
   763     RateShotgun:= rate * 1024;
       
   764     ResetTargets;
   577 end;
   765 end;
   578 
   766 
   579 function RateHammer(Me: PGear): LongInt;
   767 function RateHammer(Me: PGear): LongInt;
   580 var x, y, i, r, rate: LongInt;
   768 var x, y, i, r, rate: LongInt;
   581 begin
   769 begin
   584 y:= hwRound(Me^.Y);
   772 y:= hwRound(Me^.Y);
   585 rate:= 0;
   773 rate:= 0;
   586 
   774 
   587 for i:= 0 to Pred(Targets.Count) do
   775 for i:= 0 to Pred(Targets.Count) do
   588     with Targets.ar[i] do
   776     with Targets.ar[i] do
   589       if matters then
       
   590          // hammer hit radius is 8, shift is 10
   777          // hammer hit radius is 8, shift is 10
   591         if abs(Point.x - x) + abs(Point.y - y) < 18 then
   778       if matters and (Kind = gtHedgehog) and (abs(Point.x - x) + abs(Point.y - y) < 18) then
   592             begin
   779             begin
   593             r:= trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   780             r:= trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)));
   594 
   781 
   595             if r <= 18 then
   782             if r <= 18 then
   596                 if Score > 0 then
   783                 if Score > 0 then