diff -r bde641cf53c8 -r e350500c4edb hedgewars/uAIMisc.pas --- a/hedgewars/uAIMisc.pas Thu Apr 04 14:37:19 2013 +0200 +++ b/hedgewars/uAIMisc.pas Tue Jun 04 22:28:12 2013 +0200 @@ -1,6 +1,6 @@ (* * Hedgewars, a free turn based strategy game - * Copyright (c) 2004-2012 Andrey Korotaev + * Copyright (c) 2004-2013 Andrey Korotaev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,14 +30,18 @@ BadTurn = Low(LongInt) div 4; -type TTarget = record +type TTarget = record // starting to look more and more like a gear Point: TPoint; - Score: LongInt; - skip, matters: boolean; + Score, Radius: LongInt; + State: LongWord; + Density: real; + skip, matters, dead: boolean; + Kind: TGearType; end; TTargets = record Count: Longword; - ar: array[0..Pred(cMaxHHs)] of TTarget; + ar: array[0..Pred(256)] of TTarget; + reset: boolean; end; TJumpType = (jmpNone, jmpHJump, jmpLJump); TGoInfo = record @@ -65,6 +69,7 @@ procedure freeModule; procedure FillTargets; +procedure ResetTargets; inline; procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline; procedure FillBonuses(isAfterAttack: boolean); procedure AwareOfExplosion(x, y, r: LongInt); inline; @@ -73,12 +78,11 @@ function TestColl(x, y, r: LongInt): boolean; inline; function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline; function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline; -function TraceShoveFall(x, y, dX, dY: Real): LongInt; -function TestCollWithLand(x, y, r: LongInt): boolean; inline; function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline; -function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; -function RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt; +function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline; +function RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; +function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt; function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt; function RateHammer(Me: PGear): LongInt; @@ -94,52 +98,82 @@ const KillScore = 200; var friendlyfactor: LongInt = 300; +var dmgMod: real = 1.0; implementation uses uCollisions, uVariables, uUtils, uLandTexture, uGearsUtils; -var +var KnownExplosion: record X, Y, Radius: LongInt end = (X: 0; Y: 0; Radius: 0); +procedure ResetTargets; inline; +var i: LongWord; +begin +if Targets.reset then + for i:= 0 to Targets.Count do + Targets.ar[i].dead:= false; +Targets.reset:= false; +end; procedure FillTargets; var i, t: Longword; f, e: LongInt; + Gear: PGear; begin Targets.Count:= 0; +Targets.reset:= false; f:= 0; e:= 0; -for t:= 0 to Pred(TeamsCount) do - with TeamsArray[t]^ do - if not hasGone then +Gear:= GearsList; +while Gear <> nil do + begin + if (((Gear^.Kind = gtHedgehog) and + (Gear <> ThinkingHH) and + (Gear^.Health > Gear^.Damage) and + not(Gear^.Hedgehog^.Team^.hasgone)) or + ((Gear^.Kind = gtExplosives) and + (Gear^.Health > Gear^.Damage)) or + ((Gear^.Kind = gtMine) and + (Gear^.Health = 0) and + (Gear^.Damage < 35)) + ) and + (Targets.Count < 256) then + begin + with Targets.ar[Targets.Count] do begin - for i:= 0 to cMaxHHIndex do - if (Hedgehogs[i].Gear <> nil) - and (Hedgehogs[i].Gear <> ThinkingHH) - and (Hedgehogs[i].Gear^.Health > Hedgehogs[i].Gear^.Damage) - then + skip:= false; + dead:= false; + Kind:= Gear^.Kind; + Radius:= Gear^.Radius; + Density:= hwFloat2Float(Gear^.Density)/3; + State:= Gear^.State; + matters:= (Gear^.AIHints and aihDoesntMatter) = 0; + + Point.X:= hwRound(Gear^.X); + Point.Y:= hwRound(Gear^.Y); + if (Gear^.Kind = gtHedgehog) then + begin + if (Gear^.Hedgehog^.Team^.Clan = CurrentTeam^.Clan) then begin - with Targets.ar[Targets.Count], Hedgehogs[i] do - begin - skip:= false; - matters:= (Hedgehogs[i].Gear^.AIHints and aihDoesntMatter) = 0; - - Point.X:= hwRound(Gear^.X); - Point.Y:= hwRound(Gear^.Y); - if Clan <> CurrentTeam^.Clan then - begin - Score:= Gear^.Health - Gear^.Damage; - inc(e) - end else - begin - Score:= Gear^.Damage - Gear^.Health; - inc(f) - end - end; - inc(Targets.Count) + Score:= Gear^.Damage - Gear^.Health; + inc(f) + end + else + begin + Score:= Gear^.Health - Gear^.Damage; + inc(e) end; + end + else if Gear^.Kind = gtExplosives then + Score:= Gear^.Health - Gear^.Damage + else if Gear^.Kind = gtMine then + Score:= max(0,35-Gear^.Damage); end; + inc(Targets.Count) + end; + Gear:= Gear^.NextGear + end; if e > f then friendlyfactor:= 300 + (e - f) * 30 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e)) @@ -185,24 +219,24 @@ gtFlame: if (Gear^.State and gsttmpFlag) <> 0 then AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 20, -50); -// avoid mines unless they are very likely to be duds, or are duds. also avoid if they are about to blow +// avoid mines unless they are very likely to be duds, or are duds. also avoid if they are about to blow gtMine: if ((Gear^.State and gstAttacking) = 0) and (((cMineDudPercent < 90) and (Gear^.Health <> 0)) or (isAfterAttack and (Gear^.Health = 0) and (Gear^.Damage > 30))) then AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -50) else if (Gear^.State and gstAttacking) <> 0 then AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 100, -50); // mine is on - + gtExplosives: if isAfterAttack then AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 75, -60 + Gear^.Health); - + gtSMine: AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 50, -30); - + gtDynamite: AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 150, -75); - + gtHedgehog: begin if Gear^.Damage >= Gear^.Health then @@ -260,6 +294,58 @@ RatePlace:= rate; end; +function CheckBounds(x, y, r: Longint): boolean; inline; +begin + CheckBounds := (((x-r) and LAND_WIDTH_MASK) = 0) and + (((x+r) and LAND_WIDTH_MASK) = 0) and + (((y-r) and LAND_HEIGHT_MASK) = 0) and + (((y+r) and LAND_HEIGHT_MASK) = 0); +end; + + +function TestCollWithEverything(x, y, r: LongInt): boolean; inline; +begin + if not CheckBounds(x, y, r) then + exit(false); + + if (Land[y-r, x-r] <> 0) or + (Land[y+r, x-r] <> 0) or + (Land[y-r, x+r] <> 0) or + (Land[y+r, x+r] <> 0) then + exit(true); + + TestCollWithEverything := false; +end; + +function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline; +begin + if not CheckBounds(x, y, r) then + exit(false); + + if (Land[y-r, x-r] > lfAllObjMask) or + (Land[y+r, x-r] > lfAllObjMask) or + (Land[y-r, x-r] > lfAllObjMask) or + (Land[y+r, x+r] > lfAllObjMask) then + exit(true); + + TestCollExcludingObjects:= false; +end; + +function TestColl(x, y, r: LongInt): boolean; inline; +begin + if not CheckBounds(x, y, r) then + exit(false); + + if (Land[y-r, x-r] and lfNotCurrentMask <> 0) or + (Land[y+r, x-r] and lfNotCurrentMask <> 0) or + (Land[y+r, x-r] and lfNotCurrentMask <> 0) or + (Land[y+r, x+r] and lfNotCurrentMask <> 0) then + exit(true); + + TestColl:= false; +end; + + // Wrapper to test various approaches. If it works reasonably, will just replace. // Right now, converting to hwFloat is a tad inefficient since the x/y were hwFloat to begin with... function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline; @@ -270,155 +356,134 @@ MeX:= hwRound(Me^.X); MeY:= hwRound(Me^.Y); // We are still inside the hog. Skip radius test - if ((((x-MeX)*(x-MeX)) + ((y-MeY)*(y-MeY))) < 256) and (Land[y, x] <= lfAllObjMask) and ((Land[y, x] and lfObjMask) < 2) then + if ((sqr(x-MeX) + sqr(y-MeY)) < 256) and (Land[y, x] and lfObjMask = 0) then exit(false); end; - TestCollExcludingMe:= TestColl(x, y, r) -end; - -function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline; -var b: boolean; -begin - b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > lfAllObjMask); - if b then - exit(true); - - b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] > lfAllObjMask); - if b then - exit(true); - - b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > lfAllObjMask); - if b then - exit(true); - - b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > lfAllObjMask); - if b then - exit(true); - - TestCollExcludingObjects:= false; + TestCollExcludingMe:= TestCollWithEverything(x, y, r) end; -function TestColl(x, y, r: LongInt): boolean; inline; -var b: boolean; -begin - 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); - if b then - exit(true); - - 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); - if b then - exit(true); - - 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); - if b then - exit(true); - - 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); - if b then - exit(true); - - TestColl:= false; -end; + -function TestCollWithLand(x, y, r: LongInt): boolean; inline; -var b: boolean; -begin - b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > lfAllObjMask); - if b then - exit(true); - - b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] > lfAllObjMask); - if b then - exit(true); - - b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > lfAllObjMask); - if b then - exit(true); - - b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > lfAllObjMask); - if b then - exit(true); - - TestCollWithLand:= false; -end; - -function TraceFall(eX, eY: LongInt; x, y, dX, dY: Real; r: LongWord): LongInt; +function TraceFall(eX, eY: LongInt; var x, y: Real; dX, dY: Real; r: LongWord; Target: TTarget): LongInt; var skipLandCheck: boolean; - rCorner: real; + rCorner, dxdy, odX, odY: real; dmg: LongInt; begin + odX:= dX; + odY:= dY; skipLandCheck:= true; - if x - eX < 0 then dX:= -dX; - if y - eY < 0 then dY:= -dY; // ok. attempt approximate search for an unbroken trajectory into water. if it continues far enough, assume out of map rCorner:= r * 0.75; while true do - begin + begin x:= x + dX; y:= y + dY; dY:= dY + cGravityf; skipLandCheck:= skipLandCheck and (r <> 0) and (abs(eX-x) + abs(eY-y) < r) and ((abs(eX-x) < rCorner) or (abs(eY-y) < rCorner)); - if (not skipLandCheck) and TestCollWithLand(trunc(x), trunc(y), cHHRadius) then - begin - if 0.4 < dY then - begin - dmg := 1 + trunc((abs(dY) - 0.4) * 70); - if dmg >= 1 then - exit(dmg); + if not skipLandCheck and TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then + with Target do + begin + if (Kind = gtHedgehog) and (0.4 < dY) then + begin + dmg := 1 + trunc((dY - 0.4) * 70); + exit(dmg) + end + else + begin + dxdy:= abs(dX)+abs(dY); + if ((Kind = gtMine) and (dxdy > 0.35)) or + ((Kind = gtExplosives) and + (((State and gstTmpFlag <> 0) and (dxdy > 0.35)) or + ((State and gstTmpFlag = 0) and + ((abs(odX) > 0.15) or ((abs(odY) > 0.15) and + (abs(odX) > 0.02))) and (dxdy > 0.35)))) then + begin + dmg := trunc(dxdy * 25); + exit(dmg) + end + 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 + begin + dmg := trunc(dy * 70); + exit(dmg) + end + end; + exit(0) end; - exit(0) - end; - if (y > cWaterLine) or (x > 4096) or (x < 0) then - exit(-1); - end; + if (y > cWaterLine) or (x > rightX) or (x < leftX) then exit(-1) + end end; -function TraceShoveFall(x, y, dX, dY: Real): LongInt; +function TraceShoveFall(var x, y: Real; dX, dY: Real; Target: TTarget): LongInt; var dmg: LongInt; + dxdy, odX, odY: real; begin + odX:= dX; + odY:= dY; //v:= random($FFFFFFFF); while true do - begin + begin x:= x + dX; y:= y + dY; dY:= dY + cGravityf; -{ if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then +{ if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then begin LandPixels[trunc(y), trunc(x)]:= v; UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true); end;} - - // consider adding dX/dY calc here for fall damage - if TestCollExcludingObjects(trunc(x), trunc(y), cHHRadius) then - begin - if 0.4 < dY then - begin - dmg := 1 + trunc((abs(dY) - 0.4) * 70); - if dmg >= 1 then + if TestCollExcludingObjects(trunc(x), trunc(y), Target.Radius) then + with Target do + begin + if (Kind = gtHedgehog) and (0.4 < dY) then + begin + dmg := trunc((dY - 0.4) * 70); exit(dmg); - end; + end + else + begin + dxdy:= abs(dX)+abs(dY); + if ((Kind = gtMine) and (dxdy > 0.4)) or + ((Kind = gtExplosives) and + (((State and gstTmpFlag <> 0) and (dxdy > 0.4)) or + ((State and gstTmpFlag = 0) and + ((abs(odX) > 0.15) or ((abs(odY) > 0.15) and + (abs(odX) > 0.02))) and (dxdy > 0.35)))) then + begin + dmg := trunc(dxdy * 50); + exit(dmg) + end + 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 + begin + dmg := trunc(dy * 70); + exit(dmg) + end + end; exit(0) end; - if (y > cWaterLine) or (x > 4096) or (x < 0) then + if (y > cWaterLine) or (x > rightX) or (x < leftX) then // returning -1 for drowning so it can be considered in the Rate routine exit(-1) end; end; -function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; +function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline; begin - RateExplosion:= RateExplosion(Me, x, y, r, 0); + RateExplosion:= RealRateExplosion(Me, x, y, r, 0); + ResetTargets; +end; +function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline; +begin + RateExplosion:= RealRateExplosion(Me, x, y, r, Flags); + ResetTargets; end; -function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; -var i, fallDmg, dmg, dmgBase, rate, erasure: LongInt; - dX, dY, dmgMod: real; +function RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; +var i, fallDmg, dmg, dmgBase, rate, subrate, erasure: LongInt; + pX, pY, dX, dY: real; hadSkips: boolean; begin fallDmg:= 0; -dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent; rate:= 0; // add our virtual position with Targets.ar[Targets.Count] do @@ -427,10 +492,12 @@ Point.y:= hwRound(Me^.Y); skip:= false; matters:= true; + Kind:= gtHedgehog; + Density:= 1; + Radius:= cHHRadius; Score:= - ThinkingHH^.Health end; // rate explosion -dmgBase:= r + cHHRadius div 2; if (Flags and afErasesLand <> 0) and (GameFlags and gfSolidLand = 0) then erasure:= r else erasure:= 0; @@ -438,98 +505,169 @@ hadSkips:= false; for i:= 0 to Targets.Count do - with Targets.ar[i] do - if not matters then hadSkips:= true - else - begin - dmg:= 0; - if abs(Point.x - x) + abs(Point.y - y) < dmgBase then - dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r)); + if not Targets.ar[i].dead then + with Targets.ar[i] do + if not matters then hadSkips:= true + else + begin + dmg:= 0; + dmgBase:= r + Radius div 2; + if abs(Point.x - x) + abs(Point.y - y) < dmgBase then + dmg:= trunc(dmgMod * min((dmgBase - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y)))) div 2, r)); - if dmg > 0 then - begin - if (Flags and afTrackFall <> 0) and (dmg < abs(Score)) then + if dmg > 0 then begin - dX:= 0.005 * dmg + 0.01; - dY:= dX; - if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and - (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then - fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod) - else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod) - end; - if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI - if Score > 0 then - inc(rate, (KillScore + Score div 10) * 1024) // Add a bit of a bonus for bigger hog drownings - else - dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs - else if (dmg+fallDmg) >= abs(Score) then - if Score > 0 then - inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill - else - dec(rate, KillScore * friendlyfactor div 100 * 1024) - else - if Score > 0 then - inc(rate, (dmg + fallDmg) * 1024) - else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024) + pX:= Point.x; + pY:= Point.y; + fallDmg:= 0; + if (Flags and afTrackFall <> 0) and (Score > 0) and (dmg < Score) then + begin + dX:= (0.005 * dmg + 0.01) / Density; + dY:= dX; + if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and + (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or + ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then + dX:= 0; + + if pX - x < 0 then dX:= -dX; + if pY - y < 0 then dY:= -dY; + + if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and + (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then + fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod) + else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod) + end; + if Kind = gtHedgehog then + begin + if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI + begin + if Score > 0 then + inc(rate, (KillScore + Score div 10) * 1024) // Add a bit of a bonus for bigger hog drownings + else + dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs + end + else if (dmg+fallDmg) >= abs(Score) then + begin + dead:= true; + Targets.reset:= true; + if dX < 0.035 then + begin + subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or (Flags and afTrackFall)); + if abs(subrate) > 2000 then inc(Rate,subrate) + end; + if Score > 0 then + inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill + else dec(rate, KillScore * friendlyfactor div 100 * 1024) + end + else + begin + if Score > 0 then + inc(rate, (dmg + fallDmg) * 1024) + else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024) + end + end + else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then + begin + dead:= true; + Targets.reset:= true; + if Kind = gtExplosives then + subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall)) + else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall)); + if abs(subrate) > 2000 then inc(Rate,subrate); + end + end end; - end; if hadSkips and (rate = 0) then - RateExplosion:= BadTurn + RealRateExplosion:= BadTurn else - RateExplosion:= rate; + RealRateExplosion:= rate; end; -function RateShove(x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt; -var i, fallDmg, dmg, rate: LongInt; - dX, dY, dmgMod: real; +function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt; +var i, fallDmg, dmg, rate, subrate: LongInt; + dX, dY, pX, pY: real; begin fallDmg:= 0; dX:= gdX * 0.01 * kick; dY:= gdY * 0.01 * kick; -dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent; rate:= 0; for i:= 0 to Pred(Targets.Count) do with Targets.ar[i] do - if skip then - if (Flags and afSetSkip = 0) then skip:= false else {still skip} - else if matters then - begin - dmg:= 0; - if abs(Point.x - x) + abs(Point.y - y) < r then - dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))); - - if dmg > 0 then + if skip then + begin + if Flags and afSetSkip = 0 then skip:= false + end + else if matters then begin - if (Flags and afSetSkip <> 0) then skip:= true; - if (Flags and afTrackFall <> 0) and (Score > 0) then - fallDmg:= trunc(TraceShoveFall(Point.x, Point.y - 2, dX, dY) * dmgMod); - if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI - if Score > 0 then - inc(rate, KillScore + Score div 10) // Add a bit of a bonus for bigger hog drownings - else - dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs - else if power+fallDmg >= abs(Score) then - if Score > 0 then - inc(rate, KillScore) - else - dec(rate, KillScore * friendlyfactor div 100) - else - if Score > 0 then - inc(rate, power+fallDmg) - else - dec(rate, (power+fallDmg) * friendlyfactor div 100) + dmg:= 0; + if abs(Point.x - x) + abs(Point.y - y) < r then + dmg:= r - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))); + + if dmg > 0 then + begin + pX:= Point.x; + pY:= Point.y-2; + fallDmg:= 0; + if (Flags and afSetSkip <> 0) then skip:= true; + if not(dead) and (Flags and afTrackFall <> 0) and (Score > 0) and (power < Score) then + if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and + (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or + ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then + fallDmg:= trunc(TraceShoveFall(pX, pY, 0, dY, Targets.ar[i]) * dmgMod) + else + fallDmg:= trunc(TraceShoveFall(pX, pY, dX, dY, Targets.ar[i]) * dmgMod); + if Kind = gtHedgehog then + begin + if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI + begin + if Score > 0 then + inc(rate, KillScore + Score div 10) // Add a bit of a bonus for bigger hog drownings + else + dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs + end + else if power+fallDmg >= abs(Score) then + begin + dead:= true; + Targets.reset:= true; + if dX < 0.035 then + begin + subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall); + if abs(subrate) > 2000 then inc(Rate,subrate div 1024) + end; + if Score > 0 then + inc(rate, KillScore) + else + dec(rate, KillScore * friendlyfactor div 100) + end + else + begin + if Score > 0 then + inc(rate, power+fallDmg) + else + dec(rate, (power+fallDmg) * friendlyfactor div 100) + end + end + else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then + begin + dead:= true; + Targets.reset:= true; + if Kind = gtExplosives then + subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or (Flags and afTrackFall)) + else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or (Flags and afTrackFall)); + if abs(subrate) > 2000 then inc(Rate,subrate div 1024); + end + end end; - end; -RateShove:= rate * 1024 +RateShove:= rate * 1024; +ResetTargets end; function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt; -var i, dmg, fallDmg, baseDmg, rate, erasure: LongInt; - dX, dY, dmgMod: real; +var i, dmg, fallDmg, baseDmg, rate, subrate, erasure: LongInt; + pX, pY, dX, dY: real; hadSkips: boolean; begin -dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent; rate:= 0; gdX:= gdX * 0.01; gdY:= gdX * 0.01; @@ -540,6 +678,9 @@ Point.y:= hwRound(Me^.Y); skip:= false; matters:= true; + Kind:= gtHedgehog; + Density:= 1; + Radius:= cHHRadius; Score:= - ThinkingHH^.Health end; // rate shot @@ -551,48 +692,80 @@ hadSkips:= false; for i:= 0 to Targets.Count do - with Targets.ar[i] do - if not matters then hadSkips:= true - else - begin - dmg:= 0; - if abs(Point.x - x) + abs(Point.y - y) < baseDmg then - begin - dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25); - dmg:= trunc(dmg * dmgMod); - end; - if dmg > 0 then + if not Targets.ar[i].dead then + with Targets.ar[i] do + if not matters then hadSkips:= true + else begin - dX:= gdX * dmg; - dY:= gdY * dmg; - if dX < 0 then dX:= dX - 0.01 - else dX:= dX + 0.01; - if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and - (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then - fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, 0) * dmgMod) - else fallDmg:= trunc(TraceFall(x, y, Point.x, Point.y, dX, dY, erasure) * dmgMod); - if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI - if Score > 0 then - inc(rate, KillScore + Score div 10) // Add a bit of a bonus for bigger hog drownings - else - dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs - else if (dmg+fallDmg) >= abs(Score) then - if Score > 0 then - inc(rate, KillScore) - else - dec(rate, KillScore * friendlyfactor div 100) - else - if Score > 0 then - inc(rate, dmg+fallDmg) - else - dec(rate, (dmg+fallDmg) * friendlyfactor div 100) + dmg:= 0; + if abs(Point.x - x) + abs(Point.y - y) < baseDmg then + begin + dmg:= min(baseDmg - trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))), 25); + dmg:= trunc(dmg * dmgMod); + end; + if dmg > 0 then + begin + if not(dead) and (Score > 0) and (dmg < Score) then + begin + pX:= Point.x; + pY:= Point.y; + dX:= gdX * dmg / Density; + dY:= gdY * dmg / Density; + if dX < 0 then dX:= dX - 0.01 + else dX:= dX + 0.01; + if (Kind = gtExplosives) and (State and gstTmpFlag = 0) and + (((abs(dY) > 0.15) and (abs(dX) < 0.02)) or + ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then + dX:= 0; + if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and + (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then + fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod) + else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod) + end; + if Kind = gtHedgehog then + begin + if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI + begin + if Score > 0 then + inc(rate, KillScore + Score div 10) // Add a bit of a bonus for bigger hog drownings + else + dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs + end + else if (dmg+fallDmg) >= abs(Score) then + begin + dead:= true; + Targets.reset:= true; + if dX < 0.035 then + begin + subrate:= RealRateExplosion(Me, round(pX), round(pY), 61, afErasesLand or afTrackFall); + if abs(subrate) > 2000 then inc(Rate,subrate div 1024) + end; + if Score > 0 then + inc(rate, KillScore) + else + dec(rate, KillScore * friendlyfactor div 100) + end + else if Score > 0 then + inc(rate, dmg+fallDmg) + else dec(rate, (dmg+fallDmg) * friendlyfactor div 100) + end + else if (fallDmg >= 0) and ((dmg+fallDmg) >= Score) then + begin + dead:= true; + Targets.reset:= true; + if Kind = gtExplosives then + subrate:= RealRateExplosion(Me, round(pX), round(pY), 151, afErasesLand or afTrackFall) + else subrate:= RealRateExplosion(Me, round(pX), round(pY), 101, afErasesLand or afTrackFall); + if abs(subrate) > 2000 then inc(Rate,subrate div 1024); + end + end end; - end; if hadSkips and (rate = 0) then RateShotgun:= BadTurn else RateShotgun:= rate * 1024; + ResetTargets; end; function RateHammer(Me: PGear): LongInt; @@ -605,14 +778,13 @@ for i:= 0 to Pred(Targets.Count) do with Targets.ar[i] do - if matters then // hammer hit radius is 8, shift is 10 - if abs(Point.x - x) + abs(Point.y - y) < 18 then + if matters and (Kind = gtHedgehog) and (abs(Point.x - x) + abs(Point.y - y) < 18) then begin r:= trunc(sqrt(sqr(Point.x - x)+sqr(Point.y - y))); if r <= 18 then - if Score > 0 then + if Score > 0 then inc(rate, Score div 3) else inc(rate, Score div 3 * friendlyfactor div 100) @@ -630,7 +802,7 @@ bY:= hwRound(Gear^.Y); case JumpType of jmpNone: exit(false); - + jmpHJump: if TestCollisionYwithGear(Gear, -1) = 0 then begin @@ -640,7 +812,7 @@ end else exit(false); - + jmpLJump: begin if TestCollisionYwithGear(Gear, -1) <> 0 then @@ -662,12 +834,12 @@ end; repeat - {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then + {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then begin LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color; UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true); end;} - + if not (hwRound(Gear^.Y) + cHHRadius < cWaterLine) then exit(false); if (Gear^.State and gstMoving) <> 0 then @@ -725,7 +897,7 @@ GoInfo.JumpType:= jmpNone; tY:= hwRound(Gear^.Y); repeat - {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then + {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then begin LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color; UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true); @@ -739,8 +911,8 @@ AddWalkBonus(pX, tY, 250, -40); exit(false) end; - - // hog is falling + + // hog is falling if (Gear^.State and gstMoving) <> 0 then begin inc(GoInfo.Ticks); @@ -749,7 +921,7 @@ begin GoInfo.FallPix:= 0; // try ljump instead of fall with damage - HHJump(AltGear, jmpLJump, GoInfo); + HHJump(AltGear, jmpLJump, GoInfo); if AltGear^.Hedgehog^.BotLevel < 4 then AddWalkBonus(pX, tY, 175, -20); exit(false)