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