- Better randomness of placing hedgehogs on the land
authorunc0rr
Sun, 01 Jul 2007 11:02:47 +0000
changeset 543 465e2ec8f05f
parent 542 ec26095f1bed
child 544 9e068d2398ca
- Better randomness of placing hedgehogs on the land - Bots can use AirAttack - Fix division by "zero" - Small optimizations
QTfrontend/hedgewars.pro
hedgewars/CCHandlers.inc
hedgewars/GSHandlers.inc
hedgewars/HHHandlers.inc
hedgewars/uAI.pas
hedgewars/uAIActions.pas
hedgewars/uAIAmmoTests.pas
hedgewars/uConsole.pas
hedgewars/uFloat.pas
hedgewars/uGears.pas
hedgewars/uMisc.pas
--- a/QTfrontend/hedgewars.pro	Sun Jun 17 14:48:15 2007 +0000
+++ b/QTfrontend/hedgewars.pro	Sun Jul 01 11:02:47 2007 +0000
@@ -41,7 +41,7 @@
            chatwidget.h
            
            
-SOURCES += binds.cpp
+SOURCES += binds.cpp \
            game.cpp \
            main.cpp \
            hwform.cpp \
--- a/hedgewars/CCHandlers.inc	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/CCHandlers.inc	Sun Jul 01 11:02:47 2007 +0000
@@ -329,7 +329,7 @@
      end
 end;
 
-procedure chPut(var s: shortstring);
+procedure doPut(putX, putY: LongInt; fromAI: boolean);
 begin
 if CheckNoTeamOrHH then exit;
 if bShowAmmoMenu then
@@ -337,6 +337,7 @@
    bSelected:= true;
    exit
    end;
+
 with CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^,
      CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog] do
      if (State and gstHHChooseTarget) <> 0 then
@@ -344,15 +345,28 @@
         isCursorVisible:= false;
         if not CurrentTeam^.ExtDriven then
            begin
-           SDL_GetMouseState(@TargetPoint.X, @TargetPoint.Y);
-           dec(TargetPoint.X, WorldDx);
-           dec(TargetPoint.Y, WorldDy);
+           if fromAI then
+              begin
+              TargetPoint.X:= putX;
+              TargetPoint.Y:= putY
+              end else
+              begin
+              SDL_GetMouseState(@TargetPoint.X, @TargetPoint.Y);
+              dec(TargetPoint.X, WorldDx);
+              dec(TargetPoint.Y, WorldDy)
+              end;
            SendIPCXY('p', TargetPoint.X, TargetPoint.Y);
            end;
-        State:= State and not gstHHChooseTarget;
+        State:= State and not gstHHChooseTarget; 
         if (Ammo^[CurSlot, CurAmmo].Propz and ammoprop_AttackingPut) <> 0 then
            Message:= Message or gm_Attack;
         end else if CurrentTeam^.ExtDriven then OutError('got /put while not being in choose target mode', false)
+
+end;
+
+procedure chPut(var s: shortstring);
+begin
+doPut(0, 0, false)
 end;
 
 procedure chCapture(var s: shortstring);
--- a/hedgewars/GSHandlers.inc	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/GSHandlers.inc	Sun Jul 01 11:02:47 2007 +0000
@@ -1047,22 +1047,18 @@
 end;
 
 ////////////////////////////////////////////////////////////////////////////////
-const cAirPlaneSpeed: hwFloat = (isNegative: false; QWordValue: 6012954214); // 1.4
-      cBombsDistance: hwFloat = (isNegative: false; QWordValue: 128849018880); // 30
-      cBombsSpeed   : hwFloat = (isNegative: false; QWordValue:  429496729);
-
 procedure doStepAirAttackWork(Gear: PGear);
 begin
 AllInactive:= false;
 Gear^.X:= Gear^.X + cAirPlaneSpeed * Gear^.Tag;
-if (Gear^.Health > 0)and( not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then
+if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then
    begin
    dec(Gear^.Health);
    case Gear^.State of
         0: FollowGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0);
         1: FollowGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine,    0, cBombsSpeed * Gear^.Tag, _0, 0);
         end;
-   Gear^.dX:= Gear^.dX + cBombsDistance * Gear^.Tag
+   Gear^.dX:= Gear^.dX + int2hwFloat(30 * Gear^.Tag)
    end;
 if (hwRound(Gear^.X) > 3072) or (hwRound(Gear^.X) < -1024) then DeleteGear(Gear)
 end;
@@ -1070,15 +1066,17 @@
 procedure doStepAirAttack(Gear: PGear);
 begin
 AllInactive:= false;
+
 if Gear^.X.QWordValue = 0 then Gear^.Tag:=  1
                           else Gear^.Tag:= -1;
 Gear^.X:= _1024 - _2048 * Gear^.Tag;
 Gear^.Y:= -_128;
-Gear^.dX:= int2hwFloat(TargetPoint.X) -
-           cBombsDistance * _5 * Gear^.Tag / _2;
+Gear^.dX:= int2hwFloat(TargetPoint.X - 5 * Gear^.Tag * 15);
 
-if TargetPoint.Y - hwRound(Gear^.Y) > 0 then
+if int2hwFloat(TargetPoint.Y) - Gear^.Y > _0 then
    Gear^.dX:= Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(TargetPoint.Y) - Gear^.Y) * 2 / cGravity) * Gear^.Tag;
+addfilelog('attack: x = '+floattostr(gear^.dx));
+
 Gear^.Health:= 6;
 Gear^.doStep:= @doStepAirAttackWork
 end;
--- a/hedgewars/HHHandlers.inc	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/HHHandlers.inc	Sun Jul 01 11:02:47 2007 +0000
@@ -47,7 +47,7 @@
            PlaySound(sndThrowRelease, false);
            end;
         xx:= SignAs(AngleSin(Angle), dX);
-        yy:= -AngleCos(Angle);
+        yy:= -AngleCos(Angle); 
              case Ammo^[CurSlot, CurAmmo].AmmoType of
                       amGrenade: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtAmmo_Bomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
                   amClusterBomb: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtClusterBomb,  0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
--- a/hedgewars/uAI.pas	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/uAI.pas	Sun Jul 01 11:02:47 2007 +0000
@@ -57,10 +57,10 @@
 BestActions.Pos:= 0
 end;
 
-procedure TestAmmos(var Actions: TActions; Me: PGear);
+procedure TestAmmos(var Actions: TActions; Me: PGear; isMoved: boolean);
 var Time, BotLevel: Longword;
-    Angle, Power, Score, ExplX, ExplY, ExplR: LongInt;
-    i: LongInt;
+    ap: TAttackParams;
+    Score, i: LongInt;
     a, aa: TAmmoType;
 begin
 BotLevel:= PHedgehog(Me^.Hedgehog)^.BotLevel;
@@ -72,35 +72,44 @@
             a:= Ammo^[CurSlot, CurAmmo].AmmoType;
        aa:= a;
        repeat
-        if CanUseAmmo[a] then
+        if (CanUseAmmo[a]) and
+           ((not isMoved) or ((AmmoTests[a].flags and amtest_OnTurn) = 0)) then
            begin
-           Score:= AmmoTests[a](Me, Targets.ar[i].Point, BotLevel, Time, Angle, Power, ExplX, ExplY, ExplR);
+           Score:= AmmoTests[a].proc(Me, Targets.ar[i].Point, BotLevel, ap);
            if Actions.Score + Score > BestActions.Score then
+            if (BestActions.Score < 0) or (Actions.Score + Score > BestActions.Score + LongInt(BotLevel) * 2048) then
               begin
               BestActions:= Actions;
               inc(BestActions.Score, Score);
 
-              AddAction(BestActions, aia_Weapon, Longword(a), 500, 0, 0);
-              if Time <> 0 then AddAction(BestActions, aia_Timer, Time div 1000, 400, 0, 0);
-              if (Angle > 0) then AddAction(BestActions, aia_LookRight, 0, 200, 0, 0)
-              else if (Angle < 0) then AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0);
+              AddAction(BestActions, aia_Weapon, Longword(a), 300 + random(400), 0, 0);
+              if (ap.Time <> 0) then AddAction(BestActions, aia_Timer, ap.Time div 1000, 400, 0, 0);
+              if (ap.Angle > 0) then AddAction(BestActions, aia_LookRight, 0, 200, 0, 0)
+              else if (ap.Angle < 0) then AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0);
               if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then
                  begin
-                 Angle:= LongInt(Me^.Angle) - Abs(Angle);
-                 if Angle > 0 then
+                 ap.Angle:= LongInt(Me^.Angle) - Abs(ap.Angle);
+                 if ap.Angle > 0 then
                     begin
-                    AddAction(BestActions, aia_Up, aim_push, 500, 0, 0);
-                    AddAction(BestActions, aia_Up, aim_release, Angle, 0, 0)
-                    end else if Angle < 0 then
+                    AddAction(BestActions, aia_Up, aim_push, 300 + random(250), 0, 0);
+                    AddAction(BestActions, aia_Up, aim_release, ap.Angle, 0, 0)
+                    end else if ap.Angle < 0 then
                     begin
-                    AddAction(BestActions, aia_Down, aim_push, 500, 0, 0);
-                    AddAction(BestActions, aia_Down, aim_release, -Angle, 0, 0)
+                    AddAction(BestActions, aia_Down, aim_push, 300 + random(250), 0, 0);
+                    AddAction(BestActions, aia_Down, aim_release, -ap.Angle, 0, 0)
                     end
                  end;
-              AddAction(BestActions, aia_attack, aim_push, 800, 0, 0);
-              AddAction(BestActions, aia_attack, aim_release, Power, 0, 0);
-              if ExplR > 0 then
-                 AddAction(BestActions, aia_AwareExpl, ExplR, 10, ExplX, ExplY);
+              if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then
+                 begin
+                 AddAction(BestActions, aia_Put, 0, 1, ap.AttackPutX, ap.AttackPutY)
+                 end;
+              if (Ammoz[a].Ammo.Propz and ammoprop_AttackingPut) = 0 then
+                 begin
+                 AddAction(BestActions, aia_attack, aim_push, 650 + random(300), 0, 0);
+                 AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0);
+                 end;
+              if ap.ExplR > 0 then
+                 AddAction(BestActions, aia_AwareExpl, ap.ExplR, 10, ap.ExplX, ap.ExplY);
               end
            end;
         if a = High(TAmmoType) then a:= Low(TAmmoType)
@@ -188,7 +197,7 @@
 if (Me^.State and gstAttacked) = 0 then maxticks:= max(0, TurnTimeLeft - 5000 - 4000 * BotLevel)
                                    else maxticks:= TurnTimeLeft;
 
-if (Me^.State and gstAttacked) = 0 then TestAmmos(Actions, Me);
+if (Me^.State and gstAttacked) = 0 then TestAmmos(Actions, Me, false);
 BestRate:= RatePlace(Me);
 BaseRate:= max(BestRate, 0);
 
@@ -232,7 +241,7 @@
           end
        else if Rate < BestRate then break;
        if ((Me^.State and gstAttacked) = 0)
-           and ((steps mod 4) = 0) then TestAmmos(Actions, Me);
+           and ((steps mod 4) = 0) then TestAmmos(Actions, Me, true);
        if GoInfo.FallPix >= FallPixForBranching then
           Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right
        end;
@@ -300,7 +309,7 @@
    end;
 FillBonuses((Me^.State and gstAttacked) <> 0);
 for a:= Low(TAmmoType) to High(TAmmoType) do
-    CanUseAmmo[a]:= Assigned(AmmoTests[a]) and HHHasAmmo(PHedgehog(Me^.Hedgehog), a);
+    CanUseAmmo[a]:= Assigned(AmmoTests[a].proc) and HHHasAmmo(PHedgehog(Me^.Hedgehog), a);
 {$IFDEF DEBUGFILE}AddFileLog('Enter Think Thread');{$ENDIF}
 BeginThread(@Think, Me, ThinkThread)
 end;
--- a/hedgewars/uAIActions.pas	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/uAIActions.pas	Sun Jul 01 11:02:47 2007 +0000
@@ -39,6 +39,7 @@
       aia_LJump      = $8007;
       aia_Skip       = $8008;
       aia_Wait       = $8009;
+      aia_Put        = $800A;
 
       aim_push       = $8000;
       aim_release    = $8001;
@@ -176,6 +177,7 @@
             aia_HJump: ParseCommand('hjump', true);
             aia_LJump: ParseCommand('ljump', true);
              aia_Skip: ParseCommand('skip', true);
+              aia_Put: doPut(X, Y, true);
              end else
         begin
         s:= ActionIdToStr[Action];
--- a/hedgewars/uAIAmmoTests.pas	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/uAIAmmoTests.pas	Sun Jul 01 11:02:47 2007 +0000
@@ -19,42 +19,55 @@
 unit uAIAmmoTests;
 interface
 uses SDLh, uGears, uConsts, uFloat;
+const amtest_OnTurn = $00000001;
 
-function TestBazooka(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
-function TestGrenade(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
-function TestShotgun(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
-function TestDesertEagle(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
-function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
-function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
+type TAttackParams = record
+                     Time: Longword;
+                     Angle, Power: LongInt;
+                     ExplX, ExplY, ExplR: LongInt;
+                     AttackPutX, AttackPutY: LongInt;
+                     end;
 
-type TAmmoTestProc = function (Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
-const AmmoTests: array[TAmmoType] of TAmmoTestProc =
+function TestBazooka(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestGrenade(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestShotgun(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestDesertEagle(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestAirAttack(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+
+type TAmmoTestProc = function (Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+     TAmmoTest = record
+                 proc: TAmmoTestProc;
+                 flags: Longword;
+                 end;
+
+const AmmoTests: array[TAmmoType] of TAmmoTest =
                  (
-{amGrenade}       @TestGrenade,
-{amClusterBomb}   nil,
-{amBazooka}       @TestBazooka,
-{amUFO}           nil,
-{amShotgun}       @TestShotgun,
-{amPickHammer}    nil,
-{amSkip}          nil,
-{amRope}          nil,
-{amMine}          nil,
-{amDEagle}        @TestDesertEagle,
-{amDynamite}      nil,
-{amFirePunch}     @TestFirePunch,
-{amBaseballBat}   @TestBaseballBat,
-{amParachute}     nil,
-{amAirAttack}     nil,
-{amMineStrike}    nil,
-{amBlowTorch}     nil,
-{amGirder}        nil,
-{amTeleport}      nil,
-{amSwitch}        nil
+                  (proc: @TestGrenade;     flags: 0), // amGrenade
+                  (proc: nil;              flags: 0), // amClusterBomb
+                  (proc: @TestBazooka;     flags: 0), // amBazooka
+                  (proc: nil;              flags: 0), // amUFO
+                  (proc: @TestShotgun;     flags: 0), // amShotgun
+                  (proc: nil;              flags: 0), // amPickHammer
+                  (proc: nil;              flags: 0), // amSkip
+                  (proc: nil;              flags: 0), // amRope
+                  (proc: nil;              flags: 0), // amMine
+                  (proc: @TestDesertEagle; flags: 0), // amDEagle
+                  (proc: nil;              flags: 0), // amDynamite
+                  (proc: @TestFirePunch;   flags: 0), // amFirePunch
+                  (proc: @TestBaseballBat; flags: 0), // amBaseballBat
+                  (proc: nil;              flags: 0), // amParachute
+                  (proc: @TestAirAttack;   flags: amtest_OnTurn), // amAirAttack
+                  (proc: nil;              flags: 0), // amMineStrike
+                  (proc: nil;              flags: 0), // amBlowTorch
+                  (proc: nil;              flags: 0), // amGirder
+                  (proc: nil;              flags: amtest_OnTurn), // amTeleport
+                  (proc: nil;              flags: 0)  // amSwitch
                   );
 
 const BadTurn = Low(LongInt) div 4;
 
-
 implementation
 uses uMisc, uAIMisc, uLand;
 
@@ -63,7 +76,7 @@
 Metric:= abs(x1 - x2) + abs(y1 - y2)
 end;
 
-function TestBazooka(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
+function TestBazooka(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 var Vx, Vy, r: hwFloat;
     rTime: LongInt;
     Score, EX, EY: LongInt;
@@ -94,9 +107,9 @@
     end;
 
 begin
-Time:= 0;
+ap.Time:= 0;
 rTime:= 350;
-ExplR:= 0;
+ap.ExplR:= 0;
 Result:= BadTurn;
 repeat
   rTime:= rTime + 300 + Level * 50 + random(300);
@@ -108,11 +121,11 @@
      Score:= CheckTrace;
      if Result <= Score then
         begin
-        Angle:= DxDy2AttackAngle(Vx, Vy) + AIrndSign(random((Level - 1) * 9));
-        Power:= hwRound(r * cMaxPower) - random((Level - 1) * 17 + 1);
-        ExplR:= 100;
-        ExplX:= EX;
-        ExplY:= EY;
+        ap.Angle:= DxDy2AttackAngle(Vx, Vy) + AIrndSign(random((Level - 1) * 9));
+        ap.Power:= hwRound(r * cMaxPower) - random((Level - 1) * 17 + 1);
+        ap.ExplR:= 100;
+        ap.ExplX:= EX;
+        ap.ExplY:= EY;
         Result:= Score
         end;
      end
@@ -120,7 +133,7 @@
 TestBazooka:= Result
 end;
 
-function TestGrenade(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
+function TestGrenade(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 const tDelta = 24;
 var Vx, Vy, r: hwFloat;
     Score, EX, EY, Result: LongInt;
@@ -149,7 +162,7 @@
 begin
 Result:= BadTurn;
 TestTime:= 0;
-ExplR:= 0;
+ap.ExplR:= 0;
 repeat
   inc(TestTime, 1000);
   Vx:= (int2hwFloat(Targ.X) - Me^.X) / int2hwFloat(TestTime + tDelta);
@@ -160,12 +173,12 @@
      Score:= CheckTrace;
      if Result < Score then
         begin
-        Angle:= DxDy2AttackAngle(Vx, Vy) + AIrndSign(random(Level));
-        Power:= hwRound(r * cMaxPower) + AIrndSign(random(Level) * 15);
-        Time:= TestTime;
-        ExplR:= 100;
-        ExplX:= EX;
-        ExplY:= EY;
+        ap.Angle:= DxDy2AttackAngle(Vx, Vy) + AIrndSign(random(Level));
+        ap.Power:= hwRound(r * cMaxPower) + AIrndSign(random(Level) * 15);
+        ap.Time:= TestTime;
+        ap.ExplR:= 100;
+        ap.ExplX:= EX;
+        ap.ExplY:= EY;
         Result:= Score
         end;
      end
@@ -173,20 +186,20 @@
 TestGrenade:= Result
 end;
 
-function TestShotgun(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
+function TestShotgun(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 var Vx, Vy, x, y: hwFloat;
     rx, ry, Result: LongInt;
 begin
-ExplR:= 0;
-Time:= 0;
-Power:= 1;
+ap.ExplR:= 0;
+ap.Time:= 0;
+ap.Power:= 1;
 if Metric(hwRound(Me^.X), hwRound(Me^.Y), Targ.X, Targ.Y) < 80 then
    exit(BadTurn);
 Vx:= (int2hwFloat(Targ.X) - Me^.X) * _1div1024;
 Vy:= (int2hwFloat(Targ.Y) - Me^.Y) * _1div1024;
 x:= Me^.X;
 y:= Me^.Y;
-Angle:= DxDy2AttackAngle(Vx, -Vy);
+ap.Angle:= DxDy2AttackAngle(Vx, -Vy);
 repeat
   x:= x + vX;
   y:= y + vY;
@@ -205,14 +218,14 @@
 TestShotgun:= BadTurn
 end;
 
-function TestDesertEagle(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
+function TestDesertEagle(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 var Vx, Vy, x, y, t: hwFloat;
     d: Longword;
     Result: LongInt;
 begin
-ExplR:= 0;
-Time:= 0;
-Power:= 1;
+ap.ExplR:= 0;
+ap.Time:= 0;
+ap.Power:= 1;
 if Abs(hwRound(Me^.X) - Targ.X) + Abs(hwRound(Me^.Y) - Targ.Y) < 80 then
    exit(BadTurn);
 t:= _0_5 / Distance(int2hwFloat(Targ.X) - Me^.X, int2hwFloat(Targ.Y) - Me^.Y);
@@ -220,7 +233,7 @@
 Vy:= (int2hwFloat(Targ.Y) - Me^.Y) * t;
 x:= Me^.X;
 y:= Me^.Y;
-Angle:= DxDy2AttackAngle(Vx, -Vy);
+ap.Angle:= DxDy2AttackAngle(Vx, -Vy);
 d:= 0;
 repeat
   x:= x + vX;
@@ -229,33 +242,33 @@
      and (Land[hwRound(y), hwRound(x)] <> 0) then inc(d);
 until (Abs(Targ.X - hwRound(x)) + Abs(Targ.Y - hwRound(y)) < 4) or (x < _0) or (y < _0) or (x > _2048) or (y > _1024) or (d > 200);
 if Abs(Targ.X - hwRound(x)) + Abs(Targ.Y - hwRound(y)) < 3 then Result:= max(0, (4 - d div 50) * 7 * 1024)
-                                                           else Result:= Low(LongInt);
+                                                           else Result:= BadTurn;
 TestDesertEagle:= Result
 end;
 
-function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
+function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 var Result: LongInt;
 begin
-ExplR:= 0;
-if (Level > 2) and not (Abs(hwRound(Me^.X) - Targ.X) + Abs(hwRound(Me^.Y) - Targ.Y) < 25) then
+ap.ExplR:= 0;
+if (Level > 2) or (Abs(hwRound(Me^.X) - Targ.X) + Abs(hwRound(Me^.Y) - Targ.Y) > 25) then
    exit(BadTurn);
 
-Time:= 0;
-Power:= 1;
-if (Targ.X) - hwRound(Me^.X) >= 0 then Angle:=   cMaxAngle div 4
-                                  else Angle:= - cMaxAngle div 4;
+ap.Time:= 0;
+ap.Power:= 1;
+if (Targ.X) - hwRound(Me^.X) >= 0 then ap.Angle:=   cMaxAngle div 4
+                                  else ap.Angle:= - cMaxAngle div 4;
 Result:= RateShove(Me, hwRound(Me^.X) + 10 * hwSign(int2hwFloat(Targ.X) - Me^.X), hwRound(Me^.Y), 15, 30);
 if Result <= 0 then Result:= BadTurn else inc(Result);
 TestBaseballBat:= Result
 end;
 
-function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var Time: Longword; var Angle, Power: LongInt; var ExplX, ExplY, ExplR: LongInt): LongInt;
+function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 var i, Result: LongInt;
 begin
-ExplR:= 0;
-Time:= 0;
-Power:= 1;
-Angle:= 0;
+ap.ExplR:= 0;
+ap.Time:= 0;
+ap.Power:= 1;
+ap.Angle:= 0;
 if (Abs(hwRound(Me^.X) - Targ.X) > 25) or (Abs(hwRound(Me^.Y) - 50 - Targ.Y) > 50) then
    exit(BadTurn);
 
@@ -267,4 +280,66 @@
 TestFirePunch:= Result
 end;
 
+function TestAirAttack(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+var X, Y, dY: hwFloat;
+    b: array[0..9] of boolean;
+    dmg: array[0..9] of LongInt;
+    fexit: boolean;
+    i, t, Result: LongInt;
+begin
+ap.ExplR:= 0;
+ap.Time:= 0;
+ap.AttackPutX:= Targ.X;
+ap.AttackPutY:= Targ.Y;
+
+X:= int2hwFloat(Targ.X - 135);
+X:= X - cBombsSpeed * hwSqrt(int2hwFloat((Targ.Y + 128) * 2) / cGravity);
+Y:= -_128;
+dY:= _0;
+
+for i:= 0 to 9 do
+    begin
+    b[i]:= true;
+    dmg[i]:= 0
+    end;
+Result:= 0;
+
+repeat
+  X:= X + cBombsSpeed;
+  Y:= Y + dY;
+  dY:= dY + cGravity;
+  fexit:= true;
+
+  for i:= 0 to 9 do
+    if b[i] then
+       begin
+       fexit:= false;
+       if TestColl(hwRound(X) + i * 30, hwRound(Y), 4) then
+          begin
+          b[i]:= false;
+          dmg[i]:= RateExplosion(Me, hwRound(X) + i * 30, hwRound(Y), 58)
+          // 58 (instead of 60) for better prediction (hh moves after explosion of one of the rockets)
+          end
+       end;
+until fexit or (Y > _1024);
+
+for i:= 0 to 5 do inc(Result, dmg[i]);
+t:= Result;
+ap.AttackPutX:= Targ.X - 60;
+
+for i:= 0 to 3 do
+    begin
+    dec(t, dmg[i]);
+    inc(t, dmg[i + 6]);
+    if t > Result then
+       begin
+       Result:= t;
+       ap.AttackPutX:= Targ.X - 30 + i * 30
+       end
+    end;
+
+if Result <= 0 then Result:= BadTurn;
+TestAirAttack:= Result
+end;
+
 end.
--- a/hedgewars/uConsole.pas	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/uConsole.pas	Sun Jul 01 11:02:47 2007 +0000
@@ -31,6 +31,8 @@
 procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean);
 function  GetLastConsoleLine: shortstring;
 
+procedure doPut(putX, putY: LongInt; fromAI: boolean);
+
 implementation
 {$J+}
 uses uMisc, uStore, Types, uConsts, uGears, uTeams, uIO, uKeys, uWorld, uLand,
--- a/hedgewars/uFloat.pas	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/uFloat.pas	Sun Jul 01 11:02:47 2007 +0000
@@ -214,7 +214,8 @@
          t.QWordValue:= t.QWordValue shl 1;
          z2.QWordValue:= z2.QWordValue shl 1
          end;
-   z.Frac:= (t.QWordValue) div (z2.Round)
+   if z2.Round > 0 then z.Frac:= (t.QWordValue) div (z2.Round)
+                   else z.Frac:= 0
    end
 end;
 
--- a/hedgewars/uGears.pas	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/uGears.pas	Sun Jul 01 11:02:47 2007 +0000
@@ -278,7 +278,7 @@
                 Result^.Tag:= Y
                 end;
      gtAirBomb: begin
-                Result^.Radius:= 10;
+                Result^.Radius:= 5;
                 end;
    gtBlowTorch: begin
                 Result^.Radius:= cHHRadius + cBlowTorchC;
@@ -776,19 +776,45 @@
 procedure AssignHHCoords;
 var Team: PTeam;
     i, t: LongInt;
+    ar: array[0..Pred(cMaxHHs)] of PGear;
+    Count: Longword;
 begin
 Team:= TeamsList;
-t:= 0;
-while Team <> nil do
+
+if (GameFlags and gfForts) <> 0 then
+   begin
+   t:= 0;
+   while Team <> nil do
+      begin
+      for i:= 0 to cMaxHHIndex do
+          with Team^.Hedgehogs[i] do
+               if Gear <> nil then FindPlace(Gear, false, t, t + 1024);
+      inc(t, 1024);
+      Team:= Team^.Next
+      end
+   end else // mix hedgehogs
+   begin
+   Count:= 0;
+   while Team <> nil do
       begin
       for i:= 0 to cMaxHHIndex do
           with Team^.Hedgehogs[i] do
                if Gear <> nil then
-                  if (GameFlags and gfForts) = 0 then FindPlace(Gear, false, 0, 2048)
-                                                 else FindPlace(Gear, false, t, t + 1024);
-      inc(t, 1024);
+                  begin
+                  ar[Count]:= Gear;
+                  inc(Count)
+                  end;
       Team:= Team^.Next
+      end;
+
+   while (Count > 0) do
+      begin
+      i:= GetRandom(Count);
+      FindPlace(ar[i], false, 0, 2048);
+      ar[i]:= ar[Count - 1];
+      dec(Count)
       end
+   end
 end;
 
 function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
--- a/hedgewars/uMisc.pas	Sun Jun 17 14:48:15 2007 +0000
+++ b/hedgewars/uMisc.pas	Sun Jul 01 11:02:47 2007 +0000
@@ -70,6 +70,9 @@
     cTimerInterval   : Longword = 5;
     cHasFocus     : boolean = true;
 
+    cAirPlaneSpeed: hwFloat = (isNegative: false; QWordValue:   6012954214); // 1.4
+    cBombsSpeed   : hwFloat = (isNegative: false; QWordValue:    429496729);
+
 var
     cSendEmptyPacketTime : LongWord = 2000;
     cSendCursorPosTime   : LongWord = 50;