1 (*
2  * Hedgewars, a free turn based strategy game
3  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *)
18 
19 {$INCLUDE "options.inc"}
20 
21 unit uAIAmmoTests;
22 interface
23 uses uConsts, uFloat, uTypes, uAIMisc;
24 const
25     amtest_Rare            = $00000001; // check only several positions
26     amtest_NoTarget        = $00000002; // each pos, but no targetting
27     amtest_MultipleAttacks = $00000004; // test could result in multiple attacks, set AttacksNum
28 
29 var windSpeed: real;
30 
31 type TAttackParams = record
32         Time, AttacksNum: Longword;
33         Angle, Power: LongInt;
34         ExplX, ExplY, ExplR: LongInt;
35         AttackPutX, AttackPutY: LongInt;
36         end;
37 
TestBazookanull38 function TestBazooka(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestBeenull39 function TestBee(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestSnowballnull40 function TestSnowball(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestGrenadenull41 function TestGrenade(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestMolotovnull42 function TestMolotov(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestClusterBombnull43 function TestClusterBomb(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestWatermelonnull44 function TestWatermelon(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestDrillRocketnull45 function TestDrillRocket(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestMortarnull46 function TestMortar(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestShotgunnull47 function TestShotgun(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestDesertEaglenull48 function TestDesertEagle(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestSniperRiflenull49 function TestSniperRifle(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestBaseballBatnull50 function TestBaseballBat(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestFirePunchnull51 function TestFirePunch(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestWhipnull52 function TestWhip(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestKamikazenull53 function TestKamikaze(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestAirAttacknull54 function TestAirAttack(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestTeleportnull55 function TestTeleport(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestHammernull56 function TestHammer(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestCakenull57 function TestCake(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
TestDynamitenull58 function TestDynamite(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
59 
60 type TAmmoTestProc = function (Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
61     TAmmoTest = record
62             proc: TAmmoTestProc;
63             flags: Longword;
64             end;
65 
66 const AmmoTests: array[TAmmoType] of TAmmoTest =
67             (
68             (proc: nil;              flags: 0), // amNothing
69             (proc: @TestGrenade;     flags: 0), // amGrenade
70             (proc: @TestClusterBomb; flags: 0), // amClusterBomb
71             (proc: @TestBazooka;     flags: 0), // amBazooka
72             (proc: @TestBee;         flags: amtest_Rare), // amBee
73             (proc: @TestShotgun;     flags: 0), // amShotgun
74             (proc: nil;              flags: 0), // amPickHammer
75             (proc: nil;              flags: 0), // amSkip
76             (proc: nil;              flags: 0), // amRope
77             (proc: nil;              flags: 0), // amMine
78             (proc: @TestDesertEagle; flags: amtest_MultipleAttacks), // amDEagle
79             (proc: @TestDynamite;    flags: amtest_NoTarget), // amDynamite
80             (proc: @TestFirePunch;   flags: amtest_NoTarget), // amFirePunch
81             (proc: @TestWhip;        flags: amtest_NoTarget), // amWhip
82             (proc: @TestBaseballBat; flags: amtest_NoTarget), // amBaseballBat
83             (proc: nil;              flags: 0), // amParachute
84             (proc: @TestAirAttack;   flags: amtest_Rare), // amAirAttack
85             (proc: nil;              flags: 0), // amMineStrike
86             (proc: nil;              flags: 0), // amBlowTorch
87             (proc: nil;              flags: 0), // amGirder
88             (proc: nil;              flags: 0), // amTeleport
89             //(proc: @TestTeleport;    flags: amtest_OnTurn), // amTeleport
90             (proc: nil;              flags: 0), // amSwitch
91             (proc: @TestMortar;      flags: 0), // amMortar
92             (proc: @TestKamikaze;    flags: 0), // amKamikaze
93             (proc: @TestCake;        flags: amtest_Rare or amtest_NoTarget), // amCake
94             (proc: nil;              flags: 0), // amSeduction
95             (proc: @TestWatermelon;  flags: 0), // amWatermelon
96             (proc: nil;              flags: 0), // amHellishBomb
97             (proc: nil;              flags: 0), // amNapalm
98             (proc: @TestDrillRocket; flags: 0), // amDrill
99             (proc: nil;              flags: 0), // amBallgun
100             (proc: nil;              flags: 0), // amRCPlane
101             (proc: nil;              flags: 0), // amLowGravity
102             (proc: nil;              flags: 0), // amExtraDamage
103             (proc: nil;              flags: 0), // amInvulnerable
104             (proc: nil;              flags: 0), // amExtraTime
105             (proc: nil;              flags: 0), // amLaserSight
106             (proc: nil;              flags: 0), // amVampiric
107             (proc: @TestSniperRifle; flags: 0), // amSniperRifle
108             (proc: nil;              flags: 0), // amJetpack
109             (proc: @TestMolotov;     flags: 0), // amMolotov
110             (proc: nil;              flags: 0), // amBirdy
111             (proc: nil;              flags: 0), // amPortalGun
112             (proc: nil;              flags: 0), // amPiano
113             (proc: @TestGrenade;     flags: 0), // amGasBomb
114             (proc: @TestShotgun;     flags: 0), // amSineGun
115             (proc: nil;              flags: 0), // amFlamethrower
116             (proc: @TestGrenade;     flags: 0), // amSMine
117             (proc: @TestHammer;      flags: amtest_NoTarget), // amHammer
118             (proc: nil;              flags: 0), // amResurrector
119             (proc: nil;              flags: 0), // amDrillStrike
120             (proc: nil;              flags: 0), // amSnowball
121             (proc: nil;              flags: 0), // amTardis
122             (proc: nil;              flags: 0), // amLandGun
123             (proc: nil;              flags: 0), // amIceGun
124             (proc: nil;              flags: 0), // amKnife
125             (proc: nil;              flags: 0), // amRubber
126             (proc: nil;              flags: 0), // amAirMine
127             (proc: nil;              flags: 0), // amCreeper
128             (proc: @TestShotgun;     flags: 0)  // amMinigun
129             );
130 
131 implementation
132 uses uVariables, uUtils, uGearsHandlers;
133 
Metricnull134 function Metric(x1, y1, x2, y2: LongInt): LongInt; inline;
135 begin
136 Metric:= abs(x1 - x2) + abs(y1 - y2)
137 end;
138 
TestBazookanull139 function TestBazooka(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
140 const cExtraTime = 300;
141 var Vx, Vy, r, mX, mY: real;
142     rTime: LongInt;
143     EX, EY: LongInt;
144     valueResult: LongInt;
145     targXWrap, x, y, dX, dY: real;
146     t: LongInt;
147     value: LongInt;
148 begin
149 mX:= hwFloat2Float(Me^.X);
150 mY:= hwFloat2Float(Me^.Y);
151 ap.Time:= 0;
152 rTime:= 350;
153 ap.ExplR:= 0;
154 if (WorldEdge = weWrap) then
155     if (Targ.Point.X < mX) then
156          targXWrap:= Targ.Point.X + (RightX-LeftX)
157     else targXWrap:= Targ.Point.X - (RightX-LeftX);
158 valueResult:= BadTurn;
159 repeat
160     rTime:= rTime + 300 + Level * 50 + random(300);
161     if (WorldEdge = weWrap) and (random(2)=0) then
162          Vx:= - windSpeed * rTime * 0.5 + (targXWrap + AIrndSign(2) + AIrndOffset(Targ, Level) - mX) / rTime
163     else Vx:= - windSpeed * rTime * 0.5 + (Targ.Point.X + AIrndSign(2) + AIrndOffset(Targ, Level) - mX) / rTime;
164     Vy:= cGravityf * rTime * 0.5 - (Targ.Point.Y + 1 - mY) / rTime;
165     r:= sqr(Vx) + sqr(Vy);
166     if not (r > 1) then
167         begin
168         x:= mX;
169         y:= mY;
170         dX:= Vx;
171         dY:= -Vy;
172         t:= rTime;
173         repeat
174             x:= CheckWrap(x);
175             x:= x + dX;
176 
177             y:= y + dY;
178             dX:= dX + windSpeed;
179             //dX:= CheckBounce(x,dX);
180             dY:= dY + cGravityf;
181             dec(t)
182         until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
183                ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5))) or (t < -cExtraTime);
184 
185         EX:= trunc(x);
186         EY:= trunc(y);
187         if t >= -cExtraTime then
188             begin
189                 if Level = 1 then
190                     value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
191                 else
192                     value:= RateExplosion(Me, EX, EY, 101);
193             end else
194                 value:= BadTurn;
195 
196         if (value = 0) and (Targ.Kind = gtHedgehog) and (Targ.Score > 0) then
197             if GameFlags and gfSolidLand = 0 then
198                  value := 1024 - Metric(Targ.Point.X, Targ.Point.Y, EX, EY) div 64
199             else value := BadTurn;
200 
201         if (valueResult < value) or ((valueResult = value) and (Level < 3)) then
202             begin
203             ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random((Level - 1) * 9));
204             ap.Power:= trunc(sqrt(r) * cMaxPower) - random((Level - 1) * 17 + 1);
205             ap.ExplR:= 100;
206             ap.ExplX:= EX;
207             ap.ExplY:= EY;
208             valueResult:= value
209             end;
210         end
211 until rTime > 5050 - Level * 800;
212 TestBazooka:= valueResult
213 end;
214 
calcBeeFlightnull215 function calcBeeFlight(Me: PGear; x, y, dx, dy, tX, tY: real; var eX, eY: LongInt): LongInt;
216 var t: Longword;
217     f: boolean;
218     speed, d: real;
219 begin
220     // parabola flight before activation
221     t:= 500;
222     repeat
223         x:= x + dx;
224         y:= y + dy;
225         dy:= dy + cGravityf;
226         f:= ((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
227            ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5));
228         dec(t)
229     until (t = 0) or (y >= cWaterLine) or f;
230 
231     if f then
232     begin
233         eX:= trunc(x);
234         eY:= trunc(y);
235         exit(RateExplosion(Me, eX, eY, 101, afTrackFall or afErasesLand));
236     end;
237 
238 
239     // activated
240     t:= 5000;
241     speed:= sqrt(sqr(dx) + sqr(dy));
242 
243     repeat
244         if (t and $F) = 0 then
245         begin
246             dx:= dx + 0.000064 * (tX - x);
247             dy:= dy + 0.000064 * (tY - y);
248             d := speed / sqrt(sqr(dx) + sqr(dy));
249             dx:= dx * d;
250             dy:= dy * d;
251         end;
252 
253         x:= x + dx;
254         y:= y + dy;
255         f:= ((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
256            ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5));
257         dec(t)
258     until (t = 0) or f;
259 
260     if f then
261     begin
262         eX:= trunc(x);
263         eY:= trunc(y);
264         exit(RateExplosion(Me, eX, eY, 101, afTrackFall or afErasesLand));
265     end
266     else
267         calcBeeFlight:= BadTurn
268 end;
269 
TestBeenull270 function TestBee(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
271 var i, j: LongInt;
272     valueResult, v, a, p: LongInt;
273     mX, mY: real;
274     eX, eY: LongInt;
275 begin
276     if Level > 1 then
277         exit(BadTurn);
278 
279     eX:= 0;
280     eY:= 0;
281     mX:= hwFloat2Float(Me^.X);
282     mY:= hwFloat2Float(Me^.Y);
283     valueResult:= BadTurn;
284     for i:= 0 to 8 do
285         for j:= 0 to 1 do
286             begin
287             a:= i * 120;
288             p:= random(cMaxPower - 200) + 180;
289 
290             if j = 0 then
291                 a:= -a;
292 
293             v:= calcBeeFlight(Me
294                     , mX
295                     , mY
296                     , sin(a * pi / 2048) * p / cPowerDivisor
297                     , -cos(a * pi / 2048) * p / cPowerDivisor
298                     , Targ.Point.X
299                     , Targ.Point.Y
300                     , eX
301                     , eY);
302 
303             if v > valueResult then
304                 begin
305                 ap.ExplR:= 100;
306                 ap.ExplX:= eX;
307                 ap.ExplY:= eY;
308                 ap.Angle:= a;
309                 ap.Power:= p;
310                 valueResult:= v
311                 end
312             end;
313 
314     ap.AttackPutX:= Targ.Point.X;
315     ap.AttackPutY:= Targ.Point.Y;
316 
317     if valueResult > 0 then
318         TestBee:= valueResult - 5000
319     else
320         TestBee:= BadTurn // no digging
321 end;
322 
TestDrillRocketnull323 function TestDrillRocket(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
324 var Vx, Vy, r, mX, mY: real;
325     rTime: LongInt;
326     EX, EY: LongInt;
327     valueResult: LongInt;
328     targXWrap, x, y, dX, dY: real;
329     t: LongInt;
330     value: LongInt;
331     t2: real;
332     timer: Longint;
333 begin
334     if (Level > 3) then exit(BadTurn);
335 
336     mX:= hwFloat2Float(Me^.X);
337     mY:= hwFloat2Float(Me^.Y);
338     ap.Time:= 0;
339     rTime:= 350;
340     ap.ExplR:= 0;
341     valueResult:= BadTurn;
342     if (WorldEdge = weWrap) then
343         if (Targ.Point.X < mX) then
344              targXWrap:= Targ.Point.X + (RightX-LeftX)
345         else targXWrap:= Targ.Point.X - (RightX-LeftX);
346     timer:= 0;
347     repeat
348         rTime:= rTime + 300 + Level * 50 + random(300);
349         if (WorldEdge = weWrap) and (random(2)=0) then
350              Vx:= - windSpeed * rTime * 0.5 + (targXWrap + AIrndSign(2) - mX) / rTime
351         else Vx:= - windSpeed * rTime * 0.5 + (Targ.Point.X + AIrndSign(2) - mX) / rTime;
352         Vy:= cGravityf * rTime * 0.5 - (Targ.Point.Y - 35 - mY) / rTime;
353         r:= sqr(Vx) + sqr(Vy);
354         if not (r > 1) then
355             begin
356             x:= mX;
357             y:= mY;
358             dX:= Vx;
359             dY:= -Vy;
360             t:= rTime;
361             repeat
362                 x:= CheckWrap(x);
363                 x:= x + dX;
364                 y:= y + dY;
365                 dX:= dX + windSpeed;
366                 dY:= dY + cGravityf;
367                 dec(t)
368             until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
369                    ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5))) or (y > cWaterLine);
370 
371             if TestCollExcludingObjects(trunc(x), trunc(y), 5) and (Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) > 21) then
372                 begin
373                 timer := 500;
374                 t2 := 0.5 / sqrt(sqr(dX) + sqr(dY));
375                 dX := dX * t2;
376                 dY := dY * t2;
377                 repeat
378                     x:= x + dX;
379                     y:= y + dY;
380                     dec(timer);
381                 until (Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) < 22)
382                     or (x < 0)
383                     or (y < 0)
384                     or (trunc(x) > LAND_WIDTH)
385                     or (trunc(y) > LAND_HEIGHT)
386                     or (not TestCollExcludingObjects(trunc(x), trunc(y), 5))
387                     or (timer = 0)
388                 end;
389             EX:= trunc(x);
390             EY:= trunc(y);
391             // Try to prevent AI from thinking firing into water will cause a drowning
392             if (EY < cWaterLine-5) and (timer > 0) and (Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) > 21) then exit(BadTurn);
393             if Level = 1 then
394                 value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
395             else value:= RateExplosion(Me, EX, EY, 101);
396             if valueResult <= value then
397                 begin
398                 ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random((Level - 1) * 9));
399                 ap.Power:= trunc(sqrt(r) * cMaxPower) - random((Level - 1) * 17 + 1);
400                 ap.ExplR:= 100;
401                 ap.ExplX:= EX;
402                 ap.ExplY:= EY;
403                 valueResult:= value-2500 // trying to make it slightly less attractive than a bazooka, to prevent waste.  AI could use awareness of weapon count
404                 end;
405             end
406     until rTime > 5050 - Level * 800;
407     TestDrillRocket:= valueResult
408 end;
409 
410 
TestSnowballnull411 function TestSnowball(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
412 var Vx, Vy, r: real;
413     rTime: LongInt;
414     EX, EY: LongInt;
415     valueResult: LongInt;
416     targXWrap, x, y, dX, dY, meX, meY: real;
417     t: LongInt;
418     value: LongInt;
419 
420 begin
421 meX:= hwFloat2Float(Me^.X);
422 meY:= hwFloat2Float(Me^.Y);
423 ap.Time:= 0;
424 rTime:= 350;
425 ap.ExplR:= 0;
426 valueResult:= BadTurn;
427 if (WorldEdge = weWrap) then
428     if (Targ.Point.X < meX) then
429          targXWrap:= Targ.Point.X + (RightX-LeftX)
430     else targXWrap:= Targ.Point.X - (RightX-LeftX);
431 repeat
432     rTime:= rTime + 300 + Level * 50 + random(1000);
433     if (WorldEdge = weWrap) and (random(2)=0) then
434          Vx:= - windSpeed * rTime * 0.5 + ((targXWrap + AIrndSign(2)) - meX) / rTime
435     else Vx:= - windSpeed * rTime * 0.5 + ((Targ.Point.X + AIrndSign(2)) - meX) / rTime;
436     Vy:= cGravityf * rTime * 0.5 - (Targ.Point.Y - meY) / rTime;
437     r:= sqr(Vx) + sqr(Vy);
438     if not (r > 1) then
439         begin
440         x:= meX;
441         y:= meY;
442         dX:= Vx;
443         dY:= -Vy;
444         t:= rTime;
445         repeat
446             x:= CheckWrap(x);
447             x:= x + dX;
448             y:= y + dY;
449             dX:= dX + windSpeed;
450             dY:= dY + cGravityf;
451             dec(t)
452         until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
453                ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5))) or (t <= 0);
454         EX:= trunc(x);
455         EY:= trunc(y);
456 
457         value:= RateShove(Me, trunc(x), trunc(y), 5, 1, trunc((abs(dX)+abs(dY))*20), -dX, -dY, afTrackFall);
458         // LOL copypasta: this is score for digging with... snowball
459         //if value = 0 then
460         //    value:= - Metric(Targ.Point.X, Targ.Point.Y, EX, EY) div 64;
461 
462         if valueResult <= value then
463             begin
464             ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random((Level - 1) * 9));
465             ap.Power:= trunc(sqrt(r) * cMaxPower) - random((Level - 1) * 17 + 1);
466             ap.ExplR:= 0;
467             ap.ExplX:= EX;
468             ap.ExplY:= EY;
469             valueResult:= value
470             end;
471      end
472 until (rTime > 5050 - Level * 800);
473 TestSnowball:= valueResult
474 end;
475 
TestMolotovnull476 function TestMolotov(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
477 var Vx, Vy, r: real;
478     Score, EX, EY, valueResult: LongInt;
479     TestTime: LongInt;
480     targXWrap, x, y, dY, meX, meY: real;
481     t: LongInt;
482 begin
483 meX:= hwFloat2Float(Me^.X);
484 meY:= hwFloat2Float(Me^.Y);
485 valueResult:= BadTurn;
486 TestTime:= 0;
487 ap.ExplR:= 0;
488 if (WorldEdge = weWrap) then
489     if (Targ.Point.X < meX) then
490          targXWrap:= Targ.Point.X + (RightX-LeftX)
491     else targXWrap:= Targ.Point.X - (RightX-LeftX);
492 repeat
493     inc(TestTime, 300);
494     if (WorldEdge = weWrap) and (random(2)=0) then
495          Vx:= (targXWrap - meX) / TestTime
496     else Vx:= (Targ.Point.X - meX) / TestTime;
497     Vy:= cGravityf * (TestTime div 2) - Targ.Point.Y - meY / TestTime;
498     r:= sqr(Vx) + sqr(Vy);
499     if not (r > 1) then
500         begin
501         x:= meX;
502         y:= meY;
503         dY:= -Vy;
504         t:= TestTime;
505         repeat
506             x:= CheckWrap(x);
507             x:= x + Vx;
508             y:= y + dY;
509             dY:= dY + cGravityf;
510             dec(t)
511         until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 6)) or
512                ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 6))) or (t = 0);
513         EX:= trunc(x);
514         EY:= trunc(y);
515         if t < 50 then
516             Score:= RateExplosion(Me, EX, EY, 97)  // average of 17 attempts, most good, but some failing spectacularly
517         else
518             Score:= BadTurn;
519 
520         if valueResult < Score then
521             begin
522             ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level));
523             ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15);
524             ap.ExplR:= 100;
525             ap.ExplX:= EX;
526             ap.ExplY:= EY;
527             valueResult:= Score
528             end;
529         end
530 until (TestTime > 5050 - Level * 800);
531 TestMolotov:= valueResult
532 end;
533 
TestGrenadenull534 function TestGrenade(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
535 const tDelta = 24;
536 var Vx, Vy, r: real;
537     Score, EX, EY, valueResult: LongInt;
538     TestTime: LongInt;
539     targXWrap, x, y, meX, meY, dY: real;
540     t: LongInt;
541 begin
542 valueResult:= BadTurn;
543 TestTime:= 0;
544 ap.ExplR:= 0;
545 meX:= hwFloat2Float(Me^.X);
546 meY:= hwFloat2Float(Me^.Y);
547 if (WorldEdge = weWrap) then
548     if (Targ.Point.X < meX) then
549          targXWrap:= Targ.Point.X + (RightX-LeftX)
550     else targXWrap:= Targ.Point.X - (RightX-LeftX);
551 repeat
552     inc(TestTime, 1000);
553     if (WorldEdge = weWrap) and (random(2)=0) then
554          Vx:= (targXWrap + AIrndOffset(Targ, Level) - meX) / (TestTime + tDelta)
555     else Vx:= (Targ.Point.X + AIrndOffset(Targ, Level) - meX) / (TestTime + tDelta);
556     Vy:= cGravityf * ((TestTime + tDelta) div 2) - (Targ.Point.Y - meY) / (TestTime + tDelta);
557     r:= sqr(Vx) + sqr(Vy);
558     if not (r > 1) then
559         begin
560         x:= meX;
561         y:= meY;
562         dY:= -Vy;
563         t:= TestTime;
564         repeat
565             x:= CheckWrap(x);
566             x:= x + Vx;
567             y:= y + dY;
568             dY:= dY + cGravityf;
569             dec(t)
570         until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
571                ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5))) or (t = 0);
572     EX:= trunc(x);
573     EY:= trunc(y);
574     if t < 50 then
575         if Level = 1 then
576             Score:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
577         else Score:= RateExplosion(Me, EX, EY, 101)
578     else
579         Score:= BadTurn;
580 
581     if (valueResult < Score) and (Score > 0) then
582         begin
583         ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level * 3));
584         ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 20);
585         ap.Time:= TestTime;
586         ap.ExplR:= 100;
587         ap.ExplX:= EX;
588         ap.ExplY:= EY;
589         valueResult:= Score
590         end;
591     end
592 //until (Score > 204800) or (TestTime > 4000);
593 until TestTime > 4500 - Level * 512;
594 TestGrenade:= valueResult
595 end;
596 
TestClusterBombnull597 function TestClusterBomb(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
598 const tDelta = 24;
599 var Vx, Vy, r: real;
600     Score, EX, EY, valueResult: LongInt;
601     TestTime: Longword;
602     x, y, dY, meX, meY: real;
603     t: LongInt;
604 begin
605 valueResult:= BadTurn;
606 TestTime:= 500;
607 ap.ExplR:= 0;
608 meX:= hwFloat2Float(Me^.X);
609 meY:= hwFloat2Float(Me^.Y);
610 repeat
611     inc(TestTime, 900);
612     // Try to overshoot slightly, seems to pay slightly better dividends in terms of hitting cluster
613     if meX<Targ.Point.X then
614         Vx:= ((Targ.Point.X+10) - meX) / (TestTime + tDelta)
615     else
616         Vx:= ((Targ.Point.X-10) - meX) / (TestTime + tDelta);
617     Vy:= cGravityf * ((TestTime + tDelta) div 2) - ((Targ.Point.Y-50) - meY) / (TestTime + tDelta);
618     r:= sqr(Vx)+sqr(Vy);
619     if not (r > 1) then
620         begin
621         x:= meX;
622         y:= meY;
623         dY:= -Vy;
624         t:= TestTime;
625     repeat
626         x:= x + Vx;
627         y:= y + dY;
628         dY:= dY + cGravityf;
629         dec(t)
630     until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
631            ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5))) or (t = 0);
632     EX:= trunc(x);
633     EY:= trunc(y);
634     if t < 50 then
635         Score:= RateExplosion(Me, EX, EY, 41)
636     else
637         Score:= BadTurn;
638 
639      if Score > 0 then
640         begin
641         ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level * 2));
642         ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15);
643         ap.Time:= TestTime div 1000 * 1000;
644         ap.ExplR:= 90;
645         ap.ExplX:= EX;
646         ap.ExplY:= EY;
647         valueResult:= Score
648         end;
649      end
650 until (TestTime = 4100);
651 TestClusterBomb:= valueResult
652 end;
653 
TestWatermelonnull654 function TestWatermelon(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
655 const tDelta = 24;
656 var Vx, Vy, r: real;
657     Score, EX, EY, valueResult: LongInt;
658     TestTime: Longword;
659     targXWrap, x, y, dY, meX, meY: real;
660     t: LongInt;
661 begin
662 valueResult:= BadTurn;
663 TestTime:= 500;
664 ap.ExplR:= 0;
665 meX:= hwFloat2Float(Me^.X);
666 meY:= hwFloat2Float(Me^.Y);
667 if (WorldEdge = weWrap) then
668     if (Targ.Point.X < meX) then
669          targXWrap:= Targ.Point.X + (RightX-LeftX)
670     else targXWrap:= Targ.Point.X - (RightX-LeftX);
671 repeat
672     inc(TestTime, 900);
673     if (WorldEdge = weWrap) and (random(2)=0) then
674 		 Vx:= (targXWrap - meX) / (TestTime + tDelta)
675     else Vx:= (Targ.Point.X - meX) / (TestTime + tDelta);
676     Vy:= cGravityf * ((TestTime + tDelta) div 2) - ((Targ.Point.Y-50) - meY) / (TestTime + tDelta);
677     r:= sqr(Vx)+sqr(Vy);
678     if not (r > 1) then
679         begin
680         x:= meX;
681         y:= meY;
682         dY:= -Vy;
683         t:= TestTime;
684         repeat
685             x:= CheckWrap(x);
686             x:= x + Vx;
687             y:= y + dY;
688             dY:= dY + cGravityf;
689             dec(t)
690        until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 6)) or
691                ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 6))) or (t = 0);
692 
693         EX:= trunc(x);
694         EY:= trunc(y);
695         if t < 50 then
696             Score:= RateExplosion(Me, EX, EY, 200) + RateExplosion(Me, EX, EY + 120, 200)
697         else
698             Score:= BadTurn;
699 
700         if valueResult < Score then
701             begin
702             ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level));
703             ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15);
704             ap.Time:= TestTime div 1000 * 1000;
705             ap.ExplR:= 300;
706             ap.ExplX:= EX;
707             ap.ExplY:= EY;
708             valueResult:= Score
709             end;
710         end
711 until (TestTime = 4100);
712 TestWatermelon:= valueResult
713 end;
714 
715 
Solvenull716     function Solve(TX, TY, MX, MY: LongInt): LongWord;
717     var A, B, D, T: real;
718         C: LongInt;
719     begin
720         A:= sqr(cGravityf);
721         B:= - cGravityf * (TY - MY) - 1;
722         C:= sqr(TY - MY) + sqr(TX - MX);
723         D:= sqr(B) - A * C;
724         if D >= 0 then
725             begin
726             D:= sqrt(D) - B;
727             if D >= 0 then
728                 T:= sqrt(D * 2 / A)
729             else
730                 T:= 0;
731             Solve:= trunc(T)
732             end
733             else
734                 Solve:= 0
735     end;
736 
TestMortarnull737 function TestMortar(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
738 //const tDelta = 24;
739 var Vx, Vy: real;
740     Score, EX, EY: LongInt;
741     TestTime: Longword;
742     x, y, dY, meX, meY: real;
743 begin
744     TestMortar:= BadTurn;
745     ap.ExplR:= 0;
746 
747     meX:= hwFloat2Float(Me^.X);
748     meY:= hwFloat2Float(Me^.Y);
749 
750     if (Level > 2) then
751         exit(BadTurn);
752 
753     TestTime:= Solve(Targ.Point.X, Targ.Point.Y, trunc(meX), trunc(meY));
754 
755     if TestTime = 0 then
756         exit(BadTurn);
757 
758     Vx:= (Targ.Point.X - meX) / TestTime;
759     Vy:= cGravityf * (TestTime div 2) - (Targ.Point.Y - meY) / TestTime;
760 
761     x:= meX;
762     y:= meY;
763     dY:= -Vy;
764 
765     repeat
766         x:= x + Vx;
767         y:= y + dY;
768         dY:= dY + cGravityf;
769         EX:= trunc(x);
770         EY:= trunc(y);
771     until (((Me = CurrentHedgehog^.Gear) and TestColl(EX, EY, 4)) or
772            ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, EX, EY, 4))) or (EY > cWaterLine);
773 
774     if (EY < cWaterLine) and (dY >= 0) then
775         begin
776         Score:= RateExplosion(Me, EX, EY, 91);
777         if (Score = 0) then
778             if (dY > 0.15) and (Targ.Kind = gtHedgehog) and (Targ.Score > 0) then
779                 Score:= - abs(Targ.Point.Y - EY) div 32
780             else
781                 Score:= BadTurn
782         else if (Score < 0) then
783             Score:= BadTurn
784         end
785     else
786         Score:= BadTurn;
787 
788     if Score > 0 then
789         begin
790         ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level));
791         ap.Power:= 1;
792         ap.ExplR:= 100;
793         ap.ExplX:= EX;
794         ap.ExplY:= EY;
795         TestMortar:= Score
796         end;
797 end;
798 
TestShotgunnull799 function TestShotgun(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
800 const
801     MIN_RANGE =  80;
802     MAX_RANGE = 400;
803 var Vx, Vy, x, y: real;
804     rx, ry, valueResult: LongInt;
805     range: integer;
806 begin
807 TestShotgun:= BadTurn;
808 ap.ExplR:= 0;
809 ap.Time:= 0;
810 ap.Power:= 1;
811 x:= hwFloat2Float(Me^.X);
812 y:= hwFloat2Float(Me^.Y);
813 range:= Metric(trunc(x), trunc(y), Targ.Point.X, Targ.Point.Y);
814 if ( range < MIN_RANGE ) or ( range > MAX_RANGE ) then
815     exit(BadTurn);
816 
817 Vx:= (Targ.Point.X - x) * 1 / 1024;
818 Vy:= (Targ.Point.Y - y) * 1 / 1024;
819 ap.Angle:= DxDy2AttackAnglef(Vx, -Vy);
820 repeat
821     x:= x + vX;
822     y:= y + vY;
823     rx:= trunc(x);
824     ry:= trunc(y);
825     if ((Me = CurrentHedgehog^.Gear) and TestColl(rx, ry, 2)) or
826         ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, rx, ry, 2)) then
827     begin
828         x:= x + vX * 8;
829         y:= y + vY * 8;
830         valueResult:= RateShotgun(Me, vX, vY, rx, ry);
831 
832         if (valueResult = 0) and (Targ.Kind = gtHedgehog) and (Targ.Score > 0) then
833             begin
834             if GameFlags and gfSolidLand = 0 then
835                  valueResult:= 1024 - Metric(Targ.Point.X, Targ.Point.Y, rx, ry) div 64
836             else valueResult := BadTurn
837             end
838         else
839             dec(valueResult, Level * 4000);
840         // 27/20 is reuse bonus
841         exit(valueResult * 27 div 20)
842     end
843 until (Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) < 4)
844     or (x < 0)
845     or (y < 0)
846     or (trunc(x) > LAND_WIDTH)
847     or (trunc(y) > LAND_HEIGHT);
848 
849 TestShotgun:= BadTurn
850 end;
851 
TestDesertEaglenull852 function TestDesertEagle(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
853 var Vx, Vy, x, y, t: real;
854     d: Longword;
855     ix, iy, valueResult: LongInt;
856 begin
857 if (Level > 4) or (Targ.Score < 0) or (Targ.Kind <> gtHedgehog) then exit(BadTurn);
858 Level:= Level; // avoid compiler hint
859 ap.ExplR:= 1;
860 ap.Time:= 0;
861 ap.Power:= 1;
862 
863 x:= hwFloat2Float(Me^.X);
864 y:= hwFloat2Float(Me^.Y);
865 
866 if Abs(trunc(x) - Targ.Point.X) + Abs(trunc(y) - Targ.Point.Y) < 20 then
867     exit(BadTurn);
868 
869 t:= 2 / sqrt(sqr(Targ.Point.X - x)+sqr(Targ.Point.Y-y));
870 Vx:= (Targ.Point.X - x) * t;
871 Vy:= (Targ.Point.Y - y) * t;
872 ap.Angle:= DxDy2AttackAnglef(Vx, -Vy);
873 d:= 0;
874 
875 ix:= trunc(x);
876 iy:= trunc(y);
877 
878 if ((ix and LAND_WIDTH_MASK) = 0) and ((iy and LAND_HEIGHT_MASK) = 0) then
879     repeat
880         if Land[iy, ix] <> 0 then
881             inc(d);
882         x:= x + vX;
883         y:= y + vY;
884         ix:= trunc(x);
885         iy:= trunc(y);
886     until (Abs(Targ.Point.X - ix) + Abs(Targ.Point.Y - iy) < 5)
887         or (x < 0)
888         or (y < 0)
889         or (ix >= LAND_WIDTH)
890         or (iy >= LAND_HEIGHT)
891         or (d > 48);
892 
893 if Abs(Targ.Point.X - ix) + Abs(Targ.Point.Y - iy) < 5 then
894     begin
895     ap.AttacksNum:= 1 + (d + 8) div 12;
896     valueResult:= RateShove(Me, Targ.Point.X, Targ.Point.Y, 1, 7, 20, vX*0.125, vY*0.125, afTrackFall) - ap.AttacksNum
897     end
898 else
899     valueResult:= BadTurn;
900 
901 TestDesertEagle:= valueResult
902 end;
903 
904 
TestSniperRiflenull905 function TestSniperRifle(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
906 var Vx, Vy, x, y, t, dmg: real;
907     d: Longword;
908     //fallDmg: LongInt;
909 begin
910 if (Level > 3) or (Targ.Score < 0) or (Targ.Kind <> gtHedgehog) then exit(BadTurn);
911 Level:= Level; // avoid compiler hint
912 ap.ExplR:= 0;
913 ap.Time:= 0;
914 ap.Power:= 1;
915 x:= hwFloat2Float(Me^.X);
916 y:= hwFloat2Float(Me^.Y);
917 if Abs(trunc(x) - Targ.Point.X) + Abs(trunc(y) - Targ.Point.Y) < 40 then
918     exit(BadTurn);
919 
920 dmg:= sqrt(sqr(Targ.Point.X - x)+sqr(Targ.Point.Y-y));
921 t:= 1.5 / dmg;
922 dmg:= dmg * 0.025; // div 40
923 Vx:= (Targ.Point.X - x) * t;
924 Vy:= (Targ.Point.Y - y) * t;
925 ap.Angle:= DxDy2AttackAnglef(Vx, -Vy);
926 d:= 0;
927 
928 repeat
929     x:= x + vX;
930     y:= y + vY;
931     if ((trunc(x) and LAND_WIDTH_MASK) = 0)and((trunc(y) and LAND_HEIGHT_MASK) = 0)
932     and (Land[trunc(y), trunc(x)] <> 0) then
933         inc(d);
934 until (Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) < 4)
935     or (x < 0)
936     or (y < 0)
937     or (trunc(x) > LAND_WIDTH)
938     or (trunc(y) > LAND_HEIGHT)
939     or (d > 22);
940 
941 if Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) < 4 then
942      TestSniperRifle:= RateShove(Me, Targ.Point.X, Targ.Point.Y, 1, trunc(dmg), 20, vX*0.166, vY*0.166, afTrackFall)
943 else TestSniperRifle:= BadTurn;
944 end;
945 
946 
TestBaseballBatnull947 function TestBaseballBat(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
948 var valueResult, a, v1, v2: LongInt;
949     x, y, trackFall: LongInt;
950     dx, dy: real;
951 begin
952     Targ:= Targ; // avoid compiler hint
953 
954     if Level < 3 then trackFall:= afTrackFall
955         else trackFall:= 0;
956 
957     ap.ExplR:= 0;
958     ap.Time:= 0;
959     ap.Power:= 1;
960     x:= hwRound(Me^.X);
961     y:= hwRound(Me^.Y);
962 
963     a:= cMaxAngle div 2;
964     valueResult:= 0;
965 
966     while a >= 0 do
967         begin
968         dx:= sin(a / cMaxAngle * pi) * 0.5;
969         dy:= cos(a / cMaxAngle * pi) * 0.5;
970 
971         v1:= RateShove(Me, x - 10, y + 2
972                 , 32, 30, 115
973                 , -dx, -dy, trackFall);
974         v2:= RateShove(Me, x + 10, y + 2
975                 , 32, 30, 115
976                 , dx, -dy, trackFall);
977         if (v1 > valueResult) or (v2 > valueResult) then
978             if (v2 > v1)
979                 or {don't encourage turning for no gain}((v2 = v1) and (not Me^.dX.isNegative)) then
980                 begin
981                 ap.Angle:= a;
982                 valueResult:= v2
983                 end
984             else
985                 begin
986                 ap.Angle:= -a;
987                 valueResult:= v1
988                 end;
989 
990         a:= a - 15 - random(cMaxAngle div 16)
991         end;
992 
993     if valueResult <= 0 then
994         valueResult:= BadTurn;
995 
996     TestBaseballBat:= valueResult;
997 end;
998 
TestFirePunchnull999 function TestFirePunch(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1000 var valueResult, v1, v2, i: LongInt;
1001     x, y, trackFall: LongInt;
1002 begin
1003     Targ:= Targ; // avoid compiler hint
1004 
1005     if Level = 1 then trackFall:= afTrackFall
1006     else trackFall:= 0;
1007 
1008     ap.ExplR:= 0;
1009     ap.Time:= 0;
1010     ap.Power:= 1;
1011     x:= hwRound(Me^.X);
1012     y:= hwRound(Me^.Y) + 4;
1013 
1014     v1:= 0;
1015     for i:= 0 to 8 do
1016         begin
1017         v1:= v1 + RateShove(Me, x - 5, y - 10 * i
1018                 , 19, 30, 40
1019                 , -0.45, -0.9, trackFall or afSetSkip);
1020         end;
1021     v1:= v1 + RateShove(Me, x - 5, y - 90
1022             , 19, 30, 40
1023             , -0.45, -0.9, trackFall);
1024 
1025 
1026     // now try opposite direction
1027     v2:= 0;
1028     for i:= 0 to 8 do
1029         begin
1030         v2:= v2 + RateShove(Me, x + 5, y - 10 * i
1031                 , 19, 30, 40
1032                 , 0.45, -0.9, trackFall or afSetSkip);
1033         end;
1034     v2:= v2 + RateShove(Me, x + 5, y - 90
1035             , 19, 30, 40
1036             , 0.45, -0.9, trackFall);
1037 
1038     if (v2 > v1)
1039         or {don't encourage turning for no gain}((v2 = v1) and (not Me^.dX.isNegative)) then
1040         begin
1041         ap.Angle:= 1;
1042         valueResult:= v2
1043         end
1044     else
1045         begin
1046         ap.Angle:= -1;
1047         valueResult:= v1
1048         end;
1049 
1050     if valueResult <= 0 then
1051         valueResult:= BadTurn;
1052 
1053     TestFirePunch:= valueResult;
1054 end;
1055 
1056 
TestWhipnull1057 function TestWhip(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1058 var valueResult, v1, v2: LongInt;
1059     x, y, trackFall: LongInt;
1060 begin
1061     Targ:= Targ; // avoid compiler hint
1062 
1063     if Level = 1 then trackFall:= afTrackFall
1064     else trackFall:= 0;
1065 
1066     ap.ExplR:= 0;
1067     ap.Time:= 0;
1068     ap.Power:= 1;
1069     x:= hwRound(Me^.X);
1070     y:= hwRound(Me^.Y);
1071 
1072     // check left direction
1073     {first RateShove checks farthermost of two whip's AmmoShove attacks
1074     to encourage distant attacks (damaged hog is excluded from view of second
1075     RateShove call)}
1076     v1:= RateShove(Me, x - 13, y
1077             , 30, 30, 25
1078             , -1, -0.8, trackFall or afSetSkip);
1079     v1:= v1 +
1080         RateShove(Me, x - 2, y
1081             , 30, 30, 25
1082             , -1, -0.8, trackFall);
1083     // now try opposite direction
1084     v2:= RateShove(Me, x + 13, y
1085             , 30, 30, 25
1086             , 1, -0.8, trackFall or afSetSkip);
1087     v2:= v2 +
1088         RateShove(Me, x + 2, y
1089             , 30, 30, 25
1090             , 1, -0.8, trackFall);
1091 
1092     if (v2 > v1)
1093         or {don't encourage turning for no gain}((v2 = v1) and (not Me^.dX.isNegative)) then
1094         begin
1095         ap.Angle:= 1;
1096         valueResult:= v2
1097         end
1098     else
1099         begin
1100         ap.Angle:= -1;
1101         valueResult:= v1
1102         end;
1103 
1104     if valueResult <= 0 then
1105         valueResult:= BadTurn
1106     else
1107         inc(valueResult);
1108 
1109     TestWhip:= valueResult;
1110 end;
1111 
TestKamikazenull1112 function TestKamikaze(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1113 const step = 8;
1114 var valueResult, i, v, tx: LongInt;
1115     trackFall: LongInt;
1116     t, d, x, y, dx, dy, cx: real;
1117 begin
1118     ap.ExplR:= 0;
1119     ap.Time:= 0;
1120     ap.Power:= 1;
1121 
1122     if Level = 1 then
1123         trackFall:= afTrackFall
1124     else if Level = 2 then
1125         trackFall:= 0
1126     else
1127         exit(BadTurn);
1128 
1129     valueResult:= 0;
1130     v:= 0;
1131 
1132     x:= hwFloat2Float(Me^.X);
1133     y:= hwFloat2Float(Me^.Y);
1134     d:= sqrt(sqr(Targ.Point.X - x) + sqr(Targ.Point.Y - y));
1135     if d < 10 then
1136         begin
1137         dx:= 0;
1138         dy:= step;
1139         ap.Angle:= 2048
1140         end
1141     else
1142         begin
1143         t:= step / d;
1144         dx:= (Targ.Point.X - x) * t;
1145         dy:= (Targ.Point.Y - y) * t;
1146 
1147         ap.Angle:= DxDy2AttackAnglef(dx, -dy)
1148         end;
1149 
1150     if dx >= 0 then cx:= 0.45 else cx:= -0.45;
1151 
1152     for i:= 1 to 512 div step - 2 do
1153         begin
1154         x:= x + dx;
1155         y:= y + dy;
1156 
1157         valueResult:= valueResult +
1158             RateShove(Me, trunc(x), trunc(y)
1159                 , 30, 30, 25
1160                 , cx, -0.9, trackFall or afSetSkip);
1161         end;
1162 
1163     if (d < 10) and (dx = 0) then
1164         begin
1165         x:= hwFloat2Float(Me^.X);
1166         y:= hwFloat2Float(Me^.Y);
1167         tx:= trunc(x);
1168         v:= RateShove(Me, tx, trunc(y)
1169                 , 30, 30, 25
1170                 , -cx, -0.9, trackFall);
1171         for i:= 1 to 512 div step - 2 do
1172             begin
1173             y:= y + dy;
1174             v:= v +
1175                 RateShove(Me, tx, trunc(y)
1176                     , 30, 30, 25
1177                     , -cx, -0.9, trackFall or afSetSkip);
1178             end
1179         end;
1180 
1181     if v > valueResult then
1182     begin
1183         cx:= -cx;
1184         ap.Angle:= -2048;
1185         valueResult:= v
1186         end;
1187 
1188     v:= RateShove(Me, trunc(x), trunc(y)
1189             , 30, 30, 25
1190             , cx, -0.9, trackFall);
1191     valueResult:= valueResult + v - KillScore * friendlyfactor div 100 * 1024;
1192 
1193     if v < 65536 then
1194         inc(valueResult, RateExplosion(Me, trunc(x), trunc(y), 30));
1195 
1196     TestKamikaze:= valueResult;
1197 end;
1198 
TestHammernull1199 function TestHammer(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1200 var rate: LongInt;
1201 begin
1202 Level:= Level; // avoid compiler hint
1203 Targ:= Targ;
1204 
1205 ap.ExplR:= 0;
1206 ap.Time:= 0;
1207 ap.Power:= 1;
1208 ap.Angle:= 0;
1209 
1210 rate:= RateHammer(Me);
1211 if rate = 0 then
1212     rate:= BadTurn;
1213 TestHammer:= rate;
1214 end;
1215 
TestAirAttacknull1216 function TestAirAttack(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1217 const cShift = 4;
1218 var bombsSpeed, X, Y, dY: real;
1219     b: array[0..9] of boolean;
1220     dmg: array[0..9] of LongInt;
1221     fexit: boolean;
1222     i, t, valueResult: LongInt;
1223 begin
1224 ap.ExplR:= 0;
1225 ap.Time:= 0;
1226 if (Level > 3) or (cGravityf = 0) then
1227     exit(BadTurn);
1228 
1229 ap.Angle:= 0;
1230 ap.AttackPutX:= Targ.Point.X;
1231 ap.AttackPutY:= Targ.Point.Y;
1232 
1233 bombsSpeed:= hwFloat2Float(cBombsSpeed);
1234 X:= Targ.Point.X - 135 - cShift; // hh center - cShift
1235 X:= X - bombsSpeed * sqrt(((Targ.Point.Y + 128) * 2) / cGravityf);
1236 Y:= -128;
1237 dY:= 0;
1238 
1239 for i:= 0 to 9 do
1240     begin
1241     b[i]:= true;
1242     dmg[i]:= 0
1243     end;
1244 valueResult:= 0;
1245 
1246 repeat
1247     X:= X + bombsSpeed;
1248     Y:= Y + dY;
1249     dY:= dY + cGravityf;
1250     fexit:= true;
1251 
1252     for i:= 0 to 9 do
1253         if b[i] then
1254             begin
1255             fexit:= false;
1256             if TestColl(trunc(X) + LongWord(i * 30), trunc(Y), 4) then
1257                 begin
1258                 b[i]:= false;
1259                 dmg[i]:= RateExplosion(Me, trunc(X) + LongWord(i * 30), trunc(Y), 58)
1260                 // 58 (instead of 60) for better prediction (hh moves after explosion of one of the rockets)
1261                 end
1262             end;
1263 until fexit or (Y > cWaterLine);
1264 
1265 for i:= 0 to 5 do
1266     if dmg[i] <> BadTurn then
1267         inc(valueResult, dmg[i]);
1268 t:= valueResult;
1269 ap.AttackPutX:= Targ.Point.X - 60;
1270 
1271 for i:= 0 to 3 do
1272     if dmg[i] <> BadTurn then
1273         begin
1274         dec(t, dmg[i]);
1275         inc(t, dmg[i + 6]);
1276         if t > valueResult then
1277             begin
1278             valueResult:= t;
1279             ap.AttackPutX:= Targ.Point.X - 30 - cShift + i * 30
1280             end
1281         end;
1282 
1283 if valueResult <= 0 then
1284     valueResult:= BadTurn;
1285 TestAirAttack:= valueResult;
1286 end;
1287 
1288 
TestTeleportnull1289 function TestTeleport(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1290 var
1291     i, failNum: longword;
1292     maxTop: longword;
1293 begin
1294     TestTeleport := BadTurn;
1295     exit(BadTurn);
1296     Level:= Level; // avoid compiler hint
1297     //FillBonuses(true, [gtCase]);
1298     if bonuses.Count = 0 then
1299         begin
1300         if Me^.Health <= 100  then
1301             begin
1302             maxTop := Targ.Point.Y - cHHRadius * 2;
1303 
1304             while not TestColl(Targ.Point.X, maxTop, cHHRadius) and (maxTop > topY + cHHRadius * 2 + 1) do
1305                 dec(maxTop, cHHRadius*2);
1306             if not TestColl(Targ.Point.X, maxTop + cHHRadius, cHHRadius) then
1307                 begin
1308                 ap.AttackPutX := Targ.Point.X;
1309                 ap.AttackPutY := maxTop + cHHRadius;
1310                 TestTeleport := Targ.Point.Y - maxTop;
1311                 end;
1312             end;
1313         end
1314     else
1315         begin
1316         failNum := 0;
1317         repeat
1318             i := random(bonuses.Count);
1319             inc(failNum);
1320         until not TestColl(bonuses.ar[i].X, bonuses.ar[i].Y - cHHRadius - bonuses.ar[i].Radius, cHHRadius)
1321         or (failNum = bonuses.Count*2);
1322 
1323         if failNum < bonuses.Count*2 then
1324             begin
1325             ap.AttackPutX := bonuses.ar[i].X;
1326             ap.AttackPutY := bonuses.ar[i].Y - cHHRadius - bonuses.ar[i].Radius;
1327             TestTeleport := 0;
1328             end;
1329         end;
1330 end;
1331 
1332 
1333 procedure checkCakeWalk(Me, Gear: PGear; var ap: TAttackParams);
1334 var i: Longword;
1335     v: LongInt;
1336 begin
1337 while (not TestColl(hwRound(Gear^.X), hwRound(Gear^.Y), 6)) and (Gear^.Y.Round < LongWord(LAND_HEIGHT)) do
1338     Gear^.Y:= Gear^.Y + _1;
1339 
1340 for i:= 0 to 2040 do
1341     begin
1342     cakeStep(Gear);
1343     v:= RateExplosion(Me, hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg * 2, afTrackFall);
1344     if v > ap.Power then
1345         begin
1346         ap.ExplX:= hwRound(Gear^.X);
1347         ap.ExplY:= hwRound(Gear^.Y);
1348         ap.Power:= v
1349         end
1350     end;
1351 end;
1352 
TestCakenull1353 function TestCake(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1354 var valueResult, v1, v2: LongInt;
1355     cake: TGear;
1356 begin
1357     Targ:= Targ; // avoid compiler hint
1358 
1359     if (Level > 2) then
1360         exit(BadTurn);
1361 
1362     ap.ExplR:= 0;
1363     ap.Time:= 0;
1364     ap.Power:= BadTurn; // use it as max score value in checkCakeWalk
1365 
1366     //FillChar(cake, sizeof(cake), 0);
1367     cake.Radius:= 7;
1368     cake.CollisionMask:= lfNotCurHogCrate;
1369     cake.Hedgehog:= Me^.Hedgehog;
1370     cake.Kind:= gtCake;
1371 
1372     // check left direction
1373     cake.Angle:= 3;
1374     cake.dX.isNegative:= true;
1375     cake.dX.QWordValue:= 0;
1376     cake.dY:= _0;
1377     cake.X:= Me^.X - _3;
1378     cake.Y:= Me^.Y;
1379     checkCakeWalk(Me, @cake, ap);
1380     v1:= ap.Power;
1381 
1382     // now try opposite direction
1383     cake.Angle:= 1;
1384     cake.dX.isNegative:= false;
1385     cake.X:= Me^.X + _3;
1386     cake.Y:= Me^.Y;
1387     checkCakeWalk(Me, @cake, ap);
1388     v2:= ap.Power;
1389 
1390     ap.Power:= 1;
1391 
1392     if (v2 > v1) then
1393         begin
1394         ap.Angle:= 1;
1395         valueResult:= v2
1396         end
1397     else
1398         begin
1399         ap.Angle:= -1;
1400         valueResult:= v1
1401         end;
1402 
1403     if valueResult <= 0 then
1404         valueResult:= BadTurn;
1405 
1406     TestCake:= valueResult;
1407 end;
1408 
TestDynamitenull1409 function TestDynamite(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
1410 var valueResult: LongInt;
1411     x, y, dx, dy: real;
1412     EX, EY, t: LongInt;
1413 begin
1414 Targ:= Targ; // avoid compiler hint
1415 
1416 x:= hwFloat2Float(Me^.X) + hwSign(Me^.dX) * 7;
1417 y:= hwFloat2Float(Me^.Y);
1418 dx:= hwSign(Me^.dX) * 0.03;
1419 dy:= 0;
1420 t:= 5000;
1421 repeat
1422     dec(t);
1423     x:= x + dx;
1424     dy:= dy + cGravityf;
1425     y:= y + dy;
1426 
1427     if TestColl(trunc(x), trunc(y), 3) then
1428         t:= 0;
1429 until t = 0;
1430 
1431 EX:= trunc(x);
1432 EY:= trunc(y);
1433 
1434 if Level = 1 then
1435     valueResult:= RateExplosion(Me, EX, EY, 76, afTrackFall or afErasesLand)
1436 else
1437     valueResult:= RateExplosion(Me, EX, EY, 76);
1438 
1439 if (valueResult > 0) then
1440     begin
1441     ap.Angle:= 0;
1442     ap.Power:= 1;
1443     ap.Time:= 0;
1444     ap.ExplR:= 150;
1445     ap.ExplX:= EX;
1446     ap.ExplY:= EY
1447     end else
1448     valueResult:= BadTurn;
1449 
1450 TestDynamite:= valueResult
1451 end;
1452 
1453 end.
1454