author | belphegorr <szabibibi@gmail.com> |
Mon, 23 Jul 2012 19:15:59 +0300 | |
changeset 7263 | 644eabbc9218 |
parent 7197 | 5a9775b97c7e |
child 7200 | 744c8a5546c6 |
permissions | -rw-r--r-- |
(* * Hedgewars, a free turn based strategy game * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com> * * 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 * the Free Software Foundation; version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA *) {$INCLUDE "options.inc"} unit uAIAmmoTests; interface uses SDLh, uConsts, uFloat, uTypes; const amtest_OnTurn = $00000001; // from one position amtest_NoTarget = $00000002; // each pos, but no targetting var windSpeed: real; type TAttackParams = record Time: Longword; Angle, Power: LongInt; ExplX, ExplY, ExplR: LongInt; AttackPutX, AttackPutY: LongInt; end; function TestBazooka(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestSnowball(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestGrenade(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestMolotov(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestClusterBomb(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestWatermelon(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestMortar(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestShotgun(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestDesertEagle(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestSniperRifle(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestWhip(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestAirAttack(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestTeleport(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestHammer(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; type TAmmoTestProc = function (Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; TAmmoTest = record proc: TAmmoTestProc; flags: Longword; end; const AmmoTests: array[TAmmoType] of TAmmoTest = ( (proc: nil; flags: 0), // amNothing (proc: @TestGrenade; flags: 0), // amGrenade (proc: @TestClusterBomb; flags: 0), // amClusterBomb (proc: @TestBazooka; flags: 0), // amBazooka (proc: nil; flags: 0), // amBee (proc: @TestShotgun; flags: 0), // amShotgun (proc: nil; flags: 0), // amPickHammer (proc: nil; flags: 0), // amSkip (proc: nil; flags: 0), // amRope (proc: nil; flags: 0), // amMine (proc: @TestDesertEagle; flags: 0), // amDEagle (proc: nil; flags: 0), // amDynamite (proc: @TestFirePunch; flags: amtest_NoTarget), // amFirePunch (proc: @TestWhip; flags: amtest_NoTarget), // amWhip (proc: @TestBaseballBat; flags: amtest_NoTarget), // amBaseballBat (proc: nil; flags: 0), // amParachute (proc: @TestAirAttack; flags: amtest_OnTurn), // amAirAttack (proc: nil; flags: 0), // amMineStrike (proc: nil; flags: 0), // amBlowTorch (proc: nil; flags: 0), // amGirder (proc: nil; flags: 0), // amTeleport //(proc: @TestTeleport; flags: amtest_OnTurn), // amTeleport (proc: nil; flags: 0), // amSwitch (proc: @TestMortar; flags: 0), // amMortar (proc: nil; flags: 0), // amKamikaze (proc: nil; flags: 0), // amCake (proc: nil; flags: 0), // amSeduction (proc: @TestWatermelon; flags: 0), // amWatermelon (proc: nil; flags: 0), // amHellishBomb (proc: nil; flags: 0), // amNapalm (proc: nil; flags: 0), // amDrill (proc: nil; flags: 0), // amBallgun (proc: nil; flags: 0), // amRCPlane (proc: nil; flags: 0), // amLowGravity (proc: nil; flags: 0), // amExtraDamage (proc: nil; flags: 0), // amInvulnerable (proc: nil; flags: 0), // amExtraTime (proc: nil; flags: 0), // amLaserSight (proc: nil; flags: 0), // amVampiric (proc: @TestSniperRifle; flags: 0), // amSniperRifle (proc: nil; flags: 0), // amJetpack (proc: @TestMolotov; flags: 0), // amMolotov (proc: nil; flags: 0), // amBirdy (proc: nil; flags: 0), // amPortalGun (proc: nil; flags: 0), // amPiano (proc: @TestGrenade; flags: 0), // amGasBomb (proc: @TestShotgun; flags: 0), // amSineGun (proc: nil; flags: 0), // amFlamethrower (proc: @TestGrenade; flags: 0), // amSMine (proc: @TestHammer; flags: amtest_NoTarget), // amHammer (proc: nil; flags: 0), // amResurrector (proc: nil; flags: 0), // amDrillStrike (proc: nil; flags: 0), // amSnowball (proc: nil; flags: 0), // amTardis (proc: nil; flags: 0), // amStructure (proc: nil; flags: 0), // amLandGun (proc: nil; flags: 0) // amIceGun ); const BadTurn = Low(LongInt) div 4; implementation uses uAIMisc, uVariables, uUtils; function Metric(x1, y1, x2, y2: LongInt): LongInt; inline; begin Metric:= abs(x1 - x2) + abs(y1 - y2) end; function TestBazooka(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var Vx, Vy, r, mX, mY: real; rTime: LongInt; EX, EY: LongInt; valueResult: LongInt; x, y, dX, dY: real; t: LongInt; value: LongInt; begin mX:= hwFloat2Float(Me^.X); mY:= hwFloat2Float(Me^.Y); ap.Time:= 0; rTime:= 350; ap.ExplR:= 0; valueResult:= BadTurn; repeat rTime:= rTime + 300 + Level * 50 + random(300); Vx:= - windSpeed * rTime * 0.5 + (Targ.X + AIrndSign(2) - mX) / rTime; Vy:= cGravityf * rTime * 0.5 - (Targ.Y - mY) / rTime; r:= sqr(Vx) + sqr(Vy); if not (r > 1) then begin x:= mX; y:= mY; dX:= Vx; dY:= -Vy; t:= rTime; repeat x:= x + dX; y:= y + dY; dX:= dX + windSpeed; dY:= dY + cGravityf; dec(t) until TestCollExcludingMe(Me, trunc(x), trunc(y), 5) or (t <= 0); EX:= trunc(x); EY:= trunc(y); if Me^.Hedgehog^.BotLevel = 1 then value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand) else value:= RateExplosion(Me, EX, EY, 101); if value = 0 then value:= - Metric(Targ.X, Targ.Y, EX, EY) div 64; if valueResult <= value then begin ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random((Level - 1) * 9)); ap.Power:= trunc(sqrt(r) * cMaxPower) - random((Level - 1) * 17 + 1); ap.ExplR:= 100; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= value end; end //until (value > 204800) or (rTime > 4250); not so useful since adding score to the drowning until rTime > 4250; TestBazooka:= valueResult end; function TestSnowball(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var Vx, Vy, r: real; rTime: LongInt; EX, EY: LongInt; valueResult: LongInt; x, y, dX, dY, meX, meY: real; t: LongInt; value: LongInt; begin meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); ap.Time:= 0; rTime:= 350; ap.ExplR:= 0; valueResult:= BadTurn; repeat rTime:= rTime + 300 + Level * 50 + random(1000); Vx:= - windSpeed * rTime * 0.5 + ((Targ.X + AIrndSign(2)) - meX) / rTime; Vy:= cGravityf * rTime * 0.5 - (Targ.Y - meY) / rTime; r:= sqr(Vx) + sqr(Vy); if not (r > 1) then begin x:= meX; y:= meY; dX:= Vx; dY:= -Vy; t:= rTime; repeat x:= x + dX; y:= y + dY; dX:= dX + windSpeed; dY:= dY + cGravityf; dec(t) until TestCollExcludingMe(Me, trunc(x), trunc(y), 5) or (t <= 0); EX:= trunc(x); EY:= trunc(y); value:= RateShove(Me, trunc(x), trunc(y), 5, 1, trunc((abs(dX)+abs(dY))*20), -dX, -dY, afTrackFall); if value = 0 then value:= - Metric(Targ.X, Targ.Y, EX, EY) div 64; if valueResult <= value then begin ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random((Level - 1) * 9)); ap.Power:= trunc(sqrt(r) * cMaxPower) - random((Level - 1) * 17 + 1); ap.ExplR:= 0; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= value end; end until (rTime > 4250); TestSnowball:= valueResult end; function TestMolotov(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var Vx, Vy, r: real; Score, EX, EY, valueResult: LongInt; TestTime: Longword; x, y, dY, meX, meY: real; t: LongInt; begin meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); valueResult:= BadTurn; TestTime:= 0; ap.ExplR:= 0; repeat inc(TestTime, 300); Vx:= (Targ.X - meX) / TestTime; Vy:= cGravityf * (TestTime div 2) - Targ.Y - meY / TestTime; r:= sqr(Vx) + sqr(Vy); if not (r > 1) then begin x:= meX; y:= meY; dY:= -Vy; t:= TestTime; repeat x:= x + Vx; y:= y + dY; dY:= dY + cGravityf; dec(t) until TestCollExcludingMe(Me, trunc(x), trunc(y), 7) or (t = 0); EX:= trunc(x); EY:= trunc(y); if t < 50 then Score:= RateExplosion(Me, EX, EY, 97) // average of 17 attempts, most good, but some failing spectacularly else Score:= BadTurn; if valueResult < Score then begin ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level)); ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15); ap.Time:= TestTime; ap.ExplR:= 100; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= Score end; end until (TestTime > 4250); TestMolotov:= valueResult end; function TestGrenade(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; const tDelta = 24; var Vx, Vy, r: real; Score, EX, EY, valueResult: LongInt; TestTime: Longword; x, y, meX, meY, dY: real; t: LongInt; begin valueResult:= BadTurn; TestTime:= 0; ap.ExplR:= 0; meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); repeat inc(TestTime, 1000); Vx:= (Targ.X - meX) / (TestTime + tDelta); Vy:= cGravityf * ((TestTime + tDelta) div 2) - (Targ.Y - meY) / (TestTime + tDelta); r:= sqr(Vx) + sqr(Vy); if not (r > 1) then begin x:= meX; y:= meY; dY:= -Vy; t:= TestTime; repeat x:= x + Vx; y:= y + dY; dY:= dY + cGravityf; dec(t) until TestCollExcludingMe(Me, trunc(x), trunc(y), 5) or (t = 0); EX:= trunc(x); EY:= trunc(y); if t < 50 then if Me^.Hedgehog^.BotLevel = 1 then Score:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand) else Score:= RateExplosion(Me, EX, EY, 101) else Score:= BadTurn; if valueResult < Score then begin ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level)); ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15); ap.Time:= TestTime; ap.ExplR:= 100; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= Score end; end //until (Score > 204800) or (TestTime > 4000); until TestTime > 4000; TestGrenade:= valueResult end; function TestClusterBomb(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; const tDelta = 24; var Vx, Vy, r: real; Score, EX, EY, valueResult: LongInt; TestTime: Longword; x, y, dY, meX, meY: real; t: LongInt; begin valueResult:= BadTurn; TestTime:= 0; ap.ExplR:= 0; meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); repeat inc(TestTime, 1000); // Try to overshoot slightly, seems to pay slightly better dividends in terms of hitting cluster if meX<Targ.X then Vx:= ((Targ.X+10) - meX) / (TestTime + tDelta) else Vx:= ((Targ.X-10) - meX) / (TestTime + tDelta); Vy:= cGravityf * ((TestTime + tDelta) div 2) - ((Targ.Y-50) - meY) / (TestTime + tDelta); r:= sqr(Vx)+sqr(Vy); if not (r > 1) then begin x:= meX; y:= meY; dY:= -Vy; t:= TestTime; repeat x:= x + Vx; y:= y + dY; dY:= dY + cGravityf; dec(t) until TestCollExcludingMe(Me, trunc(x), trunc(y), 5) or (t = 0); EX:= trunc(x); EY:= trunc(y); if t < 50 then Score:= RateExplosion(Me, EX, EY, 41) else Score:= BadTurn; if valueResult < Score then begin ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level)); ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15); ap.Time:= TestTime; ap.ExplR:= 90; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= Score end; end until (TestTime = 4000); TestClusterBomb:= valueResult end; function TestWatermelon(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; const tDelta = 24; var Vx, Vy, r: real; Score, EX, EY, valueResult: LongInt; TestTime: Longword; x, y, dY, meX, meY: real; t: LongInt; begin valueResult:= BadTurn; TestTime:= 0; ap.ExplR:= 0; meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); repeat inc(TestTime, 1000); Vx:= (Targ.X - meX) / (TestTime + tDelta); Vy:= cGravityf * ((TestTime + tDelta) div 2) - ((Targ.Y-50) - meY) / (TestTime + tDelta); r:= sqr(Vx)+sqr(Vy); if not (r > 1) then begin x:= meX; y:= meY; dY:= -Vy; t:= TestTime; repeat x:= x + Vx; y:= y + dY; dY:= dY + cGravityf; dec(t) until TestCollExcludingMe(Me, trunc(x), trunc(y), 7) or (t = 0); EX:= trunc(x); EY:= trunc(y); if t < 50 then Score:= RateExplosion(Me, EX, EY, 200) + RateExplosion(Me, EX, EY + 120, 200) else Score:= BadTurn; if valueResult < Score then begin ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level)); ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15); ap.Time:= TestTime; ap.ExplR:= 300; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= Score end; end until (TestTime = 4000); TestWatermelon:= valueResult end; function Solve(TX, TY, MX, MY: LongInt): LongWord; var A, B, D, T: real; C: LongInt; begin A:= sqr(cGravityf); B:= - cGravityf * (TY - MY) - 1; C:= sqr(TY - MY) + sqr(TX - MX); D:= sqr(B) - A * C; if D >= 0 then begin D:= sqrt(D) - B; if D >= 0 then T:= sqrt(D * 2 / A) else T:= 0; Solve:= trunc(T) end else Solve:= 0 end; function TestMortar(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; //const tDelta = 24; var Vx, Vy: real; Score, EX, EY: LongInt; TestTime: Longword; x, y, dY, meX, meY: real; begin TestMortar:= BadTurn; ap.ExplR:= 0; meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); if (Level > 2) then exit(BadTurn); TestTime:= Solve(Targ.X, Targ.Y, trunc(meX), trunc(meY)); if TestTime = 0 then exit(BadTurn); Vx:= (Targ.X - meX) / TestTime; Vy:= cGravityf * (TestTime div 2) - (Targ.Y - meY) / TestTime; x:= meX; y:= meY; dY:= -Vy; repeat x:= x + Vx; y:= y + dY; dY:= dY + cGravityf; EX:= trunc(x); EY:= trunc(y); until TestCollExcludingMe(Me, EX, EY, 5) or (EY > cWaterLine); if (EY < cWaterLine) and (dY >= 0) then begin Score:= RateExplosion(Me, EX, EY, 91); if (Score = 0) then if (dY > 0.15) then Score:= - abs(Targ.Y - EY) div 32 else Score:= BadTurn else if (Score < 0) then Score:= BadTurn end else Score:= BadTurn; if BadTurn < Score then begin ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level)); ap.Power:= 1; ap.ExplR:= 100; ap.ExplX:= EX; ap.ExplY:= EY; TestMortar:= Score end; end; function TestShotgun(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; const MIN_RANGE = 80; MAX_RANGE = 400; var Vx, Vy, x, y: real; rx, ry, valueResult: LongInt; range: integer; begin TestShotgun:= BadTurn; ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); range:= Metric(trunc(x), trunc(y), Targ.X, Targ.Y); if ( range < MIN_RANGE ) or ( range > MAX_RANGE ) then exit(BadTurn); Vx:= (Targ.X - x) * 1 / 1024; Vy:= (Targ.Y - y) * 1 / 1024; ap.Angle:= DxDy2AttackAnglef(Vx, -Vy); repeat x:= x + vX; y:= y + vY; rx:= trunc(x); ry:= trunc(y); if TestCollExcludingMe(Me, rx, ry, 2) then begin x:= x + vX * 8; y:= y + vY * 8; valueResult:= RateShotgun(Me, vX, vY, rx, ry); if valueResult = 0 then valueResult:= - Metric(Targ.X, Targ.Y, rx, ry) div 64 else dec(valueResult, Level * 4000); // 27/20 is reuse bonus exit(valueResult * 27 div 20) end until (Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 4) or (x < 0) or (y < 0) or (trunc(x) > LAND_WIDTH) or (trunc(y) > LAND_HEIGHT); TestShotgun:= BadTurn end; function TestDesertEagle(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var Vx, Vy, x, y, t, dmgMod: real; d: Longword; fallDmg, valueResult: LongInt; begin dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent; Level:= Level; // avoid compiler hint ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); if Abs(trunc(x) - Targ.X) + Abs(trunc(y) - Targ.Y) < 40 then begin TestDesertEagle:= BadTurn; exit(BadTurn); end; t:= 2 / sqrt(sqr(Targ.X - x)+sqr(Targ.Y-y)); Vx:= (Targ.X - x) * t; Vy:= (Targ.Y - y) * t; ap.Angle:= DxDy2AttackAnglef(Vx, -Vy); d:= 0; repeat x:= x + vX; y:= y + vY; if ((trunc(x) and LAND_WIDTH_MASK) = 0)and((trunc(y) and LAND_HEIGHT_MASK) = 0) and (Land[trunc(y), trunc(x)] <> 0) then inc(d); until (Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 5) or (x < 0) or (y < 0) or (trunc(x) > LAND_WIDTH) or (trunc(y) > LAND_HEIGHT) or (d > 50); if Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 5 then begin fallDmg:= TraceShoveFall(Me, Targ.X, Targ.Y, vX * 0.00125 * 20, vY * 0.00125 * 20); if fallDmg < 0 then valueResult:= 204800 else valueResult:= Max(0, (4 - d div 50) * trunc((7+fallDmg)*dmgMod) * 1024) end else valueResult:= BadTurn; TestDesertEagle:= valueResult end; function TestSniperRifle(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var Vx, Vy, x, y, t, dmg, dmgMod: real; d: Longword; fallDmg, valueResult: LongInt; begin dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent; Level:= Level; // avoid compiler hint ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); if Abs(trunc(x) - Targ.X) + Abs(trunc(y) - Targ.Y) < 40 then exit(BadTurn); dmg:= sqrt(sqr(Targ.X - x)+sqr(Targ.Y-y)); t:= 1.5 / dmg; dmg:= dmg * 0.33333333; Vx:= (Targ.X - x) * t; Vy:= (Targ.Y - y) * t; ap.Angle:= DxDy2AttackAnglef(Vx, -Vy); d:= 0; repeat x:= x + vX; y:= y + vY; if ((trunc(x) and LAND_WIDTH_MASK) = 0)and((trunc(y) and LAND_HEIGHT_MASK) = 0) and (Land[trunc(y), trunc(x)] <> 0) then inc(d); until (Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 4) or (x < 0) or (y < 0) or (trunc(x) > LAND_WIDTH) or (trunc(y) > LAND_HEIGHT) or (d > 23); if Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 4 then begin fallDmg:= TraceShoveFall(Me, Targ.X, Targ.Y, vX * 0.00166 * dmg, vY * 0.00166 * dmg); if fallDmg < 0 then TestSniperRifle:= BadTurn else TestSniperRifle:= Max(0, trunc((dmg + fallDmg) * dmgMod) * 1024) end else TestSniperRifle:= BadTurn end; function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var valueResult: LongInt; x, y: real; begin Level:= Level; // avoid compiler hint TestBaseballBat:= BadTurn; ap.ExplR:= 0; x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); if (Level > 2) then exit(BadTurn); ap.Time:= 0; ap.Power:= 1; if (Targ.X) - trunc(x) >= 0 then ap.Angle:= cMaxAngle div 4 else ap.Angle:= - cMaxAngle div 4; valueResult:= RateShove(Me, trunc(x) + LongWord(10*hwSignf(Targ.X - x)), trunc(y), 15, 30, 115, hwSign(Me^.dX)*0.353, -0.353, afTrackFall); if valueResult <= 0 then valueResult:= BadTurn else inc(valueResult); TestBaseballBat:= valueResult; end; function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var val1, val2, i, t: LongInt; x, y: real; begin Level:= Level; // avoid compiler hint TestFirePunch:= BadTurn; ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; ap.Angle:= hwSign(Me^.dX); x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); { // this block is for digging with firepunch when blocked close to walls (notice TestColl check) if (Abs(trunc(x) - Targ.X) > 25) or (Abs(trunc(y) + 50 - Targ.Y) > 50) then begin if TestColl(trunc(x), trunc(y) - 16, 6) and (RateShove(Me, trunc(x) + LongWord(10 * hwSign(Me^.dX)), trunc(y) - 40, 30, 30, 40, hwSign(Me^.dX)*0.45, -0.9, 1) >= 0) then val1:= Succ(BadTurn) else val1:= BadTurn; exit(val1); end; } // and this is actual try to attack val1:= 0; for i:= 0 to 4 do begin t:= RateShove(Me, trunc(x) + 10 * hwSignf(Targ.X - x), trunc(y) - 20 * i - 5, 10, 30, 40, hwSign(Me^.dX)*0.45, -0.9, afTrackFall); if (val1 < 0) or (t < 0) then val1:= BadTurn else if t > 0 then val1:= t; end; val2:= 0; for i:= 0 to 4 do begin t:= RateShove(Me, trunc(x) + 10 * hwSignf(Targ.X - x), trunc(y) - 20 * i - 5, 10, 30, 40, -hwSign(Me^.dX)*0.45, -0.9, afTrackFall); if (val2 < 0) or (t < 0) then val2:= BadTurn else if t > 0 then val2:= t; end; if (val1 > val2) and (val1 > 0) then TestFirePunch:= val1 else if (val2 > val1) and (val2 > 0) then begin ap.Angle:= -hwSign(Me^.dX); TestFirePunch:= val2 end else TestFirePunch:= BadTurn; end; function TestWhip(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var valueResult, v1, v2: LongInt; x, y: real; begin Level:= Level; // avoid compiler hint ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); // check left direction {first RateShove checks fartherest of two whip's AmmoShove attacks to encourage distant attacks (damaged hog is excluded from view of second RateShove call)} v1:= RateShove(Me, trunc(x) - 15, trunc(y) , 30, 30, 40 , -1, -0.8, afTrackFall or afSetSkip); v1:= v1 + RateShove(Me, trunc(x), trunc(y) , 30, 30, 40 , -1, -0.8, afTrackFall); // now try opposite direction v2:= RateShove(Me, trunc(x) + 15, trunc(y) , 30, 30, 40 , 1, -0.8, afTrackFall or afSetSkip); v2:= v2 + RateShove(Me, trunc(x), trunc(y) , 30, 30, 40 , 1, -0.8, afTrackFall); if (v2 > v1) or {don't encourage turning for no gain}((v2 = v1) and (not Me^.dX.isNegative)) then begin ap.Angle:= 1; valueResult:= v2 end else begin ap.Angle:= -1; valueResult:= v1 end; if valueResult <= 0 then valueResult:= BadTurn else inc(valueResult); TestWhip:= valueResult; end; function TestHammer(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var rate: LongInt; begin Level:= Level; // avoid compiler hint ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; ap.Angle:= 0; rate:= RateHammer(Me); if rate = 0 then rate:= BadTurn; TestHammer:= rate; end; function TestAirAttack(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; const cShift = 4; var bombsSpeed, X, Y, dY: real; b: array[0..9] of boolean; dmg: array[0..9] of LongInt; fexit: boolean; i, t, valueResult: LongInt; begin ap.ExplR:= 0; ap.Time:= 0; if (Level > 3) then exit(BadTurn); ap.Angle:= 0; ap.AttackPutX:= Targ.X; ap.AttackPutY:= Targ.Y; bombsSpeed:= hwFloat2Float(cBombsSpeed); X:= Targ.X - 135 - cShift; // hh center - cShift X:= X - bombsSpeed * sqrt(((Targ.Y + 128) * 2) / cGravityf); Y:= -128; dY:= 0; for i:= 0 to 9 do begin b[i]:= true; dmg[i]:= 0 end; valueResult:= 0; repeat X:= X + bombsSpeed; Y:= Y + dY; dY:= dY + cGravityf; fexit:= true; for i:= 0 to 9 do if b[i] then begin fexit:= false; if TestColl(trunc(X) + LongWord(i * 30), trunc(Y), 4) then begin b[i]:= false; dmg[i]:= RateExplosion(Me, trunc(X) + LongWord(i * 30), trunc(Y), 58) // 58 (instead of 60) for better prediction (hh moves after explosion of one of the rockets) end end; until fexit or (Y > cWaterLine); for i:= 0 to 5 do inc(valueResult, dmg[i]); t:= valueResult; ap.AttackPutX:= Targ.X - 60; for i:= 0 to 3 do begin dec(t, dmg[i]); inc(t, dmg[i + 6]); if t > valueResult then begin valueResult:= t; ap.AttackPutX:= Targ.X - 30 - cShift + i * 30 end end; if valueResult <= 0 then valueResult:= BadTurn; TestAirAttack:= valueResult; end; function TestTeleport(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var i, failNum: longword; maxTop: longword; begin TestTeleport := BadTurn; exit(BadTurn); Level:= Level; // avoid compiler hint //FillBonuses(true, [gtCase]); if bonuses.Count = 0 then begin if Me^.Health <= 100 then begin maxTop := Targ.Y - cHHRadius * 2; while not TestColl(Targ.X, maxTop, cHHRadius) and (maxTop > topY + cHHRadius * 2 + 1) do dec(maxTop, cHHRadius*2); if not TestColl(Targ.X, maxTop + cHHRadius, cHHRadius) then begin ap.AttackPutX := Targ.X; ap.AttackPutY := maxTop + cHHRadius; TestTeleport := Targ.Y - maxTop; end; end; end else begin failNum := 0; repeat i := random(bonuses.Count); inc(failNum); until not TestColl(bonuses.ar[i].X, bonuses.ar[i].Y - cHHRadius - bonuses.ar[i].Radius, cHHRadius) or (failNum = bonuses.Count*2); if failNum < bonuses.Count*2 then begin ap.AttackPutX := bonuses.ar[i].X; ap.AttackPutY := bonuses.ar[i].Y - cHHRadius - bonuses.ar[i].Radius; TestTeleport := 0; end; end; end; end.