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