author | nemo |
Tue, 01 Jan 2013 14:21:51 -0500 | |
changeset 8349 | a1dbe148f10f |
parent 8161 | 0b8beacff8a5 |
child 8330 | aaefa587e277 |
child 8370 | 0c79946e96f8 |
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_Rare = $00000001; // check only several positions 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 TestDrillRocket(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 TestKamikaze(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; function TestCake(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_Rare), // 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: @TestKamikaze; flags: 0), // amKamikaze (proc: @TestCake; flags: amtest_Rare or amtest_NoTarget), // amCake (proc: nil; flags: 0), // amSeduction (proc: @TestWatermelon; flags: 0), // amWatermelon (proc: nil; flags: 0), // amHellishBomb (proc: nil; flags: 0), // amNapalm (proc: @TestDrillRocket; 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 (proc: nil; flags: 0) // amKnife ); implementation uses uAIMisc, uVariables, uUtils, uGearsHandlers; 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 + 1 - 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 (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, trunc(x), trunc(y), 5))) or (t <= 0); EX:= trunc(x); EY:= trunc(y); if Level = 1 then value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand) else value:= RateExplosion(Me, EX, EY, 101); if value = 0 then value:= 1024 - 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 TestDrillRocket(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 - 35 - 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 (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, trunc(x), trunc(y), 5))) or (y > cWaterLine); EX:= trunc(x); EY:= trunc(y); if Level = 1 then value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand) else value:= RateExplosion(Me, EX, EY, 101); if value = 0 then value:= 1024 - 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 rTime > 4250; TestDrillRocket:= 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 (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, trunc(x), trunc(y), 5))) or (t <= 0); EX:= trunc(x); EY:= trunc(y); value:= RateShove(trunc(x), trunc(y), 5, 1, trunc((abs(dX)+abs(dY))*20), -dX, -dY, afTrackFall); // LOL copypasta: this is score for digging with... snowball //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 (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 6)) or ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, trunc(x), trunc(y), 6))) 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.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 (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, trunc(x), trunc(y), 5))) or (t = 0); EX:= trunc(x); EY:= trunc(y); if t < 50 then if Level = 1 then Score:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand) else Score:= RateExplosion(Me, EX, EY, 101) else Score:= BadTurn; if (valueResult < Score) and (Score > 0) 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:= 500; ap.ExplR:= 0; meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); repeat inc(TestTime, 900); // 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 (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or ((Me <> CurrentHedgehog^.Gear) and 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 div 1000 * 1000; ap.ExplR:= 90; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= Score end; end until (TestTime = 4100); 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:= 500; ap.ExplR:= 0; meX:= hwFloat2Float(Me^.X); meY:= hwFloat2Float(Me^.Y); repeat inc(TestTime, 900); 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 (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 6)) or ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, trunc(x), trunc(y), 6))) 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 div 1000 * 1000; ap.ExplR:= 300; ap.ExplX:= EX; ap.ExplY:= EY; valueResult:= Score end; end until (TestTime = 4100); 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 (((Me = CurrentHedgehog^.Gear) and TestColl(EX, EY, 4)) or ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, EX, EY, 4))) 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 ((Me = CurrentHedgehog^.Gear) and TestColl(rx, ry, 2)) or ((Me <> CurrentHedgehog^.Gear) and 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:= 1024 - 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 if Level > 4 then exit(BadTurn); 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) < 20 then exit(BadTurn); 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 > 48); if Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 5 then begin fallDmg:= TraceShoveFall(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 12) * 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: LongInt; begin if Level > 3 then exit(BadTurn); 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.025; // div 40 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 > 22); if Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 4 then begin fallDmg:= TraceShoveFall(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, a, v1, v2: LongInt; x, y, trackFall: LongInt; dx, dy: real; begin Targ:= Targ; // avoid compiler hint if Level < 3 then trackFall:= afTrackFall else trackFall:= 0; ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; x:= hwRound(Me^.X); y:= hwRound(Me^.Y); a:= cMaxAngle div 2; valueResult:= 0; while a >= 0 do begin dx:= sin(a / cMaxAngle * pi) * 0.5; dy:= cos(a / cMaxAngle * pi) * 0.5; v1:= RateShove(x - 10, y + 2 , 32, 30, 115 , -dx, -dy, trackFall); v2:= RateShove(x + 10, y + 2 , 32, 30, 115 , dx, -dy, trackFall); if (v1 > valueResult) or (v2 > valueResult) then if (v2 > v1) or {don't encourage turning for no gain}((v2 = v1) and (not Me^.dX.isNegative)) then begin ap.Angle:= a; valueResult:= v2 end else begin ap.Angle:= -a; valueResult:= v1 end; a:= a - 15 - random(cMaxAngle div 16) end; if valueResult <= 0 then valueResult:= BadTurn; TestBaseballBat:= valueResult; end; function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var valueResult, v1, v2, i: LongInt; x, y, trackFall: LongInt; begin Targ:= Targ; // avoid compiler hint if Level = 1 then trackFall:= afTrackFall else trackFall:= 0; ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; x:= hwRound(Me^.X); y:= hwRound(Me^.Y) + 4; v1:= 0; for i:= 0 to 8 do begin v1:= v1 + RateShove(x - 5, y - 10 * i , 19, 30, 40 , -0.45, -0.9, trackFall or afSetSkip); end; v1:= v1 + RateShove(x - 5, y - 90 , 19, 30, 40 , -0.45, -0.9, trackFall); // now try opposite direction v2:= 0; for i:= 0 to 8 do begin v2:= v2 + RateShove(x + 5, y - 10 * i , 19, 30, 40 , 0.45, -0.9, trackFall or afSetSkip); end; v2:= v2 + RateShove(x + 5, y - 90 , 19, 30, 40 , 0.45, -0.9, trackFall); 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; TestFirePunch:= valueResult; end; function TestWhip(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var valueResult, v1, v2: LongInt; x, y, trackFall: LongInt; begin Targ:= Targ; // avoid compiler hint if Level = 1 then trackFall:= afTrackFall else trackFall:= 0; ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; x:= hwRound(Me^.X); y:= hwRound(Me^.Y); // check left direction {first RateShove checks farthermost of two whip's AmmoShove attacks to encourage distant attacks (damaged hog is excluded from view of second RateShove call)} v1:= RateShove(x - 13, y , 30, 30, 25 , -1, -0.8, trackFall or afSetSkip); v1:= v1 + RateShove(x - 2, y , 30, 30, 25 , -1, -0.8, trackFall); // now try opposite direction v2:= RateShove(x + 13, y , 30, 30, 25 , 1, -0.8, trackFall or afSetSkip); v2:= v2 + RateShove(x + 2, y , 30, 30, 25 , 1, -0.8, trackFall); 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 TestKamikaze(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; const step = 8; var valueResult, i, v, tx: LongInt; trackFall: LongInt; t, d, x, y, dx, dy, cx: real; begin ap.ExplR:= 0; ap.Time:= 0; ap.Power:= 1; if Level = 1 then trackFall:= afTrackFall else if Level = 2 then trackFall:= 0 else exit(BadTurn); valueResult:= 0; v:= 0; x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); d:= sqrt(sqr(Targ.X - x) + sqr(Targ.Y - y)); if d < 10 then begin dx:= 0; dy:= 8; ap.Angle:= 2048 end else begin t:= step / d; dx:= (Targ.X - x) * t; dy:= (Targ.Y - y) * t; ap.Angle:= DxDy2AttackAnglef(dx, -dy) end; if dx >= 0 then cx:= 0.45 else cx:= -0.45; for i:= 0 to 512 div step - 2 do begin valueResult:= valueResult + RateShove(trunc(x), trunc(y) , 30, 30, 25 , cx, -0.9, trackFall or afSetSkip); x:= x + dx; y:= y + dy; end; if dx = 0 then begin x:= hwFloat2Float(Me^.X); y:= hwFloat2Float(Me^.Y); tx:= trunc(x); v:= RateShove(tx, trunc(y) , 30, 30, 25 , -cx, -0.9, trackFall); for i:= 1 to 512 div step - 2 do begin y:= y + dy; v:= v + RateShove(tx, trunc(y) , 30, 30, 25 , -cx, -0.9, trackFall or afSetSkip); end end; if v > valueResult then begin ap.Angle:= -2048; valueResult:= v end; v:= RateShove(trunc(x), trunc(y) , 30, 30, 25 , cx, -0.9, trackFall); valueResult:= valueResult + v - KillScore * friendlyfactor div 100 * 1024; if v < 65536 then inc(valueResult, RateExplosion(Me, trunc(x), trunc(y), 30)); TestKamikaze:= valueResult; end; function TestHammer(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var rate: LongInt; begin Level:= Level; // avoid compiler hint Targ:= Targ; 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; procedure checkCakeWalk(Me, Gear: PGear; var ap: TAttackParams); var i: Longword; v: LongInt; begin while (not TestColl(hwRound(Gear^.X), hwRound(Gear^.Y), 6)) and (Gear^.Y.Round < LAND_HEIGHT) do Gear^.Y:= Gear^.Y + _1; for i:= 0 to 2040 do begin cakeStep(Gear); v:= RateExplosion(Me, hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg * 2, afTrackFall); if v > ap.Power then begin ap.ExplX:= hwRound(Gear^.X); ap.ExplY:= hwRound(Gear^.Y); ap.Power:= v end end; end; function TestCake(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var valueResult, v1, v2: LongInt; cake: TGear; begin Targ:= Targ; // avoid compiler hint if (Level > 2) then exit(BadTurn); ap.ExplR:= 0; ap.Time:= 0; ap.Power:= BadTurn; // use it as max score value in checkCakeWalk //FillChar(cake, sizeof(cake), 0); cake.Radius:= 7; cake.CollisionMask:= $FF7F; cake.Hedgehog:= Me^.Hedgehog; // check left direction cake.Angle:= 3; cake.dX.isNegative:= true; cake.X:= Me^.X - _3; cake.Y:= Me^.Y; checkCakeWalk(Me, @cake, ap); v1:= ap.Power; // now try opposite direction cake.Angle:= 1; cake.dX.isNegative:= false; cake.X:= Me^.X + _3; cake.Y:= Me^.Y; checkCakeWalk(Me, @cake, ap); v2:= ap.Power; ap.Power:= 1; if (v2 > v1) then begin ap.Angle:= 1; valueResult:= v2 end else begin ap.Angle:= -1; valueResult:= v1 end; if valueResult <= 0 then valueResult:= BadTurn; TestCake:= valueResult; end; end.