hedgewars/GSHandlers.inc
branchspacecampaign
changeset 9308 4bddcc1a1450
parent 9296 5cc010e79431
parent 9306 c9978ada9a3d
child 9310 1469147c110f
equal deleted inserted replaced
9296:5cc010e79431 9308:4bddcc1a1450
     1 (*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2004-2013 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
       
    17  *)
       
    18 
       
    19 (*
       
    20  * This file contains the step handlers for gears.
       
    21  *
       
    22  * Important: Since gears change the course of the game, calculations that
       
    23  *            lead to different results for different clients/players/machines
       
    24  *            should NOT occur!
       
    25  *            Use safe functions and data types! (e.g. GetRandom() and hwFloat)
       
    26  *)
       
    27 
       
    28 procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean);
       
    29 var
       
    30     dX, dY, sX, sY: hwFloat;
       
    31     i, steps: LongWord;
       
    32     caller: TGearStepProcedure;
       
    33 begin
       
    34     dX:= Gear^.dX;
       
    35     dY:= Gear^.dY;
       
    36     steps:= max(abs(hwRound(Gear^.X+dX)-hwRound(Gear^.X)), abs(hwRound(Gear^.Y+dY)-hwRound(Gear^.Y)));
       
    37 
       
    38     // Gear is still on the same Pixel it was before
       
    39     if steps < 1 then
       
    40         begin
       
    41         if onlyCheckIfChanged then
       
    42             begin
       
    43             Gear^.X := Gear^.X + dX;
       
    44             Gear^.Y := Gear^.Y + dY;
       
    45             EXIT;
       
    46             end
       
    47         else
       
    48             steps := 1;
       
    49         end;
       
    50 
       
    51     if steps > 1 then
       
    52         begin
       
    53         sX:= dX / steps;
       
    54         sY:= dY / steps;
       
    55         end
       
    56 
       
    57     else
       
    58         begin
       
    59         sX:= dX;
       
    60         sY:= dY;
       
    61         end;
       
    62 
       
    63     caller:= Gear^.doStep;
       
    64 
       
    65     for i:= 1 to steps do
       
    66         begin
       
    67         Gear^.X := Gear^.X + sX;
       
    68         Gear^.Y := Gear^.Y + sY;
       
    69         step(Gear);
       
    70         if (Gear^.doStep <> caller)
       
    71         or ((Gear^.State and gstCollision) <> 0)
       
    72         or ((Gear^.State and gstMoving) = 0) then
       
    73             break;
       
    74         end;
       
    75 end;
       
    76 
       
    77 procedure makeHogsWorry(x, y: hwFloat; r: LongInt);
       
    78 var
       
    79     gi: PGear;
       
    80     d: LongInt;
       
    81 begin
       
    82     gi := GearsList;
       
    83     while gi <> nil do
       
    84         begin
       
    85         if (gi^.Kind = gtHedgehog) then
       
    86             begin
       
    87             d := r - hwRound(Distance(gi^.X - x, gi^.Y - y));
       
    88             if (d > 1) and (not gi^.Invulnerable) and (GetRandom(2) = 0) then
       
    89                 begin
       
    90                 if (CurrentHedgehog^.Gear = gi) then
       
    91                     PlaySoundV(sndOops, gi^.Hedgehog^.Team^.voicepack)
       
    92 
       
    93                 else
       
    94                     begin
       
    95                     if ((gi^.State and gstMoving) = 0) and (gi^.Hedgehog^.Effects[heFrozen] = 0) then
       
    96                         begin
       
    97                         gi^.dX.isNegative:= X<gi^.X;
       
    98                         gi^.State := gi^.State or gstLoser;
       
    99                         end;
       
   100 
       
   101                     if d > r div 2 then
       
   102                         PlaySoundV(sndNooo, gi^.Hedgehog^.Team^.voicepack)
       
   103                     else
       
   104                         PlaySoundV(sndUhOh, gi^.Hedgehog^.Team^.voicepack);
       
   105                     end;
       
   106                 end;
       
   107             end;
       
   108 
       
   109         gi := gi^.NextGear
       
   110         end;
       
   111 end;
       
   112 
       
   113 procedure HideHog(HH: PHedgehog);
       
   114 begin
       
   115     ScriptCall('onHogHide', HH^.Gear^.Uid);
       
   116     DeleteCI(HH^.Gear);
       
   117     if FollowGear = HH^.Gear then
       
   118         FollowGear:= nil;
       
   119 
       
   120     if lastGearByUID = HH^.Gear then
       
   121         lastGearByUID := nil;
       
   122 
       
   123     HH^.Gear^.Message:= HH^.Gear^.Message or gmRemoveFromList;
       
   124     with HH^.Gear^ do
       
   125         begin
       
   126         Z := cHHZ;
       
   127         HH^.Gear^.Active:= false;
       
   128         State:= State and (not (gstHHDriven or gstAttacking or gstAttacked));
       
   129         Message := Message and (not gmAttack);
       
   130     end;
       
   131     HH^.GearHidden:= HH^.Gear;
       
   132     HH^.Gear:= nil
       
   133 end;
       
   134 
       
   135 
       
   136 ////////////////////////////////////////////////////////////////////////////////
       
   137 procedure doStepDrowningGear(Gear: PGear);
       
   138     begin
       
   139     AllInactive := false;
       
   140     Gear^.Y := Gear^.Y + cDrownSpeed;
       
   141     Gear^.X := Gear^.X + Gear^.dX * cDrownSpeed;
       
   142     // Create some bubbles (0.5% might be better but causes too few bubbles sometimes)
       
   143     if ((not SuddenDeathDmg and (WaterOpacity < $FF))
       
   144     or (SuddenDeathDmg and (SDWaterOpacity < $FF))) and ((GameTicks and $1F) = 0) then
       
   145         if (Gear^.Kind = gtHedgehog) and (Random(4) = 0) then
       
   146             AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble)
       
   147     else if Random(12) = 0 then
       
   148              AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble);
       
   149     if (not SuddenDeathDmg and (WaterOpacity > $FE))
       
   150     or (SuddenDeathDmg and (SDWaterOpacity > $FE))
       
   151     or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then
       
   152         DeleteGear(Gear);
       
   153     end;
       
   154 
       
   155 ////////////////////////////////////////////////////////////////////////////////
       
   156 procedure doStepFallingGear(Gear: PGear);
       
   157 var
       
   158     isFalling: boolean;
       
   159     //tmp: QWord;
       
   160     tdX, tdY: hwFloat;
       
   161     collV, collH: LongInt;
       
   162     land: word;
       
   163 begin
       
   164     // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems.
       
   165     if Gear^.dX.Round > 2 then
       
   166         Gear^.dX.QWordValue:= 8589934592;
       
   167     if Gear^.dY.Round > 2 then
       
   168         Gear^.dY.QWordValue:= 8589934592;
       
   169 
       
   170     if (Gear^.State and gstSubmersible <> 0) and (hwRound(Gear^.Y) > cWaterLine) then
       
   171         begin
       
   172         Gear^.dX:= Gear^.dX * _0_999;
       
   173         Gear^.dY:= Gear^.dY * _0_999
       
   174         end;
       
   175 
       
   176     Gear^.State := Gear^.State and (not gstCollision);
       
   177     collV := 0;
       
   178     collH := 0;
       
   179     tdX := Gear^.dX;
       
   180     tdY := Gear^.dY;
       
   181 
       
   182 
       
   183 
       
   184 // might need some testing/adjustments - just to avoid projectiles to fly forever (accelerated by wind/skips)
       
   185     if (hwRound(Gear^.X) < min(LAND_WIDTH div -2, -2048))
       
   186     or (hwRound(Gear^.X) > max(LAND_WIDTH * 3 div 2, 6144)) then
       
   187         Gear^.State := Gear^.State or gstCollision;
       
   188 
       
   189     if Gear^.dY.isNegative then
       
   190         begin
       
   191         isFalling := true;
       
   192         land:= TestCollisionYwithGear(Gear, -1);
       
   193         if land <> 0 then
       
   194             begin
       
   195             collV := -1;
       
   196             if land and lfIce <> 0 then
       
   197                 Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1)
       
   198             else
       
   199                 Gear^.dX := Gear^.dX * Gear^.Friction;
       
   200 
       
   201             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
   202             Gear^.State := Gear^.State or gstCollision
       
   203             end
       
   204         else if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, 1) <> 0) then
       
   205             collV := 1;
       
   206         end
       
   207     else
       
   208         begin // Gear^.dY.isNegative is false
       
   209         land:= TestCollisionYwithGear(Gear, 1);
       
   210         if land <> 0 then
       
   211             begin
       
   212             collV := 1;
       
   213             isFalling := false;
       
   214             if land and lfIce <> 0 then
       
   215                 Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1)
       
   216             else
       
   217                 Gear^.dX := Gear^.dX * Gear^.Friction;
       
   218 
       
   219             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
   220             Gear^.State := Gear^.State or gstCollision
       
   221             end
       
   222         else
       
   223             begin
       
   224             isFalling := true;
       
   225             if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, -1) <> 0) then
       
   226                 collV := -1
       
   227             end
       
   228         end;
       
   229 
       
   230 
       
   231     if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
       
   232         begin
       
   233         collH := hwSign(Gear^.dX);
       
   234         Gear^.dX := - Gear^.dX * Gear^.Elasticity;
       
   235         Gear^.dY :=   Gear^.dY * Gear^.Elasticity;
       
   236         Gear^.State := Gear^.State or gstCollision
       
   237         end
       
   238     else if (Gear^.AdvBounce=1) and TestCollisionXwithGear(Gear, -hwSign(Gear^.dX)) then
       
   239         collH := -hwSign(Gear^.dX);
       
   240     //if Gear^.AdvBounce and (collV <>0) and (collH <> 0) and (hwSqr(tdX) + hwSqr(tdY) > _0_08) then
       
   241     if (Gear^.AdvBounce=1) and (collV <>0) and (collH <> 0) and ((collV=-1)
       
   242     or ((tdX.QWordValue + tdY.QWordValue) > _0_2.QWordValue)) then
       
   243         begin
       
   244         Gear^.dX := tdY*Gear^.Elasticity*Gear^.Friction;
       
   245         Gear^.dY := tdX*Gear^.Elasticity;
       
   246         //*Gear^.Friction;
       
   247         Gear^.dY.isNegative := not tdY.isNegative;
       
   248         isFalling := false;
       
   249         Gear^.AdvBounce := 10;
       
   250         end;
       
   251 
       
   252     if Gear^.AdvBounce > 1 then
       
   253         dec(Gear^.AdvBounce);
       
   254 
       
   255     if isFalling then
       
   256         begin
       
   257         Gear^.dY := Gear^.dY + cGravity;
       
   258         if (GameFlags and gfMoreWind) <> 0 then
       
   259             Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density
       
   260             end;
       
   261 
       
   262     Gear^.X := Gear^.X + Gear^.dX;
       
   263     Gear^.Y := Gear^.Y + Gear^.dY;
       
   264     if Gear^.Kind <> gtBee then
       
   265         CheckGearDrowning(Gear);
       
   266     //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and
       
   267     if (not isFalling) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) then
       
   268         Gear^.State := Gear^.State and (not gstMoving)
       
   269     else
       
   270         Gear^.State := Gear^.State or gstMoving;
       
   271 
       
   272     if (Gear^.nImpactSounds > 0) and
       
   273         (Gear^.State and gstCollision <> 0) and
       
   274         (((Gear^.Kind <> gtMine) and (Gear^.Damage <> 0)) or (Gear^.State and gstMoving <> 0)) and
       
   275         (((Gear^.Radius < 3) and (Gear^.dY < -_0_1)) or
       
   276             ((Gear^.Radius >= 3) and
       
   277                 ((Gear^.dX.QWordValue > _0_1.QWordValue) or (Gear^.dY.QWordValue > _0_1.QWordValue)))) then
       
   278         PlaySound(TSound(ord(Gear^.ImpactSound) + LongInt(GetRandom(Gear^.nImpactSounds))), true);
       
   279 end;
       
   280 
       
   281 ////////////////////////////////////////////////////////////////////////////////
       
   282 procedure doStepBomb(Gear: PGear);
       
   283 var
       
   284     i, x, y: LongInt;
       
   285     dX, dY, gdX: hwFloat;
       
   286     vg: PVisualGear;
       
   287 begin
       
   288     AllInactive := false;
       
   289 
       
   290     doStepFallingGear(Gear);
       
   291 
       
   292     dec(Gear^.Timer);
       
   293     if Gear^.Timer = 1000 then // might need adjustments
       
   294         case Gear^.Kind of
       
   295             gtGrenade: makeHogsWorry(Gear^.X, Gear^.Y, 50);
       
   296             gtClusterBomb: makeHogsWorry(Gear^.X, Gear^.Y, 20);
       
   297             gtWatermelon: makeHogsWorry(Gear^.X, Gear^.Y, 75);
       
   298             gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, 90);
       
   299             gtGasBomb: makeHogsWorry(Gear^.X, Gear^.Y, 50);
       
   300         end;
       
   301 
       
   302     if (Gear^.Kind = gtBall) and ((Gear^.State and gstTmpFlag) <> 0) then
       
   303         begin
       
   304         CheckCollision(Gear);
       
   305         if (Gear^.State and gstCollision) <> 0 then
       
   306             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx);
       
   307         end;
       
   308 
       
   309     if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then
       
   310         begin
       
   311         vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite);
       
   312         if vg <> nil then
       
   313             vg^.Tint:= $FFC0C000;
       
   314         end;
       
   315 
       
   316     if Gear^.Timer = 0 then
       
   317         begin
       
   318         case Gear^.Kind of
       
   319             gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   320             gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound);
       
   321             gtClusterBomb:
       
   322                 begin
       
   323                 x := hwRound(Gear^.X);
       
   324                 y := hwRound(Gear^.Y);
       
   325                 gdX:= Gear^.dX;
       
   326                 doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound);
       
   327                 for i:= 0 to 4 do
       
   328                     begin
       
   329                     dX := rndSign(GetRandomf * _0_1) + gdX / 5;
       
   330                     dY := (GetRandomf - _3) * _0_08;
       
   331                     FollowGear := AddGear(x, y, gtCluster, 0, dX, dY, 25)
       
   332                     end
       
   333                 end;
       
   334             gtWatermelon:
       
   335                 begin
       
   336                 x := hwRound(Gear^.X);
       
   337                 y := hwRound(Gear^.Y);
       
   338                 gdX:= Gear^.dX;
       
   339                 doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound);
       
   340                 for i:= 0 to 5 do
       
   341                     begin
       
   342                     dX := rndSign(GetRandomf * _0_1) + gdX / 5;
       
   343                     dY := (GetRandomf - _1_5) * _0_3;
       
   344                     FollowGear:= AddGear(x, y, gtMelonPiece, 0, dX, dY, 75);
       
   345                     FollowGear^.DirAngle := i * 60
       
   346                     end
       
   347                 end;
       
   348             gtHellishBomb:
       
   349                 begin
       
   350                 x := hwRound(Gear^.X);
       
   351                 y := hwRound(Gear^.Y);
       
   352                 doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound);
       
   353 
       
   354                 for i:= 0 to 127 do
       
   355                     begin
       
   356                     dX := AngleCos(i * 16) * _0_5 * (GetRandomf + _1);
       
   357                     dY := AngleSin(i * 16) * _0_5 * (GetRandomf + _1);
       
   358                     if i mod 2 = 0 then
       
   359                         begin
       
   360                         AddGear(x, y, gtFlame, gstTmpFlag, dX, dY, 0);
       
   361                         AddGear(x, y, gtFlame, 0, dX, -dY, 0)
       
   362                         end
       
   363                     else
       
   364                         begin
       
   365                         AddGear(x, y, gtFlame, 0, dX, dY, 0);
       
   366                         AddGear(x, y, gtFlame, gstTmpFlag, dX, -dY, 0)
       
   367                         end;
       
   368                     end
       
   369                 end;
       
   370             gtGasBomb:
       
   371                 begin
       
   372                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);
       
   373                 for i:= 0 to 2 do
       
   374                     begin
       
   375                     x:= GetRandom(60);
       
   376                     y:= GetRandom(40);
       
   377                     FollowGear:= AddGear(hwRound(Gear^.X) - 30 + x, hwRound(Gear^.Y) - 20 + y, gtPoisonCloud, 0, _0, _0, 0);
       
   378                     end
       
   379                 end;
       
   380             end;
       
   381         DeleteGear(Gear);
       
   382         exit
       
   383         end;
       
   384 
       
   385     CalcRotationDirAngle(Gear);
       
   386 
       
   387     if Gear^.Kind = gtHellishBomb then
       
   388         begin
       
   389 
       
   390         if Gear^.Timer = 3000 then
       
   391             begin
       
   392             Gear^.nImpactSounds := 0;
       
   393             PlaySound(sndHellish);
       
   394             end;
       
   395 
       
   396         if (GameTicks and $3F) = 0 then
       
   397             if (Gear^.State and gstCollision) = 0 then
       
   398                 AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace);
       
   399         end;
       
   400 end;
       
   401 
       
   402 ////////////////////////////////////////////////////////////////////////////////
       
   403 procedure doStepMolotov(Gear: PGear);
       
   404 var
       
   405     s: Longword;
       
   406     i, gX, gY: LongInt;
       
   407     dX, dY: hwFloat;
       
   408     smoke, glass: PVisualGear;
       
   409 begin
       
   410     AllInactive := false;
       
   411 
       
   412     doStepFallingGear(Gear);
       
   413     CalcRotationDirAngle(Gear);
       
   414 
       
   415     // let's add some smoke depending on speed
       
   416     s:= max(32,152 - round((abs(hwFloat2FLoat(Gear^.dX))+abs(hwFloat2Float(Gear^.dY)))*120))+random(10);
       
   417     if (GameTicks mod s) = 0 then
       
   418         begin
       
   419         // adjust angle to match the texture
       
   420         if Gear^.dX.isNegative then
       
   421              i:= 130
       
   422         else i:= 50;
       
   423 
       
   424         smoke:= AddVisualGear(hwRound(Gear^.X)-round(cos((Gear^.DirAngle+i) * pi / 180)*20), hwRound(Gear^.Y)-round(sin((Gear^.DirAngle+i) * pi / 180)*20), vgtSmoke);
       
   425         if smoke <> nil then
       
   426             smoke^.Scale:= 0.75;
       
   427         end;
       
   428 
       
   429     if (Gear^.State and gstCollision) <> 0 then
       
   430         begin
       
   431         PlaySound(sndMolotov);
       
   432         gX := hwRound(Gear^.X);
       
   433         gY := hwRound(Gear^.Y);
       
   434         for i:= 0 to 4 do
       
   435             begin
       
   436             (*glass:= AddVisualGear(gx+random(7)-3, gy+random(5)-2, vgtEgg);
       
   437             if glass <> nil then
       
   438                 begin
       
   439                 glass^.Frame:= 2;
       
   440                 glass^.Tint:= $41B83ED0 - i * $10081000;
       
   441                 glass^.dX:= 1/(10*(random(11)-5));
       
   442                 glass^.dY:= -1/(random(4)+5);
       
   443                 end;*)
       
   444             glass:= AddVisualGear(gx+random(7)-3, gy+random(7)-3, vgtStraightShot);
       
   445             if glass <> nil then
       
   446                 with glass^ do
       
   447                     begin
       
   448                     Frame:= 2;
       
   449                     Tint:= $41B83ED0 - i * $10081000;
       
   450                     Angle:= random(360);
       
   451                     dx:= 0.0000001;
       
   452                     dy:= 0;
       
   453                     if random(2) = 0 then
       
   454                         dx := -dx;
       
   455                     FrameTicks:= 750;
       
   456                     State:= ord(sprEgg)
       
   457                     end;
       
   458             end;
       
   459         for i:= 0 to 24 do
       
   460             begin
       
   461             dX := AngleCos(i * 2) * ((_0_15*(i div 5))) * (GetRandomf + _1);
       
   462             dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1);
       
   463             AddGear(gX, gY, gtFlame, gstTmpFlag, dX, dY, 0);
       
   464             AddGear(gX, gY, gtFlame, gstTmpFlag, dX,-dY, 0);
       
   465             AddGear(gX, gY, gtFlame, gstTmpFlag,-dX, dY, 0);
       
   466             AddGear(gX, gY, gtFlame, gstTmpFlag,-dX,-dY, 0);
       
   467             end;
       
   468         DeleteGear(Gear);
       
   469         exit
       
   470         end;
       
   471 end;
       
   472 
       
   473 ////////////////////////////////////////////////////////////////////////////////
       
   474 
       
   475 procedure doStepCluster(Gear: PGear);
       
   476 begin
       
   477     AllInactive := false;
       
   478     doStepFallingGear(Gear);
       
   479     if (Gear^.State and gstCollision) <> 0 then
       
   480         begin
       
   481         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound);
       
   482         DeleteGear(Gear);
       
   483         exit
       
   484     end;
       
   485 
       
   486     if (Gear^.Kind = gtMelonPiece)
       
   487     or (Gear^.Kind = gtBall) then
       
   488         CalcRotationDirAngle(Gear)
       
   489     else if (GameTicks and $1F) = 0 then
       
   490         begin
       
   491         if hwRound(Gear^.Y) > cWaterLine then
       
   492              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
   493         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
   494         end
       
   495 end;
       
   496 
       
   497 ////////////////////////////////////////////////////////////////////////////////
       
   498 procedure doStepShell(Gear: PGear);
       
   499 begin
       
   500     AllInactive := false;
       
   501     if (GameFlags and gfMoreWind) = 0 then
       
   502         Gear^.dX := Gear^.dX + cWindSpeed;
       
   503     doStepFallingGear(Gear);
       
   504     if (Gear^.State and gstCollision) <> 0 then
       
   505         begin
       
   506         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   507         DeleteGear(Gear);
       
   508         exit
       
   509         end;
       
   510     if (GameTicks and $3F) = 0 then
       
   511         begin
       
   512         if hwRound(Gear^.Y) > cWaterLine then
       
   513              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
   514         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
   515         end
       
   516 end;
       
   517 
       
   518 ////////////////////////////////////////////////////////////////////////////////
       
   519 procedure doStepSnowball(Gear: PGear);
       
   520 var kick, i: LongInt;
       
   521     particle: PVisualGear;
       
   522     gdX, gdY: hwFloat;
       
   523 begin
       
   524     AllInactive := false;
       
   525     if (GameFlags and gfMoreWind) = 0 then
       
   526         Gear^.dX := Gear^.dX + cWindSpeed;
       
   527     gdX := Gear^.dX;
       
   528     gdY := Gear^.dY;
       
   529     doStepFallingGear(Gear);
       
   530     CalcRotationDirAngle(Gear);
       
   531     if (Gear^.State and gstCollision) <> 0 then
       
   532         begin
       
   533         kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * _20);
       
   534         Gear^.dX:= gdX;
       
   535         Gear^.dY:= gdY;
       
   536         AmmoShove(Gear, 0, kick);
       
   537         for i:= 15 + kick div 10 downto 0 do
       
   538             begin
       
   539             particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust);
       
   540             if particle <> nil then
       
   541                 particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
       
   542             end;
       
   543         DeleteGear(Gear);
       
   544         exit
       
   545         end;
       
   546     if ((GameTicks and $1F) = 0) and (Random(3) = 0) then
       
   547         begin
       
   548         particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust);
       
   549         if particle <> nil then
       
   550             particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
       
   551         end
       
   552 end;
       
   553 
       
   554 ////////////////////////////////////////////////////////////////////////////////
       
   555 procedure doStepSnowflake(Gear: PGear);
       
   556 var xx, yy, px, py, rx, ry, lx, ly: LongInt;
       
   557     move, draw, allpx, gun: Boolean;
       
   558     s: PSDL_Surface;
       
   559     p: PLongwordArray;
       
   560     lf: LongWord;
       
   561 begin
       
   562 inc(Gear^.Pos);
       
   563 gun:= (Gear^.State and gstTmpFlag) <> 0;
       
   564 move:= false;
       
   565 draw:= false;
       
   566 if gun then
       
   567     begin
       
   568     Gear^.State:= Gear^.State and (not gstInvisible);
       
   569     doStepFallingGear(Gear);
       
   570     CheckCollision(Gear);
       
   571     if ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then
       
   572         draw:= true;
       
   573     xx:= hwRound(Gear^.X);
       
   574     yy:= hwRound(Gear^.Y);
       
   575     end
       
   576 else if GameTicks and $7 = 0 then
       
   577     begin
       
   578     with Gear^ do
       
   579         begin
       
   580         State:= State and (not gstInvisible);
       
   581         X:= X + cWindSpeed * 3200 + dX;
       
   582         Y:= Y + dY + cGravity * vobFallSpeed * 8;  // using same value as flakes to try and get similar results
       
   583         xx:= hwRound(X);
       
   584         yy:= hwRound(Y);
       
   585         if vobVelocity <> 0 then
       
   586             begin
       
   587             DirAngle := DirAngle + (Damage / 1000);
       
   588             if DirAngle < 0 then
       
   589                 DirAngle := DirAngle + 360
       
   590             else if 360 < DirAngle then
       
   591                 DirAngle := DirAngle - 360;
       
   592             end;
       
   593 (*
       
   594 We aren't using frametick right now, so just a waste of cycles.
       
   595         inc(Health, 8);
       
   596         if longword(Health) > vobFrameTicks then
       
   597             begin
       
   598             dec(Health, vobFrameTicks);
       
   599             inc(Timer);
       
   600             if Timer = vobFramesCount then
       
   601                 Timer:= 0
       
   602             end;
       
   603 *)
       
   604     // move back to cloud layer
       
   605         if yy > cWaterLine then
       
   606             move:= true
       
   607         else if (xx > snowRight) or (xx < snowLeft) then
       
   608             move:=true
       
   609         // Solid pixel encountered
       
   610         else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
       
   611             begin
       
   612             lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible);
       
   613             if lf = 0 then lf:= lfObject;
       
   614             // If there's room below keep falling
       
   615             if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then
       
   616                 begin
       
   617                 X:= X - cWindSpeed * 1600 - dX;
       
   618                 end
       
   619             // If there's room below, on the sides, fill the gaps
       
   620             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(1*hwSign(cWindSpeed)))] = 0) then
       
   621                 begin
       
   622                 X:= X - _0_8 * hwSign(cWindSpeed);
       
   623                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   624                 end
       
   625             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(2*hwSign(cWindSpeed)))] = 0) then
       
   626                 begin
       
   627                 X:= X - _0_8 * 2 * hwSign(cWindSpeed);
       
   628                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   629                 end
       
   630             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(1*hwSign(cWindSpeed)))] = 0) then
       
   631                 begin
       
   632                 X:= X + _0_8 * hwSign(cWindSpeed);
       
   633                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   634                 end
       
   635             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(2*hwSign(cWindSpeed)))] = 0) then
       
   636                 begin
       
   637                 X:= X + _0_8 * 2 * hwSign(cWindSpeed);
       
   638                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   639                 end
       
   640             // if there's an hog/object below do nothing
       
   641             else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0))
       
   642                 then move:=true
       
   643             else draw:= true
       
   644             end
       
   645         end
       
   646     end;
       
   647 if draw then
       
   648     with Gear^ do
       
   649         begin
       
   650         // we've collided with land. draw some stuff and get back into the clouds
       
   651         move:= true;
       
   652         if (Pos > 20) and ((CurAmmoGear = nil)
       
   653         or (CurAmmoGear^.Kind <> gtRope)) then
       
   654             begin
       
   655 ////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS ////////////////////////////////////
       
   656             if not gun then
       
   657                 begin
       
   658                 dec(yy,3);
       
   659                 dec(xx,1)
       
   660                 end;
       
   661             s:= SpritesData[sprSnow].Surface;
       
   662             p:= s^.pixels;
       
   663             allpx:= true;
       
   664             for py:= 0 to Pred(s^.h) do
       
   665                 begin
       
   666                 for px:= 0 to Pred(s^.w) do
       
   667                     begin
       
   668                     lx:=xx + px; ly:=yy + py;
       
   669                     if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then
       
   670                         begin
       
   671                         rx:= lx;
       
   672                         ry:= ly;
       
   673                         if cReducedQuality and rqBlurryLand <> 0 then
       
   674                             begin
       
   675                             rx:= rx div 2;ry:= ry div 2;
       
   676                             end;
       
   677                         if Land[yy + py, xx + px] <= lfAllObjMask then
       
   678                             if gun then
       
   679                                 begin
       
   680                                 LandDirty[yy div 32, xx div 32]:= 1;
       
   681                                 if LandPixels[ry, rx] = 0 then
       
   682                                     Land[ly, lx]:=  lfDamaged or lfObject
       
   683                                 else Land[ly, lx]:=  lfDamaged or lfBasic
       
   684                                 end
       
   685                             else Land[ly, lx]:= lf;
       
   686                         if gun then
       
   687                             LandPixels[ry, rx]:= (ExplosionBorderColor and (not AMask)) or (p^[px] and AMask)
       
   688                         else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]);
       
   689                         end
       
   690                     else allpx:= false
       
   691                     end;
       
   692                 p:= @(p^[s^.pitch shr 2])
       
   693                 end;
       
   694 
       
   695             // Why is this here.  For one thing, there's no test on +1 being safe.
       
   696             //Land[py, px+1]:= lfBasic;
       
   697 
       
   698             if allpx then
       
   699                 UpdateLandTexture(xx, Pred(s^.h), yy, Pred(s^.w), true)
       
   700             else
       
   701                 begin
       
   702                 UpdateLandTexture(
       
   703                     max(0, min(LAND_WIDTH, xx)),
       
   704                     min(LAND_WIDTH - xx, Pred(s^.w)),
       
   705                     max(0, min(LAND_WIDTH, yy)),
       
   706                     min(LAND_HEIGHT - yy, Pred(s^.h)), false // could this be true without unnecessarily creating blanks?
       
   707                 );
       
   708                 end;
       
   709 ////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS ////////////////////////////////////
       
   710             end
       
   711         end;
       
   712 
       
   713 if move then
       
   714     begin
       
   715     if gun then
       
   716         begin
       
   717         DeleteGear(Gear);
       
   718         exit
       
   719         end;
       
   720     Gear^.Pos:= 0;
       
   721     Gear^.X:= int2hwFloat(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft);
       
   722     Gear^.Y:= int2hwFloat(LAND_HEIGHT + LongInt(GetRandom(50)) - 1325);
       
   723     Gear^.State:= Gear^.State or gstInvisible;
       
   724     end
       
   725 end;
       
   726 
       
   727 ////////////////////////////////////////////////////////////////////////////////
       
   728 procedure doStepGrave(Gear: PGear);
       
   729 begin
       
   730     if (Gear^.Message and gmDestroy) <> 0 then
       
   731         begin
       
   732         DeleteGear(Gear);
       
   733         exit
       
   734         end;
       
   735 
       
   736     AllInactive := false;
       
   737 
       
   738     if Gear^.dY.isNegative then
       
   739         if TestCollisionY(Gear, -1) then
       
   740             Gear^.dY := _0;
       
   741 
       
   742     if not Gear^.dY.isNegative then
       
   743         if TestCollisionY(Gear, 1) then
       
   744         begin
       
   745             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
   746             if Gear^.dY > - _1div1024 then
       
   747             begin
       
   748                 Gear^.Active := false;
       
   749                 exit
       
   750             end
       
   751             else if Gear^.dY < - _0_03 then
       
   752                 PlaySound(Gear^.ImpactSound)
       
   753         end;
       
   754 
       
   755     Gear^.Y := Gear^.Y + Gear^.dY;
       
   756     CheckGearDrowning(Gear);
       
   757     Gear^.dY := Gear^.dY + cGravity
       
   758 end;
       
   759 
       
   760 ////////////////////////////////////////////////////////////////////////////////
       
   761 procedure doStepBeeWork(Gear: PGear);
       
   762 var
       
   763     t: hwFloat;
       
   764     gX,gY,i: LongInt;
       
   765     uw, nuw: boolean;
       
   766     flower: PVisualGear;
       
   767 
       
   768 begin
       
   769     AllInactive := false;
       
   770     gX := hwRound(Gear^.X);
       
   771     gY := hwRound(Gear^.Y);
       
   772     uw := (Gear^.Tag <> 0); // was bee underwater last tick?
       
   773     nuw := (cWaterLine < gy + Gear^.Radius); // is bee underwater now?
       
   774 
       
   775     // if water entered or left
       
   776     if nuw <> uw then
       
   777         begin
       
   778         AddVisualGear(gX, cWaterLine, vgtSplash);
       
   779         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   780         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   781         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   782         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   783         StopSoundChan(Gear^.SoundChannel);
       
   784         if nuw then
       
   785             begin
       
   786             Gear^.SoundChannel := LoopSound(sndBeeWater);
       
   787             Gear^.Tag := 1;
       
   788         end
       
   789         else
       
   790             begin
       
   791             Gear^.SoundChannel := LoopSound(sndBee);
       
   792             Gear^.Tag := 0;
       
   793             end;
       
   794         end;
       
   795 
       
   796 
       
   797     if Gear^.Timer = 0 then
       
   798         Gear^.RenderTimer:= false
       
   799     else
       
   800         begin
       
   801         if (GameTicks and $F) = 0 then
       
   802             begin
       
   803             if (GameTicks and $30) = 0 then
       
   804                 AddVisualGear(gX, gY, vgtBeeTrace);
       
   805             Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX));
       
   806             Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY));
       
   807             // make sure new speed isn't higher than original one (which we stored in Friction variable)
       
   808             t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY);
       
   809             Gear^.dX := Gear^.dX * t;
       
   810             Gear^.dY := Gear^.dY * t;
       
   811             end;
       
   812 
       
   813         Gear^.X := Gear^.X + Gear^.dX;
       
   814         Gear^.Y := Gear^.Y + Gear^.dY;
       
   815 
       
   816         end;
       
   817 
       
   818 
       
   819     CheckCollision(Gear);
       
   820     if ((Gear^.State and gstCollision) <> 0) then
       
   821         begin
       
   822         StopSoundChan(Gear^.SoundChannel);
       
   823         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   824         for i:= 0 to 31 do
       
   825             begin
       
   826             flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
       
   827             if flower <> nil then
       
   828                 with flower^ do
       
   829                     begin
       
   830                     Scale:= 0.75;
       
   831                     dx:= 0.001 * (random(200));
       
   832                     dy:= 0.001 * (random(200));
       
   833                     if random(2) = 0 then
       
   834                         dx := -dx;
       
   835                     if random(2) = 0 then
       
   836                         dy := -dy;
       
   837                     FrameTicks:= random(250) + 250;
       
   838                     State:= ord(sprTargetBee);
       
   839                     end;
       
   840             end;
       
   841         DeleteGear(Gear);
       
   842     end;
       
   843 
       
   844     if (Gear^.Timer > 0) then
       
   845         dec(Gear^.Timer)
       
   846     else
       
   847         begin
       
   848         if nuw then
       
   849            begin
       
   850             StopSoundChan(Gear^.SoundChannel);
       
   851             CheckGearDrowning(Gear);
       
   852             end
       
   853         else
       
   854             doStepFallingGear(Gear);
       
   855         end;
       
   856 end;
       
   857 
       
   858 procedure doStepBee(Gear: PGear);
       
   859 begin
       
   860     AllInactive := false;
       
   861     Gear^.X := Gear^.X + Gear^.dX;
       
   862     Gear^.Y := Gear^.Y + Gear^.dY;
       
   863     Gear^.dY := Gear^.dY + cGravity;
       
   864     CheckCollision(Gear);
       
   865     if (Gear^.State and gstCollision) <> 0 then
       
   866         begin
       
   867         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   868         DeleteGear(Gear);
       
   869         exit
       
   870     end;
       
   871     dec(Gear^.Timer);
       
   872     if Gear^.Timer = 0 then
       
   873         begin
       
   874         Gear^.Hedgehog^.Gear^.Message:= Gear^.Hedgehog^.Gear^.Message and (not gmAttack);
       
   875         Gear^.Hedgehog^.Gear^.State:= Gear^.Hedgehog^.Gear^.State and (not gstAttacking);
       
   876         AttackBar:= 0;
       
   877 
       
   878         Gear^.SoundChannel := LoopSound(sndBee);
       
   879         Gear^.Timer := 5000;
       
   880         // save initial speed in otherwise unused Friction variable
       
   881         Gear^.Friction := Distance(Gear^.dX, Gear^.dY);
       
   882         Gear^.doStep := @doStepBeeWork
       
   883         end;
       
   884 end;
       
   885 
       
   886 ////////////////////////////////////////////////////////////////////////////////
       
   887 procedure doStepShotIdle(Gear: PGear);
       
   888 begin
       
   889     AllInactive := false;
       
   890     inc(Gear^.Timer);
       
   891     if Gear^.Timer > 75 then
       
   892         begin
       
   893         DeleteGear(Gear);
       
   894         AfterAttack
       
   895         end
       
   896 end;
       
   897 
       
   898 procedure doStepShotgunShot(Gear: PGear);
       
   899 var
       
   900     i: LongWord;
       
   901     shell: PVisualGear;
       
   902 begin
       
   903     AllInactive := false;
       
   904 
       
   905     if ((Gear^.State and gstAnimation) = 0) then
       
   906         begin
       
   907         dec(Gear^.Timer);
       
   908         if Gear^.Timer = 0 then
       
   909             begin
       
   910             PlaySound(sndShotgunFire);
       
   911             shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell);
       
   912             if shell <> nil then
       
   913                 begin
       
   914                 shell^.dX := gear^.dX.QWordValue / -17179869184;
       
   915                 shell^.dY := gear^.dY.QWordValue / -17179869184;
       
   916                 shell^.Frame := 0
       
   917                 end;
       
   918             Gear^.State := Gear^.State or gstAnimation
       
   919             end;
       
   920             exit
       
   921         end else
       
   922         if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then
       
   923             begin
       
   924             DeleteGear(Gear);
       
   925             AfterAttack;
       
   926             exit
       
   927             end
       
   928     else
       
   929         inc(Gear^.Timer);
       
   930 
       
   931         i := 200;
       
   932     repeat
       
   933         Gear^.X := Gear^.X + Gear^.dX;
       
   934         Gear^.Y := Gear^.Y + Gear^.dY;
       
   935         CheckCollision(Gear);
       
   936         if (Gear^.State and gstCollision) <> 0 then
       
   937             begin
       
   938             Gear^.X := Gear^.X + Gear^.dX * 8;
       
   939             Gear^.Y := Gear^.Y + Gear^.dY * 8;
       
   940             ShotgunShot(Gear);
       
   941             Gear^.doStep := @doStepShotIdle;
       
   942             exit
       
   943             end;
       
   944 
       
   945         CheckGearDrowning(Gear);
       
   946         if (Gear^.State and gstDrowning) <> 0 then
       
   947             begin
       
   948             Gear^.doStep := @doStepShotIdle;
       
   949             exit
       
   950             end;
       
   951         dec(i)
       
   952     until i = 0;
       
   953     if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
       
   954         Gear^.doStep := @doStepShotIdle
       
   955 end;
       
   956 
       
   957 ////////////////////////////////////////////////////////////////////////////////
       
   958 procedure spawnBulletTrail(Bullet: PGear);
       
   959 var oX, oY: hwFloat;
       
   960     VGear: PVisualGear;
       
   961 begin
       
   962     if Bullet^.PortalCounter = 0 then
       
   963         begin
       
   964         ox:= CurrentHedgehog^.Gear^.X + Int2hwFloat(GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle));
       
   965         oy:= CurrentHedgehog^.Gear^.Y + Int2hwFloat(GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle));
       
   966         end
       
   967     else
       
   968         begin
       
   969         ox:= Bullet^.Elasticity;
       
   970         oy:= Bullet^.Friction;
       
   971         end;
       
   972 
       
   973         // Bullet trail
       
   974         VGear := AddVisualGear(hwRound(ox), hwRound(oy), vgtLineTrail);
       
   975 
       
   976         if VGear <> nil then
       
   977             begin
       
   978             VGear^.X:= hwFloat2Float(ox);
       
   979             VGear^.Y:= hwFloat2Float(oy);
       
   980             VGear^.dX:= hwFloat2Float(Bullet^.X);
       
   981             VGear^.dY:= hwFloat2Float(Bullet^.Y);
       
   982 
       
   983             // reached edge of land. assume infinite beam. Extend it way out past camera
       
   984             if (hwRound(Bullet^.X) and LAND_WIDTH_MASK <> 0)
       
   985             or (hwRound(Bullet^.Y) and LAND_HEIGHT_MASK <> 0) then
       
   986                     // only extend if not under water
       
   987                     if hwRound(Bullet^.Y) < cWaterLine then
       
   988                         begin
       
   989                         VGear^.dX := VGear^.dX + max(LAND_WIDTH,4096) * (VGear^.dX - VGear^.X);
       
   990                         VGear^.dY := VGear^.dY + max(LAND_WIDTH,4096) * (VGear^.dY - VGear^.Y);
       
   991                         end;
       
   992 
       
   993             VGear^.Timer := 200;
       
   994             end;
       
   995 end;
       
   996 
       
   997 procedure doStepBulletWork(Gear: PGear);
       
   998 var
       
   999     i, x, y: LongWord;
       
  1000     oX, oY: hwFloat;
       
  1001     VGear: PVisualGear;
       
  1002 begin
       
  1003     AllInactive := false;
       
  1004     inc(Gear^.Timer);
       
  1005     i := 80;
       
  1006     oX := Gear^.X;
       
  1007     oY := Gear^.Y;
       
  1008     repeat
       
  1009         Gear^.X := Gear^.X + Gear^.dX;
       
  1010         Gear^.Y := Gear^.Y + Gear^.dY;
       
  1011         x := hwRound(Gear^.X);
       
  1012         y := hwRound(Gear^.Y);
       
  1013 
       
  1014         if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then
       
  1015             inc(Gear^.Damage);
       
  1016         // let's interrupt before a collision to give portals a chance to catch the bullet
       
  1017         if (Gear^.Damage = 1) and (Gear^.Tag = 0) and not(CheckLandValue(x, y, lfLandMask)) then
       
  1018             begin
       
  1019             Gear^.Tag := 1;
       
  1020             Gear^.Damage := 0;
       
  1021             Gear^.X := Gear^.X - Gear^.dX;
       
  1022             Gear^.Y := Gear^.Y - Gear^.dY;
       
  1023             CheckGearDrowning(Gear);
       
  1024             break;
       
  1025             end
       
  1026         else
       
  1027             Gear^.Tag := 0;
       
  1028 
       
  1029         if Gear^.Damage > 5 then
       
  1030             if Gear^.AmmoType = amDEagle then
       
  1031                 AmmoShove(Gear, 7, 20)
       
  1032         else
       
  1033             AmmoShove(Gear, Gear^.Timer, 20);
       
  1034         CheckGearDrowning(Gear);
       
  1035         dec(i)
       
  1036     until (i = 0) or (Gear^.Damage > Gear^.Health) or ((Gear^.State and gstDrowning) <> 0);
       
  1037 
       
  1038     if Gear^.Damage > 0 then
       
  1039         begin
       
  1040         DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 82 - i, 1);
       
  1041         dec(Gear^.Health, Gear^.Damage);
       
  1042         Gear^.Damage := 0
       
  1043         end;
       
  1044     if ((Gear^.State and gstDrowning) <> 0) and (Gear^.Damage < Gear^.Health) and ((not SuddenDeathDmg and (WaterOpacity < $FF)) or (SuddenDeathDmg and (SDWaterOpacity < $FF))) then
       
  1045         begin
       
  1046         for i:=(Gear^.Health - Gear^.Damage) * 4 downto 0 do
       
  1047             begin
       
  1048             if Random(6) = 0 then
       
  1049                 AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble);
       
  1050             Gear^.X := Gear^.X + Gear^.dX;
       
  1051             Gear^.Y := Gear^.Y + Gear^.dY;
       
  1052             end;
       
  1053         end;
       
  1054 
       
  1055     if (Gear^.Health <= 0)
       
  1056         or (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0)
       
  1057         or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
       
  1058             begin
       
  1059             if (Gear^.Kind = gtSniperRifleShot) and ((GameFlags and gfLaserSight) = 0) then
       
  1060                 cLaserSighting := false;
       
  1061             if (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and ((GameFlags and gfArtillery) = 0) then
       
  1062                 cArtillery := false;
       
  1063 
       
  1064         // Bullet Hit
       
  1065             if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then
       
  1066                 begin
       
  1067                 VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit);
       
  1068                 if VGear <> nil then
       
  1069                     begin
       
  1070                     VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY);
       
  1071                     end;
       
  1072                 end;
       
  1073 
       
  1074             spawnBulletTrail(Gear);
       
  1075             Gear^.doStep := @doStepShotIdle
       
  1076             end;
       
  1077 end;
       
  1078 
       
  1079 procedure doStepDEagleShot(Gear: PGear);
       
  1080 begin
       
  1081     PlaySound(sndGun);
       
  1082     // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just plain old weird angles
       
  1083     Gear^.X := Gear^.X + Gear^.dX * 3;
       
  1084     Gear^.Y := Gear^.Y + Gear^.dY * 3;
       
  1085     Gear^.doStep := @doStepBulletWork
       
  1086 end;
       
  1087 
       
  1088 procedure doStepSniperRifleShot(Gear: PGear);
       
  1089 var
       
  1090     HHGear: PGear;
       
  1091     shell: PVisualGear;
       
  1092 begin
       
  1093     cArtillery := true;
       
  1094     HHGear := Gear^.Hedgehog^.Gear;
       
  1095     HHGear^.State := HHGear^.State or gstNotKickable;
       
  1096     HedgehogChAngle(HHGear);
       
  1097     if not cLaserSighting then
       
  1098         // game does not have default laser sight. turn it on and give them a chance to aim
       
  1099         begin
       
  1100         cLaserSighting := true;
       
  1101         HHGear^.Message := 0;
       
  1102         if (HHGear^.Angle >= 32) then
       
  1103             dec(HHGear^.Angle,32)
       
  1104         end;
       
  1105 
       
  1106     if (HHGear^.Message and gmAttack) <> 0 then
       
  1107         begin
       
  1108         shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell);
       
  1109         if shell <> nil then
       
  1110             begin
       
  1111             shell^.dX := gear^.dX.QWordValue / -8589934592;
       
  1112             shell^.dY := gear^.dY.QWordValue / -8589934592;
       
  1113             shell^.Frame := 1
       
  1114             end;
       
  1115         Gear^.State := Gear^.State or gstAnimation;
       
  1116         Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _0_5;
       
  1117         Gear^.dY := -AngleCos(HHGear^.Angle) * _0_5;
       
  1118         PlaySound(sndGun);
       
  1119         // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just weird angles
       
  1120         Gear^.X := Gear^.X + Gear^.dX * 3;
       
  1121         Gear^.Y := Gear^.Y + Gear^.dY * 3;
       
  1122         Gear^.doStep := @doStepBulletWork;
       
  1123         end
       
  1124     else
       
  1125         if (GameTicks mod 32) = 0 then
       
  1126             if (GameTicks mod 4096) < 2048 then
       
  1127                 begin
       
  1128                 if (HHGear^.Angle + 1 <= cMaxAngle) then
       
  1129                     inc(HHGear^.Angle)
       
  1130                 end
       
  1131     else
       
  1132         if (HHGear^.Angle >= 1) then
       
  1133             dec(HHGear^.Angle);
       
  1134 
       
  1135     if (TurnTimeLeft > 0) then
       
  1136         dec(TurnTimeLeft)
       
  1137     else
       
  1138         begin
       
  1139         DeleteGear(Gear);
       
  1140         AfterAttack
       
  1141         end;
       
  1142 end;
       
  1143 
       
  1144 ////////////////////////////////////////////////////////////////////////////////
       
  1145 procedure doStepActionTimer(Gear: PGear);
       
  1146 begin
       
  1147 dec(Gear^.Timer);
       
  1148 case Gear^.Kind of
       
  1149     gtATStartGame:
       
  1150         begin
       
  1151         AllInactive := false;
       
  1152         if Gear^.Timer = 0 then
       
  1153             begin
       
  1154             AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState);
       
  1155             end
       
  1156         end;
       
  1157     gtATFinishGame:
       
  1158         begin
       
  1159         AllInactive := false;
       
  1160         if Gear^.Timer = 1000 then
       
  1161             begin
       
  1162             ScreenFade := sfToBlack;
       
  1163             ScreenFadeValue := 0;
       
  1164             ScreenFadeSpeed := 1;
       
  1165             end;
       
  1166         if Gear^.Timer = 0 then
       
  1167             begin
       
  1168             SendIPC(_S'N');
       
  1169             SendIPC(_S'q');
       
  1170             GameState := gsExit
       
  1171             end
       
  1172         end;
       
  1173     end;
       
  1174 if Gear^.Timer = 0 then
       
  1175     DeleteGear(Gear)
       
  1176 end;
       
  1177 
       
  1178 ////////////////////////////////////////////////////////////////////////////////
       
  1179 procedure doStepPickHammerWork(Gear: PGear);
       
  1180 var
       
  1181     i, ei, x, y: LongInt;
       
  1182     HHGear: PGear;
       
  1183 begin
       
  1184     AllInactive := false;
       
  1185     HHGear := Gear^.Hedgehog^.Gear;
       
  1186     dec(Gear^.Timer);
       
  1187     if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then
       
  1188         dec(TurnTimeLeft);
       
  1189     if (TurnTimeLeft = 0) or (Gear^.Timer = 0)
       
  1190     or((Gear^.Message and gmDestroy) <> 0)
       
  1191     or((HHGear^.State and gstHHDriven) =0) then
       
  1192         begin
       
  1193         StopSoundChan(Gear^.SoundChannel);
       
  1194         DeleteGear(Gear);
       
  1195         AfterAttack;
       
  1196         doStepHedgehogMoving(HHGear);  // for gfInfAttack
       
  1197         exit
       
  1198         end;
       
  1199 
       
  1200     x:= hwRound(Gear^.X);
       
  1201     y:= hwRound(Gear^.Y);
       
  1202     if (Gear^.Timer mod 33) = 0 then
       
  1203         begin
       
  1204         HHGear^.State := HHGear^.State or gstNoDamage;
       
  1205         doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw);
       
  1206         HHGear^.State := HHGear^.State and (not gstNoDamage)
       
  1207         end;
       
  1208 
       
  1209     if (Gear^.Timer mod 47) = 0 then
       
  1210         begin
       
  1211         // ok. this was an attempt to turn off dust if not actually drilling land.  I have no idea why it isn't working as expected
       
  1212         if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then
       
  1213             for i:= 0 to 1 do
       
  1214                 AddVisualGear(x - 5 + Random(10), y + 12, vgtDust);
       
  1215 
       
  1216         i := x - Gear^.Radius - LongInt(GetRandom(2));
       
  1217         ei := x + Gear^.Radius + LongInt(GetRandom(2));
       
  1218         while i <= ei do
       
  1219             begin
       
  1220             DrawExplosion(i, y + 3, 3);
       
  1221             inc(i, 1)
       
  1222             end;
       
  1223 
       
  1224         if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9), lfIndestructible) then
       
  1225             begin
       
  1226             Gear^.X := Gear^.X + Gear^.dX;
       
  1227             Gear^.Y := Gear^.Y + _1_9;
       
  1228             end;
       
  1229         SetAllHHToActive;
       
  1230         end;
       
  1231     if TestCollisionYwithGear(Gear, 1) <> 0 then
       
  1232         begin
       
  1233         Gear^.dY := _0;
       
  1234         SetLittle(HHGear^.dX);
       
  1235         HHGear^.dY := _0;
       
  1236         end
       
  1237     else
       
  1238         begin
       
  1239         if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), lfLandMask) then
       
  1240             begin
       
  1241             Gear^.dY := Gear^.dY + cGravity;
       
  1242             Gear^.Y := Gear^.Y + Gear^.dY
       
  1243             end;
       
  1244         if hwRound(Gear^.Y) > cWaterLine then
       
  1245             Gear^.Timer := 1
       
  1246         end;
       
  1247 
       
  1248     Gear^.X := Gear^.X + HHGear^.dX;
       
  1249     if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, lfLandMask) then
       
  1250         begin
       
  1251         HHGear^.X := Gear^.X;
       
  1252         HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius)
       
  1253         end;
       
  1254 
       
  1255     if (Gear^.Message and gmAttack) <> 0 then
       
  1256         if (Gear^.State and gsttmpFlag) <> 0 then
       
  1257             Gear^.Timer := 1
       
  1258     else //there would be a mistake.
       
  1259     else
       
  1260         if (Gear^.State and gsttmpFlag) = 0 then
       
  1261             Gear^.State := Gear^.State or gsttmpFlag;
       
  1262     if ((Gear^.Message and gmLeft) <> 0) then
       
  1263         Gear^.dX := - _0_3
       
  1264     else
       
  1265         if ((Gear^.Message and gmRight) <> 0) then
       
  1266             Gear^.dX := _0_3
       
  1267     else Gear^.dX := _0;
       
  1268 end;
       
  1269 
       
  1270 procedure doStepPickHammer(Gear: PGear);
       
  1271 var
       
  1272     i, y: LongInt;
       
  1273     ar: TRangeArray;
       
  1274     HHGear: PGear;
       
  1275 begin
       
  1276     i := 0;
       
  1277     HHGear := Gear^.Hedgehog^.Gear;
       
  1278 
       
  1279     y := hwRound(Gear^.Y) - cHHRadius * 2;
       
  1280     while y < hwRound(Gear^.Y) do
       
  1281         begin
       
  1282         ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2));
       
  1283         ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
       
  1284         inc(y, 2);
       
  1285         inc(i)
       
  1286         end;
       
  1287 
       
  1288     DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i));
       
  1289     Gear^.dY := HHGear^.dY;
       
  1290     DeleteCI(HHGear);
       
  1291 
       
  1292     Gear^.SoundChannel := LoopSound(sndPickhammer);
       
  1293     doStepPickHammerWork(Gear);
       
  1294     Gear^.doStep := @doStepPickHammerWork
       
  1295 end;
       
  1296 
       
  1297 ////////////////////////////////////////////////////////////////////////////////
       
  1298 var
       
  1299     BTPrevAngle, BTSteps: LongInt;
       
  1300 
       
  1301 procedure doStepBlowTorchWork(Gear: PGear);
       
  1302 var
       
  1303     HHGear: PGear;
       
  1304     b: boolean;
       
  1305     prevX: LongInt;
       
  1306 begin
       
  1307     AllInactive := false;
       
  1308     dec(Gear^.Timer);
       
  1309     if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then
       
  1310         dec(TurnTimeLeft);
       
  1311 
       
  1312     HHGear := Gear^.Hedgehog^.Gear;
       
  1313 
       
  1314     HedgehogChAngle(HHGear);
       
  1315 
       
  1316     b := false;
       
  1317 
       
  1318     if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7  then
       
  1319         begin
       
  1320         Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX);
       
  1321         Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5);
       
  1322         BTPrevAngle := HHGear^.Angle;
       
  1323         b := true
       
  1324         end;
       
  1325 
       
  1326     if ((HHGear^.State and gstMoving) <> 0) then
       
  1327         begin
       
  1328         doStepHedgehogMoving(HHGear);
       
  1329         if (HHGear^.State and gstHHDriven) = 0 then
       
  1330             Gear^.Timer := 0
       
  1331         end;
       
  1332 
       
  1333     if Gear^.Timer mod cHHStepTicks = 0 then
       
  1334         begin
       
  1335         b := true;
       
  1336         if Gear^.dX.isNegative then
       
  1337             HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmLeft
       
  1338         else
       
  1339             HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmRight;
       
  1340 
       
  1341         if ((HHGear^.State and gstMoving) = 0) then
       
  1342             begin
       
  1343             HHGear^.State := HHGear^.State and (not gstAttacking);
       
  1344             prevX := hwRound(HHGear^.X);
       
  1345 
       
  1346             // why the call to HedgehogStep then a further increment of X?
       
  1347             if (prevX = hwRound(HHGear^.X)) and
       
  1348                CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y),
       
  1349                lfIndestructible) then HedgehogStep(HHGear);
       
  1350 
       
  1351             if (prevX = hwRound(HHGear^.X)) and
       
  1352                CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y),
       
  1353                lfIndestructible) then HHGear^.X := HHGear^.X + SignAs(_1, HHGear^.dX);
       
  1354             HHGear^.State := HHGear^.State or gstAttacking
       
  1355             end;
       
  1356 
       
  1357         inc(BTSteps);
       
  1358         if BTSteps = 7 then
       
  1359             begin
       
  1360             BTSteps := 0;
       
  1361             if CheckLandValue(hwRound(HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC) + SignAs(_6,Gear^.dX)), hwRound(HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC)),lfIndestructible) then
       
  1362                 begin
       
  1363                 Gear^.X := HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC);
       
  1364                 Gear^.Y := HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC);
       
  1365                 end;
       
  1366             HHGear^.State := HHGear^.State or gstNoDamage;
       
  1367             AmmoShove(Gear, 2, 15);
       
  1368             HHGear^.State := HHGear^.State and (not gstNoDamage)
       
  1369             end;
       
  1370         end;
       
  1371 
       
  1372     if b then
       
  1373     begin
       
  1374         DrawTunnel(HHGear^.X + Gear^.dX * cHHRadius,
       
  1375         HHGear^.Y + Gear^.dY * cHHRadius - _1 -
       
  1376         ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7),
       
  1377         Gear^.dX, Gear^.dY,
       
  1378         cHHStepTicks, cHHRadius * 2 + 7);
       
  1379     end;
       
  1380 
       
  1381     if (TurnTimeLeft = 0) or (Gear^.Timer = 0)
       
  1382     or ((HHGear^.Message and gmAttack) <> 0) then
       
  1383         begin
       
  1384         HHGear^.Message := 0;
       
  1385         HHGear^.State := HHGear^.State and (not gstNotKickable);
       
  1386         DeleteGear(Gear);
       
  1387         AfterAttack
       
  1388         end
       
  1389 end;
       
  1390 
       
  1391 procedure doStepBlowTorch(Gear: PGear);
       
  1392 var
       
  1393     HHGear: PGear;
       
  1394 begin
       
  1395     BTPrevAngle := High(LongInt);
       
  1396     BTSteps := 0;
       
  1397     HHGear := Gear^.Hedgehog^.Gear;
       
  1398     HedgehogChAngle(HHGear);
       
  1399     Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX);
       
  1400     Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5);
       
  1401     DrawTunnel(HHGear^.X,
       
  1402         HHGear^.Y + Gear^.dY * cHHRadius - _1 -
       
  1403         ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7),
       
  1404         Gear^.dX, Gear^.dY,
       
  1405         cHHStepTicks, cHHRadius * 2 + 7);
       
  1406     HHGear^.Message := 0;
       
  1407     HHGear^.State := HHGear^.State or gstNotKickable;
       
  1408     Gear^.doStep := @doStepBlowTorchWork
       
  1409 end;
       
  1410 
       
  1411 ////////////////////////////////////////////////////////////////////////////////
       
  1412 procedure doStepMine(Gear: PGear);
       
  1413 var vg: PVisualGear;
       
  1414     dxdy: hwFloat;
       
  1415 begin
       
  1416     if Gear^.Health = 0 then dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY);
       
  1417     if (Gear^.State and gstMoving) <> 0 then
       
  1418         begin
       
  1419         DeleteCI(Gear);
       
  1420         doStepFallingGear(Gear);
       
  1421         if (Gear^.State and gstMoving) = 0 then
       
  1422             begin
       
  1423             AddGearCI(Gear);
       
  1424             Gear^.dX := _0;
       
  1425             Gear^.dY := _0
       
  1426             end;
       
  1427         CalcRotationDirAngle(Gear);
       
  1428         AllInactive := false
       
  1429         end
       
  1430     else if (GameTicks and $3F) = 25 then
       
  1431         doStepFallingGear(Gear);
       
  1432     if (Gear^.Health = 0) then
       
  1433         begin
       
  1434         if (dxdy > _0_4) and (Gear^.State and gstCollision <> 0) then
       
  1435             inc(Gear^.Damage, hwRound(dxdy * _50));
       
  1436 
       
  1437         if ((GameTicks and $FF) = 0) and (Gear^.Damage > random(30)) then
       
  1438             begin
       
  1439             vg:= AddVisualGear(hwRound(Gear^.X) - 4  + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke);
       
  1440             if vg <> nil then
       
  1441                 vg^.Scale:= 0.5
       
  1442             end;
       
  1443 
       
  1444         if (Gear^.Damage > 35) then
       
  1445             begin
       
  1446             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  1447             DeleteGear(Gear);
       
  1448             exit
       
  1449             end
       
  1450         end;
       
  1451 
       
  1452     if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then
       
  1453         if ((Gear^.State and gstAttacking) = 0) then
       
  1454             begin
       
  1455             if ((GameTicks and $1F) = 0) then
       
  1456                 if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then
       
  1457                     Gear^.State := Gear^.State or gstAttacking
       
  1458             end
       
  1459         else // gstAttacking <> 0
       
  1460             begin
       
  1461             AllInactive := false;
       
  1462             if (Gear^.Timer and $FF) = 0 then
       
  1463                 PlaySound(sndMineTick);
       
  1464             if Gear^.Timer = 0 then
       
  1465                 begin
       
  1466                 if ((Gear^.State and gstWait) <> 0)
       
  1467                 or (cMineDudPercent = 0)
       
  1468                 or (getRandom(100) > cMineDudPercent) then
       
  1469                     begin
       
  1470                     doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  1471                     DeleteGear(Gear)
       
  1472                     end
       
  1473                 else
       
  1474                     begin
       
  1475                     vg:= AddVisualGear(hwRound(Gear^.X) - 4  + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke);
       
  1476                     if vg <> nil then
       
  1477                         vg^.Scale:= 0.5;
       
  1478                     PlaySound(sndVaporize);
       
  1479                     Gear^.Health := 0;
       
  1480                     Gear^.Damage := 0;
       
  1481                     Gear^.State := Gear^.State and (not gstAttacking)
       
  1482                     end;
       
  1483                 exit
       
  1484                 end;
       
  1485             dec(Gear^.Timer);
       
  1486             end
       
  1487     else // gsttmpFlag = 0
       
  1488         if (TurnTimeLeft = 0)
       
  1489         or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime))
       
  1490         or (Gear^.Hedgehog^.Gear = nil) then
       
  1491             Gear^.State := Gear^.State or gsttmpFlag;
       
  1492 end;
       
  1493 
       
  1494 ////////////////////////////////////////////////////////////////////////////////
       
  1495 procedure doStepSMine(Gear: PGear);
       
  1496 begin
       
  1497     // TODO: do real calculation?
       
  1498     if TestCollisionXwithGear(Gear, 2)
       
  1499     or (TestCollisionYwithGear(Gear, -2) <> 0)
       
  1500     or TestCollisionXwithGear(Gear, -2)
       
  1501     or (TestCollisionYwithGear(Gear, 2) <> 0) then
       
  1502         begin
       
  1503         if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then
       
  1504             begin
       
  1505             PlaySound(sndRopeAttach);
       
  1506             Gear^.dX:= _0;
       
  1507             Gear^.dY:= _0;
       
  1508             AddGearCI(Gear);
       
  1509             end;
       
  1510         end
       
  1511     else
       
  1512         begin
       
  1513         DeleteCI(Gear);
       
  1514         doStepFallingGear(Gear);
       
  1515         AllInactive := false;
       
  1516         CalcRotationDirAngle(Gear);
       
  1517         end;
       
  1518 
       
  1519     if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then
       
  1520         begin
       
  1521         if ((Gear^.State and gstAttacking) = 0) then
       
  1522             begin
       
  1523             if ((GameTicks and $1F) = 0) then
       
  1524                 if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then
       
  1525                     Gear^.State := Gear^.State or gstAttacking
       
  1526             end
       
  1527         else // gstAttacking <> 0
       
  1528         begin
       
  1529             AllInactive := false;
       
  1530             if Gear^.Timer = 0 then
       
  1531                 begin
       
  1532                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  1533                 DeleteGear(Gear);
       
  1534                 exit
       
  1535             end else
       
  1536                 if (Gear^.Timer and $FF) = 0 then
       
  1537                     PlaySound(sndMineTick);
       
  1538 
       
  1539             dec(Gear^.Timer);
       
  1540                 end
       
  1541         end
       
  1542     else // gsttmpFlag = 0
       
  1543         if (TurnTimeLeft = 0)
       
  1544         or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime))
       
  1545         or (Gear^.Hedgehog^.Gear = nil) then
       
  1546             Gear^.State := Gear^.State or gsttmpFlag;
       
  1547 end;
       
  1548 
       
  1549 ////////////////////////////////////////////////////////////////////////////////
       
  1550 procedure doStepDynamite(Gear: PGear);
       
  1551 begin
       
  1552     doStepFallingGear(Gear);
       
  1553     AllInactive := false;
       
  1554     if Gear^.Timer mod 166 = 0 then
       
  1555         inc(Gear^.Tag);
       
  1556     if Gear^.Timer = 1000 then // might need better timing
       
  1557         makeHogsWorry(Gear^.X, Gear^.Y, 75);
       
  1558     if Gear^.Timer = 0 then
       
  1559         begin
       
  1560         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, Gear^.Hedgehog, EXPLAutoSound);
       
  1561         DeleteGear(Gear);
       
  1562         exit
       
  1563         end;
       
  1564     dec(Gear^.Timer);
       
  1565 end;
       
  1566 
       
  1567 ///////////////////////////////////////////////////////////////////////////////
       
  1568 
       
  1569 procedure doStepRollingBarrel(Gear: PGear);
       
  1570 var
       
  1571     i: LongInt;
       
  1572     particle: PVisualGear;
       
  1573     dxdy: hwFloat;
       
  1574 begin
       
  1575     if (Gear^.dY.QWordValue = 0) and (Gear^.dY.QWordValue = 0) and (TestCollisionYwithGear(Gear, 1) = 0) then
       
  1576         SetLittle(Gear^.dY);
       
  1577     Gear^.State := Gear^.State or gstAnimation;
       
  1578     if Gear^.Health < cBarrelHealth then Gear^.State:= Gear^.State and not gstFrozen;
       
  1579 
       
  1580     if ((Gear^.dX.QWordValue <> 0)
       
  1581     or (Gear^.dY.QWordValue <> 0))  then
       
  1582         begin
       
  1583         DeleteCI(Gear);
       
  1584         AllInactive := false;
       
  1585         dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY);
       
  1586         doStepFallingGear(Gear);
       
  1587         if (Gear^.State and gstCollision <> 0) and(dxdy > _0_4) then
       
  1588             begin
       
  1589             if (TestCollisionYwithGear(Gear, 1) <> 0) then
       
  1590                 begin
       
  1591                 Gear^.State := Gear^.State or gsttmpFlag;
       
  1592                 for i:= min(12, hwRound(dxdy*_10)) downto 0 do
       
  1593                     begin
       
  1594                     particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12,vgtDust);
       
  1595                     if particle <> nil then
       
  1596                         particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
       
  1597                     end
       
  1598                 end;
       
  1599             inc(Gear^.Damage, hwRound(dxdy * _50))
       
  1600             end;
       
  1601         CalcRotationDirAngle(Gear);
       
  1602         //CheckGearDrowning(Gear)
       
  1603         end
       
  1604     else
       
  1605         begin
       
  1606         Gear^.State := Gear^.State or gsttmpFlag;
       
  1607         AddGearCI(Gear)
       
  1608         end;
       
  1609 
       
  1610 (*
       
  1611 Attempt to make a barrel knock itself over an edge.  Would need more checks to avoid issues like burn damage
       
  1612     begin
       
  1613     x:= hwRound(Gear^.X);
       
  1614     y:= hwRound(Gear^.Y);
       
  1615     if (((y+1) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
       
  1616         if (Land[y+1, x] = 0) then
       
  1617             begin
       
  1618             if (((y+1) and LAND_HEIGHT_MASK) = 0) and (((x+Gear^.Radius-2) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x+Gear^.Radius-2] = 0) then
       
  1619                 Gear^.dX:= -_0_08
       
  1620             else if (((y+1 and LAND_HEIGHT_MASK)) = 0) and (((x-(Gear^.Radius-2)) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x-(Gear^.Radius-2)] = 0) then
       
  1621                 Gear^.dX:= _0_08;
       
  1622             end;
       
  1623     if Gear^.dX.QWordValue = 0 then AddGearCI(Gear)
       
  1624     end; *)
       
  1625 
       
  1626     if not Gear^.dY.isNegative and (Gear^.dY < _0_001) and (TestCollisionYwithGear(Gear, 1) <> 0) then
       
  1627         Gear^.dY := _0;
       
  1628     if hwAbs(Gear^.dX) < _0_001 then
       
  1629         Gear^.dX := _0;
       
  1630 
       
  1631     if (Gear^.Health > 0) and ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then
       
  1632         if (cBarrelHealth div Gear^.Health) > 2 then
       
  1633             AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke)
       
  1634     else
       
  1635         AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite);
       
  1636     dec(Gear^.Health, Gear^.Damage);
       
  1637     Gear^.Damage := 0;
       
  1638     if Gear^.Health <= 0 then
       
  1639         doStepCase(Gear);
       
  1640 end;
       
  1641 
       
  1642 procedure doStepCase(Gear: PGear);
       
  1643 var
       
  1644     i, x, y: LongInt;
       
  1645     k: TGearType;
       
  1646     dX, dY: HWFloat;
       
  1647     hog: PHedgehog;
       
  1648     sparkles: PVisualGear;
       
  1649     gi: PGear;
       
  1650 begin
       
  1651     k := Gear^.Kind;
       
  1652 
       
  1653     if (Gear^.Message and gmDestroy) > 0 then
       
  1654         begin
       
  1655         DeleteGear(Gear);
       
  1656         FreeActionsList;
       
  1657         SetAllToActive;
       
  1658         // something (hh, mine, etc...) could be on top of the case
       
  1659         with CurrentHedgehog^ do
       
  1660             if Gear <> nil then
       
  1661                 Gear^.Message := Gear^.Message and (not (gmLJump or gmHJump));
       
  1662         exit
       
  1663         end;
       
  1664     if (k = gtExplosives) and (Gear^.Health < cBarrelHealth) then Gear^.State:= Gear^.State and not gstFrozen;
       
  1665 
       
  1666     if ((k <> gtExplosives) and (Gear^.Damage > 0)) or ((k = gtExplosives) and (Gear^.Health<=0)) then
       
  1667         begin
       
  1668         x := hwRound(Gear^.X);
       
  1669         y := hwRound(Gear^.Y);
       
  1670         hog:= Gear^.Hedgehog;
       
  1671 
       
  1672         DeleteGear(Gear);
       
  1673         // <-- delete gear!
       
  1674 
       
  1675         if k = gtCase then
       
  1676             begin
       
  1677             doMakeExplosion(x, y, 25, hog, EXPLAutoSound);
       
  1678             for i:= 0 to 63 do
       
  1679                 AddGear(x, y, gtFlame, 0, _0, _0, 0);
       
  1680             end
       
  1681         else if k = gtExplosives then
       
  1682                 begin
       
  1683                 doMakeExplosion(x, y, 75, hog, EXPLAutoSound);
       
  1684                 for i:= 0 to 31 do
       
  1685                     begin
       
  1686                     dX := AngleCos(i * 64) * _0_5 * (getrandomf + _1);
       
  1687                     dY := AngleSin(i * 64) * _0_5 * (getrandomf + _1);
       
  1688                     AddGear(x, y, gtFlame, 0, dX, dY, 0);
       
  1689                     AddGear(x, y, gtFlame, gstTmpFlag, -dX, -dY, 0);
       
  1690                     end
       
  1691                 end;
       
  1692             exit
       
  1693         end;
       
  1694 
       
  1695     if k = gtExplosives then
       
  1696         begin
       
  1697         //if V > _0_03 then Gear^.State:= Gear^.State or gstAnimation;
       
  1698         if (hwAbs(Gear^.dX) > _0_15) or ((hwAbs(Gear^.dY) > _0_15) and (hwAbs(Gear^.dX) > _0_02)) then
       
  1699             begin
       
  1700             Gear^.doStep := @doStepRollingBarrel;
       
  1701             exit;
       
  1702             end
       
  1703         else Gear^.dX:= _0;
       
  1704 
       
  1705         if ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then
       
  1706             if (cBarrelHealth div Gear^.Health) > 2 then
       
  1707                 AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke)
       
  1708             else
       
  1709                 AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite);
       
  1710         dec(Gear^.Health, Gear^.Damage);
       
  1711         Gear^.Damage := 0;
       
  1712         end
       
  1713     else
       
  1714         begin
       
  1715         if (Gear^.Pos <> posCaseHealth) and (GameTicks and $1FFF = 0) then // stir 'em up periodically
       
  1716             begin
       
  1717             gi := GearsList;
       
  1718             while gi <> nil do
       
  1719                 begin
       
  1720                 if gi^.Kind = gtGenericFaller then
       
  1721                     begin
       
  1722                     gi^.Active:= true;
       
  1723                     gi^.X:=  int2hwFloat(GetRandom(rightX-leftX)+leftX);
       
  1724                     gi^.Y:=  int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY);
       
  1725                     gi^.dX:= _90-(GetRandomf*_360);
       
  1726                     gi^.dY:= _90-(GetRandomf*_360)
       
  1727                     end;
       
  1728                 gi := gi^.NextGear
       
  1729                 end
       
  1730             end;
       
  1731 
       
  1732         if Gear^.Timer = 500 then
       
  1733             begin
       
  1734 (* Can't make sparkles team coloured without working out what the next team is going to be. This should be solved, really, since it also screws up
       
  1735    voices. Reinforcements voices is heard for active team, not team-to-be.  Either that or change crate spawn from end of turn to start, although that
       
  1736    has its own complexities. *)
       
  1737             // Abuse a couple of gear values to track origin
       
  1738             Gear^.Angle:= hwRound(Gear^.Y);
       
  1739             Gear^.Tag:= random(2);
       
  1740             inc(Gear^.Timer)
       
  1741             end;
       
  1742         if Gear^.Timer < 1833 then inc(Gear^.Timer);
       
  1743         if Gear^.Timer = 1000 then
       
  1744             begin
       
  1745             sparkles:= AddVisualGear(hwRound(Gear^.X), Gear^.Angle, vgtDust, 1);
       
  1746             if sparkles <> nil then
       
  1747                 begin
       
  1748                 sparkles^.dX:= 0;
       
  1749                 sparkles^.dY:= 0;
       
  1750                 sparkles^.Angle:= 270;
       
  1751                 if Gear^.Tag = 1 then
       
  1752                     sparkles^.Tint:= $3744D7FF
       
  1753                 else sparkles^.Tint:= $FAB22CFF
       
  1754                 end;
       
  1755             end;
       
  1756         if Gear^.Timer < 1000 then
       
  1757             begin
       
  1758             AllInactive:= false;
       
  1759             exit
       
  1760             end
       
  1761         end;
       
  1762 
       
  1763 
       
  1764     if (Gear^.dY.QWordValue <> 0)
       
  1765     or (TestCollisionYwithGear(Gear, 1) = 0) then
       
  1766         begin
       
  1767         AllInactive := false;
       
  1768 
       
  1769         Gear^.dY := Gear^.dY + cGravity;
       
  1770 
       
  1771         if (Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, -1) <> 0) then
       
  1772             Gear^.dY := _0;
       
  1773 
       
  1774         Gear^.Y := Gear^.Y + Gear^.dY;
       
  1775 
       
  1776         if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then
       
  1777             SetAllHHToActive(false);
       
  1778 
       
  1779         if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then
       
  1780             begin
       
  1781             if (Gear^.dY > _0_2) and (k = gtExplosives) then
       
  1782                 inc(Gear^.Damage, hwRound(Gear^.dY * _70));
       
  1783 
       
  1784             if Gear^.dY > _0_2 then
       
  1785                 for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do
       
  1786                     AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
       
  1787 
       
  1788             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
  1789             if Gear^.dY > - _0_001 then
       
  1790                 Gear^.dY := _0
       
  1791             else if Gear^.dY < - _0_03 then
       
  1792                 PlaySound(Gear^.ImpactSound);
       
  1793             end;
       
  1794         //if Gear^.dY > - _0_001 then Gear^.dY:= _0
       
  1795         CheckGearDrowning(Gear);
       
  1796         end;
       
  1797 
       
  1798     if (Gear^.dY.QWordValue = 0) then
       
  1799         AddGearCI(Gear)
       
  1800     else if (Gear^.dY.QWordValue <> 0) then
       
  1801         DeleteCI(Gear)
       
  1802 end;
       
  1803 
       
  1804 ////////////////////////////////////////////////////////////////////////////////
       
  1805 
       
  1806 procedure doStepTarget(Gear: PGear);
       
  1807 begin
       
  1808     if (Gear^.Timer = 0) and (Gear^.Tag = 0) then
       
  1809         PlaySound(sndWarp);
       
  1810 
       
  1811     if (Gear^.Tag = 0) and (Gear^.Timer < 1000) then
       
  1812         inc(Gear^.Timer)
       
  1813     else if Gear^.Tag = 1 then
       
  1814         Gear^.Tag := 2
       
  1815     else if Gear^.Tag = 2 then
       
  1816             if Gear^.Timer > 0 then
       
  1817                 dec(Gear^.Timer)
       
  1818     else
       
  1819         begin
       
  1820         DeleteGear(Gear);
       
  1821         exit;
       
  1822         end;
       
  1823 
       
  1824     doStepCase(Gear)
       
  1825 end;
       
  1826 
       
  1827 ////////////////////////////////////////////////////////////////////////////////
       
  1828 procedure doStepIdle(Gear: PGear);
       
  1829 begin
       
  1830     AllInactive := false;
       
  1831     dec(Gear^.Timer);
       
  1832     if Gear^.Timer = 0 then
       
  1833         begin
       
  1834         DeleteGear(Gear);
       
  1835         AfterAttack
       
  1836         end
       
  1837 end;
       
  1838 
       
  1839 ////////////////////////////////////////////////////////////////////////////////
       
  1840 procedure doStepShover(Gear: PGear);
       
  1841 var
       
  1842     HHGear: PGear;
       
  1843 begin
       
  1844     HHGear := Gear^.Hedgehog^.Gear;
       
  1845     HHGear^.State := HHGear^.State or gstNoDamage;
       
  1846     DeleteCI(HHGear);
       
  1847 
       
  1848     AmmoShove(Gear, 30, 115);
       
  1849 
       
  1850     HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving;
       
  1851     Gear^.Timer := 250;
       
  1852     Gear^.doStep := @doStepIdle
       
  1853 end;
       
  1854 
       
  1855 ////////////////////////////////////////////////////////////////////////////////
       
  1856 procedure doStepWhip(Gear: PGear);
       
  1857 var
       
  1858     HHGear: PGear;
       
  1859     i: LongInt;
       
  1860 begin
       
  1861     HHGear := Gear^.Hedgehog^.Gear;
       
  1862     HHGear^.State := HHGear^.State or gstNoDamage;
       
  1863     DeleteCI(HHGear);
       
  1864 
       
  1865     for i:= 0 to 3 do
       
  1866         begin
       
  1867         AmmoShove(Gear, 30, 25);
       
  1868         Gear^.X := Gear^.X + Gear^.dX * 5
       
  1869         end;
       
  1870 
       
  1871     HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving;
       
  1872 
       
  1873     Gear^.Timer := 250;
       
  1874     Gear^.doStep := @doStepIdle
       
  1875 end;
       
  1876 
       
  1877 ////////////////////////////////////////////////////////////////////////////////
       
  1878 procedure doStepFlame(Gear: PGear);
       
  1879 var
       
  1880     gX,gY,i: LongInt;
       
  1881     sticky: Boolean;
       
  1882     vgt: PVisualGear;
       
  1883     tdX,tdY: HWFloat;
       
  1884 begin
       
  1885     sticky:= (Gear^.State and gsttmpFlag) <> 0;
       
  1886     if not sticky then AllInactive := false;
       
  1887 
       
  1888     if TestCollisionYwithGear(Gear, 1) = 0 then
       
  1889         begin
       
  1890         AllInactive := false;
       
  1891 
       
  1892         if ((GameTicks mod 100) = 0) then
       
  1893             begin
       
  1894             vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire, gstTmpFlag);
       
  1895             if vgt <> nil then
       
  1896                 begin
       
  1897                 vgt^.dx:= 0;
       
  1898                 vgt^.dy:= 0;
       
  1899                 vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2);
       
  1900                 end;
       
  1901             end;
       
  1902 
       
  1903 
       
  1904         if Gear^.dX.QWordValue > _0_01.QWordValue then
       
  1905             Gear^.dX := Gear^.dX * _0_995;
       
  1906 
       
  1907         Gear^.dY := Gear^.dY + cGravity;
       
  1908         // if sticky then Gear^.dY := Gear^.dY + cGravity;
       
  1909 
       
  1910         if Gear^.dY.QWordValue > _0_2.QWordValue then
       
  1911             Gear^.dY := Gear^.dY * _0_995;
       
  1912 
       
  1913         //if sticky then Gear^.X := Gear^.X + Gear^.dX else
       
  1914         Gear^.X := Gear^.X + Gear^.dX + cWindSpeed * 640;
       
  1915         Gear^.Y := Gear^.Y + Gear^.dY;
       
  1916 
       
  1917         if (hwRound(Gear^.Y) > cWaterLine) then
       
  1918             begin
       
  1919             gX := hwRound(Gear^.X);
       
  1920             for i:= 0 to 3 do
       
  1921                 AddVisualGear(gX - 16 + Random(32), cWaterLine - 16 + Random(16), vgtSteam);
       
  1922             PlaySound(sndVaporize);
       
  1923             DeleteGear(Gear);
       
  1924             exit
       
  1925             end
       
  1926         end
       
  1927     else
       
  1928         begin
       
  1929         if sticky then
       
  1930             begin
       
  1931             Gear^.Radius := 7;
       
  1932             tdX:= Gear^.dX;
       
  1933             tdY:= Gear^.dY;
       
  1934             Gear^.dX.QWordValue:= 214748365;
       
  1935             Gear^.dY.QWordValue:= 429496730;
       
  1936             Gear^.dX.isNegative:= getrandom(2)<>1;
       
  1937             Gear^.dY.isNegative:= true;
       
  1938             AmmoShove(Gear, 2, 125);
       
  1939             Gear^.dX:= tdX;
       
  1940             Gear^.dY:= tdY;
       
  1941             Gear^.Radius := 1
       
  1942             end;
       
  1943         if Gear^.Timer > 0 then
       
  1944             begin
       
  1945             dec(Gear^.Timer);
       
  1946             inc(Gear^.Damage)
       
  1947             end
       
  1948         else
       
  1949             begin
       
  1950             gX := hwRound(Gear^.X);
       
  1951             gY := hwRound(Gear^.Y);
       
  1952             // Standard fire
       
  1953             if not sticky then
       
  1954                 begin
       
  1955                 if ((GameTicks and $1) = 0) then
       
  1956                     begin
       
  1957                     Gear^.Radius := 7;
       
  1958                     tdX:= Gear^.dX;
       
  1959                     tdY:= Gear^.dY;
       
  1960                     Gear^.dX.QWordValue:= 214748365;
       
  1961                     Gear^.dY.QWordValue:= 429496730;
       
  1962                     Gear^.dX.isNegative:= getrandom(2)<>1;
       
  1963                     Gear^.dY.isNegative:= true;
       
  1964                     AmmoShove(Gear, 6, 100);
       
  1965                     Gear^.dX:= tdX;
       
  1966                     Gear^.dY:= tdY;
       
  1967                     Gear^.Radius := 1;
       
  1968                     end
       
  1969                 else if ((GameTicks and $3) = 3) then
       
  1970                     doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage);
       
  1971                 //DrawExplosion(gX, gY, 4);
       
  1972 
       
  1973                 if ((GameTicks and $7) = 0) and (Random(2) = 0) then
       
  1974                     for i:= Random(2) downto 0 do
       
  1975                         AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  1976 
       
  1977                 if Gear^.Health > 0 then
       
  1978                     dec(Gear^.Health);
       
  1979                 Gear^.Timer := 450 - Gear^.Tag * 8
       
  1980                 end
       
  1981             else
       
  1982                 begin
       
  1983                 // Modified fire
       
  1984                 if ((GameTicks and $7FF) = 0) and ((GameFlags and gfSolidLand) = 0) then
       
  1985                     begin
       
  1986                     DrawExplosion(gX, gY, 4);
       
  1987 
       
  1988                     for i:= Random(3) downto 0 do
       
  1989                         AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  1990                     end;
       
  1991 
       
  1992 // This one is interesting.  I think I understand the purpose, but I wonder if a bit more fuzzy of kicking could be done with getrandom.
       
  1993                 Gear^.Timer := 100 - Gear^.Tag * 3;
       
  1994                 if (Gear^.Damage > 3000+Gear^.Tag*1500) then
       
  1995                     Gear^.Health := 0
       
  1996                 end
       
  1997             end
       
  1998         end;
       
  1999     if Gear^.Health = 0 then
       
  2000         begin
       
  2001         gX := hwRound(Gear^.X);
       
  2002         gY := hwRound(Gear^.Y);
       
  2003         if not sticky then
       
  2004             begin
       
  2005             if ((GameTicks and $3) = 0) and (Random(1) = 0) then
       
  2006                 for i:= Random(2) downto 0 do
       
  2007                     AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  2008             end
       
  2009         else
       
  2010             for i:= Random(3) downto 0 do
       
  2011                 AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  2012 
       
  2013         DeleteGear(Gear)
       
  2014         end;
       
  2015 end;
       
  2016 
       
  2017 ////////////////////////////////////////////////////////////////////////////////
       
  2018 procedure doStepFirePunchWork(Gear: PGear);
       
  2019 var
       
  2020     HHGear: PGear;
       
  2021 begin
       
  2022     AllInactive := false;
       
  2023     if ((Gear^.Message and gmDestroy) <> 0) then
       
  2024         begin
       
  2025         DeleteGear(Gear);
       
  2026         AfterAttack;
       
  2027         exit
       
  2028         end;
       
  2029 
       
  2030     HHGear := Gear^.Hedgehog^.Gear;
       
  2031     if hwRound(HHGear^.Y) <= Gear^.Tag - 2 then
       
  2032         begin
       
  2033         Gear^.Tag := hwRound(HHGear^.Y);
       
  2034         DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y - _1, _0_5, _0, cHHRadius * 4, 2);
       
  2035         HHGear^.State := HHGear^.State or gstNoDamage;
       
  2036         Gear^.Y := HHGear^.Y;
       
  2037         AmmoShove(Gear, 30, 40);
       
  2038         HHGear^.State := HHGear^.State and (not gstNoDamage)
       
  2039         end;
       
  2040 
       
  2041     HHGear^.dY := HHGear^.dY + cGravity;
       
  2042     if not (HHGear^.dY.isNegative) then
       
  2043         begin
       
  2044         HHGear^.State := HHGear^.State or gstMoving;
       
  2045         DeleteGear(Gear);
       
  2046         AfterAttack;
       
  2047         exit
       
  2048         end;
       
  2049 
       
  2050     if CheckLandValue(hwRound(HHGear^.X), hwRound(HHGear^.Y + HHGear^.dY + SignAs(_6,Gear^.dY)),
       
  2051         lfIndestructible) then
       
  2052             HHGear^.Y := HHGear^.Y + HHGear^.dY
       
  2053 end;
       
  2054 
       
  2055 procedure doStepFirePunch(Gear: PGear);
       
  2056 var
       
  2057     HHGear: PGear;
       
  2058 begin
       
  2059     AllInactive := false;
       
  2060     HHGear := Gear^.Hedgehog^.Gear;
       
  2061     DeleteCI(HHGear);
       
  2062     //HHGear^.X := int2hwFloat(hwRound(HHGear^.X)) - _0_5; WTF?
       
  2063     HHGear^.dX := SignAs(cLittle, Gear^.dX);
       
  2064 
       
  2065     HHGear^.dY := - _0_3;
       
  2066 
       
  2067     Gear^.X := HHGear^.X;
       
  2068     Gear^.dX := SignAs(_0_45, Gear^.dX);
       
  2069     Gear^.dY := - _0_9;
       
  2070     Gear^.doStep := @doStepFirePunchWork;
       
  2071     DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y + _1, _0_5, _0, cHHRadius * 4, 5);
       
  2072 
       
  2073     PlaySoundV(TSound(ord(sndFirePunch1) + GetRandom(6)), HHGear^.Hedgehog^.Team^.voicepack)
       
  2074 end;
       
  2075 
       
  2076 ////////////////////////////////////////////////////////////////////////////////
       
  2077 
       
  2078 procedure doStepParachuteWork(Gear: PGear);
       
  2079 var
       
  2080     HHGear: PGear;
       
  2081 begin
       
  2082     HHGear := Gear^.Hedgehog^.Gear;
       
  2083 
       
  2084     inc(Gear^.Timer);
       
  2085 
       
  2086     if (TestCollisionYwithGear(HHGear, 1) <> 0)
       
  2087     or ((HHGear^.State and gstHHDriven) = 0)
       
  2088     or CheckGearDrowning(HHGear)
       
  2089     or ((Gear^.Message and gmAttack) <> 0) then
       
  2090         begin
       
  2091         with HHGear^ do
       
  2092             begin
       
  2093             Message := 0;
       
  2094             SetLittle(dX);
       
  2095             dY := _0;
       
  2096             State := State or gstMoving;
       
  2097             end;
       
  2098         DeleteGear(Gear);
       
  2099         isCursorVisible := false;
       
  2100         ApplyAmmoChanges(HHGear^.Hedgehog^);
       
  2101         exit
       
  2102         end;
       
  2103 
       
  2104     HHGear^.X := HHGear^.X + cWindSpeed * 200;
       
  2105 
       
  2106     if (Gear^.Message and gmLeft) <> 0 then
       
  2107         HHGear^.X := HHGear^.X - cMaxWindSpeed * 80
       
  2108 
       
  2109     else if (Gear^.Message and gmRight) <> 0 then
       
  2110         HHGear^.X := HHGear^.X + cMaxWindSpeed * 80;
       
  2111 
       
  2112     if (Gear^.Message and gmUp) <> 0 then
       
  2113         HHGear^.Y := HHGear^.Y - cGravity * 40
       
  2114 
       
  2115     else if (Gear^.Message and gmDown) <> 0 then
       
  2116         HHGear^.Y := HHGear^.Y + cGravity * 40;
       
  2117 
       
  2118     // don't drift into obstacles
       
  2119     if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
       
  2120         HHGear^.X := HHGear^.X - int2hwFloat(hwSign(HHGear^.dX));
       
  2121     HHGear^.Y := HHGear^.Y + cGravity * 100;
       
  2122     Gear^.X := HHGear^.X;
       
  2123     Gear^.Y := HHGear^.Y
       
  2124 end;
       
  2125 
       
  2126 procedure doStepParachute(Gear: PGear);
       
  2127 var
       
  2128     HHGear: PGear;
       
  2129 begin
       
  2130     HHGear := Gear^.Hedgehog^.Gear;
       
  2131 
       
  2132     DeleteCI(HHGear);
       
  2133 
       
  2134     AfterAttack;
       
  2135 
       
  2136     HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked or gstMoving));
       
  2137     HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2138 
       
  2139     Gear^.doStep := @doStepParachuteWork;
       
  2140 
       
  2141     Gear^.Message := HHGear^.Message;
       
  2142     doStepParachuteWork(Gear)
       
  2143 end;
       
  2144 
       
  2145 ////////////////////////////////////////////////////////////////////////////////
       
  2146 procedure doStepAirAttackWork(Gear: PGear);
       
  2147 begin
       
  2148     AllInactive := false;
       
  2149     Gear^.X := Gear^.X + cAirPlaneSpeed * Gear^.Tag;
       
  2150 
       
  2151     if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then
       
  2152         begin
       
  2153         dec(Gear^.Health);
       
  2154             case Gear^.State of
       
  2155                 0: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0);
       
  2156                 1: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine,    0, cBombsSpeed * Gear^.Tag, _0, 0);
       
  2157                 2: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0);
       
  2158                 3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed * Gear^.Tag, _0, Gear^.Timer + 1);
       
  2159             //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed *
       
  2160             //                 Gear^.Tag, _0, 5000);
       
  2161             end;
       
  2162         Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag);
       
  2163         StopSoundChan(Gear^.SoundChannel, 4000);
       
  2164         end;
       
  2165 
       
  2166     if (GameTicks and $3F) = 0 then
       
  2167         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
       
  2168 
       
  2169     if (hwRound(Gear^.X) > (max(LAND_WIDTH,4096)+2048)) or (hwRound(Gear^.X) < -2048) then
       
  2170         begin
       
  2171         // avoid to play forever (is this necessary?)
       
  2172         StopSoundChan(Gear^.SoundChannel);
       
  2173         DeleteGear(Gear)
       
  2174         end;
       
  2175 end;
       
  2176 
       
  2177 procedure doStepAirAttack(Gear: PGear);
       
  2178 begin
       
  2179     AllInactive := false;
       
  2180 
       
  2181     if Gear^.X.QWordValue = 0 then
       
  2182         begin
       
  2183         Gear^.Tag :=  1;
       
  2184         Gear^.X := -_2048;
       
  2185         end
       
  2186     else
       
  2187         begin
       
  2188         Gear^.Tag := -1;
       
  2189         Gear^.X := int2hwFloat(max(LAND_WIDTH,4096) + 2048);
       
  2190         end;
       
  2191 
       
  2192     Gear^.Y := int2hwFloat(topY-300);
       
  2193     Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15);
       
  2194 
       
  2195     // calcs for Napalm Strike, so that it will hit the target (without wind at least :P)
       
  2196     if (Gear^.State = 2) then
       
  2197         Gear^.dX := Gear^.dX - cBombsSpeed * Gear^.Tag * 900
       
  2198     // calcs for regular falling gears
       
  2199     else if (int2hwFloat(Gear^.Target.Y) - Gear^.Y > _0) then
       
  2200             Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 /
       
  2201                 cGravity) * Gear^.Tag;
       
  2202 
       
  2203     Gear^.Health := 6;
       
  2204     Gear^.doStep := @doStepAirAttackWork;
       
  2205     Gear^.SoundChannel := LoopSound(sndPlane, 4000);
       
  2206 
       
  2207 end;
       
  2208 
       
  2209 ////////////////////////////////////////////////////////////////////////////////
       
  2210 
       
  2211 procedure doStepAirBomb(Gear: PGear);
       
  2212 begin
       
  2213     AllInactive := false;
       
  2214     doStepFallingGear(Gear);
       
  2215     if (Gear^.State and gstCollision) <> 0 then
       
  2216         begin
       
  2217         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  2218         DeleteGear(Gear);
       
  2219         with mobileRecord do
       
  2220             if (performRumble <> nil) and (not fastUntilLag) then
       
  2221                 performRumble(kSystemSoundID_Vibrate);
       
  2222         exit
       
  2223         end;
       
  2224     if (GameTicks and $3F) = 0 then
       
  2225         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
  2226 end;
       
  2227 
       
  2228 ////////////////////////////////////////////////////////////////////////////////
       
  2229 
       
  2230 procedure doStepGirder(Gear: PGear);
       
  2231 var
       
  2232     HHGear: PGear;
       
  2233     x, y, tx, ty: hwFloat;
       
  2234 begin
       
  2235     AllInactive := false;
       
  2236 
       
  2237     HHGear := Gear^.Hedgehog^.Gear;
       
  2238     tx := int2hwFloat(Gear^.Target.X);
       
  2239     ty := int2hwFloat(Gear^.Target.Y);
       
  2240     x := HHGear^.X;
       
  2241     y := HHGear^.Y;
       
  2242 
       
  2243     if (Distance(tx - x, ty - y) > _256)
       
  2244     or (not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprAmGirder].Width div 2, Gear^.Target.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true, false)) then
       
  2245         begin
       
  2246         PlaySound(sndDenied);
       
  2247         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2248         HHGear^.State := HHGear^.State and (not gstAttacking);
       
  2249         HHGear^.State := HHGear^.State or gstHHChooseTarget;
       
  2250         isCursorVisible := true;
       
  2251         DeleteGear(Gear)
       
  2252         end
       
  2253     else
       
  2254         begin
       
  2255         PlaySound(sndPlaced);
       
  2256         DeleteGear(Gear);
       
  2257         AfterAttack;
       
  2258         end;
       
  2259 
       
  2260     HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked));
       
  2261     HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2262 end;
       
  2263 
       
  2264 ////////////////////////////////////////////////////////////////////////////////
       
  2265 procedure doStepTeleportAfter(Gear: PGear);
       
  2266 var
       
  2267     HHGear: PGear;
       
  2268 begin
       
  2269     HHGear := Gear^.Hedgehog^.Gear;
       
  2270     doStepHedgehogMoving(HHGear);
       
  2271     // if not infattack mode wait for hedgehog finish falling to collect cases
       
  2272     if ((GameFlags and gfInfAttack) <> 0)
       
  2273     or ((HHGear^.State and gstMoving) = 0)
       
  2274     or (Gear^.Hedgehog^.Gear^.Damage > 0)
       
  2275     or ((HHGear^.State and gstDrowning) = 1) then
       
  2276         begin
       
  2277         DeleteGear(Gear);
       
  2278         AfterAttack
       
  2279         end
       
  2280 end;
       
  2281 
       
  2282 procedure doStepTeleportAnim(Gear: PGear);
       
  2283 begin
       
  2284     if (Gear^.Hedgehog^.Gear^.Damage > 0) then
       
  2285         begin
       
  2286         DeleteGear(Gear);
       
  2287         AfterAttack;
       
  2288         end;
       
  2289     inc(Gear^.Timer);
       
  2290     if Gear^.Timer = 65 then
       
  2291         begin
       
  2292         Gear^.Timer := 0;
       
  2293         inc(Gear^.Pos);
       
  2294         if Gear^.Pos = 11 then
       
  2295             Gear^.doStep := @doStepTeleportAfter
       
  2296         end;
       
  2297 end;
       
  2298 
       
  2299 procedure doStepTeleport(Gear: PGear);
       
  2300 var
       
  2301     HHGear: PGear;
       
  2302 begin
       
  2303     AllInactive := false;
       
  2304 
       
  2305     HHGear := Gear^.Hedgehog^.Gear;
       
  2306     if not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprHHTelepMask].Width div 2,
       
  2307         Gear^.Target.Y - SpritesData[sprHHTelepMask].Height div 2,
       
  2308         sprHHTelepMask, 0, false, false) then
       
  2309         begin
       
  2310         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2311         HHGear^.State := HHGear^.State and (not gstAttacking);
       
  2312         HHGear^.State := HHGear^.State or gstHHChooseTarget;
       
  2313         DeleteGear(Gear);
       
  2314         isCursorVisible := true;
       
  2315         PlaySound(sndDenied)
       
  2316         end
       
  2317     else
       
  2318         begin
       
  2319         DeleteCI(HHGear);
       
  2320         SetAllHHToActive;
       
  2321         Gear^.doStep := @doStepTeleportAnim;
       
  2322 
       
  2323   // copy old HH position and direction to Gear (because we need them for drawing the vanishing hog)
       
  2324         Gear^.dX := HHGear^.dX;
       
  2325         // retrieve the cursor direction (it was previously copied to X so it doesn't get lost)
       
  2326         HHGear^.dX.isNegative := (Gear^.X.QWordValue <> 0);
       
  2327         Gear^.X := HHGear^.X;
       
  2328         Gear^.Y := HHGear^.Y;
       
  2329         HHGear^.X := int2hwFloat(Gear^.Target.X);
       
  2330         HHGear^.Y := int2hwFloat(Gear^.Target.Y);
       
  2331         HHGear^.State := HHGear^.State or gstMoving;
       
  2332         Gear^.Hedgehog^.Unplaced := false;
       
  2333         isCursorVisible := false;
       
  2334         playSound(sndWarp)
       
  2335         end;
       
  2336     Gear^.Target.X:= NoPointX
       
  2337 end;
       
  2338 
       
  2339 ////////////////////////////////////////////////////////////////////////////////
       
  2340 procedure doStepSwitcherWork(Gear: PGear);
       
  2341 var
       
  2342     HHGear: PGear;
       
  2343     hedgehog: PHedgehog;
       
  2344     State: Longword;
       
  2345 begin
       
  2346     AllInactive := false;
       
  2347 
       
  2348     if ((Gear^.Message and (not gmSwitch)) <> 0) or (TurnTimeLeft = 0) then
       
  2349         begin
       
  2350         hedgehog := Gear^.Hedgehog;
       
  2351         //Msg := Gear^.Message and (not gmSwitch);
       
  2352         DeleteGear(Gear);
       
  2353         ApplyAmmoChanges(hedgehog^);
       
  2354 
       
  2355         HHGear := CurrentHedgehog^.Gear;
       
  2356         ApplyAmmoChanges(HHGear^.Hedgehog^);
       
  2357         //HHGear^.Message := Msg;
       
  2358         exit
       
  2359         end;
       
  2360 
       
  2361     if (Gear^.Message and gmSwitch) <> 0 then
       
  2362         begin
       
  2363         HHGear := CurrentHedgehog^.Gear;
       
  2364         HHGear^.Message := HHGear^.Message and (not gmSwitch);
       
  2365         Gear^.Message := Gear^.Message and (not gmSwitch);
       
  2366         State := HHGear^.State;
       
  2367         HHGear^.State := 0;
       
  2368         HHGear^.Z := cHHZ;
       
  2369         HHGear^.Active := false;
       
  2370         HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList;
       
  2371 
       
  2372         PlaySound(sndSwitchHog);
       
  2373 
       
  2374         repeat
       
  2375             CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber);
       
  2376         until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and
       
  2377               (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0) and
       
  2378               (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen]=0);
       
  2379 
       
  2380         SwitchCurrentHedgehog(@CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]);
       
  2381         AmmoMenuInvalidated:= true;
       
  2382 
       
  2383         HHGear := CurrentHedgehog^.Gear;
       
  2384         HHGear^.State := State;
       
  2385         HHGear^.Active := true;
       
  2386         FollowGear := HHGear;
       
  2387         HHGear^.Z := cCurrHHZ;
       
  2388         HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList;
       
  2389         Gear^.X := HHGear^.X;
       
  2390         Gear^.Y := HHGear^.Y
       
  2391         end;
       
  2392 end;
       
  2393 
       
  2394 procedure doStepSwitcher(Gear: PGear);
       
  2395 var
       
  2396     HHGear: PGear;
       
  2397 begin
       
  2398     Gear^.doStep := @doStepSwitcherWork;
       
  2399 
       
  2400     HHGear := Gear^.Hedgehog^.Gear;
       
  2401     OnUsedAmmo(HHGear^.Hedgehog^);
       
  2402     with HHGear^ do
       
  2403         begin
       
  2404         State := State and (not gstAttacking);
       
  2405         Message := Message and (not gmAttack)
       
  2406         end
       
  2407 end;
       
  2408 
       
  2409 ////////////////////////////////////////////////////////////////////////////////
       
  2410 procedure doStepMortar(Gear: PGear);
       
  2411 var
       
  2412     dX, dY, gdX, gdY: hwFloat;
       
  2413     i: LongInt;
       
  2414 begin
       
  2415     AllInactive := false;
       
  2416     gdX := Gear^.dX;
       
  2417     gdY := Gear^.dY;
       
  2418 
       
  2419     doStepFallingGear(Gear);
       
  2420     if (Gear^.State and gstCollision) <> 0 then
       
  2421         begin
       
  2422         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);
       
  2423         gdX.isNegative := not gdX.isNegative;
       
  2424         gdY.isNegative := not gdY.isNegative;
       
  2425         gdX:= gdX*_0_2;
       
  2426         gdY:= gdY*_0_2;
       
  2427 
       
  2428         for i:= 0 to 4 do
       
  2429             begin
       
  2430             dX := gdX + rndSign(GetRandomf) * _0_03;
       
  2431             dY := gdY + rndSign(GetRandomf) * _0_03;
       
  2432             AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtCluster, 0, dX, dY, 25);
       
  2433             end;
       
  2434 
       
  2435         DeleteGear(Gear);
       
  2436         exit
       
  2437         end;
       
  2438 
       
  2439     if (GameTicks and $3F) = 0 then
       
  2440         begin
       
  2441         if hwRound(Gear^.Y) > cWaterLine then
       
  2442              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
  2443         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
  2444         end
       
  2445 end;
       
  2446 
       
  2447 ////////////////////////////////////////////////////////////////////////////////
       
  2448 procedure doStepKamikazeWork(Gear: PGear);
       
  2449 var
       
  2450     i: LongWord;
       
  2451     HHGear: PGear;
       
  2452     sparkles: PVisualGear;
       
  2453     hasWishes: boolean;
       
  2454 begin
       
  2455     AllInactive := false;
       
  2456     hasWishes:= ((Gear^.Message and (gmPrecise or gmSwitch)) = (gmPrecise or gmSwitch));
       
  2457     if hasWishes then
       
  2458         Gear^.AdvBounce:= 1;
       
  2459 
       
  2460     HHGear := Gear^.Hedgehog^.Gear;
       
  2461     if HHGear = nil then
       
  2462         begin
       
  2463         DeleteGear(Gear);
       
  2464         exit
       
  2465         end;
       
  2466 
       
  2467     HHGear^.State := HHGear^.State or gstNoDamage;
       
  2468     DeleteCI(HHGear);
       
  2469 
       
  2470     Gear^.X := HHGear^.X;
       
  2471     Gear^.Y := HHGear^.Y;
       
  2472     if (GameTicks mod 2 = 0) and hasWishes then
       
  2473         begin
       
  2474         sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1);
       
  2475         if sparkles <> nil then
       
  2476             begin
       
  2477             sparkles^.Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF;
       
  2478             sparkles^.Angle:= random(360);
       
  2479             end
       
  2480         end;
       
  2481 
       
  2482     i := 2;
       
  2483     repeat
       
  2484 
       
  2485         Gear^.X := Gear^.X + HHGear^.dX;
       
  2486         Gear^.Y := Gear^.Y + HHGear^.dY;
       
  2487         HHGear^.X := Gear^.X;
       
  2488         HHGear^.Y := Gear^.Y;
       
  2489 
       
  2490         inc(Gear^.Damage, 2);
       
  2491 
       
  2492         //  if TestCollisionXwithGear(HHGear, hwSign(Gear^.dX))
       
  2493         //      or TestCollisionYwithGear(HHGear, hwSign(Gear^.dY)) then inc(Gear^.Damage, 3);
       
  2494 
       
  2495         dec(i)
       
  2496     until (i = 0)
       
  2497     or (Gear^.Damage > Gear^.Health);
       
  2498 
       
  2499     inc(upd);
       
  2500     if upd > 3 then
       
  2501         begin
       
  2502         if Gear^.Health < 1500 then
       
  2503             begin
       
  2504             if Gear^.AdvBounce <> 0 then
       
  2505                 Gear^.Pos := 3
       
  2506             else
       
  2507                 Gear^.Pos := 2;
       
  2508             end;
       
  2509 
       
  2510         AmmoShove(Gear, 30, 40);
       
  2511 
       
  2512         DrawTunnel(HHGear^.X - HHGear^.dX * 10,
       
  2513                     HHGear^.Y - _2 - HHGear^.dY * 10 + hwAbs(HHGear^.dY) * 2,
       
  2514         HHGear^.dX,
       
  2515         HHGear^.dY,
       
  2516         20 + cHHRadius * 2,
       
  2517         cHHRadius * 2 + 7);
       
  2518 
       
  2519         upd := 0
       
  2520         end;
       
  2521 
       
  2522     if Gear^.Health < Gear^.Damage then
       
  2523         begin
       
  2524         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  2525         if hasWishes then
       
  2526             for i:= 0 to 31 do
       
  2527                 begin
       
  2528                 sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
       
  2529                 if sparkles <> nil then
       
  2530                     with sparkles^ do
       
  2531                         begin
       
  2532                         Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF;
       
  2533                         Angle:= random(360);
       
  2534                         dx:= 0.001 * (random(200));
       
  2535                         dy:= 0.001 * (random(200));
       
  2536                         if random(2) = 0 then
       
  2537                             dx := -dx;
       
  2538                         if random(2) = 0 then
       
  2539                             dy := -dy;
       
  2540                         FrameTicks:= random(400) + 250
       
  2541                         end
       
  2542                 end;
       
  2543         AfterAttack;
       
  2544         HHGear^.Message:= HHGear^.Message or gmDestroy;
       
  2545         DeleteGear(Gear);
       
  2546     end
       
  2547     else
       
  2548         begin
       
  2549         dec(Gear^.Health, Gear^.Damage);
       
  2550         Gear^.Damage := 0
       
  2551         end
       
  2552 end;
       
  2553 
       
  2554 procedure doStepKamikazeIdle(Gear: PGear);
       
  2555 begin
       
  2556     AllInactive := false;
       
  2557     dec(Gear^.Timer);
       
  2558     if Gear^.Timer = 0 then
       
  2559         begin
       
  2560         Gear^.Pos := 1;
       
  2561         PlaySoundV(sndKamikaze, Gear^.Hedgehog^.Team^.voicepack);
       
  2562         Gear^.doStep := @doStepKamikazeWork
       
  2563         end
       
  2564 end;
       
  2565 
       
  2566 procedure doStepKamikaze(Gear: PGear);
       
  2567 var
       
  2568     HHGear: PGear;
       
  2569 begin
       
  2570     AllInactive := false;
       
  2571 
       
  2572     HHGear := Gear^.Hedgehog^.Gear;
       
  2573 
       
  2574     HHGear^.dX := Gear^.dX;
       
  2575     HHGear^.dY := Gear^.dY;
       
  2576 
       
  2577     Gear^.dX := SignAs(_0_45, Gear^.dX);
       
  2578     Gear^.dY := - _0_9;
       
  2579 
       
  2580     Gear^.Timer := 550;
       
  2581 
       
  2582     Gear^.doStep := @doStepKamikazeIdle
       
  2583 end;
       
  2584 
       
  2585 ////////////////////////////////////////////////////////////////////////////////
       
  2586 
       
  2587 const cakeh =   27;
       
  2588 var
       
  2589     CakePoints: array[0..Pred(cakeh)] of record
       
  2590         x, y: hwFloat;
       
  2591     end;
       
  2592     CakeI: Longword;
       
  2593 
       
  2594 procedure doStepCakeExpl(Gear: PGear);
       
  2595 begin
       
  2596     AllInactive := false;
       
  2597 
       
  2598     inc(Gear^.Tag);
       
  2599     if Gear^.Tag < 2250 then
       
  2600         exit;
       
  2601 
       
  2602     doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound);
       
  2603     AfterAttack;
       
  2604     DeleteGear(Gear)
       
  2605 end;
       
  2606 
       
  2607 procedure doStepCakeDown(Gear: PGear);
       
  2608 var
       
  2609     gi: PGear;
       
  2610     dmg, dmgBase: LongInt;
       
  2611     fX, fY, tdX, tdY: hwFloat;
       
  2612 begin
       
  2613     AllInactive := false;
       
  2614 
       
  2615     inc(Gear^.Tag);
       
  2616     if Gear^.Tag < 100 then
       
  2617         exit;
       
  2618     Gear^.Tag := 0;
       
  2619 
       
  2620     if Gear^.Pos = 0 then
       
  2621         begin
       
  2622 ///////////// adapted from doMakeExplosion ///////////////////////////
       
  2623         //fX:= Gear^.X;
       
  2624         //fY:= Gear^.Y;
       
  2625         //fX.QWordValue:= fX.QWordValue and $FFFFFFFF00000000;
       
  2626         //fY.QWordValue:= fY.QWordValue and $FFFFFFFF00000000;
       
  2627         fX:= int2hwFloat(hwRound(Gear^.X));
       
  2628         fY:= int2hwFloat(hwRound(Gear^.Y));
       
  2629         dmgBase:= cakeDmg shl 1 + cHHRadius div 2;
       
  2630         gi := GearsList;
       
  2631         while gi <> nil do
       
  2632             begin
       
  2633             if gi^.Kind = gtHedgehog then
       
  2634                 begin
       
  2635                 dmg:= 0;
       
  2636                 tdX:= gi^.X-fX;
       
  2637                 tdY:= gi^.Y-fY;
       
  2638                 if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then
       
  2639                     dmg:= dmgBase - max(hwRound(Distance(tdX, tdY)),gi^.Radius);
       
  2640                 if (dmg > 1) then dmg:= ModifyDamage(min(dmg div 2, cakeDmg), gi);
       
  2641                 if (dmg > 1) then
       
  2642                     if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then
       
  2643                         gi^.State := gi^.State or gstLoser
       
  2644                     else
       
  2645                         gi^.State := gi^.State or gstWinner;
       
  2646                 end;
       
  2647             gi := gi^.NextGear
       
  2648             end;
       
  2649 //////////////////////////////////////////////////////////////////////
       
  2650         Gear^.doStep := @doStepCakeExpl;
       
  2651         PlaySound(sndCake)
       
  2652         end
       
  2653     else dec(Gear^.Pos)
       
  2654 end;
       
  2655 
       
  2656 
       
  2657 procedure doStepCakeWork(Gear: PGear);
       
  2658 var
       
  2659     tdx, tdy: hwFloat;
       
  2660 begin
       
  2661     AllInactive := false;
       
  2662 
       
  2663     inc(Gear^.Tag);
       
  2664     if Gear^.Tag < 7 then
       
  2665         exit;
       
  2666 
       
  2667     dec(Gear^.Health);
       
  2668     Gear^.Timer := Gear^.Health*10;
       
  2669     if Gear^.Health mod 100 = 0 then
       
  2670         Gear^.PortalCounter:= 0;
       
  2671     // This is not seconds, but at least it is *some* feedback
       
  2672     if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then
       
  2673         begin
       
  2674         FollowGear := Gear;
       
  2675         Gear^.RenderTimer := false;
       
  2676         Gear^.doStep := @doStepCakeDown;
       
  2677         exit
       
  2678         end;
       
  2679 
       
  2680     cakeStep(Gear);
       
  2681 
       
  2682     if Gear^.Tag = 0 then
       
  2683         begin
       
  2684         CakeI := (CakeI + 1) mod cakeh;
       
  2685         tdx := CakePoints[CakeI].x - Gear^.X;
       
  2686         tdy := - CakePoints[CakeI].y + Gear^.Y;
       
  2687         CakePoints[CakeI].x := Gear^.X;
       
  2688         CakePoints[CakeI].y := Gear^.Y;
       
  2689         Gear^.DirAngle := DxDy2Angle(tdx, tdy);
       
  2690         end;
       
  2691 end;
       
  2692 
       
  2693 procedure doStepCakeUp(Gear: PGear);
       
  2694 var
       
  2695     i: Longword;
       
  2696 begin
       
  2697     AllInactive := false;
       
  2698 
       
  2699     inc(Gear^.Tag);
       
  2700     if Gear^.Tag < 100 then
       
  2701         exit;
       
  2702     Gear^.Tag := 0;
       
  2703 
       
  2704     if Gear^.Pos = 6 then
       
  2705         begin
       
  2706         for i:= 0 to Pred(cakeh) do
       
  2707             begin
       
  2708             CakePoints[i].x := Gear^.X;
       
  2709             CakePoints[i].y := Gear^.Y
       
  2710             end;
       
  2711         CakeI := 0;
       
  2712         Gear^.doStep := @doStepCakeWork
       
  2713         end
       
  2714     else
       
  2715         inc(Gear^.Pos)
       
  2716 end;
       
  2717 
       
  2718 procedure doStepCakeFall(Gear: PGear);
       
  2719 begin
       
  2720     AllInactive := false;
       
  2721 
       
  2722     Gear^.dY := Gear^.dY + cGravity;
       
  2723     if TestCollisionYwithGear(Gear, 1) <> 0 then
       
  2724         Gear^.doStep := @doStepCakeUp
       
  2725     else
       
  2726         begin
       
  2727         Gear^.Y := Gear^.Y + Gear^.dY;
       
  2728         if CheckGearDrowning(Gear) then
       
  2729             AfterAttack
       
  2730         end
       
  2731 end;
       
  2732 
       
  2733 procedure doStepCake(Gear: PGear);
       
  2734 var
       
  2735     HHGear: PGear;
       
  2736 begin
       
  2737     AllInactive := false;
       
  2738 
       
  2739     HHGear := Gear^.Hedgehog^.Gear;
       
  2740     HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2741     Gear^.CollisionMask:= lfNotCurrentMask;
       
  2742 
       
  2743     FollowGear := Gear;
       
  2744 
       
  2745     Gear^.doStep := @doStepCakeFall
       
  2746 end;
       
  2747 
       
  2748 ////////////////////////////////////////////////////////////////////////////////
       
  2749 procedure doStepSeductionWork(Gear: PGear);
       
  2750 var i: LongInt;
       
  2751     hogs: PGearArrayS;
       
  2752 begin
       
  2753     AllInactive := false;
       
  2754     hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius);
       
  2755     if hogs.size > 0 then
       
  2756         begin
       
  2757         for i:= 0 to hogs.size - 1 do
       
  2758             with hogs.ar^[i]^ do
       
  2759                 begin
       
  2760                 if hogs.ar^[i] <> CurrentHedgehog^.Gear then
       
  2761                     begin
       
  2762                     dX:= _50 * cGravity * (Gear^.X - X) / _25;
       
  2763                     dY:= -_450 * cGravity;
       
  2764                     Active:= true;
       
  2765                     end
       
  2766                 end;
       
  2767         end ;
       
  2768         AfterAttack;
       
  2769         DeleteGear(Gear);
       
  2770 (*
       
  2771     Gear^.X := Gear^.X + Gear^.dX;
       
  2772     Gear^.Y := Gear^.Y + Gear^.dY;
       
  2773     x := hwRound(Gear^.X);
       
  2774     y := hwRound(Gear^.Y);
       
  2775 
       
  2776     if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
       
  2777         if (Land[y, x] <> 0) then
       
  2778             begin
       
  2779             Gear^.dX.isNegative := not Gear^.dX.isNegative;
       
  2780             Gear^.dY.isNegative := not Gear^.dY.isNegative;
       
  2781             Gear^.dX := Gear^.dX * _1_5;
       
  2782             Gear^.dY := Gear^.dY * _1_5 - _0_3;
       
  2783             AmmoShove(Gear, 0, 40);
       
  2784             AfterAttack;
       
  2785             DeleteGear(Gear)
       
  2786             end
       
  2787         else
       
  2788     else
       
  2789         begin
       
  2790         AfterAttack;
       
  2791         DeleteGear(Gear)
       
  2792         end*)
       
  2793 end;
       
  2794 
       
  2795 procedure doStepSeductionWear(Gear: PGear);
       
  2796 var heart: PVisualGear;
       
  2797 begin
       
  2798     AllInactive := false;
       
  2799     inc(Gear^.Timer);
       
  2800     if Gear^.Timer > 250 then
       
  2801         begin
       
  2802         Gear^.Timer := 0;
       
  2803         inc(Gear^.Pos);
       
  2804         if Gear^.Pos = 5 then
       
  2805             PlaySoundV(sndYoohoo, Gear^.Hedgehog^.Team^.voicepack)
       
  2806         end;
       
  2807 
       
  2808     if (Gear^.Pos = 14) and (RealTicks and $3 = 0) then
       
  2809         begin
       
  2810         heart:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
       
  2811         if heart <> nil then
       
  2812             with heart^ do
       
  2813                 begin
       
  2814                 dx:= 0.001 * (random(200));
       
  2815                 dy:= 0.001 * (random(200));
       
  2816                 if random(2) = 0 then
       
  2817                     dx := -dx;
       
  2818                 if random(2) = 0 then
       
  2819                     dy := -dy;
       
  2820                 FrameTicks:= random(750) + 1000;
       
  2821                 State:= ord(sprSeduction)
       
  2822                 end;
       
  2823         end;
       
  2824 
       
  2825     if Gear^.Pos = 15 then
       
  2826         Gear^.doStep := @doStepSeductionWork
       
  2827 end;
       
  2828 
       
  2829 procedure doStepSeduction(Gear: PGear);
       
  2830 begin
       
  2831     AllInactive := false;
       
  2832     //DeleteCI(Gear^.Hedgehog^.Gear);
       
  2833     Gear^.doStep := @doStepSeductionWear
       
  2834 end;
       
  2835 
       
  2836 ////////////////////////////////////////////////////////////////////////////////
       
  2837 procedure doStepWaterUp(Gear: PGear);
       
  2838 var
       
  2839     i: LongWord;
       
  2840 begin
       
  2841     if (Gear^.Tag = 0)
       
  2842     or (cWaterLine = 0) then
       
  2843         begin
       
  2844         DeleteGear(Gear);
       
  2845         exit
       
  2846         end;
       
  2847 
       
  2848     AllInactive := false;
       
  2849 
       
  2850     inc(Gear^.Timer);
       
  2851     if Gear^.Timer = 17 then
       
  2852         Gear^.Timer := 0
       
  2853     else
       
  2854         exit;
       
  2855 
       
  2856     if cWaterLine > 0 then
       
  2857         begin
       
  2858         dec(cWaterLine);
       
  2859         for i:= 0 to LAND_WIDTH - 1 do
       
  2860             Land[cWaterLine, i] := 0;
       
  2861         SetAllToActive
       
  2862         end;
       
  2863 
       
  2864     dec(Gear^.Tag);
       
  2865 end;
       
  2866 
       
  2867 ////////////////////////////////////////////////////////////////////////////////
       
  2868 procedure doStepDrill(Gear: PGear);
       
  2869 forward;
       
  2870 
       
  2871 procedure doStepDrillDrilling(Gear: PGear);
       
  2872 var
       
  2873     t: PGearArray;
       
  2874     tempColl: Word;
       
  2875 begin
       
  2876     AllInactive := false;
       
  2877     if (Gear^.Timer > 0) and (Gear^.Timer mod 10 <> 0) then
       
  2878         begin
       
  2879         dec(Gear^.Timer);
       
  2880         exit;
       
  2881         end;
       
  2882 
       
  2883     DrawTunnel(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 2, 6);
       
  2884     Gear^.X := Gear^.X + Gear^.dX;
       
  2885     Gear^.Y := Gear^.Y + Gear^.dY;
       
  2886     if (Gear^.Timer mod 30) = 0 then
       
  2887         AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust);
       
  2888     if (CheckGearDrowning(Gear)) then
       
  2889         begin
       
  2890         StopSoundChan(Gear^.SoundChannel);
       
  2891         exit
       
  2892     end;
       
  2893 
       
  2894     tempColl:= Gear^.CollisionMask;
       
  2895     Gear^.CollisionMask:= $007F;
       
  2896     if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) <> 0) or TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) or (GameTicks > Gear^.FlightTime) then
       
  2897         t := CheckGearsCollision(Gear)
       
  2898     else t := nil;
       
  2899     Gear^.CollisionMask:= tempColl;
       
  2900     //fixes drill not exploding when touching HH bug
       
  2901 
       
  2902     if (Gear^.Timer = 0) or ((t <> nil) and (t^.Count <> 0))
       
  2903     or ( ((Gear^.State and gsttmpFlag) = 0) and (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))))
       
  2904 // CheckLandValue returns true if the type isn't matched
       
  2905     or (not CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y), lfIndestructible)) then
       
  2906         begin
       
  2907         //out of time or exited ground
       
  2908         StopSoundChan(Gear^.SoundChannel);
       
  2909         if (Gear^.State and gsttmpFlag) <> 0 then
       
  2910             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
       
  2911         else
       
  2912             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  2913         DeleteGear(Gear);
       
  2914         exit
       
  2915         end
       
  2916 
       
  2917     else if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))) then
       
  2918         begin
       
  2919         StopSoundChan(Gear^.SoundChannel);
       
  2920         Gear^.Tag := 1;
       
  2921         Gear^.doStep := @doStepDrill
       
  2922         end;
       
  2923 
       
  2924     dec(Gear^.Timer);
       
  2925 end;
       
  2926 
       
  2927 procedure doStepDrill(Gear: PGear);
       
  2928 var
       
  2929     t: PGearArray;
       
  2930     oldDx, oldDy: hwFloat;
       
  2931     t2: hwFloat;
       
  2932 begin
       
  2933     AllInactive := false;
       
  2934 
       
  2935     if (Gear^.State and gsttmpFlag) = 0 then
       
  2936         Gear^.dX := Gear^.dX + cWindSpeed;
       
  2937 
       
  2938     oldDx := Gear^.dX;
       
  2939     oldDy := Gear^.dY;
       
  2940 
       
  2941     doStepFallingGear(Gear);
       
  2942 
       
  2943     if (GameTicks and $3F) = 0 then
       
  2944         begin
       
  2945         if hwRound(Gear^.Y) > cWaterLine then
       
  2946              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
  2947         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
  2948         end;
       
  2949 
       
  2950     if ((Gear^.State and gstCollision) <> 0) then
       
  2951         begin
       
  2952         //hit
       
  2953         Gear^.dX := oldDx;
       
  2954         Gear^.dY := oldDy;
       
  2955 
       
  2956         if GameTicks > Gear^.FlightTime then
       
  2957             t := CheckGearsCollision(Gear)
       
  2958         else
       
  2959             t := nil;
       
  2960         if (t = nil) or (t^.Count = 0) then
       
  2961             begin
       
  2962             //hit the ground not the HH
       
  2963             t2 := _0_5 / Distance(Gear^.dX, Gear^.dY);
       
  2964             Gear^.dX := Gear^.dX * t2;
       
  2965             Gear^.dY := Gear^.dY * t2;
       
  2966             end
       
  2967 
       
  2968         else if (t <> nil) then
       
  2969             begin
       
  2970             //explode right on contact with HH
       
  2971             if (Gear^.State and gsttmpFlag) <> 0 then
       
  2972                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
       
  2973             else
       
  2974                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  2975             DeleteGear(Gear);
       
  2976             exit;
       
  2977             end;
       
  2978 
       
  2979         Gear^.SoundChannel := LoopSound(sndDrillRocket);
       
  2980         Gear^.doStep := @doStepDrillDrilling;
       
  2981 
       
  2982         if (Gear^.State and gsttmpFlag) <> 0 then
       
  2983             gear^.RenderTimer:= true;
       
  2984         if Gear^.Timer > 0 then dec(Gear^.Timer)
       
  2985         end
       
  2986     else if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Tag <> 0) then
       
  2987         begin
       
  2988         if Gear^.Timer > 0 then
       
  2989             dec(Gear^.Timer)
       
  2990         else
       
  2991             begin
       
  2992             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  2993             DeleteGear(Gear);
       
  2994             end
       
  2995         end;
       
  2996 end;
       
  2997 
       
  2998 ////////////////////////////////////////////////////////////////////////////////
       
  2999 procedure doStepBallgunWork(Gear: PGear);
       
  3000 var
       
  3001     HHGear, ball: PGear;
       
  3002     rx, ry: hwFloat;
       
  3003     gX, gY: LongInt;
       
  3004 begin
       
  3005     AllInactive := false;
       
  3006     dec(Gear^.Timer);
       
  3007     HHGear := Gear^.Hedgehog^.Gear;
       
  3008     HedgehogChAngle(HHGear);
       
  3009     gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
       
  3010     gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
       
  3011     if (Gear^.Timer mod 100) = 0 then
       
  3012         begin
       
  3013         rx := rndSign(getRandomf * _0_1);
       
  3014         ry := rndSign(getRandomf * _0_1);
       
  3015 
       
  3016         ball:= AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0);
       
  3017         ball^.CollisionMask:= lfNotCurrentMask;
       
  3018 
       
  3019         PlaySound(sndGun);
       
  3020         end;
       
  3021 
       
  3022     if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then
       
  3023         begin
       
  3024         DeleteGear(Gear);
       
  3025         AfterAttack
       
  3026         end
       
  3027 end;
       
  3028 
       
  3029 procedure doStepBallgun(Gear: PGear);
       
  3030 var
       
  3031     HHGear: PGear;
       
  3032 begin
       
  3033     HHGear := Gear^.Hedgehog^.Gear;
       
  3034     HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown));
       
  3035     HHGear^.State := HHGear^.State or gstNotKickable;
       
  3036     Gear^.doStep := @doStepBallgunWork
       
  3037 end;
       
  3038 
       
  3039 ////////////////////////////////////////////////////////////////////////////////
       
  3040 procedure doStepRCPlaneWork(Gear: PGear);
       
  3041 
       
  3042 const cAngleSpeed =   3;
       
  3043 var
       
  3044     HHGear: PGear;
       
  3045     i: LongInt;
       
  3046     dX, dY: hwFloat;
       
  3047     fChanged: boolean;
       
  3048     trueAngle: Longword;
       
  3049     t: PGear;
       
  3050 begin
       
  3051     AllInactive := false;
       
  3052 
       
  3053     HHGear := Gear^.Hedgehog^.Gear;
       
  3054     FollowGear := Gear;
       
  3055 
       
  3056     if Gear^.Timer > 0 then
       
  3057         dec(Gear^.Timer);
       
  3058 
       
  3059     fChanged := false;
       
  3060     if ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then
       
  3061         begin
       
  3062         fChanged := true;
       
  3063         if Gear^.Angle > 2048 then
       
  3064             dec(Gear^.Angle)
       
  3065         else if Gear^.Angle < 2048 then
       
  3066             inc(Gear^.Angle)
       
  3067         else fChanged := false
       
  3068     end
       
  3069     else
       
  3070         begin
       
  3071         if ((Gear^.Message and gmLeft) <> 0) then
       
  3072             begin
       
  3073             fChanged := true;
       
  3074             Gear^.Angle := (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096
       
  3075             end;
       
  3076 
       
  3077         if ((Gear^.Message and gmRight) <> 0) then
       
  3078             begin
       
  3079             fChanged := true;
       
  3080             Gear^.Angle := (Gear^.Angle + cAngleSpeed) mod 4096
       
  3081             end
       
  3082         end;
       
  3083 
       
  3084     if fChanged then
       
  3085         begin
       
  3086         Gear^.dX.isNegative := (Gear^.Angle > 2048);
       
  3087         if Gear^.dX.isNegative then
       
  3088             trueAngle := 4096 - Gear^.Angle
       
  3089         else
       
  3090             trueAngle := Gear^.Angle;
       
  3091 
       
  3092         Gear^.dX := SignAs(AngleSin(trueAngle), Gear^.dX) * _0_25;
       
  3093         Gear^.dY := AngleCos(trueAngle) * -_0_25;
       
  3094         end;
       
  3095 
       
  3096     Gear^.X := Gear^.X + Gear^.dX;
       
  3097     Gear^.Y := Gear^.Y + Gear^.dY;
       
  3098 
       
  3099     if (GameTicks and $FF) = 0 then
       
  3100         if Gear^.Timer < 3500 then
       
  3101             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace)
       
  3102     else
       
  3103         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
       
  3104 
       
  3105     if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then
       
  3106         begin
       
  3107         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  3108         AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, Gear^.dX * _0_5, Gear^.dY *
       
  3109         _0_5, 0);
       
  3110         dec(Gear^.Health)
       
  3111         end;
       
  3112 
       
  3113     if ((HHGear^.Message and gmLJump) <> 0) and ((Gear^.State and gsttmpFlag) = 0) then
       
  3114         begin
       
  3115         Gear^.State := Gear^.State or gsttmpFlag;
       
  3116         PauseMusic;
       
  3117         playSound(sndRideOfTheValkyries);
       
  3118         end;
       
  3119 
       
  3120     // pickup bonuses
       
  3121     t := CheckGearNear(Gear, gtCase, 36, 36);
       
  3122     if t <> nil then
       
  3123         PickUp(HHGear, t);
       
  3124 
       
  3125     CheckCollision(Gear);
       
  3126 
       
  3127     if ((Gear^.State and gstCollision) <> 0) or CheckGearDrowning(Gear) then
       
  3128         begin
       
  3129         StopSoundChan(Gear^.SoundChannel);
       
  3130         StopSound(sndRideOfTheValkyries);
       
  3131         ResumeMusic;
       
  3132 
       
  3133         if ((Gear^.State and gstCollision) <> 0) then
       
  3134             begin
       
  3135             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound);
       
  3136             for i:= 0 to 15 do
       
  3137                 begin
       
  3138                 dX := AngleCos(i * 64) * _0_5 * (GetRandomf + _1);
       
  3139                 dY := AngleSin(i * 64) * _0_5 * (GetRandomf + _1);
       
  3140                 AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, dY, 0);
       
  3141                 AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, -dY, 0);
       
  3142                 end;
       
  3143             DeleteGear(Gear)
       
  3144             end;
       
  3145 
       
  3146         AfterAttack;
       
  3147         CurAmmoGear := nil;
       
  3148         if (GameFlags and gfInfAttack) = 0 then
       
  3149             begin
       
  3150             if TagTurnTimeLeft = 0 then
       
  3151                 TagTurnTimeLeft:= TurnTimeLeft;
       
  3152 
       
  3153             TurnTimeLeft:= 14 * 125;
       
  3154             end;
       
  3155 
       
  3156         HHGear^.Message := 0;
       
  3157         ParseCommand('/taunt ' + #1, true)
       
  3158         end
       
  3159 end;
       
  3160 
       
  3161 procedure doStepRCPlane(Gear: PGear);
       
  3162 var
       
  3163     HHGear: PGear;
       
  3164 begin
       
  3165     HHGear := Gear^.Hedgehog^.Gear;
       
  3166     HHGear^.Message := 0;
       
  3167     HHGear^.State := HHGear^.State or gstNotKickable;
       
  3168     Gear^.Angle := HHGear^.Angle;
       
  3169     Gear^.Tag := hwSign(HHGear^.dX);
       
  3170 
       
  3171     if HHGear^.dX.isNegative then
       
  3172         Gear^.Angle := 4096 - Gear^.Angle;
       
  3173     Gear^.doStep := @doStepRCPlaneWork
       
  3174 end;
       
  3175 
       
  3176 ////////////////////////////////////////////////////////////////////////////////
       
  3177 procedure doStepJetpackWork(Gear: PGear);
       
  3178 var
       
  3179     HHGear: PGear;
       
  3180     fuel, i: LongInt;
       
  3181     move: hwFloat;
       
  3182     isUnderwater: Boolean;
       
  3183     bubble: PVisualGear;
       
  3184 begin
       
  3185     isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius;
       
  3186     if Gear^.Pos > 0 then
       
  3187         dec(Gear^.Pos);
       
  3188     AllInactive := false;
       
  3189     HHGear := Gear^.Hedgehog^.Gear;
       
  3190     //dec(Gear^.Timer);
       
  3191     move := _0_2;
       
  3192     fuel := 50;
       
  3193 (*if (HHGear^.Message and gmPrecise) <> 0 then
       
  3194     begin
       
  3195     move:= _0_02;
       
  3196     fuel:= 5;
       
  3197     end;*)
       
  3198     if HHGear^.Message and gmPrecise <> 0 then
       
  3199         HedgehogChAngle(HHGear)
       
  3200     else if Gear^.Health > 0 then
       
  3201         begin
       
  3202         if HHGear^.Message and gmUp <> 0 then
       
  3203             begin
       
  3204             if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then
       
  3205                 begin
       
  3206                 if isUnderwater then
       
  3207                     begin
       
  3208                     HHGear^.dY := HHGear^.dY - (move * _0_7);
       
  3209                     for i:= random(10)+10 downto 0 do
       
  3210                         begin
       
  3211                         bubble := AddVisualGear(hwRound(HHGear^.X) - 8 + random(16), hwRound(HHGear^.Y) + 16 + random(8), vgtBubble);
       
  3212                         if bubble <> nil then
       
  3213                             bubble^.dY:= random(20)/10+0.1;
       
  3214                         end
       
  3215                     end
       
  3216                 else HHGear^.dY := HHGear^.dY - move;
       
  3217                 end;
       
  3218             dec(Gear^.Health, fuel);
       
  3219             Gear^.MsgParam := Gear^.MsgParam or gmUp;
       
  3220             Gear^.Timer := GameTicks
       
  3221             end;
       
  3222         move.isNegative := (HHGear^.Message and gmLeft) <> 0;
       
  3223         if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
       
  3224             begin
       
  3225             HHGear^.dX := HHGear^.dX + (move * _0_1);
       
  3226             if isUnderwater then
       
  3227                 begin
       
  3228                 for i:= random(5)+5 downto 0 do
       
  3229                     begin
       
  3230                     bubble := AddVisualGear(hwRound(HHGear^.X)+random(8), hwRound(HHGear^.Y) - 8 + random(16), vgtBubble);
       
  3231                     if bubble <> nil then
       
  3232                         begin
       
  3233                         bubble^.dX:= (random(10)/10 + 0.02) * -1;
       
  3234                         if (move.isNegative) then
       
  3235                             begin
       
  3236                             bubble^.X := bubble^.X + 28;
       
  3237                             bubble^.dX:= bubble^.dX * (-1)
       
  3238                             end
       
  3239                         else bubble^.X := bubble^.X - 28;
       
  3240                         end;
       
  3241                     end
       
  3242                 end;
       
  3243             dec(Gear^.Health, fuel div 5);
       
  3244             Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
       
  3245             Gear^.Timer := GameTicks
       
  3246             end
       
  3247         end;
       
  3248 
       
  3249     // erases them all at once :-/
       
  3250     if (Gear^.Timer <> 0) and (GameTicks - Gear^.Timer > 250) then
       
  3251         begin
       
  3252         Gear^.Timer := 0;
       
  3253         Gear^.MsgParam := 0
       
  3254         end;
       
  3255 
       
  3256     if Gear^.Health < 0 then
       
  3257         Gear^.Health := 0;
       
  3258 
       
  3259     i:= Gear^.Health div 20;
       
  3260 
       
  3261     if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  3262         begin
       
  3263         Gear^.Damage:= i;
       
  3264         //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate);
       
  3265         FreeTexture(Gear^.Tex);
       
  3266         Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + '%', cWhiteColor, fntSmall)
       
  3267         end;
       
  3268 
       
  3269     if (HHGear^.Message and (gmAttack or gmUp or gmLeft or gmRight) <> 0) and
       
  3270        (HHGear^.Message and gmPrecise = 0) then
       
  3271         Gear^.State := Gear^.State and (not gsttmpFlag);
       
  3272 
       
  3273     if HHGear^.Message and gmPrecise = 0 then
       
  3274         HHGear^.Message := HHGear^.Message and (not (gmUp or gmLeft or gmRight));
       
  3275     HHGear^.State := HHGear^.State or gstMoving;
       
  3276 
       
  3277     Gear^.X := HHGear^.X;
       
  3278     Gear^.Y := HHGear^.Y;
       
  3279 
       
  3280     if not isUnderWater and hasBorder and ((HHGear^.X < _0)
       
  3281     or (hwRound(HHGear^.X) > LAND_WIDTH)) then
       
  3282         HHGear^.dY.isNegative:= false;
       
  3283 
       
  3284     if ((Gear^.State and gsttmpFlag) = 0)
       
  3285     or (HHGear^.dY < _0) then
       
  3286         doStepHedgehogMoving(HHGear);
       
  3287 
       
  3288     if // (Gear^.Health = 0)
       
  3289         (HHGear^.Damage <> 0)
       
  3290         //or CheckGearDrowning(HHGear)
       
  3291         or (cWaterLine + cVisibleWater * 4 < hwRound(HHGear^.Y))
       
  3292         or (TurnTimeLeft = 0)
       
  3293         // allow brief ground touches - to be fair on this, might need another counter
       
  3294         or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0))
       
  3295         or ((Gear^.Message and gmAttack) <> 0) then
       
  3296             begin
       
  3297             with HHGear^ do
       
  3298                 begin
       
  3299                 Message := 0;
       
  3300                 Active := true;
       
  3301                 State := State or gstMoving
       
  3302                 end;
       
  3303             DeleteGear(Gear);
       
  3304             isCursorVisible := false;
       
  3305             ApplyAmmoChanges(HHGear^.Hedgehog^);
       
  3306         //    if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
       
  3307 
       
  3308 //    Gear^.Tex:= RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) + '%', cWhiteColor, fntSmall)
       
  3309 
       
  3310 //AddCaption(trmsg[sidFuel]+': '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate);
       
  3311             end
       
  3312 end;
       
  3313 
       
  3314 procedure doStepJetpack(Gear: PGear);
       
  3315 var
       
  3316     HHGear: PGear;
       
  3317 begin
       
  3318     Gear^.Pos:= 0;
       
  3319     Gear^.doStep := @doStepJetpackWork;
       
  3320 
       
  3321     HHGear := Gear^.Hedgehog^.Gear;
       
  3322     FollowGear := HHGear;
       
  3323     AfterAttack;
       
  3324     with HHGear^ do
       
  3325         begin
       
  3326         State := State and (not gstAttacking);
       
  3327         Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight));
       
  3328 
       
  3329         if (dY < _0_1) and (dY > -_0_1) then
       
  3330             begin
       
  3331             Gear^.State := Gear^.State or gsttmpFlag;
       
  3332             dY := dY - _0_2
       
  3333             end
       
  3334         end
       
  3335 end;
       
  3336 
       
  3337 ////////////////////////////////////////////////////////////////////////////////
       
  3338 procedure doStepBirdyDisappear(Gear: PGear);
       
  3339 begin
       
  3340     AllInactive := false;
       
  3341     Gear^.Pos := 0;
       
  3342     if Gear^.Timer < 2000 then
       
  3343         inc(Gear^.Timer, 1)
       
  3344     else
       
  3345         begin
       
  3346         DeleteGear(Gear);
       
  3347         end;
       
  3348 end;
       
  3349 
       
  3350 procedure doStepBirdyFly(Gear: PGear);
       
  3351 var
       
  3352     HHGear: PGear;
       
  3353     fuel, i: LongInt;
       
  3354     move: hwFloat;
       
  3355 begin
       
  3356     HHGear := Gear^.Hedgehog^.Gear;
       
  3357     if HHGear = nil then
       
  3358         begin
       
  3359         DeleteGear(Gear);
       
  3360         exit
       
  3361         end;
       
  3362 
       
  3363     move := _0_2;
       
  3364     fuel := 50;
       
  3365 
       
  3366     if Gear^.Pos > 0 then
       
  3367         dec(Gear^.Pos, 1)
       
  3368     else if (HHGear^.Message and (gmLeft or gmRight or gmUp)) <> 0 then
       
  3369             Gear^.Pos := 500;
       
  3370 
       
  3371     if HHGear^.dX.isNegative then
       
  3372         Gear^.Tag := -1
       
  3373     else
       
  3374         Gear^.Tag := 1;
       
  3375 
       
  3376     if (HHGear^.Message and gmUp) <> 0 then
       
  3377         begin
       
  3378         if (not HHGear^.dY.isNegative)
       
  3379         or (HHGear^.Y > -_256) then
       
  3380             HHGear^.dY := HHGear^.dY - move;
       
  3381 
       
  3382         dec(Gear^.Health, fuel);
       
  3383         Gear^.MsgParam := Gear^.MsgParam or gmUp;
       
  3384         end;
       
  3385 
       
  3386     if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true;
       
  3387     if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
       
  3388         begin
       
  3389         HHGear^.dX := HHGear^.dX + (move * _0_1);
       
  3390         dec(Gear^.Health, fuel div 5);
       
  3391         Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
       
  3392         end;
       
  3393 
       
  3394     if Gear^.Health < 0 then
       
  3395         Gear^.Health := 0;
       
  3396 
       
  3397     if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then
       
  3398         for i:= ((500-Gear^.Health) div 250) downto 0 do
       
  3399             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather);
       
  3400 
       
  3401     if (HHGear^.Message and gmAttack <> 0) then
       
  3402         begin
       
  3403         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  3404         if Gear^.FlightTime > 0 then
       
  3405             begin
       
  3406             AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0);
       
  3407             PlaySound(sndBirdyLay);
       
  3408             dec(Gear^.FlightTime)
       
  3409             end;
       
  3410         end;
       
  3411 
       
  3412     if HHGear^.Message and (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then
       
  3413         Gear^.State := Gear^.State and (not gsttmpFlag);
       
  3414 
       
  3415     HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight));
       
  3416     HHGear^.State := HHGear^.State or gstMoving;
       
  3417 
       
  3418     Gear^.X := HHGear^.X;
       
  3419     Gear^.Y := HHGear^.Y - int2hwFloat(32);
       
  3420     // For some reason I need to reapply followgear here, something else grabs it otherwise.
       
  3421     // this is probably not needed anymore
       
  3422     if not CurrentTeam^.ExtDriven then FollowGear := HHGear;
       
  3423 
       
  3424     if ((Gear^.State and gsttmpFlag) = 0)
       
  3425     or (HHGear^.dY < _0) then
       
  3426         doStepHedgehogMoving(HHGear);
       
  3427 
       
  3428     if  (Gear^.Health = 0)
       
  3429         or (HHGear^.Damage <> 0)
       
  3430         or CheckGearDrowning(HHGear)
       
  3431         or (TurnTimeLeft = 0)
       
  3432         // allow brief ground touches - to be fair on this, might need another counter
       
  3433         or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0))
       
  3434         or ((Gear^.Message and gmAttack) <> 0) then
       
  3435             begin
       
  3436             with HHGear^ do
       
  3437                 begin
       
  3438                 Message := 0;
       
  3439                 Active := true;
       
  3440                 State := State or gstMoving
       
  3441                 end;
       
  3442             Gear^.State := Gear^.State or gstAnimation or gstTmpFlag;
       
  3443             if HHGear^.dY < _0 then
       
  3444                 begin
       
  3445                 Gear^.dX := HHGear^.dX;
       
  3446                 Gear^.dY := HHGear^.dY;
       
  3447                 end;
       
  3448             Gear^.Timer := 0;
       
  3449             Gear^.doStep := @doStepBirdyDisappear;
       
  3450             CurAmmoGear := nil;
       
  3451             isCursorVisible := false;
       
  3452             AfterAttack;
       
  3453             end
       
  3454 end;
       
  3455 
       
  3456 procedure doStepBirdyDescend(Gear: PGear);
       
  3457 var
       
  3458     HHGear: PGear;
       
  3459 begin
       
  3460     if Gear^.Timer > 0 then
       
  3461         dec(Gear^.Timer, 1)
       
  3462     else if Gear^.Hedgehog^.Gear = nil then
       
  3463         begin
       
  3464         DeleteGear(Gear);
       
  3465         AfterAttack;
       
  3466         exit
       
  3467         end;
       
  3468     HHGear := Gear^.Hedgehog^.Gear;
       
  3469     HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight));
       
  3470     if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then
       
  3471         begin
       
  3472         if Gear^.Timer = 0 then
       
  3473             Gear^.Y := Gear^.Y + _0_1
       
  3474         end
       
  3475     else if Gear^.Timer = 0 then
       
  3476         begin
       
  3477         Gear^.doStep := @doStepBirdyFly;
       
  3478         HHGear^.dY := -_0_2
       
  3479         end
       
  3480 end;
       
  3481 
       
  3482 procedure doStepBirdyAppear(Gear: PGear);
       
  3483 begin
       
  3484     Gear^.Pos := 0;
       
  3485     if Gear^.Timer < 2000 then
       
  3486         inc(Gear^.Timer, 1)
       
  3487     else
       
  3488         begin
       
  3489         Gear^.Timer := 500;
       
  3490         Gear^.dX := _0;
       
  3491         Gear^.dY := _0;
       
  3492         Gear^.State :=  Gear^.State and (not gstAnimation);
       
  3493         Gear^.doStep := @doStepBirdyDescend;
       
  3494         end
       
  3495 end;
       
  3496 
       
  3497 procedure doStepBirdy(Gear: PGear);
       
  3498 var
       
  3499     HHGear: PGear;
       
  3500 begin
       
  3501     gear^.State :=  gear^.State or gstAnimation and (not gstTmpFlag);
       
  3502     Gear^.doStep := @doStepBirdyAppear;
       
  3503 
       
  3504     if CurrentHedgehog = nil then
       
  3505         begin
       
  3506         DeleteGear(Gear);
       
  3507         exit
       
  3508         end;
       
  3509 
       
  3510     HHGear := CurrentHedgehog^.Gear;
       
  3511 
       
  3512     if HHGear^.dX.isNegative then
       
  3513         Gear^.Tag := -1
       
  3514     else
       
  3515         Gear^.Tag := 1;
       
  3516     Gear^.Pos := 0;
       
  3517     AllInactive := false;
       
  3518     FollowGear := HHGear;
       
  3519     with HHGear^ do
       
  3520         begin
       
  3521         State := State and (not gstAttacking);
       
  3522         Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight))
       
  3523         end
       
  3524 end;
       
  3525 
       
  3526 ////////////////////////////////////////////////////////////////////////////////
       
  3527 procedure doStepEggWork(Gear: PGear);
       
  3528 var
       
  3529     vg: PVisualGear;
       
  3530     i: LongInt;
       
  3531 begin
       
  3532     AllInactive := false;
       
  3533     Gear^.dX := Gear^.dX;
       
  3534     doStepFallingGear(Gear);
       
  3535     //    CheckGearDrowning(Gear); // already checked for in doStepFallingGear
       
  3536     CalcRotationDirAngle(Gear);
       
  3537 
       
  3538     if (Gear^.State and gstCollision) <> 0 then
       
  3539         begin
       
  3540         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0);
       
  3541         PlaySound(sndEggBreak);
       
  3542         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
       
  3543         vg := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
       
  3544         if vg <> nil then
       
  3545             vg^.Frame := 2;
       
  3546 
       
  3547         for i:= 10 downto 0 do
       
  3548             begin
       
  3549             vg := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), hwRound(Gear^.Y) - 3 + Random(6),
       
  3550                   vgtDust);
       
  3551             if vg <> nil then
       
  3552                 vg^.dX := vg^.dX + (Gear^.dX.QWordValue / 21474836480);
       
  3553             end;
       
  3554 
       
  3555         DeleteGear(Gear);
       
  3556         exit
       
  3557         end;
       
  3558 end;
       
  3559 
       
  3560 ////////////////////////////////////////////////////////////////////////////////
       
  3561 procedure doPortalColorSwitch();
       
  3562 var CurWeapon: PAmmo;
       
  3563 begin
       
  3564     if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.Message and gmSwitch) <> 0) then
       
  3565             with CurrentHedgehog^ do
       
  3566                 if (CurAmmoType = amPortalGun) then
       
  3567                     begin
       
  3568                     CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSwitch);
       
  3569 
       
  3570                     CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^);
       
  3571                     if CurWeapon^.Pos <> 0 then
       
  3572                         CurWeapon^.Pos := 0
       
  3573 
       
  3574                     else
       
  3575                     CurWeapon^.Pos := 1;
       
  3576                     end;
       
  3577 end;
       
  3578 
       
  3579 procedure doStepPortal(Gear: PGear);
       
  3580 var
       
  3581     iterator, conPortal: PGear;
       
  3582     s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed,
       
  3583     resetx, resety, resetdx, resetdy: hwFloat;
       
  3584     sx, sy, rh, resetr: LongInt;
       
  3585     hasdxy, isbullet, iscake, isCollision: Boolean;
       
  3586 begin
       
  3587     doPortalColorSwitch();
       
  3588 
       
  3589     // destroy portal if ground it was attached too is gone
       
  3590     if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask)
       
  3591     or (Gear^.Timer < 1)
       
  3592     or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team)
       
  3593     or (hwRound(Gear^.Y) > cWaterLine) then
       
  3594         begin
       
  3595         deleteGear(Gear);
       
  3596         EXIT;
       
  3597         end;
       
  3598 
       
  3599     if (TurnTimeLeft < 1)
       
  3600     or (Gear^.Health < 1) then
       
  3601         dec(Gear^.Timer);
       
  3602 
       
  3603     if Gear^.Timer < 10000 then
       
  3604         gear^.RenderTimer := true;
       
  3605 
       
  3606     // abort if there is no other portal connected to this one
       
  3607     if (Gear^.LinkedGear = nil) then
       
  3608         exit;
       
  3609     if ((Gear^.LinkedGear^.Tag and 1) = 0) then // or if it's still moving;
       
  3610         exit;
       
  3611 
       
  3612     conPortal := Gear^.LinkedGear;
       
  3613 
       
  3614     // check all gears for stuff to port through
       
  3615     iterator := nil;
       
  3616     while true do
       
  3617     begin
       
  3618 
       
  3619         // iterate through GearsList
       
  3620         if iterator = nil then
       
  3621             iterator := GearsList
       
  3622         else
       
  3623             iterator := iterator^.NextGear;
       
  3624 
       
  3625         // end of list?
       
  3626         if iterator = nil then
       
  3627             break;
       
  3628 
       
  3629         // don't port portals or other gear that wouldn't make sense
       
  3630         if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack, gtIceGun])
       
  3631         or (iterator^.PortalCounter > 32) then
       
  3632             continue;
       
  3633 
       
  3634         // don't port hogs on rope
       
  3635         // TODO: this will also prevent hogs while falling after rope use from
       
  3636         //       falling through portals... fix that!
       
  3637 
       
  3638         // check if gear fits through portal
       
  3639         if (iterator^.Radius > Gear^.Radius) then
       
  3640             continue;
       
  3641 
       
  3642         // this is the max range we accept incoming gears in
       
  3643         r := Int2hwFloat(iterator^.Radius+Gear^.Radius);
       
  3644 
       
  3645         // too far away?
       
  3646         if (iterator^.X < Gear^.X - r)
       
  3647         or (iterator^.X > Gear^.X + r)
       
  3648         or (iterator^.Y < Gear^.Y - r)
       
  3649         or (iterator^.Y > Gear^.Y + r) then
       
  3650             continue;
       
  3651 
       
  3652         hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0)) or ((iterator^.State or gstMoving) = 0));
       
  3653 
       
  3654         // in case the object is not moving, let's asume it's falling towards the portal
       
  3655         if not hasdxy then
       
  3656             begin
       
  3657             if Gear^.Y < iterator^.Y then
       
  3658                 continue;
       
  3659             ox:= Gear^.X - iterator^.X;
       
  3660             oy:= Gear^.Y - iterator^.Y;
       
  3661             end
       
  3662         else
       
  3663             begin
       
  3664             ox:= iterator^.dX;
       
  3665             oy:= iterator^.dY;
       
  3666             end;
       
  3667 
       
  3668         // cake will need extra treatment... it's so delicious and moist!
       
  3669         iscake:= (iterator^.Kind = gtCake);
       
  3670 
       
  3671         // won't port stuff that does not move towards the front/portal entrance
       
  3672         if iscake then
       
  3673             begin
       
  3674             if not (((iterator^.X - Gear^.X)*ox + (iterator^.Y - Gear^.Y)*oy).isNegative) then
       
  3675                 continue;
       
  3676             end
       
  3677         else
       
  3678             if not ((Gear^.dX*ox + Gear^.dY*oy).isNegative) then
       
  3679                 continue;
       
  3680 
       
  3681         isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]);
       
  3682 
       
  3683         r:= int2hwFloat(iterator^.Radius);
       
  3684 
       
  3685         if not (isbullet or iscake) then
       
  3686             begin
       
  3687             // wow! good candidate there, let's see if the distance and direction is okay!
       
  3688             if hasdxy then
       
  3689                 begin
       
  3690                 s := Distance(iterator^.dX, iterator^.dY);
       
  3691                 // if the resulting distance is 0 skip this gear
       
  3692                 if s.QWordValue = 0 then
       
  3693                     continue;
       
  3694                 s := r / s;
       
  3695                 ox:= iterator^.X + s * iterator^.dX;
       
  3696                 oy:= iterator^.Y + s * iterator^.dY;
       
  3697                 end
       
  3698             else
       
  3699                 begin
       
  3700                 ox:= iterator^.X;
       
  3701                 oy:= iterator^.Y + r;
       
  3702                 end;
       
  3703 
       
  3704             if (hwRound(Distance(Gear^.X-ox,Gear^.Y-oy)) > Gear^.Radius + 1 ) then
       
  3705                 continue;
       
  3706             end;
       
  3707 
       
  3708         // draw bullet trail
       
  3709         if isbullet then
       
  3710             spawnBulletTrail(iterator);
       
  3711 
       
  3712         // calc gear offset in portal vector direction
       
  3713         ox := (iterator^.X - Gear^.X);
       
  3714         oy := (iterator^.Y - Gear^.Y);
       
  3715         poffs:= (Gear^.dX * ox + Gear^.dY * oy);
       
  3716 
       
  3717         if not isBullet and poffs.isNegative then
       
  3718             continue;
       
  3719 
       
  3720         // only port bullets close to the portal
       
  3721         if isBullet and (not (hwAbs(poffs) < _3)) then
       
  3722             continue;
       
  3723 
       
  3724         //
       
  3725         // gears that make it till here will definately be ported
       
  3726         //
       
  3727         // (but old position/movement vector might be restored in case there's
       
  3728         // not enough space on the other side)
       
  3729         //
       
  3730 
       
  3731         resetr  := iterator^.Radius;
       
  3732         resetx  := iterator^.X;
       
  3733         resety  := iterator^.Y;
       
  3734         resetdx := iterator^.dX;
       
  3735         resetdy := iterator^.dY;
       
  3736 
       
  3737         // create a normal of the portal vector, but ...
       
  3738         nx := Gear^.dY;
       
  3739         ny := Gear^.dX;
       
  3740         // ... decide where the top is based on the hog's direction when firing the portal
       
  3741         if Gear^.Elasticity.isNegative then
       
  3742             nx.isNegative := (not nx.isNegative)
       
  3743         else
       
  3744             ny.isNegative := not ny.isNegative;
       
  3745 
       
  3746         // calc gear offset in portal normal vector direction
       
  3747         noffs:= (nx * ox + ny * oy);
       
  3748 
       
  3749         if isBullet and (noffs.Round >= Longword(Gear^.Radius)) then
       
  3750             continue;
       
  3751 
       
  3752         // avoid gravity related loops of not really moving gear
       
  3753         if not (iscake or isbullet)
       
  3754         and (Gear^.dY.isNegative)
       
  3755         and (conPortal^.dY.isNegative)
       
  3756         and ((iterator^.dX.QWordValue + iterator^.dY.QWordValue) < _0_08.QWordValue)
       
  3757         and (iterator^.PortalCounter > 0) then
       
  3758             continue;
       
  3759 
       
  3760         // calc gear speed along to the vector and the normal vector of the portal
       
  3761         if hasdxy then
       
  3762             begin
       
  3763             pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY);
       
  3764             nspeed:= (nx * iterator^.dX + ny * iterator^.dY);
       
  3765             end
       
  3766         else
       
  3767             begin
       
  3768             pspeed:= hwAbs(cGravity * oy);
       
  3769             nspeed:= _0;
       
  3770             end;
       
  3771 
       
  3772         // creating normal vector of connected (exit) portal
       
  3773         nx := conPortal^.dY;
       
  3774         ny := conPortal^.dX;
       
  3775         if conPortal^.Elasticity.isNegative then
       
  3776             nx.isNegative := (not nx.isNegative)
       
  3777         else
       
  3778             ny.isNegative := not ny.isNegative;
       
  3779 
       
  3780         // inverse cake's normal movement direction,
       
  3781         // as if it just walked through a hole
       
  3782         //if iscake then nspeed.isNegative:= not nspeed.isNegative;
       
  3783 
       
  3784 //AddFileLog('poffs:'+cstr(poffs)+' noffs:'+cstr(noffs)+' pspeed:'+cstr(pspeed)+' nspeed:'+cstr(nspeed));
       
  3785         iterator^.dX := -pspeed * conPortal^.dX + nspeed * nx;
       
  3786         iterator^.dY := -pspeed * conPortal^.dY + nspeed * ny;
       
  3787 
       
  3788         // make the gear's exit position close to the portal while
       
  3789         // still respecting the movement direction
       
  3790 
       
  3791         // determine the distance (in exit vector direction)
       
  3792         // that we want the gear at
       
  3793         if iscake then
       
  3794             ox:= (r - _0_7)
       
  3795         else
       
  3796             ox:= (r * _1_5);
       
  3797         s:= ox / poffs;
       
  3798         poffs:= ox;
       
  3799         if (nspeed.QWordValue <> 0)
       
  3800         and (pspeed > _0) then
       
  3801             noffs:= noffs * s * (nspeed / pspeed);
       
  3802 
       
  3803         // move stuff with high normal offset closer to the portal's center
       
  3804         if not isbullet then
       
  3805             begin
       
  3806             s := hwAbs(noffs) + r - int2hwFloat(Gear^.Radius);
       
  3807             if s > _0 then
       
  3808                 noffs:= noffs - SignAs(s,noffs)
       
  3809             end;
       
  3810 
       
  3811         iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx;
       
  3812         iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny;
       
  3813 
       
  3814         if not hasdxy and (not (conPortal^.dY.isNegative)) then
       
  3815             begin
       
  3816             iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y))
       
  3817             end;
       
  3818 
       
  3819         // see if the space on the exit side actually is enough
       
  3820 
       
  3821         if not (isBullet or isCake) then
       
  3822             begin
       
  3823             // TestCollisionXwithXYShift requires a hwFloat for xShift
       
  3824             ox.QWordValue := _1.QWordValue;
       
  3825             ox.isNegative := not iterator^.dX.isNegative;
       
  3826 
       
  3827             sx := hwSign(iterator^.dX);
       
  3828             sy := hwSign(iterator^.dY);
       
  3829 
       
  3830             if iterator^.Radius > 1 then
       
  3831                 iterator^.Radius := iterator^.Radius - 1;
       
  3832 
       
  3833             // check front
       
  3834             isCollision := TestCollisionY(iterator, sy)
       
  3835                         or TestCollisionX(iterator, sx);
       
  3836 
       
  3837             if not isCollision then
       
  3838                 begin
       
  3839                 // check center area (with half the radius so that the
       
  3840                 // the square check won't check more pixels than we want to)
       
  3841                 iterator^.Radius := 1 + resetr div 2;
       
  3842                 rh := resetr div 4;
       
  3843                 isCollision := TestCollisionYwithXYShift(iterator,       0, -sy * rh, sy, false)
       
  3844                             or TestCollisionXwithXYShift(iterator, ox * rh,        0, sx, false);
       
  3845                 end;
       
  3846 
       
  3847             iterator^.Radius := resetr;
       
  3848 
       
  3849             if isCollision then
       
  3850                 begin
       
  3851                 // collision! oh crap! go back!
       
  3852                 iterator^.X  := resetx;
       
  3853                 iterator^.Y  := resety;
       
  3854                 iterator^.dX := resetdx;
       
  3855                 iterator^.dY := resetdy;
       
  3856                 continue;
       
  3857                 end;
       
  3858             end;
       
  3859 
       
  3860         //
       
  3861         // You're now officially portaled!
       
  3862         //
       
  3863 
       
  3864         // Until loops are reliably broken
       
  3865         if iscake then
       
  3866             iterator^.PortalCounter:= 33
       
  3867         else
       
  3868             begin
       
  3869             inc(iterator^.PortalCounter);
       
  3870             iterator^.Active:= true;
       
  3871             iterator^.State:= iterator^.State and (not gstHHHJump) or gstMoving;
       
  3872             end;
       
  3873 
       
  3874         // is it worth adding an arcsin table?  Just how often would we end up doing something like this?
       
  3875         // SYNCED ANGLE UPDATE
       
  3876         if iterator^.Kind = gtRCPlane then
       
  3877             begin
       
  3878             // recycling as temp vars
       
  3879             resety.isNegative:= false;
       
  3880             resety.QWordValue:= 4294967296 * 112;
       
  3881             resetx.isNegative:= false;
       
  3882             resetx.QWordValue:= 4294967296 * 35;
       
  3883             resetdx.isNegative:= false;
       
  3884             resetdx.QWordValue:= 4294967296 * 1152;
       
  3885 
       
  3886             resetdy:=hwAbs(iterator^.dX*4);
       
  3887             resetdy:= resetdy + hwPow(resetdy,3)/_6 + _3 * hwPow(resetdy,5) / _40 + _5 * hwPow(resetdy,7) / resety + resetx * hwPow(resetdy,9) / resetdx;
       
  3888             iterator^.Angle:= hwRound(resetdy*_2048 / _PI);
       
  3889             if not iterator^.dY.isNegative then iterator^.Angle:= 2048-iterator^.Angle;
       
  3890             if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle;
       
  3891             end
       
  3892         // VISUAL USE OF ANGLE ONLY
       
  3893         else if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtKamikaze) and (CurAmmoGear^.Hedgehog = iterator^.Hedgehog) then
       
  3894             begin
       
  3895             iterator^.Angle:= DxDy2AttackAngle(iterator^.dX, iterator^.dY);
       
  3896             iterator^.Angle:= 2048-iterator^.Angle;
       
  3897             if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle;
       
  3898             end;
       
  3899 
       
  3900         if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil)
       
  3901         and (iterator = CurrentHedgehog^.Gear)
       
  3902         and (CurAmmoGear <> nil)
       
  3903         and (CurAmmoGear^.Kind =gtRope) then
       
  3904                CurAmmoGear^.PortalCounter:= 1;
       
  3905 
       
  3906         if not isbullet and (iterator^.State and gstInvisible = 0)
       
  3907         and (iterator^.Kind <> gtFlake) then
       
  3908             FollowGear := iterator;
       
  3909 
       
  3910         // store X/Y values of exit for net bullet trail
       
  3911         if isbullet then
       
  3912             begin
       
  3913             iterator^.Elasticity:= iterator^.X;
       
  3914             iterator^.Friction  := iterator^.Y;
       
  3915             end;
       
  3916 
       
  3917         if Gear^.Health > 1 then
       
  3918             dec(Gear^.Health);
       
  3919     end;
       
  3920 end;
       
  3921 
       
  3922 
       
  3923 
       
  3924 procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean);
       
  3925 var
       
  3926     CurWeapon: PAmmo;
       
  3927 begin
       
  3928     if CurrentHedgehog <> nil then
       
  3929         with CurrentHedgehog^ do
       
  3930             begin
       
  3931             CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^);
       
  3932             if (CurAmmoType = amPortalGun) then
       
  3933                 begin
       
  3934                 if not destroyGear then
       
  3935                     begin
       
  3936                     // switch color of ball to opposite of oldPortal
       
  3937                     if (oldPortal^.Tag and 2) = 0 then
       
  3938                         CurWeapon^.Pos:= 1
       
  3939                     else
       
  3940                         CurWeapon^.Pos:= 0;
       
  3941                     end;
       
  3942 
       
  3943                 // make the ball visible
       
  3944                 CurWeapon^.Timer := 0;
       
  3945                 end
       
  3946             end;
       
  3947     if destroyGear then
       
  3948         oldPortal^.Timer:= 0;
       
  3949 end;
       
  3950 
       
  3951 procedure doStepMovingPortal_real(Gear: PGear);
       
  3952 var
       
  3953     x, y, tx, ty: LongInt;
       
  3954     s: hwFloat;
       
  3955 begin
       
  3956     x := hwRound(Gear^.X);
       
  3957     y := hwRound(Gear^.Y);
       
  3958     tx := 0;
       
  3959     ty := 0;
       
  3960     // avoid compiler hints
       
  3961 
       
  3962     if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then
       
  3963         begin
       
  3964         Gear^.State := Gear^.State or gstCollision;
       
  3965         Gear^.State := Gear^.State and (not gstMoving);
       
  3966 
       
  3967         if (Land[y, x] and lfBouncy <> 0)
       
  3968         or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255))
       
  3969         or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain
       
  3970             begin
       
  3971             loadNewPortalBall(Gear, true);
       
  3972             EXIT;
       
  3973             end;
       
  3974 
       
  3975         // making a normalized normal vector
       
  3976         s := _1/DistanceI(tx,ty);
       
  3977         Gear^.dX :=  s * ty;
       
  3978         Gear^.dY := -s * tx;
       
  3979 
       
  3980         Gear^.DirAngle := DxDy2Angle(-Gear^.dY,Gear^.dX);
       
  3981         if not Gear^.dX.isNegative then
       
  3982             Gear^.DirAngle := 180-Gear^.DirAngle;
       
  3983 
       
  3984         if ((Gear^.LinkedGear = nil)
       
  3985         or (hwRound(Distance(Gear^.X - Gear^.LinkedGear^.X,Gear^.Y-Gear^.LinkedGear^.Y)) >=Gear^.Radius*2)) then
       
  3986             begin
       
  3987             loadNewPortalBall(Gear, false);
       
  3988             inc(Gear^.Tag);
       
  3989             Gear^.doStep := @doStepPortal;
       
  3990         end
       
  3991         else
       
  3992             loadNewPortalBall(Gear, true);
       
  3993     end
       
  3994 
       
  3995     else if (y > cWaterLine)
       
  3996     or (y < -max(LAND_WIDTH,4096))
       
  3997     or (x > 2*max(LAND_WIDTH,4096))
       
  3998     or (x < -max(LAND_WIDTH,4096)) then
       
  3999         loadNewPortalBall(Gear, true);
       
  4000 end;
       
  4001 
       
  4002 procedure doStepMovingPortal(Gear: PGear);
       
  4003 begin
       
  4004     doPortalColorSwitch();
       
  4005     doStepPerPixel(Gear, @doStepMovingPortal_real, true);
       
  4006     if (Gear^.Timer < 1)
       
  4007     or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) then
       
  4008         deleteGear(Gear);
       
  4009 end;
       
  4010 
       
  4011 procedure doStepPortalShot(newPortal: PGear);
       
  4012 var
       
  4013     iterator: PGear;
       
  4014     s: hwFloat;
       
  4015     CurWeapon: PAmmo;
       
  4016 begin
       
  4017     s:= Distance (newPortal^.dX, newPortal^.dY);
       
  4018 
       
  4019     // Adds the hog speed (only that part in/directly against shot direction)
       
  4020     // to the shot speed (which we triple previously btw)
       
  4021     // (This is done my projecting the hog movement vector onto the shot movement vector and then adding the resulting length
       
  4022     // to the scaler)
       
  4023     s := (_2 * s + (newPortal^.dX * CurrentHedgehog^.Gear^.dX + newPortal^.dY * CurrentHedgehog^.Gear^.dY ) / s) / s;
       
  4024     newPortal^.dX := newPortal^.dX * s;
       
  4025     newPortal^.dY := newPortal^.dY * s;
       
  4026 
       
  4027     newPortal^.LinkedGear := nil;
       
  4028 
       
  4029     if CurrentHedgehog <> nil then
       
  4030         with CurrentHedgehog^ do
       
  4031             begin
       
  4032             CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^);
       
  4033             // let's save the HH's dX's direction so we can decide where the "top" of the portal hole
       
  4034             newPortal^.Elasticity.isNegative := CurrentHedgehog^.Gear^.dX.isNegative;
       
  4035             // when doing a backjump the dx is the opposite of the facing direction
       
  4036             if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then
       
  4037                 newPortal^.Elasticity.isNegative := not newPortal^.Elasticity.isNegative;
       
  4038 
       
  4039             // make portal gun look unloaded
       
  4040             if (CurWeapon <> nil) and (CurAmmoType = amPortalGun) then
       
  4041                 CurWeapon^.Timer := CurWeapon^.Timer or 2;
       
  4042 
       
  4043             iterator := GearsList;
       
  4044             while iterator <> nil do
       
  4045                 begin
       
  4046                 if (iterator^.Kind = gtPortal) then
       
  4047                     if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then
       
  4048                         begin
       
  4049                         if ((iterator^.Tag and 2) = (newPortal^.Tag and 2)) then
       
  4050                             begin
       
  4051                             iterator^.Timer:= 0;
       
  4052                             end
       
  4053                         else
       
  4054                             begin
       
  4055                             // link portals with each other
       
  4056                             newPortal^.LinkedGear := iterator;
       
  4057                             iterator^.LinkedGear := newPortal;
       
  4058                             iterator^.Health := newPortal^.Health;
       
  4059                             end;
       
  4060                         end;
       
  4061                 iterator^.PortalCounter:= 0;
       
  4062                 iterator := iterator^.NextGear
       
  4063                 end;
       
  4064 
       
  4065             if newPortal^.LinkedGear <> nil then
       
  4066                 begin
       
  4067                 // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
       
  4068                 iterator:= GearsList;
       
  4069                 while iterator <> nil do
       
  4070                     begin
       
  4071                     if not (iterator^.Kind in [gtPortal, gtAirAttack, gtKnife]) and ((iterator^.Hedgehog <> CurrentHedgehog)
       
  4072                     or ((iterator^.Message and gmAllStoppable) = 0)) then
       
  4073                             begin
       
  4074                             iterator^.Active:= true;
       
  4075                             if iterator^.dY.QWordValue = 0 then
       
  4076                                 iterator^.dY.isNegative:= false;
       
  4077                             iterator^.State:= iterator^.State or gstMoving;
       
  4078                             DeleteCI(iterator);
       
  4079                         //inc(iterator^.dY.QWordValue,10);
       
  4080                             end;
       
  4081                     iterator:= iterator^.NextGear
       
  4082                     end
       
  4083                 end
       
  4084             end;
       
  4085     newPortal^.State := newPortal^.State and (not gstCollision);
       
  4086     newPortal^.State := newPortal^.State or gstMoving;
       
  4087     newPortal^.doStep := @doStepMovingPortal;
       
  4088 end;
       
  4089 
       
  4090 ////////////////////////////////////////////////////////////////////////////////
       
  4091 procedure doStepPiano(Gear: PGear);
       
  4092 var
       
  4093     r0, r1: LongInt;
       
  4094     odY: hwFloat;
       
  4095 begin
       
  4096     AllInactive := false;
       
  4097     if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and
       
  4098         ((CurrentHedgehog^.Gear^.Message and gmSlot) <> 0) then
       
  4099             begin
       
  4100                 case CurrentHedgehog^.Gear^.MsgParam of
       
  4101                 0: PlaySound(sndPiano0);
       
  4102                 1: PlaySound(sndPiano1);
       
  4103                 2: PlaySound(sndPiano2);
       
  4104                 3: PlaySound(sndPiano3);
       
  4105                 4: PlaySound(sndPiano4);
       
  4106                 5: PlaySound(sndPiano5);
       
  4107                 6: PlaySound(sndPiano6);
       
  4108                 7: PlaySound(sndPiano7);
       
  4109                 else PlaySound(sndPiano8);
       
  4110             end;
       
  4111         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote);
       
  4112         CurrentHedgehog^.Gear^.MsgParam := 0;
       
  4113         CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSlot);
       
  4114         end;
       
  4115 
       
  4116     if (*((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or*) (Gear^.Pos = 5) then
       
  4117         begin
       
  4118         Gear^.dY := Gear^.dY + cGravity * 2;
       
  4119         Gear^.Y := Gear^.Y + Gear^.dY;
       
  4120         if CheckGearDrowning(Gear) then
       
  4121             begin
       
  4122             Gear^.Y:= Gear^.Y + _50;
       
  4123             OnUsedAmmo(CurrentHedgehog^);
       
  4124             if CurrentHedgehog^.Gear <> nil then
       
  4125                 begin
       
  4126                 // Drown the hedgehog.  Could also just delete it, but hey, this gets a caption
       
  4127                 CurrentHedgehog^.Gear^.Active := true;
       
  4128                 CurrentHedgehog^.Gear^.X := Gear^.X;
       
  4129                 CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128;
       
  4130                 CurrentHedgehog^.Unplaced := false;
       
  4131                 if TagTurnTimeLeft = 0 then
       
  4132                     TagTurnTimeLeft:= TurnTimeLeft;
       
  4133                 TurnTimeLeft:= 0
       
  4134                 end;
       
  4135             ResumeMusic
       
  4136             end;
       
  4137         exit
       
  4138         end;
       
  4139 
       
  4140     odY:= Gear^.dY;
       
  4141     doStepFallingGear(Gear);
       
  4142 
       
  4143     if (Gear^.State and gstDrowning) <> 0 then
       
  4144         begin
       
  4145         Gear^.Y:= Gear^.Y + _50;
       
  4146         OnUsedAmmo(CurrentHedgehog^);
       
  4147         if CurrentHedgehog^.Gear <> nil then
       
  4148             begin
       
  4149             // Drown the hedgehog.  Could also just delete it, but hey, this gets a caption
       
  4150             CurrentHedgehog^.Gear^.Active := true;
       
  4151             CurrentHedgehog^.Gear^.X := Gear^.X;
       
  4152             CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128;
       
  4153             CurrentHedgehog^.Unplaced := false;
       
  4154             if TagTurnTimeLeft = 0 then
       
  4155                 TagTurnTimeLeft:= TurnTimeLeft;
       
  4156             TurnTimeLeft:= 0
       
  4157             end;
       
  4158         ResumeMusic
       
  4159         end
       
  4160     else if (Gear^.State and gstCollision) <> 0 then
       
  4161         begin
       
  4162         r0 := GetRandom(21);
       
  4163         r1 := GetRandom(21);
       
  4164         doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0);
       
  4165         doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0);
       
  4166         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound);
       
  4167         for r0:= 0 to 4 do
       
  4168             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote);
       
  4169         Gear^.dY := cGravity * 2 - odY;
       
  4170         Gear^.Pos := Gear^.Pos + 1;
       
  4171         end
       
  4172     else
       
  4173         Gear^.dY := Gear^.dY + cGravity * 2;
       
  4174     // let it fall faster so itdoesn't take too long for the whole attack
       
  4175 end;
       
  4176 
       
  4177 
       
  4178 ////////////////////////////////////////////////////////////////////////////////
       
  4179 procedure doStepSineGunShotWork(Gear: PGear);
       
  4180 var
       
  4181     x, y, rX, rY, t, tmp, initHealth: LongInt;
       
  4182     oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat;
       
  4183     justCollided: boolean;
       
  4184 begin
       
  4185     AllInactive := false;
       
  4186     initHealth := Gear^.Health;
       
  4187     lX := Gear^.X;
       
  4188     lY := Gear^.Y;
       
  4189     ldX := Gear^.dX;
       
  4190     ldY := Gear^.dY;
       
  4191     sdy := _0_5/Distance(Gear^.dX,Gear^.dY);
       
  4192     ldX := ldX * sdy;
       
  4193     ldY := ldY * sdy;
       
  4194     sdY := hwAbs(ldX) + hwAbs(ldY);
       
  4195     sdX := _1 - hwAbs(ldX/sdY);
       
  4196     sdY := _1 - hwAbs(ldY/sdY);
       
  4197     if (ldX.isNegative = ldY.isNegative) then
       
  4198         sdY := -sdY;
       
  4199 
       
  4200     // initial angle depends on current GameTicks
       
  4201     t := getRandom(4096);
       
  4202 
       
  4203 
       
  4204     // used for a work-around detection of area that is within land array, but outside borders
       
  4205     justCollided := false;
       
  4206 
       
  4207     repeat
       
  4208         lX := lX + ldX;
       
  4209         lY := lY + ldY;
       
  4210         oX := Gear^.X;
       
  4211         oY := Gear^.Y;
       
  4212         rX := hwRound(oX);
       
  4213         rY := hwRound(oY);
       
  4214         tmp := t mod 4096;
       
  4215         amp := _128 * (_1 - hwSqr(int2hwFloat(Gear^.Health)/initHealth));
       
  4216         sine := amp * AngleSin(tmp mod 2048);
       
  4217         sine.isNegative := (tmp < 2048);
       
  4218         inc(t,Gear^.Health div 313);
       
  4219         Gear^.X := lX + (sine * sdX);
       
  4220         Gear^.Y := ly + (sine * sdY);
       
  4221         Gear^.dX := Gear^.X - oX;
       
  4222         Gear^.dY := Gear^.Y - oY;
       
  4223 
       
  4224         x := hwRound(Gear^.X);
       
  4225         y := hwRound(Gear^.Y);
       
  4226 
       
  4227         // if borders are on, stop outside land array
       
  4228         if hasBorder and (((x and LAND_WIDTH_MASK) <> 0) or ((y and LAND_HEIGHT_MASK) <> 0)) then
       
  4229             begin
       
  4230             Gear^.Damage := 0;
       
  4231             Gear^.Health := 0;
       
  4232             end
       
  4233         else
       
  4234             begin
       
  4235             if (rY <= cWaterLine) or (y <= cWaterLine) then
       
  4236                 begin
       
  4237                 if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0)
       
  4238                     and (Land[y, x] <> 0) then
       
  4239                         begin
       
  4240                             if justCollided then
       
  4241                                 begin
       
  4242                                 Gear^.Damage := 0;
       
  4243                                 Gear^.Health := 0;
       
  4244                                 end
       
  4245                             else
       
  4246                                 begin
       
  4247                                 inc(Gear^.Damage,3);
       
  4248                                 justCollided := true;
       
  4249                                 end;
       
  4250                         end
       
  4251                 else
       
  4252                     justCollided := false;
       
  4253 
       
  4254                 // kick nearby hogs, dig tunnel and add some fire
       
  4255                 // if at least 5 collisions occured
       
  4256                 if Gear^.Damage > 0 then
       
  4257                     begin
       
  4258                     DrawExplosion(rX,rY,Gear^.Radius);
       
  4259 
       
  4260                     // kick nearby hogs
       
  4261                     AmmoShove(Gear, 35, 50);
       
  4262 
       
  4263                     dec(Gear^.Health, Gear^.Damage);
       
  4264                     Gear^.Damage := 0;
       
  4265 
       
  4266                     // add some fire to the tunnel
       
  4267                     if getRandom(6) = 0 then
       
  4268                         begin
       
  4269                         tmp:= GetRandom(2 * Gear^.Radius);
       
  4270                         AddGear(x - Gear^.Radius + tmp, y - GetRandom(Gear^.Radius + 1), gtFlame, gsttmpFlag, _0, _0, 0)
       
  4271                         end
       
  4272                     end;
       
  4273 
       
  4274                 if random(100) = 0 then
       
  4275                     AddVisualGear(x, y, vgtSmokeTrace);
       
  4276                 end
       
  4277                 else dec(Gear^.Health, 5); // if underwater get additional damage
       
  4278             end;
       
  4279 
       
  4280         dec(Gear^.Health);
       
  4281 
       
  4282         // decrease bullet size towards the end
       
  4283         if (Gear^.Radius > 4) then
       
  4284             begin
       
  4285             if (Gear^.Health <= (initHealth div 3)) then
       
  4286                 dec(Gear^.Radius)
       
  4287             end
       
  4288         else if (Gear^.Radius > 3) then
       
  4289             begin
       
  4290             if (Gear^.Health <= (initHealth div 4)) then
       
  4291                 dec(Gear^.Radius)
       
  4292             end
       
  4293         else if (Gear^.Radius > 2) then begin
       
  4294             if (Gear^.Health <= (initHealth div 5)) then
       
  4295                 dec(Gear^.Radius)
       
  4296             end
       
  4297         else if (Gear^.Radius > 1) then
       
  4298             begin
       
  4299             if (Gear^.Health <= (initHealth div 6)) then
       
  4300                 dec(Gear^.Radius)
       
  4301             end;
       
  4302 
       
  4303     until (Gear^.Health <= 0);
       
  4304 
       
  4305     DeleteGear(Gear);
       
  4306     AfterAttack;
       
  4307 end;
       
  4308 
       
  4309 procedure doStepSineGunShot(Gear: PGear);
       
  4310 var
       
  4311     HHGear: PGear;
       
  4312 begin
       
  4313     PlaySound(sndSineGun);
       
  4314 
       
  4315     // push the shooting Hedgehog back
       
  4316     HHGear := CurrentHedgehog^.Gear;
       
  4317     Gear^.dX.isNegative := not Gear^.dX.isNegative;
       
  4318     Gear^.dY.isNegative := not Gear^.dY.isNegative;
       
  4319     HHGear^.dX := Gear^.dX;
       
  4320     HHGear^.dY := Gear^.dY;
       
  4321     AmmoShove(Gear, 0, 80);
       
  4322     Gear^.dX.isNegative := not Gear^.dX.isNegative;
       
  4323     Gear^.dY.isNegative := not Gear^.dY.isNegative;
       
  4324 
       
  4325     Gear^.doStep := @doStepSineGunShotWork;
       
  4326     with mobileRecord do
       
  4327         if (performRumble <> nil) and (not fastUntilLag) then
       
  4328             performRumble(kSystemSoundID_Vibrate);
       
  4329 end;
       
  4330 
       
  4331 ////////////////////////////////////////////////////////////////////////////////
       
  4332 procedure doStepFlamethrowerWork(Gear: PGear);
       
  4333 var
       
  4334     HHGear, flame: PGear;
       
  4335     rx, ry, speed: hwFloat;
       
  4336     i, gX, gY: LongInt;
       
  4337 begin
       
  4338     AllInactive := false;
       
  4339     HHGear := Gear^.Hedgehog^.Gear;
       
  4340     HedgehogChAngle(HHGear);
       
  4341     gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
       
  4342     gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
       
  4343 
       
  4344     if (GameTicks and $FF) = 0 then
       
  4345         begin
       
  4346         if (HHGear^.Message and gmRight) <> 0 then
       
  4347             begin
       
  4348             if HHGear^.dX.isNegative and (Gear^.Tag < 20) then
       
  4349                 inc(Gear^.Tag)
       
  4350             else if Gear^.Tag > 5 then
       
  4351                 dec(Gear^.Tag);
       
  4352             end
       
  4353         else if (HHGear^.Message and gmLeft) <> 0 then
       
  4354             begin
       
  4355             if HHGear^.dX.isNegative and (Gear^.Tag > 5) then
       
  4356                 dec(Gear^.Tag)
       
  4357             else if Gear^.Tag < 20 then
       
  4358                 inc(Gear^.Tag);
       
  4359             end
       
  4360         end;
       
  4361 
       
  4362     dec(Gear^.Timer);
       
  4363     if Gear^.Timer = 0 then
       
  4364         begin
       
  4365         dec(Gear^.Health);
       
  4366         if (Gear^.Health mod 5) = 0 then
       
  4367             begin
       
  4368             rx := rndSign(getRandomf * _0_1);
       
  4369             ry := rndSign(getRandomf * _0_1);
       
  4370             speed := _0_5 * (_10 / Gear^.Tag);
       
  4371 
       
  4372             flame:= AddGear(gx, gy, gtFlame, gstTmpFlag,
       
  4373                     SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
       
  4374                     AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
       
  4375             flame^.CollisionMask:= lfNotCurrentMask;
       
  4376 
       
  4377             if (Gear^.Health mod 30) = 0 then
       
  4378                 begin
       
  4379                 flame:= AddGear(gx, gy, gtFlame, 0,
       
  4380                         SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
       
  4381                         AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
       
  4382                 flame^.CollisionMask:= lfNotCurrentMask;
       
  4383                 end
       
  4384             end;
       
  4385         Gear^.Timer:= Gear^.Tag
       
  4386         end;
       
  4387 
       
  4388     if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then
       
  4389         begin
       
  4390         DeleteGear(Gear);
       
  4391         AfterAttack
       
  4392         end
       
  4393     else
       
  4394         begin
       
  4395         i:= Gear^.Health div 5;
       
  4396         if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  4397             begin
       
  4398             Gear^.Damage:= i;
       
  4399             FreeTexture(Gear^.Tex);
       
  4400             Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) +
       
  4401                          '%', cWhiteColor, fntSmall)
       
  4402             end
       
  4403         end
       
  4404 end;
       
  4405 
       
  4406 procedure doStepFlamethrower(Gear: PGear);
       
  4407 var
       
  4408     HHGear: PGear;
       
  4409 begin
       
  4410     HHGear := Gear^.Hedgehog^.Gear;
       
  4411     HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight));
       
  4412     HHGear^.State := HHGear^.State or gstNotKickable;
       
  4413     Gear^.doStep := @doStepFlamethrowerWork
       
  4414 end;
       
  4415 
       
  4416 ////////////////////////////////////////////////////////////////////////////////
       
  4417 procedure doStepLandGunWork(Gear: PGear);
       
  4418 var
       
  4419     HHGear, land: PGear;
       
  4420     rx, ry, speed: hwFloat;
       
  4421     i, gX, gY: LongInt;
       
  4422 begin
       
  4423     AllInactive := false;
       
  4424     HHGear := Gear^.Hedgehog^.Gear;
       
  4425     HedgehogChAngle(HHGear);
       
  4426     gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
       
  4427     gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
       
  4428 
       
  4429     if (GameTicks and $FF) = 0 then
       
  4430         begin
       
  4431         if (HHGear^.Message and gmRight) <> 0 then
       
  4432             begin
       
  4433             if HHGear^.dX.isNegative and (Gear^.Tag < 20) then
       
  4434                 inc(Gear^.Tag)
       
  4435             else if Gear^.Tag > 5 then
       
  4436                 dec(Gear^.Tag);
       
  4437             end
       
  4438         else if (HHGear^.Message and gmLeft) <> 0 then
       
  4439             begin
       
  4440             if HHGear^.dX.isNegative and (Gear^.Tag > 5) then
       
  4441                 dec(Gear^.Tag)
       
  4442             else if Gear^.Tag < 20 then
       
  4443                 inc(Gear^.Tag);
       
  4444             end
       
  4445         end;
       
  4446 
       
  4447     dec(Gear^.Timer);
       
  4448     if Gear^.Timer = 0 then
       
  4449         begin
       
  4450         dec(Gear^.Health);
       
  4451 
       
  4452         rx := rndSign(getRandomf * _0_1);
       
  4453         ry := rndSign(getRandomf * _0_1);
       
  4454         speed := (_3 / Gear^.Tag);
       
  4455 
       
  4456         land:= AddGear(gx, gy, gtFlake, gstTmpFlag,
       
  4457                 SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
       
  4458                 AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
       
  4459         land^.CollisionMask:= lfNotCurrentMask;
       
  4460 
       
  4461         Gear^.Timer:= Gear^.Tag
       
  4462         end;
       
  4463 
       
  4464     if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then
       
  4465         begin
       
  4466         HHGear^.Message:= HHGear^.Message and (not gmAttack);
       
  4467         DeleteGear(Gear);
       
  4468         AfterAttack
       
  4469         end
       
  4470     else
       
  4471         begin
       
  4472         i:= Gear^.Health div 10;
       
  4473         if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  4474             begin
       
  4475             Gear^.Damage:= i;
       
  4476             FreeTexture(Gear^.Tex);
       
  4477             Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) +
       
  4478                          '%', cWhiteColor, fntSmall)
       
  4479             end
       
  4480         end
       
  4481 end;
       
  4482 
       
  4483 procedure doStepLandGun(Gear: PGear);
       
  4484 var
       
  4485     HHGear: PGear;
       
  4486 begin
       
  4487     HHGear := Gear^.Hedgehog^.Gear;
       
  4488     HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight or gmAttack));
       
  4489     HHGear^.State := HHGear^.State or gstNotKickable;
       
  4490     Gear^.doStep := @doStepLandGunWork
       
  4491 end;
       
  4492 
       
  4493 ////////////////////////////////////////////////////////////////////////////////
       
  4494 procedure doStepPoisonCloud(Gear: PGear);
       
  4495 begin
       
  4496     if Gear^.Timer = 0 then
       
  4497         begin
       
  4498         DeleteGear(Gear);
       
  4499         exit
       
  4500         end;
       
  4501     dec(Gear^.Timer);
       
  4502     Gear^.X:= Gear^.X + Gear^.dX;
       
  4503     Gear^.Y:= Gear^.Y + Gear^.dY;
       
  4504     Gear^.dX := Gear^.dX + cWindSpeed / 4;
       
  4505     Gear^.dY := Gear^.dY + cGravity / 100;
       
  4506     if (GameTicks and $FF) = 0 then
       
  4507         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned);
       
  4508     AllInactive:= false;
       
  4509 end;
       
  4510 
       
  4511 ////////////////////////////////////////////////////////////////////////////////
       
  4512 procedure doStepHammer(Gear: PGear);
       
  4513 var HHGear, tmp, tmp2: PGear;
       
  4514          t: PGearArray;
       
  4515          i: LongInt;
       
  4516 begin
       
  4517 HHGear:= Gear^.Hedgehog^.Gear;
       
  4518 HHGear^.State:= HHGear^.State or gstNoDamage;
       
  4519 DeleteCI(HHGear);
       
  4520 
       
  4521 t:= CheckGearsCollision(Gear);
       
  4522 
       
  4523 for i:= 5 downto 0 do
       
  4524     AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
       
  4525 
       
  4526 i:= t^.Count;
       
  4527 while i > 0 do
       
  4528     begin
       
  4529     dec(i);
       
  4530     tmp:= t^.ar[i];
       
  4531     if (tmp^.State and gstNoDamage) = 0 then
       
  4532         if (tmp^.Kind = gtHedgehog) or (tmp^.Kind = gtMine) or (tmp^.Kind = gtExplosives) then
       
  4533             begin
       
  4534             //tmp^.State:= tmp^.State or gstFlatened;
       
  4535             if not tmp^.Invulnerable then
       
  4536                 ApplyDamage(tmp, CurrentHedgehog, tmp^.Health div 3, dsUnknown);
       
  4537             //DrawTunnel(tmp^.X, tmp^.Y - _1, _0, _0_5, cHHRadius * 6, cHHRadius * 3);
       
  4538             tmp2:= AddGear(hwRound(tmp^.X), hwRound(tmp^.Y), gtHammerHit, 0, _0, _0, 0);
       
  4539             tmp2^.LinkedGear:= tmp;
       
  4540             SetAllToActive
       
  4541             end
       
  4542         else
       
  4543             begin
       
  4544             end
       
  4545     end;
       
  4546 
       
  4547 HHGear^.State:= HHGear^.State and (not gstNoDamage);
       
  4548 Gear^.Timer:= 250;
       
  4549 Gear^.doStep:= @doStepIdle
       
  4550 end;
       
  4551 
       
  4552 procedure doStepHammerHitWork(Gear: PGear);
       
  4553 var
       
  4554     i, j, ei: LongInt;
       
  4555     HitGear: PGear;
       
  4556 begin
       
  4557     AllInactive := false;
       
  4558     HitGear := Gear^.LinkedGear;
       
  4559     dec(Gear^.Timer);
       
  4560     if (HitGear = nil) or (Gear^.Timer = 0) or ((Gear^.Message and gmDestroy) <> 0) then
       
  4561         begin
       
  4562         DeleteGear(Gear);
       
  4563         exit
       
  4564         end;
       
  4565 
       
  4566     if (Gear^.Timer mod 5) = 0 then
       
  4567         begin
       
  4568         AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
       
  4569 
       
  4570         i := hwRound(Gear^.X) - HitGear^.Radius + 2;
       
  4571         ei := hwRound(Gear^.X) + HitGear^.Radius - 2;
       
  4572         for j := 1 to 4 do DrawExplosion(i - GetRandom(5), hwRound(Gear^.Y) + 6*j, 3);
       
  4573         for j := 1 to 4 do DrawExplosion(ei + LongInt(GetRandom(5)), hwRound(Gear^.Y) + 6*j, 3);
       
  4574         while i <= ei do
       
  4575             begin
       
  4576             for j := 1 to 11 do DrawExplosion(i, hwRound(Gear^.Y) + 3*j, 3);
       
  4577             inc(i, 1)
       
  4578             end;
       
  4579 
       
  4580         if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9)
       
  4581            , lfIndestructible) then
       
  4582             begin
       
  4583             //Gear^.X := Gear^.X + Gear^.dX;
       
  4584             Gear^.Y := Gear^.Y + _1_9
       
  4585             end;
       
  4586         end;
       
  4587     if TestCollisionYwithGear(Gear, 1) <> 0 then
       
  4588         begin
       
  4589         Gear^.dY := _0;
       
  4590         SetLittle(HitGear^.dX);
       
  4591         HitGear^.dY := _0;
       
  4592         end
       
  4593     else
       
  4594         begin
       
  4595         //Gear^.dY := Gear^.dY + cGravity;
       
  4596         //Gear^.Y := Gear^.Y + Gear^.dY;
       
  4597         if hwRound(Gear^.Y) > cWaterLine then
       
  4598             Gear^.Timer := 1
       
  4599         end;
       
  4600 
       
  4601     //Gear^.X := Gear^.X + HitGear^.dX;
       
  4602     HitGear^.X := Gear^.X;
       
  4603     HitGear^.Y := Gear^.Y;
       
  4604     SetLittle(HitGear^.dY);
       
  4605     HitGear^.Active:= true;
       
  4606 end;
       
  4607 
       
  4608 procedure doStepHammerHit(Gear: PGear);
       
  4609 var
       
  4610     i, y: LongInt;
       
  4611     ar: TRangeArray;
       
  4612     HHGear: PGear;
       
  4613 begin
       
  4614     i := 0;
       
  4615     HHGear := Gear^.Hedgehog^.Gear;
       
  4616 
       
  4617     y := hwRound(Gear^.Y) - cHHRadius * 2;
       
  4618     while y < hwRound(Gear^.Y) do
       
  4619         begin
       
  4620         ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2));
       
  4621         ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
       
  4622         inc(y, 2);
       
  4623         inc(i)
       
  4624         end;
       
  4625 
       
  4626     DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i));
       
  4627     Gear^.dY := HHGear^.dY;
       
  4628     DeleteCI(HHGear);
       
  4629 
       
  4630     doStepHammerHitWork(Gear);
       
  4631     Gear^.doStep := @doStepHammerHitWork
       
  4632 end;
       
  4633 
       
  4634 ////////////////////////////////////////////////////////////////////////////////
       
  4635 procedure doStepResurrectorWork(Gear: PGear);
       
  4636 var
       
  4637     graves: PGearArrayS;
       
  4638     resgear: PGear;
       
  4639     hh: PHedgehog;
       
  4640     i: LongInt;
       
  4641 begin
       
  4642     if (TurnTimeLeft > 0) then
       
  4643         dec(TurnTimeLeft);
       
  4644 
       
  4645     AllInactive := false;
       
  4646     hh := Gear^.Hedgehog;
       
  4647 
       
  4648     // no, you can't do that here
       
  4649     {DrawCentered(hwRound(hh^.Gear^.X) + WorldDx, hwRound(hh^.Gear^.Y) + WorldDy -
       
  4650             cHHRadius - 14 - hh^.HealthTagTex^.h, hh^.HealthTagTex);
       
  4651     }
       
  4652     (*DrawCircle(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, 1.5, 0, 0, $FF,
       
  4653             $FF);*)
       
  4654 
       
  4655     if ((Gear^.Message and gmUp) <> 0) then
       
  4656         begin
       
  4657         if (GameTicks and $F) <> 0 then
       
  4658         exit;
       
  4659         end
       
  4660     else if (GameTicks and $1FF) <> 0 then
       
  4661         exit;
       
  4662 
       
  4663     if Gear^.Power < 45 then
       
  4664         begin
       
  4665         inc(Gear^.Power);
       
  4666         if TestCollisionYwithGear(hh^.Gear, -1) = 0 then
       
  4667             hh^.Gear^.Y := hh^.Gear^.Y - _1;
       
  4668         end;
       
  4669 
       
  4670     graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius);
       
  4671 
       
  4672     if graves.size = 0 then
       
  4673         begin
       
  4674         StopSoundChan(Gear^.SoundChannel);
       
  4675         Gear^.Timer := 250;
       
  4676         Gear^.doStep := @doStepIdle;
       
  4677         exit;
       
  4678         end;
       
  4679 
       
  4680     if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then
       
  4681         begin
       
  4682         if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0;
       
  4683         dec(hh^.Gear^.Health);
       
  4684         if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then
       
  4685             hh^.Gear^.Damage:= 1;
       
  4686         RenderHealth(hh^);
       
  4687         RecountTeamHealth(hh^.Team);
       
  4688         inc(graves.ar^[Gear^.Tag]^.Health);
       
  4689         inc(Gear^.Tag)
       
  4690 {-for i:= 0 to High(graves) do begin
       
  4691             if hh^.Gear^.Health > 0 then begin
       
  4692                 dec(hh^.Gear^.Health);
       
  4693                 inc(graves[i]^.Health);
       
  4694             end;
       
  4695         end; -}
       
  4696         end
       
  4697     else
       
  4698         begin
       
  4699         // now really resurrect the hogs with the hp saved in the graves
       
  4700         for i:= 0 to graves.size - 1 do
       
  4701             if graves.ar^[i]^.Health > 0 then
       
  4702                 begin
       
  4703                 resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0);
       
  4704                 resgear^.Hedgehog := graves.ar^[i]^.Hedgehog;
       
  4705                 resgear^.Health := graves.ar^[i]^.Health;
       
  4706                 PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := resgear;
       
  4707                 graves.ar^[i]^.Message:= graves.ar^[i]^.Message or gmDestroy;
       
  4708                 graves.ar^[i]^.Active:= true;
       
  4709                 RenderHealth(resgear^.Hedgehog^);
       
  4710                 RecountTeamHealth(resgear^.Hedgehog^.Team);
       
  4711                 resgear^.Hedgehog^.Effects[heResurrected]:= 1;
       
  4712                 // only make hat-less hedgehogs look like zombies, preserve existing hats
       
  4713 
       
  4714                 if resgear^.Hedgehog^.Hat = 'NoHat' then
       
  4715                     LoadHedgehogHat(resgear^.Hedgehog^, 'Reserved/Zombie');
       
  4716                 end;
       
  4717 
       
  4718         hh^.Gear^.dY := _0;
       
  4719         hh^.Gear^.dX := _0;
       
  4720         doStepHedgehogMoving(hh^.Gear);
       
  4721         StopSoundChan(Gear^.SoundChannel);
       
  4722         Gear^.Timer := 250;
       
  4723         Gear^.doStep := @doStepIdle;
       
  4724         end
       
  4725     //if hh^.Gear^.Health = 0 then doStepHedgehogFree(hh^.Gear);
       
  4726 end;
       
  4727 
       
  4728 procedure doStepResurrector(Gear: PGear);
       
  4729 var
       
  4730     graves: PGearArrayS;
       
  4731     hh: PHedgehog;
       
  4732     i: LongInt;
       
  4733 begin
       
  4734     AllInactive := false;
       
  4735     graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius);
       
  4736 
       
  4737     if graves.size > 0 then
       
  4738         begin
       
  4739         hh := Gear^.Hedgehog;
       
  4740         for i:= 0 to graves.size - 1 do
       
  4741             begin
       
  4742             PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := nil;
       
  4743             graves.ar^[i]^.Health := 0;
       
  4744             end;
       
  4745         Gear^.doStep := @doStepResurrectorWork;
       
  4746         if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then
       
  4747             begin
       
  4748             if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0;
       
  4749             dec(hh^.Gear^.Health);
       
  4750             if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then
       
  4751                 hh^.Gear^.Damage:= 1;
       
  4752             RenderHealth(hh^);
       
  4753             RecountTeamHealth(hh^.Team);
       
  4754             inc(graves.ar^[Gear^.Tag]^.Health);
       
  4755             inc(Gear^.Tag)
       
  4756             end
       
  4757         end
       
  4758     else
       
  4759         begin
       
  4760         StopSoundChan(Gear^.SoundChannel);
       
  4761         Gear^.Timer := 250;
       
  4762         Gear^.doStep := @doStepIdle;
       
  4763         end
       
  4764 end;
       
  4765 
       
  4766 ////////////////////////////////////////////////////////////////////////////////
       
  4767 procedure doStepNapalmBomb(Gear: PGear);
       
  4768 var
       
  4769     i, gX, gY: LongInt;
       
  4770     dX, dY: hwFloat;
       
  4771 begin
       
  4772     AllInactive := false;
       
  4773     doStepFallingGear(Gear);
       
  4774     if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then
       
  4775     begin
       
  4776         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound);
       
  4777         gX := hwRound(Gear^.X);
       
  4778         gY := hwRound(Gear^.Y);
       
  4779         for i:= 0 to 10 do
       
  4780         begin
       
  4781             dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandomf + _1);
       
  4782             dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1);
       
  4783             AddGear(gX, gY, gtFlame, 0, dX, dY, 0);
       
  4784             AddGear(gX, gY, gtFlame, 0, dX, -dY, 0);
       
  4785             AddGear(gX, gY, gtFlame, 0, -dX, dY, 0);
       
  4786             AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0);
       
  4787         end;
       
  4788         DeleteGear(Gear);
       
  4789         exit
       
  4790     end;
       
  4791     if (Gear^.Timer = 0) then
       
  4792         begin
       
  4793         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound);
       
  4794         for i:= -19 to 19 do
       
  4795            FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0);
       
  4796         DeleteGear(Gear);
       
  4797         exit
       
  4798         end;
       
  4799     if (GameTicks and $3F) = 0 then
       
  4800         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
       
  4801     dec(Gear^.Timer)
       
  4802 end;
       
  4803 
       
  4804 ////////////////////////////////////////////////////////////////////////////////
       
  4805 procedure doStepStructure(Gear: PGear);
       
  4806 var
       
  4807     x, y: LongInt;
       
  4808     HH: PHedgehog;
       
  4809     t: PGear;
       
  4810 begin
       
  4811     HH:= Gear^.Hedgehog;
       
  4812 
       
  4813     if (Gear^.State and gstMoving) <> 0 then
       
  4814         begin
       
  4815         AddGearCI(Gear);
       
  4816         Gear^.dX:= _0;
       
  4817         Gear^.dY:= _0;
       
  4818         Gear^.State:= Gear^.State and (not gstMoving);
       
  4819         end;
       
  4820 
       
  4821     dec(Gear^.Health, Gear^.Damage);
       
  4822     Gear^.Damage:= 0;
       
  4823 
       
  4824     if Gear^.Pos = 1 then
       
  4825         begin
       
  4826         AddGearCI(Gear);
       
  4827         AfterAttack;
       
  4828         if Gear = CurAmmoGear then
       
  4829             CurAmmoGear:= nil;
       
  4830         if HH^.Gear <> nil then
       
  4831             HideHog(HH);
       
  4832         Gear^.Pos:= 2
       
  4833         end;
       
  4834 
       
  4835     if Gear^.Pos = 2 then
       
  4836         begin
       
  4837         if ((GameTicks mod 100) = 0) and (Gear^.Timer < 1000) then
       
  4838             begin
       
  4839             if (Gear^.Timer mod 10) = 0 then
       
  4840                 begin
       
  4841                 DeleteCI(Gear);
       
  4842                 Gear^.Y:= Gear^.Y - _0_5;
       
  4843                 AddGearCI(Gear);
       
  4844                 end;
       
  4845             inc(Gear^.Timer);
       
  4846             end;
       
  4847         if Gear^.Tag <= TotalRounds then
       
  4848             Gear^.Pos:= 3;
       
  4849         end;
       
  4850 
       
  4851     if Gear^.Pos = 3 then
       
  4852         if Gear^.Timer < 1000 then
       
  4853             begin
       
  4854             if (Gear^.Timer mod 10) = 0 then
       
  4855                 begin
       
  4856                 DeleteCI(Gear);
       
  4857                 Gear^.Y:= Gear^.Y - _0_5;
       
  4858                 AddGearCI(Gear);
       
  4859                 end;
       
  4860             inc(Gear^.Timer);
       
  4861             end
       
  4862         else
       
  4863             begin
       
  4864             if HH^.GearHidden <> nil then
       
  4865                 RestoreHog(HH);
       
  4866             Gear^.Pos:= 4;
       
  4867             end;
       
  4868 
       
  4869     if Gear^.Pos = 4 then
       
  4870         if ((GameTicks mod 1000) = 0) and ((GameFlags and gfInvulnerable) = 0) then
       
  4871             begin
       
  4872             t:= GearsList;
       
  4873             while t <> nil do
       
  4874                 begin
       
  4875                 if (t^.Kind = gtHedgehog) and (t^.Hedgehog^.Team^.Clan = HH^.Team^.Clan) then
       
  4876                     t^.Invulnerable:= true;
       
  4877                 t:= t^.NextGear;
       
  4878                 end;
       
  4879             end;
       
  4880 
       
  4881     if Gear^.Health <= 0 then
       
  4882         begin
       
  4883         if HH^.GearHidden <> nil then
       
  4884             RestoreHog(HH);
       
  4885 
       
  4886         x := hwRound(Gear^.X);
       
  4887         y := hwRound(Gear^.Y);
       
  4888 
       
  4889         DeleteCI(Gear);
       
  4890         DeleteGear(Gear);
       
  4891 
       
  4892         doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound);
       
  4893         end;
       
  4894 end;
       
  4895 
       
  4896 ////////////////////////////////////////////////////////////////////////////////
       
  4897 (*
       
  4898  TARDIS needs
       
  4899  Warp in.  Pos = 1
       
  4900  Pause.    Pos = 2
       
  4901  Hide gear  (TARDIS hedgehog was nil)
       
  4902  Warp out. Pos = 3
       
  4903  ... idle active for some time period ...  Pos = 4
       
  4904  Warp in.  Pos = 1
       
  4905  Pause.    Pos = 2
       
  4906  Restore gear  (TARDIS hedgehog was not nil)
       
  4907  Warp out. Pos = 3
       
  4908 *)
       
  4909 
       
  4910 procedure doStepTardisWarp(Gear: PGear);
       
  4911 var HH: PHedgehog;
       
  4912     i,j,cnt: LongWord;
       
  4913 begin
       
  4914 HH:= Gear^.Hedgehog;
       
  4915 if Gear^.Pos = 2 then
       
  4916     begin
       
  4917     StopSoundChan(Gear^.SoundChannel);
       
  4918     if (Gear^.Timer = 0) then
       
  4919         begin
       
  4920         if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible = 0) then
       
  4921             begin
       
  4922             AfterAttack;
       
  4923             if Gear = CurAmmoGear then CurAmmoGear := nil;
       
  4924             if (HH^.Gear^.Damage = 0) and  (HH^.Gear^.Health > 0) and
       
  4925             ((Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then
       
  4926                 HideHog(HH)
       
  4927             end
       
  4928         //else if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible <> 0) then
       
  4929         else if (HH^.GearHidden <> nil) then// and (HH^.Gear^.State and gstInvisible <> 0) then
       
  4930             RestoreHog(HH)
       
  4931         end;
       
  4932 
       
  4933     inc(Gear^.Timer);
       
  4934     if (Gear^.Timer > 2000) and ((GameTicks mod 2000) = 1000) then
       
  4935         begin
       
  4936         Gear^.SoundChannel := LoopSound(sndTardis);
       
  4937         Gear^.Pos:= 3
       
  4938         end
       
  4939     end;
       
  4940 
       
  4941 if (Gear^.Pos = 1) and (GameTicks and $1F = 0) and (Gear^.Power < 255) then
       
  4942     begin
       
  4943     inc(Gear^.Power);
       
  4944     if (Gear^.Power = 172) and (HH^.Gear <> nil) and
       
  4945         (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and
       
  4946         ((HH^.Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then
       
  4947             with HH^.Gear^ do
       
  4948                 begin
       
  4949                 State:= State or gstAnimation;
       
  4950                 Tag:= 2;
       
  4951                 Timer:= 0;
       
  4952                 Pos:= 0
       
  4953                 end
       
  4954     end;
       
  4955 if (Gear^.Pos = 3) and (GameTicks and $1F = 0) and (Gear^.Power > 0) then
       
  4956     dec(Gear^.Power);
       
  4957 if (Gear^.Pos = 1) and (Gear^.Power = 255) and ((GameTicks mod 2000) = 1000) then
       
  4958     Gear^.Pos:= 2;
       
  4959 if (Gear^.Pos = 3) and (Gear^.Power = 0) then
       
  4960     begin
       
  4961     StopSoundChan(Gear^.SoundChannel);
       
  4962     if HH^.GearHidden = nil then
       
  4963         begin
       
  4964         DeleteGear(Gear);
       
  4965         exit
       
  4966         end;
       
  4967     Gear^.Pos:= 4;
       
  4968     // This condition might need tweaking
       
  4969     Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime
       
  4970     end;
       
  4971 
       
  4972 if (Gear^.Pos = 4) then
       
  4973     begin
       
  4974     cnt:= 0;
       
  4975     for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do
       
  4976         for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do
       
  4977             if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil)
       
  4978             and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0)
       
  4979             and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then
       
  4980                 inc(cnt);
       
  4981     if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then
       
  4982         begin
       
  4983         if HH^.GearHidden <> nil then
       
  4984             FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH,true);
       
  4985 
       
  4986         if HH^.GearHidden <> nil then
       
  4987             begin
       
  4988             Gear^.X:= HH^.GearHidden^.X;
       
  4989             Gear^.Y:= HH^.GearHidden^.Y;
       
  4990             end;
       
  4991         Gear^.Timer:= 0;
       
  4992 
       
  4993         if (HH^.GearHidden <> nil) and (cnt = 0) then // do an emergency jump back in this case. the team needs you!
       
  4994             begin
       
  4995             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplosion);
       
  4996             Gear^.Pos:= 2;
       
  4997             Gear^.Power:= 255;
       
  4998             end
       
  4999         else begin
       
  5000             Gear^.SoundChannel := LoopSound(sndTardis);
       
  5001             Gear^.Pos:= 1;
       
  5002             Gear^.Power:= 0;
       
  5003             end
       
  5004         end
       
  5005     else if (CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan) then dec(Gear^.Timer)
       
  5006     end;
       
  5007 
       
  5008 end;
       
  5009 
       
  5010 procedure doStepTardis(Gear: PGear);
       
  5011 var i,j,cnt: LongWord;
       
  5012     HH: PHedgehog;
       
  5013 begin
       
  5014 (*
       
  5015     Conditions for not activating.
       
  5016     1. Hog is last of his clan
       
  5017     2. Sudden Death is in play
       
  5018     3. Hog is a king
       
  5019 *)
       
  5020     HH:= Gear^.Hedgehog;
       
  5021     if HH^.Gear <> nil then
       
  5022     if (HH^.Gear = nil) or (HH^.King) or (SuddenDeathDmg) then
       
  5023         begin
       
  5024         if HH^.Gear <> nil then
       
  5025             begin
       
  5026             HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack);
       
  5027             HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking);
       
  5028             end;
       
  5029         PlaySound(sndDenied);
       
  5030         DeleteGear(gear);
       
  5031         exit
       
  5032         end;
       
  5033     cnt:= 0;
       
  5034     for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do
       
  5035         for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do
       
  5036             if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil)
       
  5037             and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0)
       
  5038             and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then
       
  5039                 inc(cnt);
       
  5040     if cnt < 2 then
       
  5041         begin
       
  5042         if HH^.Gear <> nil then
       
  5043             begin
       
  5044             HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack);
       
  5045             HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking);
       
  5046             end;
       
  5047             PlaySound(sndDenied);
       
  5048             DeleteGear(gear);
       
  5049             exit
       
  5050         end;
       
  5051     Gear^.SoundChannel := LoopSound(sndTardis);
       
  5052     Gear^.doStep:= @doStepTardisWarp
       
  5053 end;
       
  5054 
       
  5055 ////////////////////////////////////////////////////////////////////////////////
       
  5056 
       
  5057 (*
       
  5058 WIP. The ice gun will have the following effects.  It has been proposed by sheepluva that it take the appearance of a large freezer
       
  5059 spewing ice cubes.  The cubes will be visual gears only.  The scatter from them and the impact snow dust should help hide imprecisions in things like the GearsNear effect.
       
  5060 For now we assume a "ray" like a deagle projected out from the gun.
       
  5061 All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks.  This is a simplifying assumption for "gun was applying freezing effect to the same target".
       
  5062   * When fired at water a layer of ice textured land is added above the water.
       
  5063   * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed.
       
  5064   * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen.  As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head.  If the effect is interrupted before reaching the top, the freezing state is cleared.
       
  5065 A frozen hog will animate differently.  To be decided, but possibly in a similar fashion to a grave when it comes to explosions.  The hog might (possibly) not be damaged by explosions.  This might make freezing potentially useful for friendlies in a bad position.  It might be better to allow damage though.
       
  5066 A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again.
       
  5067 *)
       
  5068 
       
  5069 
       
  5070 procedure updateFuel(Gear: PGear);
       
  5071 var
       
  5072   t:LongInt;
       
  5073 begin
       
  5074     t:= Gear^.Health div 10;
       
  5075     if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  5076     begin
       
  5077     Gear^.Damage:= t;
       
  5078     FreeTexture(Gear^.Tex);
       
  5079     Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) +
       
  5080               '%', cWhiteColor, fntSmall)
       
  5081     end;
       
  5082     if Gear^.Message and (gmUp or gmDown) <> 0 then
       
  5083         begin
       
  5084         StopSoundChan(Gear^.SoundChannel);
       
  5085         Gear^.SoundChannel:= -1;
       
  5086         if GameTicks mod 40 = 0 then dec(Gear^.Health)
       
  5087         end
       
  5088     else 
       
  5089         begin
       
  5090         if Gear^.SoundChannel = -1 then
       
  5091             Gear^.SoundChannel := LoopSound(sndIceBeam);
       
  5092         if GameTicks mod 10 = 0 then dec(Gear^.Health)
       
  5093         end
       
  5094 end;
       
  5095 
       
  5096 
       
  5097 procedure updateTarget(Gear:PGear; newX, newY:HWFloat);
       
  5098 //    var
       
  5099 //    iter:PGear;
       
  5100 begin
       
  5101   with Gear^ do
       
  5102   begin
       
  5103     dX:= newX;
       
  5104     dY:= newY;
       
  5105     Pos:= 0;
       
  5106     Target.X:= NoPointX;
       
  5107     LastDamage:= nil;
       
  5108     X:= Hedgehog^.Gear^.X;
       
  5109     Y:= Hedgehog^.Gear^.Y;
       
  5110   end;
       
  5111 end;
       
  5112 
       
  5113 procedure doStepIceGun(Gear: PGear);
       
  5114 const iceWaitCollision = 0;
       
  5115 const iceCollideWithGround = 1;
       
  5116 //const iceWaitNextTarget:Longint = 2;
       
  5117 //const iceCollideWithHog:Longint = 4;
       
  5118 const iceCollideWithWater = 5;
       
  5119 //const waterFreezingTime:Longint = 500;
       
  5120 const groundFreezingTime = 1000;
       
  5121 const iceRadius = 32;
       
  5122 const iceHeight = 40;
       
  5123 var
       
  5124     HHGear, iter: PGear;
       
  5125     landRect: TSDL_Rect;
       
  5126     ndX, ndY: hwFloat;
       
  5127     i, t, gX, gY: LongInt;
       
  5128     hogs: PGearArrayS;
       
  5129     vg: PVisualGear;
       
  5130 begin
       
  5131     HHGear := Gear^.Hedgehog^.Gear;
       
  5132     if (Gear^.Message and gmAttack <> 0) or (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) or (HHGear^.dX.QWordValue > 4294967)  then
       
  5133         begin
       
  5134         StopSoundChan(Gear^.SoundChannel);
       
  5135         DeleteGear(Gear);
       
  5136         AfterAttack;
       
  5137         exit
       
  5138         end;
       
  5139     updateFuel(Gear);
       
  5140 
       
  5141     with Gear^ do
       
  5142         begin
       
  5143         HedgehogChAngle(HHGear);
       
  5144         ndX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _4;
       
  5145         ndY:= -AngleCos(HHGear^.Angle) * _4;
       
  5146         if (ndX <> dX) or (ndY <> dY) or
       
  5147            ((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and
       
  5148              (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0))) then
       
  5149             begin
       
  5150             updateTarget(Gear, ndX, ndY);
       
  5151             Timer := iceWaitCollision;
       
  5152             end
       
  5153         else
       
  5154             begin
       
  5155             X:= X + dX;
       
  5156             Y:= Y + dY;
       
  5157             gX:= hwRound(X);
       
  5158             gY:= hwRound(Y);
       
  5159             if Target.X = NoPointX then t:= hwRound(hwSqr(X-HHGear^.X)+hwSqr(Y-HHGear^.Y));
       
  5160 
       
  5161             if Target.X <> NoPointX then
       
  5162                 begin
       
  5163                 CheckCollision(Gear);
       
  5164                 if (State and gstCollision) <> 0 then
       
  5165                     begin
       
  5166                     if Timer = iceWaitCollision then
       
  5167                         begin
       
  5168                         Timer := iceCollideWithGround;
       
  5169                         Power := GameTicks;
       
  5170                         end
       
  5171                     end
       
  5172                 else if (target.y >= cWaterLine) then
       
  5173                     begin
       
  5174                     if Timer = iceWaitCollision then
       
  5175                         begin
       
  5176                         Timer := iceCollideWithWater;
       
  5177                         Power := GameTicks;
       
  5178                         end;
       
  5179                     end;
       
  5180 
       
  5181                 if (abs(gX-Target.X) < 2) and (abs(gY-Target.Y) < 2) then
       
  5182                     begin
       
  5183                     X:= HHGear^.X;
       
  5184                     Y:= HHGear^.Y
       
  5185                     end;
       
  5186 
       
  5187                 if (Timer = iceCollideWithGround) and ((GameTicks - Power) > groundFreezingTime) then
       
  5188                     begin
       
  5189                     FillRoundInLand(target.x, target.y, iceRadius, icePixel);
       
  5190                     landRect.x := min(max(target.x - iceRadius, 0), LAND_WIDTH - 1);
       
  5191                     landRect.y := min(max(target.y - iceRadius, 0), LAND_HEIGHT - 1);
       
  5192                     landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1);
       
  5193                     landRect.h := min(2*iceRadius, LAND_HEIGHT - landRect.y - 1);
       
  5194                     UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true);
       
  5195                     
       
  5196                     // Freeze nearby mines/explosives/cases too
       
  5197                     iter := GearsList;
       
  5198                     while iter <> nil do
       
  5199                         begin
       
  5200                         if (iter^.State and gstFrozen = 0) and
       
  5201                            ((iter^.Kind = gtExplosives) or (iter^.Kind = gtCase) or (iter^.Kind = gtMine)) and 
       
  5202                            (abs(iter^.X.Round-target.x)+abs(iter^.Y.Round-target.y)+2<2*iceRadius) and (Distance(iter^.X-int2hwFloat(target.x),iter^.Y-int2hwFloat(target.y))<int2hwFloat(iceRadius*2)) then
       
  5203                             begin
       
  5204                             for t:= 0 to 5 do
       
  5205                                 begin
       
  5206                                 vg:= AddVisualGear(hwRound(iter^.X)+random(4)-8, hwRound(iter^.Y)+random(8), vgtDust, 1);
       
  5207                                 if vg <> nil then
       
  5208                                     begin
       
  5209                                     i:= random(100) + 155;
       
  5210                                     vg^.Tint:= (i shl 24) or (i shl 16) or ($FF shl 8) or (random(200) + 55);
       
  5211                                     vg^.Angle:= random(360);
       
  5212                                     vg^.dx:= 0.001 * random(80);
       
  5213                                     vg^.dy:= 0.001 * random(80)
       
  5214                                     end
       
  5215                                 end;
       
  5216                             PlaySound(sndHogFreeze);
       
  5217                             if iter^.Kind = gtMine then // dud mine block
       
  5218                                 begin
       
  5219                                 iter^.State:= iter^.State or gstFrozen;
       
  5220                                 vg:= AddVisualGear(hwRound(iter^.X) - 4  + Random(8), hwRound(iter^.Y) - 4 - Random(4), vgtSmoke);
       
  5221                                 if vg <> nil then
       
  5222                                     vg^.Scale:= 0.5;
       
  5223                                 PlaySound(sndVaporize);
       
  5224                                 iter^.Health := 0;
       
  5225                                 iter^.Damage := 0;
       
  5226                                 iter^.State := iter^.State and (not gstAttacking)
       
  5227                                 end
       
  5228                             else if iter^.Kind = gtCase then
       
  5229                                 begin
       
  5230                                 DeleteCI(iter);
       
  5231                                 iter^.State:= iter^.State or gstFrozen;
       
  5232                                 AddGearCI(iter)
       
  5233                                 end
       
  5234                             else // gtExplosives
       
  5235                                 begin
       
  5236                                 iter^.State:= iter^.State or gstFrozen;
       
  5237                                 iter^.Health:= iter^.Health + cBarrelHealth
       
  5238                                 end
       
  5239                             end;
       
  5240                         iter:= iter^.NextGear
       
  5241                         end;
       
  5242 
       
  5243                     // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius);
       
  5244                     SetAllHHToActive;
       
  5245                     Timer := iceWaitCollision;
       
  5246                     end;
       
  5247 
       
  5248                 if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then
       
  5249                     begin
       
  5250                     PlaySound(sndHogFreeze);
       
  5251                     DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight);
       
  5252                     SetAllHHToActive;
       
  5253                     Timer := iceWaitCollision;
       
  5254                     end;
       
  5255 (*
       
  5256  Any ideas for something that would look good here?
       
  5257                 if (Target.X <> NoPointX) and ((Timer = iceCollideWithGround) or (Timer = iceCollideWithWater)) and (GameTicks mod max((groundFreezingTime-((GameTicks - Power)*2)),2) = 0) then //and CheckLandValue(Target.X, Target.Y, lfIce) then
       
  5258                     begin
       
  5259                         vg:= AddVisualGear(Target.X+random(20)-10, Target.Y+random(40)-10, vgtDust, 1);
       
  5260                         if vg <> nil then
       
  5261                             begin
       
  5262                             i:= random(100) + 155;
       
  5263                             vg^.Tint:= IceColor or $FF;
       
  5264                             vg^.Angle:= random(360);
       
  5265                             vg^.dx:= 0.001 * random(80);
       
  5266                             vg^.dy:= 0.001 * random(80)
       
  5267                             end
       
  5268                     end;
       
  5269 *)
       
  5270 
       
  5271 // freeze nearby hogs
       
  5272                 hogs := GearsNear(int2hwFloat(Target.X), int2hwFloat(Target.Y), gtHedgehog, Gear^.Radius*2);
       
  5273                 if hogs.size > 0 then
       
  5274                     for i:= 0 to hogs.size - 1 do
       
  5275                         if hogs.ar^[i] <> HHGear then
       
  5276                             if GameTicks mod 5 = 0 then
       
  5277                                 begin
       
  5278                                 hogs.ar^[i]^.Active:= true;
       
  5279                                 if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] < 256 then
       
  5280                                     hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] := hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] + 1
       
  5281                                 else if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] = 256 then
       
  5282                                     begin
       
  5283                                     hogs.ar^[i]^.Hedgehog^.Effects[heFrozen]:= 200000-1;//cHedgehogTurnTime + cReadyDelay
       
  5284                                     PlaySound(sndHogFreeze);
       
  5285                                     end;
       
  5286                                 end;
       
  5287                 inc(Pos)
       
  5288                 end
       
  5289             else if (t > 400) and ((gY > cWaterLine) or
       
  5290                     (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0))
       
  5291                         and (Land[gY, gX] <> 0))) then
       
  5292                 begin
       
  5293                 Target.X:= gX;
       
  5294                 Target.Y:= gY;
       
  5295                 X:= HHGear^.X;
       
  5296                 Y:= HHGear^.Y
       
  5297                 end;
       
  5298             if (gX > max(LAND_WIDTH,4096)*2) or
       
  5299                     (gX < -max(LAND_WIDTH,4096)) or
       
  5300                     (gY < -max(LAND_HEIGHT,4096)) or
       
  5301                     (gY > max(LAND_HEIGHT,4096)+512) then
       
  5302                 begin
       
  5303                 //X:= HHGear^.X;
       
  5304                 //Y:= HHGear^.Y
       
  5305                 Target.X:= gX;
       
  5306                 Target.Y:= gY;
       
  5307                 end
       
  5308         end
       
  5309     end;
       
  5310 end;
       
  5311 
       
  5312 procedure doStepAddAmmo(Gear: PGear);
       
  5313 var a: TAmmoType;
       
  5314     gi: PGear;
       
  5315 begin
       
  5316 if Gear^.Timer > 0 then dec(Gear^.Timer)
       
  5317 else
       
  5318     begin
       
  5319     if Gear^.Pos = posCaseUtility then
       
  5320         a:= GetUtility(Gear^.Hedgehog)
       
  5321     else
       
  5322         a:= GetAmmo(Gear^.Hedgehog);
       
  5323     CheckSum:= CheckSum xor GameTicks;
       
  5324     gi := GearsList;
       
  5325     while gi <> nil do
       
  5326         begin
       
  5327         with gi^ do CheckSum:= CheckSum xor X.round xor X.frac xor dX.round xor dX.frac xor Y.round xor Y.frac xor dY.round xor dY.frac;
       
  5328         AddRandomness(CheckSum);
       
  5329         if gi^.Kind = gtGenericFaller then gi^.State:= gi^.State and not gstTmpFlag;
       
  5330         gi := gi^.NextGear
       
  5331         end;
       
  5332     AddPickup(Gear^.Hedgehog^, a, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y));
       
  5333     DeleteGear(Gear)
       
  5334     end;
       
  5335 end;
       
  5336 
       
  5337 procedure doStepGenericFaller(Gear: PGear);
       
  5338 begin
       
  5339 if Gear^.Timer < $FFFFFFFF then
       
  5340     if Gear^.Timer > 0 then
       
  5341         dec(Gear^.Timer)
       
  5342     else
       
  5343         begin
       
  5344         DeleteGear(Gear);
       
  5345         exit
       
  5346         end;
       
  5347 if (Gear^.State and gstTmpFlag <> 0) or (GameTicks and $7 = 0) then
       
  5348     begin
       
  5349     doStepFallingGear(Gear);
       
  5350     if (Gear^.State and gstInvisible <> 0) and (GameTicks and $FF = 0) and (hwRound(Gear^.X) < LongInt(leftX)) or (hwRound(Gear^.X) > LongInt(rightX)) or (hwRound(Gear^.Y) < LongInt(topY)) then
       
  5351         begin
       
  5352         Gear^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX);
       
  5353         Gear^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY);
       
  5354         Gear^.dX:= _90-(GetRandomf*_360);
       
  5355         Gear^.dY:= _90-(GetRandomf*_360)
       
  5356         end;
       
  5357     end
       
  5358 end;
       
  5359 
       
  5360 procedure doStepCreeper(Gear: PGear);
       
  5361 var hogs: PGearArrayS;
       
  5362     HHGear: PGear;
       
  5363     tdX: hwFloat;
       
  5364     dir: LongInt;
       
  5365 begin
       
  5366 doStepFallingGear(Gear);
       
  5367 if Gear^.Timer > 0 then dec(Gear^.Timer);
       
  5368 // creeper sleep phase
       
  5369 if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit;
       
  5370 
       
  5371 if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear
       
  5372 else HHGear:= nil;
       
  5373 
       
  5374 // creeper boom phase
       
  5375 if (Gear^.State and gstTmpFlag <> 0) then
       
  5376     begin
       
  5377     if (Gear^.Timer = 0) then
       
  5378         begin
       
  5379         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound);
       
  5380         DeleteGear(Gear)
       
  5381         end;
       
  5382     // ssssss he essssscaped
       
  5383     if (Gear^.Timer > 250) and ((HHGear = nil) or
       
  5384             (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) >  180) and
       
  5385             (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then
       
  5386         begin
       
  5387         Gear^.State:= Gear^.State and (not gstTmpFlag);
       
  5388         Gear^.Timer:= 0
       
  5389         end;
       
  5390     exit
       
  5391     end;
       
  5392 
       
  5393 // Search out a new target, as target seek time has expired, target is dead, target is out of range, or we did not have a target
       
  5394 if (HHGear = nil) or (Gear^.Timer = 0) or
       
  5395    (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) >  Gear^.Angle) and
       
  5396         (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle)))
       
  5397     then
       
  5398     begin
       
  5399     hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle);
       
  5400     if hogs.size > 1 then
       
  5401         Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog
       
  5402     else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog
       
  5403     else Gear^.Hedgehog:= nil;
       
  5404     if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000;
       
  5405     exit
       
  5406     end;
       
  5407 
       
  5408 // we have a target. move the creeper.
       
  5409 if HHGear <> nil then
       
  5410     begin
       
  5411     // GOTCHA
       
  5412     if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) <  50) and
       
  5413          (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then
       
  5414         begin
       
  5415         // hisssssssssss
       
  5416         Gear^.State:= Gear^.State or gstTmpFlag;
       
  5417         Gear^.Timer:= 1500;
       
  5418         exit
       
  5419         end;
       
  5420     if (Gear^.State and gstMoving <> 0) then
       
  5421         begin
       
  5422         Gear^.dY:= _0;
       
  5423         Gear^.dX:= _0;
       
  5424         end
       
  5425     else if (GameTicks and $FF = 0) then
       
  5426         begin
       
  5427         tdX:= HHGear^.X-Gear^.X;
       
  5428         dir:= hwSign(tdX);
       
  5429         if not TestCollisionX(Gear, dir) then
       
  5430             Gear^.X:= Gear^.X + signAs(_1,tdX);
       
  5431         if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then
       
  5432             begin
       
  5433             Gear^.dX:= SignAs(_0_15, tdX);
       
  5434             Gear^.dY:= -_0_3;
       
  5435             Gear^.State:= Gear^.State or gstMoving
       
  5436             end
       
  5437         end;
       
  5438     end;
       
  5439 end;
       
  5440 
       
  5441 ////////////////////////////////////////////////////////////////////////////////
       
  5442 procedure doStepKnife(Gear: PGear);
       
  5443 //var ox, oy: LongInt;
       
  5444 //    la: hwFloat;
       
  5445 var   a: real;
       
  5446 begin
       
  5447     // Gear is shrunk so it can actually escape the hog without carving into the terrain
       
  5448     if (Gear^.Radius = 4) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 7;
       
  5449     if Gear^.Damage > 100 then Gear^.CollisionMask:= 0
       
  5450     else if Gear^.Damage > 30 then
       
  5451         if GetRandom(max(4,18-Gear^.Damage div 10)) < 3 then Gear^.CollisionMask:= 0;
       
  5452     Gear^.Damage:= 0;
       
  5453     if Gear^.Timer > 0 then dec(Gear^.Timer);
       
  5454     if (Gear^.State and gstMoving <> 0) and (Gear^.State and gstCollision = 0) then
       
  5455         begin
       
  5456         DeleteCI(Gear);
       
  5457         Gear^.Radius:= 7;
       
  5458         // used for damage and impact calc. needs balancing I think
       
  5459         Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4));
       
  5460         doStepFallingGear(Gear);
       
  5461         AllInactive := false;
       
  5462         a:= Gear^.DirAngle;
       
  5463         CalcRotationDirAngle(Gear);
       
  5464         Gear^.DirAngle:= a+(Gear^.DirAngle-a)*2*hwSign(Gear^.dX) // double rotation
       
  5465         end
       
  5466     else if (Gear^.CollisionIndex = -1) and (Gear^.Timer = 0) then
       
  5467         begin
       
  5468         (*ox:= 0; oy:= 0;
       
  5469         if TestCollisionYwithGear(Gear, -1) <> 0 then oy:= -1;
       
  5470         if TestCollisionXwithGear(Gear, 1)       then ox:=  1;
       
  5471         if TestCollisionXwithGear(Gear, -1)      then ox:= -1;
       
  5472         if TestCollisionYwithGear(Gear, 1) <> 0  then oy:=  1;
       
  5473         if Gear^.Health > 0 then
       
  5474             PlaySound(sndRopeAttach);
       
  5475 
       
  5476         la:= _10000;
       
  5477         if (ox <> 0) or (oy <> 0) then
       
  5478             la:= CalcSlopeNearGear(Gear, ox, oy);
       
  5479         if la = _10000 then
       
  5480             begin
       
  5481             // debug for when we couldn't get an angle
       
  5482             //AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite);
       
  5483 *)
       
  5484             Gear^.DirAngle:= DxDy2Angle(Gear^.dX, Gear^.dY) + (random(30)-15);
       
  5485             if (Gear^.dX.isNegative and Gear^.dY.isNegative) or
       
  5486              ((not Gear^.dX.isNegative) and (not Gear^.dY.isNegative)) then Gear^.DirAngle:= Gear^.DirAngle-90;
       
  5487  //           end
       
  5488  //       else Gear^.DirAngle:= hwFloat2Float(la)*90; // sheepluva's comment claims 45deg = 0.5 - yet orientation doesn't seem consistent?
       
  5489  //       AddFileLog('la: '+floattostr(la)+' DirAngle: '+inttostr(round(Gear^.DirAngle)));
       
  5490         Gear^.dX:= _0;
       
  5491         Gear^.dY:= _0;
       
  5492         Gear^.State:= Gear^.State and (not gstMoving) or gstCollision;
       
  5493         Gear^.Radius:= 16;
       
  5494         if Gear^.Health > 0 then AmmoShove(Gear, Gear^.Health, 0);
       
  5495         Gear^.Health:= 0;
       
  5496         Gear^.Timer:= 500;
       
  5497         AddGearCI(Gear)
       
  5498         end
       
  5499     else if GameTicks and $3F = 0 then
       
  5500         begin
       
  5501         if  (TestCollisionYwithGear(Gear, -1) = 0)
       
  5502         and (not TestCollisionXwithGear(Gear, 1))
       
  5503         and (not TestCollisionXwithGear(Gear, -1))
       
  5504         and (TestCollisionYwithGear(Gear, 1) = 0) then Gear^.State:= Gear^.State and (not gstCollision) or gstMoving;
       
  5505         end
       
  5506 end;
       
  5507 (*
       
  5508  This didn't end up getting used, but, who knows, might be reasonable for javellin or something
       
  5509 // Make the knife initial angle based on the hog attack angle, or is that too hard?
       
  5510 procedure doStepKnife(Gear: PGear);
       
  5511 var t,
       
  5512     gx, gy, ga,  // gear x,y,angle
       
  5513     lx, ly, la, // land x,y,angle
       
  5514     ox, oy, // x,y offset
       
  5515     w, h,   // wXh of clip area
       
  5516     tx, ty  // tip position in sprite
       
  5517     : LongInt;
       
  5518     surf: PSDL_Surface;
       
  5519     s: hwFloat;
       
  5520 
       
  5521 begin
       
  5522     Gear^.dY := Gear^.dY + cGravity;
       
  5523     if (GameFlags and gfMoreWind) <> 0 then
       
  5524         Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density;
       
  5525     Gear^.X := Gear^.X + Gear^.dX;
       
  5526     Gear^.Y := Gear^.Y + Gear^.dY;
       
  5527     CheckGearDrowning(Gear);
       
  5528     gx:= hwRound(Gear^.X);
       
  5529     gy:= hwRound(Gear^.Y);
       
  5530     if Gear^.State and gstDrowning <> 0 then exit;
       
  5531     with Gear^ do
       
  5532         begin
       
  5533         if CheckLandValue(gx, gy, lfLandMask) then
       
  5534             begin
       
  5535             t:= Angle + hwRound((hwAbs(dX)+hwAbs(dY)) * _10);
       
  5536 
       
  5537             if t < 0 then inc(t, 4096)
       
  5538             else if 4095 < t then dec(t, 4096);
       
  5539             Angle:= t;
       
  5540 
       
  5541             DirAngle:= Angle / 4096 * 360
       
  5542             end
       
  5543         else
       
  5544             begin
       
  5545 //This is the set of postions for the knife.
       
  5546 //Using FlipSurface and copyToXY the knife can be written to the LandPixels at 32 positions, and an appropriate line drawn in Land.
       
  5547             t:= Angle mod 1024;
       
  5548             case t div 128 of
       
  5549                 0:  begin
       
  5550                     ox:=   2; oy:= 5;
       
  5551                     w :=  25;  h:= 5;
       
  5552                     tx:=   0; ty:= 2
       
  5553                     end;
       
  5554                 1:  begin
       
  5555                     ox:=   2; oy:= 15;
       
  5556                      w:=  24;  h:=  8;
       
  5557                     tx:=   0; ty:=  7
       
  5558                     end;
       
  5559                 2:  begin
       
  5560                     ox:=   2; oy:= 27;
       
  5561                      w:=  23;  h:= 12;
       
  5562                     tx:= -12; ty:= -5
       
  5563                     end;
       
  5564                 3:  begin
       
  5565                     ox:=   2; oy:= 43;
       
  5566                      w:=  21;  h:= 15;
       
  5567                     tx:=   0; ty:= 14
       
  5568                     end;
       
  5569                 4:  begin
       
  5570                     ox:= 29; oy:=  8;
       
  5571                      w:= 19;  h:= 19;
       
  5572                     tx:=  0; ty:= 17
       
  5573                     end;
       
  5574                 5:  begin
       
  5575                     ox:= 29; oy:=  32;
       
  5576                      w:= 15;  h:=  21;
       
  5577                     tx:=  0; ty:=  20
       
  5578                     end;
       
  5579                 6:  begin
       
  5580                     ox:= 51; oy:=   3;
       
  5581                      w:= 11;  h:=  23;
       
  5582                     tx:=  0; ty:=  22
       
  5583                     end;
       
  5584                 7:  begin
       
  5585                     ox:= 51; oy:=  34;
       
  5586                      w:=  7;  h:=  24;
       
  5587                     tx:=  0; ty:=  23
       
  5588                     end
       
  5589                 end;
       
  5590 
       
  5591             surf:= SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RMask, GMask, BMask, AMask);
       
  5592             copyToXYFromRect(SpritesData[sprKnife].Surface, surf, ox, oy, w, h, 0, 0);
       
  5593             // try to make the knife hit point first
       
  5594             lx := 0;
       
  5595             ly := 0;
       
  5596             if CalcSlopeTangent(Gear, gx, gy, lx, ly, 255) then
       
  5597                 begin
       
  5598                 la:= vector2Angle(int2hwFloat(lx), int2hwFloat(ly));
       
  5599                 ga:= vector2Angle(dX, dY);
       
  5600                 AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle));
       
  5601                 // change  to 0 to 4096 forced by LongWord in Gear
       
  5602                 if la < 0 then la:= 4096+la;
       
  5603                 if ga < 0 then ga:= 4096+ga;
       
  5604                 if ((Angle > ga) and (Angle < la)) or ((Angle < ga) and (Angle > la)) then
       
  5605                     begin
       
  5606                     if Angle >= 2048 then dec(Angle, 2048)
       
  5607                     else if Angle < 2048 then inc(Angle, 2048)
       
  5608                     end;
       
  5609                 AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle))
       
  5610                 end;
       
  5611             case Angle div 1024 of
       
  5612                 0:  begin
       
  5613                     flipSurface(surf, true);
       
  5614                     flipSurface(surf, true);
       
  5615                     BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-(h-ty), w, surf)
       
  5616                     end;
       
  5617                 1:  begin
       
  5618                     flipSurface(surf, false);
       
  5619                     BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-ty, w, surf)
       
  5620                     end;
       
  5621                 2:  begin // knife was actually drawn facing this way...
       
  5622                     BlitImageAndGenerateCollisionInfo(gx-tx, gy-ty, w, surf)
       
  5623                     end;
       
  5624                 3:  begin
       
  5625                     flipSurface(surf, true);
       
  5626                     BlitImageAndGenerateCollisionInfo(gx-tx, gy-(h-ty), w, surf)
       
  5627                     end
       
  5628                 end;
       
  5629             SDL_FreeSurface(surf);
       
  5630             // this needs to calculate actual width/height + land clipping since update texture doesn't.
       
  5631             // i.e. this will crash if you fire near sides of map, but until I get the blit right, not going to put real values
       
  5632             UpdateLandTexture(hwRound(X)-32, 64, hwRound(Y)-32, 64, true);
       
  5633             DeleteGear(Gear);
       
  5634             exit
       
  5635             end
       
  5636         end;
       
  5637 end;
       
  5638 *)