hedgewars/GSHandlers.inc
author nemo
Sun, 02 Oct 2011 10:36:43 -0400
changeset 6081 537bbd5c1a62
parent 6011 519f8a58c021
child 6092 fd602b5838ab
permissions -rw-r--r--
Basic test implementation of an ice flag. Allows for slick parts of terrain. Intended for ice gun, or "ice" mask on portions of land objects. In this test variant it is triggered on girders/objects/bridges of the snow/christmas theme, or on a map that uses blue as a mask colour. Probably needs sheepluva's slope detection to make slopes more slippery to climb.

(*
 * Hedgewars, a free turn based strategy game
 * Copyright (c) 2004-2011 Andrey Korotaev <unC0Rr@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 *)

(*
 * This file contains the step handlers for gears.
 *
 * Important: Since gears change the course of the game, calculations that
 *            lead to different results for different clients/players/machines
 *            should NOT occur!
 *            Use safe functions and data types! (e.g. GetRandom() and hwFloat)
 *)

procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean);
var
    dX, dY, sX, sY: hwFloat;
    i, steps: LongWord;
    caller: TGearStepProcedure;
begin
    dX:= Gear^.dX;
    dY:= Gear^.dY;
    steps:= max(abs(hwRound(Gear^.X+dX)-hwRound(Gear^.X)), abs(hwRound(Gear^.Y+dY)-hwRound(Gear^.Y)));

    // Gear is still on the same Pixel it was before
    if steps < 1 then
        begin
        if onlyCheckIfChanged then
            begin
            Gear^.X := Gear^.X + dX;
            Gear^.Y := Gear^.Y + dY;
            EXIT;
            end
        else
            steps := 1;
        end;

    if steps > 1 then
        begin
        sX:= dX / steps;
        sY:= dY / steps;
        end
    else
        begin
        sX:= dX;
        sY:= dY;
        end;

    caller:= Gear^.doStep;

    for i:= 1 to steps do
        begin
        Gear^.X := Gear^.X + sX;
        Gear^.Y := Gear^.Y + sY;
        step(Gear);
        if (Gear^.doStep <> caller)
        or ((Gear^.State and gstCollision) <> 0)
        or ((Gear^.State and gstMoving) = 0) then
            break;
        end;
end;

procedure makeHogsWorry(x, y: hwFloat; r: LongInt);
var 
    gi: PGear;
    d: LongInt;
begin
    gi := GearsList;
    while gi <> nil do
        begin
        if (gi^.Kind = gtHedgehog) then
            begin
            d := r - hwRound(Distance(gi^.X - x, gi^.Y - y));
            if (d > 1) and not gi^.Invulnerable and (GetRandom(2) = 0) then
                begin
                if (CurrentHedgehog^.Gear = gi) then
                    PlaySound(sndOops, gi^.Hedgehog^.Team^.voicepack)
                else
                    begin
                    if (gi^.State and gstMoving) = 0 then
                        gi^.State := gi^.State or gstLoser;
                    if d > r div 2 then
                        PlaySound(sndNooo, gi^.Hedgehog^.Team^.voicepack)
                    else
                        PlaySound(sndUhOh, gi^.Hedgehog^.Team^.voicepack);
                    end;
                end;
            end;
        gi := gi^.NextGear
        end;
end;

procedure HideHog(HH: PHedgehog);
begin
ScriptCall('onHogHide', HH^.Gear^.Uid);
DeleteCI(HH^.Gear);
if FollowGear = HH^.Gear then FollowGear:= nil;
if lastGearByUID = HH^.Gear then lastGearByUID := nil;
RemoveGearFromList(HH^.Gear);
with HH^.Gear^ do
    begin
    Z := cHHZ;
    Active := false;
    State:= State and not (gstHHDriven or gstAttacking or gstAttacked);
    Message := Message and not gmAttack;
    end;
HH^.GearHidden:= HH^.Gear;
HH^.Gear:= nil
end;

procedure RestoreHog(HH: PHedgehog);
begin
HH^.Gear:=HH^.GearHidden;
HH^.GearHidden:= nil;
InsertGearToList(HH^.Gear);
HH^.Gear^.State:= (HH^.Gear^.State and not (gstHHDriven or gstInvisible or gstAttacking)) or gstAttacked;
AddGearCI(HH^.Gear);
HH^.Gear^.Active:= true;
ScriptCall('onHogRestore', HH^.Gear^.Uid)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepDrowningGear(Gear: PGear);
forward;

function CheckGearDrowning(Gear: PGear): boolean;
var 
    skipSpeed, skipAngle, skipDecay: hwFloat;
    i, maxDrops, X, Y: LongInt;
    vdX, vdY: real;
    particle: PVisualGear;
    isSubmersible: boolean;
begin
    isSubmersible:= (Gear = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.AmmoType = amJetpack);
    // probably needs tweaking. might need to be in a case statement based upon gear type
    Y:= hwRound(Gear^.Y);
    if cWaterLine < Y + Gear^.Radius then
        begin
        skipSpeed := _0_25;
        skipAngle := _1_9;
        skipDecay := _0_87;
        X:= hwRound(Gear^.X);
        vdX:= hwFloat2Float(Gear^.dX);
        vdY:= hwFloat2Float(Gear^.dY);
        // this could perhaps be a tiny bit higher.
        if  (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed) and
           (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)) then
            begin
            Gear^.dY.isNegative := true;
            Gear^.dY := Gear^.dY * skipDecay;
            Gear^.dX := Gear^.dX * skipDecay;
            CheckGearDrowning := false;
            PlaySound(sndSkip)
            end
        else
            begin
            if not isSubmersible then
                begin
                CheckGearDrowning := true;
                Gear^.State := gstDrowning;
                Gear^.RenderTimer := false;
                if (Gear^.Kind <> gtSniperRifleShot) and (Gear^.Kind <> gtShotgunShot) and 
                   (Gear^.Kind <> gtDEagleShot) and (Gear^.Kind <> gtSineGunShot) then
                    if Gear^.Kind = gtHedgehog then 
                        begin
                        if Gear^.Hedgehog^.Effects[heResurrectable] then
                            ResurrectHedgehog(Gear)
                        else
                            begin
                            Gear^.doStep := @doStepDrowningGear;
                            Gear^.State := Gear^.State and (not gstHHDriven);
                            AddCaption(Format(GetEventString(eidDrowned), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
                            end
                        end
                    else Gear^.doStep := @doStepDrowningGear;
                    if Gear^.Kind = gtFlake then exit // skip splashes 
                end;
            if ((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius)) or
               (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0) and (CurAmmoGear^.dY < _0_01))) then
                // don't play splash if they are already way past the surface
                PlaySound(sndSplash)
            end;

        if ((cReducedQuality and rqPlainSplash) = 0) and 
           (((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius)) or
             (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0) and (CurAmmoGear^.dY < _0_01)))) then
            begin
            AddVisualGear(X, cWaterLine, vgtSplash);

            maxDrops := (Gear^.Radius div 2) + round(vdX * Gear^.Radius * 2) + round(vdY * Gear^.Radius * 2);
            for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do
                begin
                particle := AddVisualGear(X - 3 + Random(6), cWaterLine, vgtDroplet);
                if particle <> nil then
                    begin
                    particle^.dX := particle^.dX - vdX / 10;
                    particle^.dY := particle^.dY - vdY / 5;
                    end
                end
            end;
        if isSubmersible and (CurAmmoGear^.Pos = 0) then CurAmmoGear^.Pos := 1000
        end
    else
        CheckGearDrowning := false;
end;

procedure CheckCollision(Gear: PGear); inline;
begin
    if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) or (TestCollisionYwithGear(Gear, hwSign(Gear^.dY)) <> 0)
        then Gear^.State := Gear^.State or      gstCollision
    else Gear^.State := Gear^.State and not gstCollision
end;

procedure CheckCollisionWithLand(Gear: PGear); inline;
begin
    if TestCollisionX(Gear, hwSign(Gear^.dX)) or TestCollisionY(Gear, hwSign(Gear^.dY)
       )
        then Gear^.State := Gear^.State or      gstCollision
    else Gear^.State := Gear^.State and not gstCollision
end;

procedure CheckHHDamage(Gear: PGear);
var 
    dmg: Longword;
    i: LongInt;
    particle: PVisualGear;
begin
    if _0_4 < Gear^.dY then
        begin
        dmg := ModifyDamage(1 + hwRound((hwAbs(Gear^.dY) - _0_4) * 70), Gear);
        PlaySound(sndBump);
        if dmg < 1 then exit;

        for i:= min(12, (3 + dmg div 10)) downto 0 do
            begin
            particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
            if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480);
            end;

        if (Gear^.Invulnerable) then exit;

        //if _0_6 < Gear^.dY then
        //    PlaySound(sndOw4, Gear^.Hedgehog^.Team^.voicepack)
        //else
        //    PlaySound(sndOw1, Gear^.Hedgehog^.Team^.voicepack);

        if Gear^.LastDamage <> nil then
            ApplyDamage(Gear, Gear^.LastDamage, dmg, dsFall)
            else
            ApplyDamage(Gear, CurrentHedgehog, dmg, dsFall);
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure CalcRotationDirAngle(Gear: PGear);
var 
    dAngle: real;
begin
    dAngle := (Gear^.dX.QWordValue + Gear^.dY.QWordValue) / $80000000;
    if not Gear^.dX.isNegative then
        Gear^.DirAngle := Gear^.DirAngle + dAngle
    else
        Gear^.DirAngle := Gear^.DirAngle - dAngle;

    if Gear^.DirAngle < 0 then Gear^.DirAngle := Gear^.DirAngle + 360
    else if 360 < Gear^.DirAngle then Gear^.DirAngle := Gear^.DirAngle - 360
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepDrowningGear(Gear: PGear);
begin
    AllInactive := false;
    Gear^.Y := Gear^.Y + cDrownSpeed;
    Gear^.X := Gear^.X + Gear^.dX * cDrownSpeed;
    // Create some bubbles (0.5% might be better but causes too few bubbles sometimes)
    if ((not SuddenDeathDmg and (cWaterOpacity < $FF)) or (SuddenDeathDmg and (cSDWaterOpacity < $FF))) and ((GameTicks and $1F) = 0) then
        if (Gear^.Kind = gtHedgehog) and (Random(4) = 0) then
            AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble)
    else if Random(12) = 0 then
             AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble);
    if (not SuddenDeathDmg and (cWaterOpacity > $FE)) or (SuddenDeathDmg and (cSDWaterOpacity > $FE)) or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then
        DeleteGear(Gear);
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFallingGear(Gear: PGear);
var 
    isFalling: boolean;
    //tmp: QWord;
    tdX, tdY: hwFloat;
    collV, collH: LongInt;
    land: word;
begin
    // clip velocity at 1.9 - over 1 per pixel, but really shouldn't cause many actual problems.
    if Gear^.dX.QWordValue > 8160437862 then Gear^.dX.QWordValue:= 8160437862;
    if Gear^.dY.QWordValue > 8160437862 then Gear^.dY.QWordValue:= 8160437862;
    Gear^.State := Gear^.State and not gstCollision;
    collV := 0;
    collH := 0;
    tdX := Gear^.dX;
    tdY := Gear^.dY;


// might need some testing/adjustments - just to avoid projectiles to fly forever (accelerated by wind/skips)
    if (hwRound(Gear^.X) < LAND_WIDTH div -2) or (hwRound(Gear^.X) > LAND_WIDTH * 3 div 2) then Gear^.State := Gear^.State or gstCollision;

    if Gear^.dY.isNegative then
        begin
        isFalling := true;
        land:= TestCollisionYwithGear(Gear, -1);
        if land <> 0 then
            begin
            collV := -1;
            if land and lfIce <> 0 then Gear^.dX := Gear^.dX * (_1 - (_1 - Gear^.Friction) / _10)
            else Gear^.dX := Gear^.dX * Gear^.Friction;

            Gear^.dY := - Gear^.dY * Gear^.Elasticity;
            Gear^.State := Gear^.State or gstCollision
            end
        else if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, 1) <> 0) then collV := 1;
        end
    else 
        begin
        land:= TestCollisionYwithGear(Gear, 1);
        if land <> 0 then
            begin
            collV := 1;
            isFalling := false;
            if land and lfIce <> 0 then Gear^.dX := Gear^.dX * (_1 - (_1 - Gear^.Friction) / _10)
            else Gear^.dX := Gear^.dX * Gear^.Friction;

            Gear^.dY := - Gear^.dY * Gear^.Elasticity;
            Gear^.State := Gear^.State or gstCollision
            end
        else
            begin
            isFalling := true;
            if (Gear^.AdvBounce=1) and not Gear^.dY.isNegative and (TestCollisionYwithGear(Gear, -1) <> 0) then
                collV := -1
            end
        end;


    if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
        begin
        collH := hwSign(Gear^.dX);
        Gear^.dX := - Gear^.dX * Gear^.Elasticity;
        Gear^.dY :=   Gear^.dY * Gear^.Elasticity;
        Gear^.State := Gear^.State or gstCollision
        end
    else if (Gear^.AdvBounce=1) and TestCollisionXwithGear(Gear, -hwSign(Gear^.dX)) then 
        collH := -hwSign(Gear^.dX); 
    //if Gear^.AdvBounce and (collV <>0) and (collH <> 0) and (hwSqr(tdX) + hwSqr(tdY) > _0_08) then
    if (Gear^.AdvBounce=1) and (collV <>0) and (collH <> 0) and ((collV=-1) or ((tdX.QWordValue +
       tdY.QWordValue) > _0_2.QWordValue)) then
        begin
        Gear^.dX := tdY*Gear^.Elasticity*Gear^.Friction;
        Gear^.dY := tdX*Gear^.Elasticity;
        //*Gear^.Friction;
        Gear^.dY.isNegative := not tdY.isNegative;
        isFalling := false;
        Gear^.AdvBounce := 10;
        end;

    if Gear^.AdvBounce > 1 then dec(Gear^.AdvBounce);

    if isFalling then 
        begin
        Gear^.dY := Gear^.dY + cGravity;
        if (GameFlags and gfMoreWind) <> 0 then Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density
        end;

    Gear^.X := Gear^.X + Gear^.dX;
    Gear^.Y := Gear^.Y + Gear^.dY;
    CheckGearDrowning(Gear);
    //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and
    if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) and
       (not isFalling) then
        Gear^.State := Gear^.State and not gstMoving
    else
        Gear^.State := Gear^.State or      gstMoving;

    if (Gear^.nImpactSounds > 0) and 
       (((Gear^.Kind <> gtMine) and (Gear^.Damage <> 0)) or 
          ((Gear^.State and (gstCollision or gstMoving)) = (gstCollision or gstMoving))) and
       (((Gear^.Radius < 3) and (Gear^.dY < -_0_1)) or
        ((Gear^.Radius >= 3) and ((Gear^.dX.QWordValue > _0_1.QWordValue) or 
          (Gear^.dY.QWordValue > _0_1.QWordValue)))) then
        PlaySound(TSound(ord(Gear^.ImpactSound) + LongInt(GetRandom(Gear^.nImpactSounds))), true);
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepBomb(Gear: PGear);
var 
    i, x, y: LongInt;
    dX, dY: hwFloat;
    Fire: PGear;
    vg: PVisualGear;
begin
    AllInactive := false;

    doStepFallingGear(Gear);

    dec(Gear^.Timer);
    if Gear^.Timer = 1000 then // might need adjustments
        case Gear^.Kind of 
            gtGrenade: makeHogsWorry(Gear^.X, Gear^.Y, 50);
            gtClusterBomb: makeHogsWorry(Gear^.X, Gear^.Y, 20);
            gtWatermelon: makeHogsWorry(Gear^.X, Gear^.Y, 75);
            gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, 90);
            gtGasBomb: makeHogsWorry(Gear^.X, Gear^.Y, 50);
        end;

    if (Gear^.Kind = gtBall) and ((Gear^.State and gstTmpFlag) <> 0) then
    begin
        CheckCollision(Gear);
        if (Gear^.State and gstCollision) <> 0 then
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx);
    end;

    if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then
    begin
        vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite);
        if vg <> nil then
            vg^.Tint:= $FFC0C000;
    end;

    if Gear^.Timer = 0 then
    begin
        case Gear^.Kind of 
            gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
            gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound);
            gtClusterBomb: 
                begin
                    x := hwRound(Gear^.X);
                    y := hwRound(Gear^.Y);
                    doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound);
                    for i:= 0 to 4 do
                        begin
                        dX := rndSign(GetRandom * _0_1) + Gear^.dX / 5;
                        dY := (GetRandom - _3) * _0_08;
                        AddGear(x, y, gtCluster, 0, dX, dY, 25);
                        end
                end;
            gtWatermelon: 
                begin
                x := hwRound(Gear^.X);
                y := hwRound(Gear^.Y);
                doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound);
                for i:= 0 to 5 do
                    begin
                    dX := rndSign(GetRandom * _0_1) + Gear^.dX / 5;
                    dY := (GetRandom - _1_5) * _0_3;
                    AddGear(x, y, gtMelonPiece, 0, dX, dY, 75)^.DirAngle := i * 60;
                    end
                end;
            gtHellishBomb: 
                begin
                x := hwRound(Gear^.X);
                y := hwRound(Gear^.Y);
                doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound);

                for i:= 0 to 127 do
                    begin
                    dX := AngleCos(i * 16) * _0_5 * (GetRandom + _1);
                    dY := AngleSin(i * 16) * _0_5 * (GetRandom + _1);
                    Fire := AddGear(x, y, gtFlame, 0, dX, dY, 0);
                    if i mod 2 = 0 then Fire^.State := Fire^.State or gsttmpFlag;
                    Fire := AddGear(x, y, gtFlame, 0, dX, -dY, 0);
                    if i mod 2 <> 0 then Fire^.State := Fire^.State or gsttmpFlag;
                    end
                end;
            gtGasBomb:
                begin
                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);
                for i:= 0 to 2 do
                    begin
                    x:= GetRandom(60);
                    y:= GetRandom(40);
                    AddGear(hwRound(Gear^.X) - 30 + x, hwRound(Gear^.Y) - 20 + y, gtPoisonCloud, 0, _0, _0, 0);
                    end
                end;
        end;
    DeleteGear(Gear);
    exit
end;

CalcRotationDirAngle(Gear);

if Gear^.Kind = gtHellishBomb then
begin

    if Gear^.Timer = 3000 then
    begin
        Gear^.nImpactSounds := 0;
        PlaySound(sndHellish);
    end;

    if (GameTicks and $3F) = 0 then
        if (Gear^.State and gstCollision) = 0 then
            AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace);
end;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepMolotov(Gear: PGear);
var 
	s: Longword;
    i, gX, gY: LongInt;
    dX, dY: hwFloat;
    Fire: PGear;
    smoke, glass: PVisualGear;
begin
    AllInactive := false;

    doStepFallingGear(Gear);
    CalcRotationDirAngle(Gear);

    // let's add some smoke depending on speed
    s:= max(32,152 - hwRound(Distance(Gear^.dX,Gear^.dY)*120))+random(10);
    if (GameTicks mod s) = 0 then
        begin
        // adjust angle to match the texture
        if Gear^.dX.isNegative then i:= 130 else i:= 50;
        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);
        if smoke <> nil then smoke^.Scale:= 0.75;
        end;

    if (Gear^.State and gstCollision) <> 0 then
    begin
        PlaySound(sndMolotov);
        gX := hwRound(Gear^.X);
        gY := hwRound(Gear^.Y);
        for i:= 0 to 4 do
            begin
            (*glass:= AddVisualGear(gx+random(7)-3, gy+random(5)-2, vgtEgg);
            if glass <> nil then
                begin
                glass^.Frame:= 2;
                glass^.Tint:= $41B83ED0 - i * $10081000;
                glass^.dX:= 1/(10*(random(11)-5));
                glass^.dY:= -1/(random(4)+5);
                end;*)
            glass:= AddVisualGear(gx+random(7)-3, gy+random(7)-3, vgtStraightShot);
            if glass <> nil then 
                with glass^ do
                    begin
                    Frame:= 2;
                    Tint:= $41B83ED0 - i * $10081000;
                    Angle:= random * 360;
                    dx:= 0.0000001;
                    dy:= 0;
                    if random(2) = 0 then dx := -dx;
                    FrameTicks:= 750;
                    State:= ord(sprEgg)
                    end;
            end;
        for i:= 0 to 24 do
        begin
            dX := AngleCos(i * 2) * ((_0_15*(i div 5))) * (GetRandom + _1);
            dY := AngleSin(i * 8) * _0_5 * (GetRandom + _1);
            Fire := AddGear(gX, gY, gtFlame, 0, dX, dY, 0);
            Fire^.State := Fire^.State or gsttmpFlag;
            Fire := AddGear(gX, gY, gtFlame, 0, dX, -dY, 0);
            Fire^.State := Fire^.State or gsttmpFlag;
            Fire := AddGear(gX, gY, gtFlame, 0, -dX, dY, 0);
            Fire^.State := Fire^.State or gsttmpFlag;
            Fire := AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0);
            Fire^.State := Fire^.State or gsttmpFlag;
        end;
        DeleteGear(Gear);
        exit
    end;
end;

////////////////////////////////////////////////////////////////////////////////

procedure doStepCluster(Gear: PGear);
begin
    AllInactive := false;
    doStepFallingGear(Gear);
    if (Gear^.State and gstCollision) <> 0 then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound);
        DeleteGear(Gear);
        exit
    end;

    if (Gear^.Kind = gtMelonPiece) or (Gear^.Kind = gtBall) then
        CalcRotationDirAngle(Gear)
    else if (GameTicks and $1F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepShell(Gear: PGear);
begin
    AllInactive := false;
    if (GameFlags and gfMoreWind) = 0 then Gear^.dX := Gear^.dX + cWindSpeed;
    doStepFallingGear(Gear);
    if (Gear^.State and gstCollision) <> 0 then
        begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
        DeleteGear(Gear);
        exit
        end;
    if (GameTicks and $3F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepSnowball(Gear: PGear);
var kick, i: LongInt;
    particle: PVisualGear;
begin
    AllInactive := false;
    if (GameFlags and gfMoreWind) = 0 then Gear^.dX := Gear^.dX + cWindSpeed;
    doStepFallingGear(Gear);
    CalcRotationDirAngle(Gear);
    if (Gear^.State and gstCollision) <> 0 then
        begin
        kick:= hwRound((hwAbs(Gear^.dX)+hwAbs(Gear^.dY)) * _20);
        Gear^.dY.isNegative:= not Gear^.dY.isNegative;
        Gear^.dX.isNegative:= not Gear^.dX.isNegative;
        AmmoShove(Gear, 1, kick);
        for i:= 15 + kick div 10 downto 0 do
            begin
            particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust);
            if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
            end;
        DeleteGear(Gear);
        exit
        end;
    if ((GameTicks and $1F) = 0) and (Random(3) = 0) then
        begin
        particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust);
        if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
        end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepSnowflake(Gear: PGear);
var xx, yy, px, py: LongInt;
    move, draw, allpx, gun: Boolean;
    s: PSDL_Surface;
    p: PLongwordArray;
    lf: LongWord;
begin
inc(Gear^.Pos);
gun:= (Gear^.State and gstTmpFlag) <> 0;
move:= false;
draw:= false;
if gun then
    begin
    Gear^.State:= Gear^.State and not gstInvisible;
    doStepFallingGear(Gear);
    CheckCollision(Gear);
    if ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then draw:= true;
    xx:= hwRound(Gear^.X);
    yy:= hwRound(Gear^.Y);
    end
else if GameTicks and $7 = 0 then
    begin
    with Gear^ do
        begin
        State:= State and not gstInvisible;
        X:= X + cWindSpeed * 3200 + dX;
        Y:= Y + dY + cGravity * vobFallSpeed * 8;  // using same value as flakes to try and get similar results
        xx:= hwRound(X);
        yy:= hwRound(Y);
        if vobVelocity <> 0 then
            begin
            DirAngle := DirAngle + (Angle / 1250000000);
            if DirAngle < 0 then DirAngle := DirAngle + 360
            else if 360 < DirAngle then DirAngle := DirAngle - 360;
            end;

        inc(Health, 8);
        if longword(Health) > vobFrameTicks then
            begin
            dec(Health, vobFrameTicks);
            inc(Timer);
            if Timer = vobFramesCount then Timer:= 0
            end;
    // move back to cloud layer
        if yy > cWaterLine then move:= true
        else if ((yy and LAND_HEIGHT_MASK) <> 0) or (xx > LAND_WIDTH + 512) or (xx < -512) then move:=true
        // Solid pixel encountered
        else if ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
            begin
            lf:= Land[yy, xx] and (lfObject or lfBasic);
            // If there's room below keep falling
            if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then
                begin
                X:= X - cWindSpeed * 1600 - dX;
                end
            // If there's room below, on the sides, fill the gaps
            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
                begin
                X:= X - _0_8 * hwSign(cWindSpeed);
                Y:= Y - dY - cGravity * vobFallSpeed * 8;
                end
            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
                begin
                X:= X - _0_8 * 2 * hwSign(cWindSpeed);
                Y:= Y - dY - cGravity * vobFallSpeed * 8;
                end
            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
                begin
                X:= X + _0_8 * hwSign(cWindSpeed);
                Y:= Y - dY - cGravity * vobFallSpeed * 8;
                end
            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
                begin
                X:= X + _0_8 * 2 * hwSign(cWindSpeed);
                Y:= Y - dY - cGravity * vobFallSpeed * 8;
                end
            // if there's an hog/object below do nothing
            else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0))
                then move:=true
            else draw:= true
            end
        end
    end;
if draw then 
    with Gear^ do
        begin
        // we've collided with land. draw some stuff and get back into the clouds
        move:= true;
        if (Pos > 20) and ((CurAmmoGear = nil) or (CurAmmoGear^.Kind <> gtRope)) then
            begin
////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS ////////////////////////////////////
            if not gun then
                begin
                dec(yy,3);
                dec(xx,1)
                end;
            s:= SpritesData[sprSnow].Surface;
            p:= s^.pixels;
            allpx:= true;
            for py:= 0 to Pred(s^.h) do
                begin
                for px:= 0 to Pred(s^.w) do
                    if ((((yy + py) and LAND_HEIGHT_MASK) = 0) and (((xx + px) and LAND_WIDTH_MASK) = 0)) and ((Land[yy + py, xx + px] and $FF) = 0) then
                        begin
                        if gun then
                            begin
                            // try to avoid speckles. might need disabling
                            LandDirty[yy div 32, xx div 32]:= 1;
                            Land[yy + py, xx + px]:= (Land[yy + py, xx + px] or lfDamaged or lfObject) and not lfBasic;
                            end
                        else if Land[yy + py, xx + px] and $FF00 = 0 then Land[yy + py, xx + px]:= lf;
                        if (cReducedQuality and rqBlurryLand) = 0 then
                            begin
                            if gun then
                                LandPixels[yy + py, xx + px]:= (cExplosionBorderColor and not AMask) or (p^[px] and AMask)
                            else LandPixels[yy + py, xx + px]:= addBgColor(LandPixels[yy + py, xx + px], p^[px]);
                            end
                        else
                            begin
                            if gun then
                                LandPixels[(yy + py) div 2, (xx + px) div 2]:= (cExplosionBorderColor and not AMask) or (p^[px] and AMask)
                            else LandPixels[(yy + py) div 2, (xx + px) div 2]:= addBgColor(LandPixels[(yy + py) div 2, (xx + px) div 2], p^[px]);
                            end;
                        end
                    else allpx:= false;
                p:= @(p^[s^.pitch shr 2])
                end;
            
            
            Land[py, px+1]:= lfBasic;
            
            if allpx then UpdateLandTexture(xx, Pred(s^.h), yy, Pred(s^.w))
            else
                begin
                UpdateLandTexture(
                    max(0, min(LAND_WIDTH, xx)),
                    min(LAND_WIDTH - xx, Pred(s^.w)),
                    max(0, min(LAND_WIDTH, yy)),
                    min(LAND_HEIGHT - yy, Pred(s^.h))
                );
                end;
////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS ////////////////////////////////////
            end
        end;

if move then
    begin
    if gun then
        begin
        DeleteGear(Gear);
        exit
        end;
    Gear^.Pos:= 0;
    Gear^.X:= int2hwFloat(GetRandom(LAND_WIDTH+1024)-512);
    Gear^.Y:= int2hwFloat(750+(GetRandom(50)-25));
    Gear^.State:= Gear^.State or gstInvisible;
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepGrave(Gear: PGear);
begin
    AllInactive := false;
    if Gear^.dY.isNegative then
        if TestCollisionY(Gear, -1) then Gear^.dY := _0;

    if not Gear^.dY.isNegative then
        if TestCollisionY(Gear, 1) then
        begin
            Gear^.dY := - Gear^.dY * Gear^.Elasticity;
            if Gear^.dY > - _1div1024 then
            begin
                Gear^.Active := false;
                exit
            end
            else if Gear^.dY < - _0_03 then PlaySound(Gear^.ImpactSound)
        end;

    Gear^.Y := Gear^.Y + Gear^.dY;
    CheckGearDrowning(Gear);
    Gear^.dY := Gear^.dY + cGravity
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepBeeWork(Gear: PGear);
var 
    t: hwFloat;
    gX,gY,i: LongInt;
    nuw: boolean;
    flower: PVisualGear;

const uw: boolean =   false;
begin
    AllInactive := false;
    gX := hwRound(Gear^.X);
    gY := hwRound(Gear^.Y);
    nuw := (cWaterLine < gy + Gear^.Radius);
    if nuw and not uw then
    begin
        AddVisualGear(gX, cWaterLine, vgtSplash);
        AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
        AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
        AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
        AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
        StopSound(Gear^.SoundChannel);
        Gear^.SoundChannel := LoopSound(sndBeeWater);
        uw := nuw
    end
    else if not nuw and uw then
        begin
            AddVisualGear(gX, cWaterLine, vgtSplash);
            StopSound(Gear^.SoundChannel);
            Gear^.SoundChannel := LoopSound(sndBee);
            uw := nuw
        end;


    if (GameTicks and $F) = 0 then
    begin
        if (GameTicks and $30) = 0 then
            AddVisualGear(gX, gY, vgtBeeTrace);
        Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX));
        Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY));
        // make sure new speed isn't higher than original one (which we stored in Friction variable)
        t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY);
        Gear^.dX := Gear^.dX * t;
        Gear^.dY := Gear^.dY * t;
    end;

    Gear^.X := Gear^.X + Gear^.dX;
    Gear^.Y := Gear^.Y + Gear^.dY;

    CheckCollision(Gear);
    dec(Gear^.Timer);
    if ((Gear^.State and gstCollision) <> 0) or (Gear^.Timer = 0) then
    begin
        StopSound(Gear^.SoundChannel);
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
        for i:= 0 to 31 do
            begin
            flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
            if flower <> nil then 
                with flower^ do
                    begin
                    Scale:= 0.75;
                    dx:= 0.001 * (random(200));
                    dy:= 0.001 * (random(200));
                    if random(2) = 0 then dx := -dx;
                    if random(2) = 0 then dy := -dy;
                    FrameTicks:= random(250) + 250;
                    State:= ord(sprTargetBee);
                    end;
            end;
        DeleteGear(Gear);
    end;
end;

procedure doStepBee(Gear: PGear);
begin
    AllInactive := false;
    Gear^.X := Gear^.X + Gear^.dX;
    Gear^.Y := Gear^.Y + Gear^.dY;
    Gear^.dY := Gear^.dY + cGravity;
    CheckCollision(Gear);
    if (Gear^.State and gstCollision) <> 0 then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
        DeleteGear(Gear);
        exit
    end;
    dec(Gear^.Timer);
    if Gear^.Timer = 0 then
    begin
        Gear^.Hedgehog^.Gear^.Message:= Gear^.Hedgehog^.Gear^.Message and not gmAttack;
        Gear^.Hedgehog^.Gear^.State:= Gear^.Hedgehog^.Gear^.State and not gstAttacking;
        AttackBar:= 0;
        
        Gear^.SoundChannel := LoopSound(sndBee);
        Gear^.Timer := 5000;
        // save initial speed in otherwise unused Friction variable
        Gear^.Friction := Distance(Gear^.dX, Gear^.dY);
        Gear^.doStep := @doStepBeeWork
    end;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepShotIdle(Gear: PGear);
begin
    AllInactive := false;
    inc(Gear^.Timer);
    if Gear^.Timer > 75 then
    begin
        DeleteGear(Gear);
        AfterAttack
    end
end;

procedure doStepShotgunShot(Gear: PGear);
var 
    i: LongWord;
    shell: PVisualGear;
begin
    AllInactive := false;

    if ((Gear^.State and gstAnimation) = 0) then
    begin
        dec(Gear^.Timer);
        if Gear^.Timer = 0 then
        begin
            PlaySound(sndShotgunFire);
            shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell);
            if shell <> nil then
            begin
                shell^.dX := gear^.dX.QWordValue / -17179869184;
                shell^.dY := gear^.dY.QWordValue / -17179869184;
                shell^.Frame := 0
            end;
            Gear^.State := Gear^.State or gstAnimation
        end;
        exit
    end
    else inc(Gear^.Timer);

    i := 200;
    repeat
        Gear^.X := Gear^.X + Gear^.dX;
        Gear^.Y := Gear^.Y + Gear^.dY;
        CheckCollision(Gear);
        if (Gear^.State and gstCollision) <> 0 then
        begin
            Gear^.X := Gear^.X + Gear^.dX * 8;
            Gear^.Y := Gear^.Y + Gear^.dY * 8;
            ShotgunShot(Gear);
            Gear^.doStep := @doStepShotIdle;
            exit
        end;

        CheckGearDrowning(Gear);
        if (Gear^.State and gstDrowning) <> 0 then
        begin
            Gear^.doStep := @doStepShotIdle;
            exit
        end;
        dec(i)
    until i = 0;
    if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0)
        then
        Gear^.doStep := @doStepShotIdle
end;

////////////////////////////////////////////////////////////////////////////////
procedure spawnBulletTrail(Bullet: PGear);
var oX, oY: hwFloat;
    VGear: PVisualGear;
begin
    if Bullet^.PortalCounter = 0 then
        begin
        ox:= CurrentHedgehog^.Gear^.X + Int2hwFloat(GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle));
        oy:= CurrentHedgehog^.Gear^.Y + Int2hwFloat(GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle));
        end
    else
        begin
        ox:= Bullet^.Elasticity;
        oy:= Bullet^.Friction;
        end;

        // Bullet trail
        VGear := AddVisualGear(hwRound(ox), hwRound(oy), vgtLineTrail);
        if VGear <> nil then
            begin
            VGear^.X:= hwFloat2Float(ox);
            VGear^.Y:= hwFloat2Float(oy);
            VGear^.dX:= hwFloat2Float(Bullet^.X);
            VGear^.dY:= hwFloat2Float(Bullet^.Y);

            // reached edge of land. assume infinite beam. Extend it way out past camera
            if (hwRound(Bullet^.X) and LAND_WIDTH_MASK <> 0)
                or (hwRound(Bullet^.Y) and LAND_HEIGHT_MASK <> 0) then
                    // only extend if not under water
                    if hwRound(Bullet^.Y) < cWaterLine then
                        begin
                        VGear^.dX := VGear^.dX + LAND_WIDTH * (VGear^.dX - VGear^.X);
                        VGear^.dY := VGear^.dY + LAND_WIDTH * (VGear^.dY - VGear^.Y);
                        end;

            VGear^.Timer := 200;
            end;
end;

procedure doStepBulletWork(Gear: PGear);
var 
    i, x, y: LongWord;
    oX, oY: hwFloat;
    VGear: PVisualGear;
begin
    AllInactive := false;
    inc(Gear^.Timer);
    i := 80;
    oX := Gear^.X;
    oY := Gear^.Y;
    repeat
        Gear^.X := Gear^.X + Gear^.dX;
        Gear^.Y := Gear^.Y + Gear^.dY;
        x := hwRound(Gear^.X);
        y := hwRound(Gear^.Y);
        if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0)
           and (Land[y, x] <> 0) then inc(Gear^.Damage);
        // let's interrupt before a collision to give portals a chance to catch the bullet
        if (Gear^.Damage = 1) and (Gear^.Tag = 0) and (Land[y, x] > 255) then
        begin
            Gear^.Tag := 1;
            Gear^.Damage := 0;
            Gear^.X := Gear^.X - Gear^.dX;
            Gear^.Y := Gear^.Y - Gear^.dY;
            CheckGearDrowning(Gear);
            break;
        end
        else
            Gear^.Tag := 0;

        if Gear^.Damage > 5 then
            if Gear^.AmmoType = amDEagle then
                AmmoShove(Gear, 7, 20)
        else
            AmmoShove(Gear, Gear^.Timer, 20);
        CheckGearDrowning(Gear);
        dec(i)
    until (i = 0) or (Gear^.Damage > Gear^.Health) or ((Gear^.State and gstDrowning) <> 0);
    if Gear^.Damage > 0 then
    begin
        DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 82 - i, 1);
        dec(Gear^.Health, Gear^.Damage);
        Gear^.Damage := 0
    end;
    if ((Gear^.State and gstDrowning) <> 0) and (Gear^.Damage < Gear^.Health) and ((not SuddenDeathDmg and (cWaterOpacity < $FF)) or (SuddenDeathDmg and (cSDWaterOpacity < $FF))) then
    begin
        for i:=(Gear^.Health - Gear^.Damage) * 4 downto 0 do
        begin
            if Random(6) = 0 then
                AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble);
            Gear^.X := Gear^.X + Gear^.dX;
            Gear^.Y := Gear^.Y + Gear^.dY;
        end;
    end;

    if (Gear^.Health <= 0)
       or (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0)
       or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
    begin
        if (Gear^.Kind = gtSniperRifleShot) and ((GameFlags and gfLaserSight) = 0) then
            cLaserSighting := false;
        if (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and
           ((GameFlags and gfArtillery) = 0) then cArtillery := false;
        
        // Bullet Hit
        if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) 
            and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then
        begin
            VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit);
            if VGear <> nil then
            begin
                VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY);
            end;
        end;
       
        spawnBulletTrail(Gear);
        Gear^.doStep := @doStepShotIdle
    end;
end;

procedure doStepDEagleShot(Gear: PGear);
begin
    PlaySound(sndGun);
    // 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
    Gear^.X := Gear^.X + Gear^.dX * 3;
    Gear^.Y := Gear^.Y + Gear^.dY * 3;
    Gear^.doStep := @doStepBulletWork
end;

procedure doStepSniperRifleShot(Gear: PGear);
var 
    HHGear: PGear;
    shell: PVisualGear;
begin
    cArtillery := true;
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.State := HHGear^.State or gstNotKickable;
    HedgehogChAngle(HHGear);
    if not cLaserSighting then
        // game does not have default laser sight. turn it on and give them a chance to aim
    begin
        cLaserSighting := true;
        HHGear^.Message := 0;
        if (HHGear^.Angle - 32 >= 0) then dec(HHGear^.Angle,32)
    end;

    if (HHGear^.Message and gmAttack) <> 0 then
    begin
        shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell);
        if shell <> nil then
        begin
            shell^.dX := gear^.dX.QWordValue / -8589934592;
            shell^.dY := gear^.dY.QWordValue / -8589934592;
            shell^.Frame := 1
        end;
        Gear^.State := Gear^.State or gstAnimation;
        Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _0_5;
        Gear^.dY := -AngleCos(HHGear^.Angle) * _0_5;
        PlaySound(sndGun);
        // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just weird angles
        Gear^.X := Gear^.X + Gear^.dX * 3;  
        Gear^.Y := Gear^.Y + Gear^.dY * 3;
        Gear^.doStep := @doStepBulletWork;
    end
    else
        if (GameTicks mod 32) = 0 then
            if (GameTicks mod 4096) < 2048 then
            begin
                if (HHGear^.Angle + 1 <= cMaxAngle) then inc(HHGear^.Angle)
            end
    else
        if (HHGear^.Angle - 1 >= 0) then dec(HHGear^.Angle);

    if (TurnTimeLeft > 0) then
        dec(TurnTimeLeft)
    else
    begin
        DeleteGear(Gear);
        AfterAttack
    end;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepActionTimer(Gear: PGear);
begin
    dec(Gear^.Timer);
    case Gear^.Kind of 
        gtATStartGame: 
    begin
        AllInactive := false;
        if Gear^.Timer = 0 then
        begin
            AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState);
        end
    end;
    gtATFinishGame: 
    begin
        AllInactive := false;
        if Gear^.Timer = 1000 then
        begin
            ScreenFade := sfToBlack;
            ScreenFadeValue := 0;
            ScreenFadeSpeed := 1;
        end;
        if Gear^.Timer = 0 then
        begin
            SendIPC('N');
            SendIPC('q');
            GameState := gsExit
        end
    end;
end;
if Gear^.Timer = 0 then DeleteGear(Gear)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepPickHammerWork(Gear: PGear);
var 
    i, ei, x, y: LongInt;
    HHGear: PGear;
begin
    AllInactive := false;
    HHGear := Gear^.Hedgehog^.Gear;
    dec(Gear^.Timer);
    if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then dec(TurnTimeLeft);
    if (TurnTimeLeft = 0) or (Gear^.Timer = 0)or((Gear^.Message and gmDestroy) <> 0)or((HHGear^.State and gstHHDriven) =
       0) then
        begin
        StopSound(Gear^.SoundChannel);
        DeleteGear(Gear);
        AfterAttack;
        doStepHedgehogMoving(HHGear);  // for gfInfAttack
        exit
        end;

    x:= hwRound(Gear^.X);
    y:= hwRound(Gear^.Y);
    if (Gear^.Timer mod 33) = 0 then
        begin
        HHGear^.State := HHGear^.State or gstNoDamage;
        doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw);
        HHGear^.State := HHGear^.State and not gstNoDamage
        end;

    if (Gear^.Timer mod 47) = 0 then
        begin
        // 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
        if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then 
            for i:= 0 to 1 do
                AddVisualGear(x - 5 + Random(10), y + 12, vgtDust);

        i := x - Gear^.Radius - LongInt(GetRandom(2));
        ei := x + Gear^.Radius + LongInt(GetRandom(2));
        while i <= ei do
            begin
            DrawExplosion(i, y + 3, 3);
            inc(i, 1)
            end;

        if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9)
           , lfIndestructible) then
            begin
            Gear^.X := Gear^.X + Gear^.dX;
            Gear^.Y := Gear^.Y + _1_9;
            end;
        SetAllHHToActive;
        end;
    if TestCollisionYwithGear(Gear, 1) <> 0 then
        begin
        Gear^.dY := _0;
        SetLittle(HHGear^.dX);
        HHGear^.dY := _0;
        end
    else
        begin
        Gear^.dY := Gear^.dY + cGravity;
        Gear^.Y := Gear^.Y + Gear^.dY;
        if hwRound(Gear^.Y) > cWaterLine then Gear^.Timer := 1
        end;

    Gear^.X := Gear^.X + HHGear^.dX;
    HHGear^.X := Gear^.X;
    HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius);

    if (Gear^.Message and gmAttack) <> 0 then
        if (Gear^.State and gsttmpFlag) <> 0 then Gear^.Timer := 1
    else
    else
        if (Gear^.State and gsttmpFlag) = 0 then Gear^.State := Gear^.State or gsttmpFlag;
    if ((Gear^.Message and gmLeft) <> 0) then Gear^.dX := - _0_3
    else
        if ((Gear^.Message and gmRight) <> 0) then Gear^.dX := _0_3
    else Gear^.dX := _0;
end;

procedure doStepPickHammer(Gear: PGear);
var 
    i, y: LongInt;
    ar: TRangeArray;
    HHGear: PGear;
begin
    i := 0;
    HHGear := Gear^.Hedgehog^.Gear;

    y := hwRound(Gear^.Y) - cHHRadius * 2;
    while y < hwRound(Gear^.Y) do
    begin
        ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2));
        ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
        inc(y, 2);
        inc(i)
    end;

    DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i));
    Gear^.dY := HHGear^.dY;
    DeleteCI(HHGear);

    Gear^.SoundChannel := LoopSound(sndPickhammer);
    doStepPickHammerWork(Gear);
    Gear^.doStep := @doStepPickHammerWork
end;

////////////////////////////////////////////////////////////////////////////////
var 
    BTPrevAngle, BTSteps: LongInt;

procedure doStepBlowTorchWork(Gear: PGear);
var 
    HHGear: PGear;
    b: boolean;
    prevX: LongInt;
begin
    AllInactive := false;
    dec(Gear^.Timer);
    if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then dec(TurnTimeLeft);
    
    HHGear := Gear^.Hedgehog^.Gear;

    HedgehogChAngle(HHGear);

    b := false;

    if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7  then
        begin
        Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX);
        Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5);
        BTPrevAngle := HHGear^.Angle;
        b := true
        end;

    if ((HHGear^.State and gstMoving) <> 0) then
        begin
        doStepHedgehogMoving(HHGear);
        if (HHGear^.State and gstHHDriven) = 0 then Gear^.Timer := 0
        end;

    if Gear^.Timer mod cHHStepTicks = 0 then
        begin
        b := true;
        if Gear^.dX.isNegative then
            HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmLeft
        else
            HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmRight;

        if ((HHGear^.State and gstMoving) = 0) then
            begin
            HHGear^.State := HHGear^.State and not gstAttacking;
            prevX := hwRound(HHGear^.X);

            // why the call to HedgehogStep then a further increment of X?
            if (prevX = hwRound(HHGear^.X)) and
               CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y),
               lfIndestructible) then HedgehogStep(HHGear);

            if (prevX = hwRound(HHGear^.X)) and
               CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y),
               lfIndestructible) then HHGear^.X := HHGear^.X + SignAs(_1, HHGear^.dX);
            HHGear^.State := HHGear^.State or gstAttacking
            end;

        inc(BTSteps);
        if BTSteps = 7 then
            begin
            BTSteps := 0;
            if CheckLandValue(hwRound(HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC) + SignAs(_6,
               Gear^.dX)), hwRound(HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC)),
               lfIndestructible) then
                begin
                Gear^.X := HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC);
                Gear^.Y := HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC);
                end;
            HHGear^.State := HHGear^.State or gstNoDamage;
            AmmoShove(Gear, 2, 15);
            HHGear^.State := HHGear^.State and not gstNoDamage
            end;
        end;

    if b then
        DrawTunnel(HHGear^.X - Gear^.dX * cHHRadius, HHGear^.Y - _4 - Gear^.dY * cHHRadius + hwAbs(
                   Gear^.dY) * 7,
        Gear^.dX, Gear^.dY,
        cHHRadius * 5, cHHRadius * 2 + 7);

    if (TurnTimeLeft = 0) or (Gear^.Timer = 0) or ((HHGear^.Message and gmAttack) <> 0) then
        begin
        HHGear^.Message := 0;
        HHGear^.State := HHGear^.State and (not gstNotKickable);
        DeleteGear(Gear);
        AfterAttack
        end
end;

procedure doStepBlowTorch(Gear: PGear);
var 
    HHGear: PGear;
begin
    BTPrevAngle := High(LongInt);
    BTSteps := 0;
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.Message := 0;
    HHGear^.State := HHGear^.State or gstNotKickable;
    Gear^.doStep := @doStepBlowTorchWork
end;

////////////////////////////////////////////////////////////////////////////////

procedure doStepRope(Gear: PGear);
forward;

procedure doStepRopeAfterAttack(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;
    if ((HHGear^.State and gstHHDriven) = 0)
       or (CheckGearDrowning(HHGear))
       or (TestCollisionYwithGear(HHGear, 1) <> 0) then
    begin
        DeleteGear(Gear);
        isCursorVisible := false;
        ApplyAmmoChanges(HHGear^.Hedgehog^);
        exit
    end;

    HedgehogChAngle(HHGear);

    if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX);

    if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then HHGear^.dY := _0;
    HHGear^.X := HHGear^.X + HHGear^.dX;
    HHGear^.Y := HHGear^.Y + HHGear^.dY;
    HHGear^.dY := HHGear^.dY + cGravity;
    if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;

    if (Gear^.Message and gmAttack) <> 0 then
    begin
        Gear^.X := HHGear^.X;
        Gear^.Y := HHGear^.Y;

        ApplyAngleBounds(Gear^.Hedgehog^, amRope);

        Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX);
        Gear^.dY := -AngleCos(HHGear^.Angle);
        Gear^.Friction := _450 * _0_01 * cRopePercent;
        Gear^.Elasticity := _0;
        Gear^.State := Gear^.State and not gsttmpflag;
        Gear^.doStep := @doStepRope;
    end
end;

procedure doStepRopeWork(Gear: PGear);
var 
    HHGear: PGear;
    len, tx, ty, nx, ny, ropeDx, ropeDy, mdX, mdY: hwFloat;
    lx, ly, cd: LongInt;
    haveCollision,
    haveDivided: boolean;

procedure DeleteMe;
begin
    with HHGear^ do
    begin
        Message := Message and not gmAttack;
        State := (State or gstMoving) and not gstWinner;
    end;
    DeleteGear(Gear)
end;

procedure WaitCollision;
begin
    with HHGear^ do
    begin
        Message := Message and not gmAttack;
        State := State or gstMoving;
    end;
    RopePoints.Count := 0;
    Gear^.Elasticity := _0;
    Gear^.doStep := @doStepRopeAfterAttack
end;

begin
    HHGear := Gear^.Hedgehog^.Gear;

    if ((HHGear^.State and gstHHDriven) = 0)
       or (CheckGearDrowning(HHGear)) then
        begin
        PlaySound(sndRopeRelease);
        DeleteMe;
        exit
        end;

    if (Gear^.Message and gmLeft  <> 0) and not TestCollisionXwithGear(HHGear, -1) then
        HHGear^.dX := HHGear^.dX - _0_0002;

    if (Gear^.Message and gmRight <> 0) and not TestCollisionXwithGear(HHGear,  1) then
        HHGear^.dX := HHGear^.dX + _0_0002;

    // vector between hedgehog and rope attaching point
    ropeDx := HHGear^.X - Gear^.X;
    ropeDy := HHGear^.Y - Gear^.Y;

    if TestCollisionYwithGear(HHGear, 1) = 0 then
        begin

        // depending on the rope vector we know which X-side to check for collision
        // in order to find out if the hog can still be moved by gravity
        if ropeDx.isNegative = RopeDy.IsNegative then
            cd:= -1
        else
            cd:= 1;

        // apply gravity if there is no obstacle
        if not TestCollisionXwithGear(HHGear, cd) then
            HHGear^.dY := HHGear^.dY + cGravity;

        if (GameFlags and gfMoreWind) <> 0 then
            // apply wind if there's no obstacle
            if not TestCollisionXwithGear(HHGear, hwSign(cWindSpeed)) then
                HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;
        end;

    mdX := ropeDx + HHGear^.dX;
    mdY := ropeDy + HHGear^.dY;
    len := _1 / Distance(mdX, mdY);
    // rope vector plus hedgehog direction vector normalized
    mdX := mdX * len;
    mdY := mdY * len;

    // for visual purposes only
    Gear^.dX := mdX;
    Gear^.dY := mdY;

    /////
    tx := HHGear^.X;
    ty := HHGear^.Y;

    if ((Gear^.Message and gmDown) <> 0) and (Gear^.Elasticity < Gear^.Friction) then
        if not (TestCollisionXwithGear(HHGear, hwSign(ropeDx))
           or (TestCollisionYwithGear(HHGear, hwSign(ropeDy)) <> 0)) then
            Gear^.Elasticity := Gear^.Elasticity + _0_3;

    if ((Gear^.Message and gmUp) <> 0) and (Gear^.Elasticity > _30) then
        if not (TestCollisionXwithGear(HHGear, -hwSign(ropeDx))
           or (TestCollisionYwithGear(HHGear, -hwSign(ropeDy)) <> 0)) then
            Gear^.Elasticity := Gear^.Elasticity - _0_3;

    HHGear^.X := Gear^.X + mdX * Gear^.Elasticity;
    HHGear^.Y := Gear^.Y + mdY * Gear^.Elasticity;

    HHGear^.dX := HHGear^.X - tx;
    HHGear^.dY := HHGear^.Y - ty;
    ////


    haveDivided := false;
    // check whether rope needs dividing

    len := Gear^.Elasticity - _5;
    nx := Gear^.X + mdX * len;
    ny := Gear^.Y + mdY * len;
    tx := mdX * _0_3; // should be the same as increase step
    ty := mdY * _0_3;

    while len > _3 do
        begin
        lx := hwRound(nx);
        ly := hwRound(ny);
        if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and ((Land[ly, lx] and $FF00) <> 0) then
            begin
            ny := _1 / Distance(ropeDx, ropeDy);
            // old rope pos
            nx := ropeDx * ny;
            ny := ropeDy * ny;

            with RopePoints.ar[RopePoints.Count] do
                begin
                X := Gear^.X;
                Y := Gear^.Y;
                if RopePoints.Count = 0 then RopePoints.HookAngle := DxDy2Angle(Gear^.dY, Gear^.dX);
                b := (nx * HHGear^.dY) > (ny * HHGear^.dX);
                dLen := len
                end;
            with RopePoints.rounded[RopePoints.Count] do
                begin
                X := hwRound(Gear^.X);
                Y := hwRound(Gear^.Y);
                end;

            Gear^.X := Gear^.X + nx * len;
            Gear^.Y := Gear^.Y + ny * len;
            inc(RopePoints.Count);
            TryDo(RopePoints.Count <= MAXROPEPOINTS, 'Rope points overflow', true);
            Gear^.Elasticity := Gear^.Elasticity - len;
            Gear^.Friction := Gear^.Friction - len;
            haveDivided := true;
            break
            end;
        nx := nx - tx;
        ny := ny - ty;

        // len := len - _0_3 // should be the same as increase step
        len.QWordValue := len.QWordValue - _0_3.QWordValue;
        end;

    if not haveDivided then
        if RopePoints.Count > 0 then // check whether the last dividing point could be removed
            begin
            tx := RopePoints.ar[Pred(RopePoints.Count)].X;
            ty := RopePoints.ar[Pred(RopePoints.Count)].Y;
            mdX := tx - Gear^.X;
            mdY := ty - Gear^.Y;
            if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * (ty - HHGear^.Y) > (tx - HHGear^.X) * mdY) then
                begin
                dec(RopePoints.Count);
                Gear^.X := RopePoints.ar[RopePoints.Count].X;
                Gear^.Y := RopePoints.ar[RopePoints.Count].Y;
                Gear^.Elasticity := Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen;
                Gear^.Friction := Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen;

                // restore hog position
                len := _1 / Distance(mdX, mdY);
                mdX := mdX * len;
                mdY := mdY * len;

                HHGear^.X := Gear^.X - mdX * Gear^.Elasticity;
                HHGear^.Y := Gear^.Y - mdY * Gear^.Elasticity;
                end
            end;

    haveCollision := false;
    if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
        begin
        HHGear^.dX := -_0_6 * HHGear^.dX;
        haveCollision := true
        end;
    if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0 then
        begin
        HHGear^.dY := -_0_6 * HHGear^.dY;
        haveCollision := true
        end;

    if haveCollision
       and (Gear^.Message and (gmLeft or gmRight) <> 0)
       and (Gear^.Message and (gmUp or gmDown) <> 0) then
        begin
        HHGear^.dX := SignAs(hwAbs(HHGear^.dX) + _0_2, HHGear^.dX);
        HHGear^.dY := SignAs(hwAbs(HHGear^.dY) + _0_2, HHGear^.dY)
        end;

    len := hwSqr(HHGear^.dX) + hwSqr(HHGear^.dY);
    if len > _0_64 then
        begin
        len := _0_8 / hwSqrt(len);
        HHGear^.dX := HHGear^.dX * len;
        HHGear^.dY := HHGear^.dY * len;
        end;

    haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)]) <> 0);

    if not haveCollision then
        begin
        // backup gear location
        tx:= Gear^.X;
        ty:= Gear^.Y;

        if RopePoints.Count > 0 then
            begin
            // set gear location to the remote end of the rope, the attachment point
            Gear^.X:= RopePoints.ar[0].X;
            Gear^.Y:= RopePoints.ar[0].Y;
            end;

        CheckCollision(Gear);
        // if we haven't found any collision yet then check the other side too
        if (Gear^.State and gstCollision) = 0 then
            begin
            Gear^.dX.isNegative:= not Gear^.dX.isNegative;
            Gear^.dY.isNegative:= not Gear^.dY.isNegative;
            CheckCollision(Gear);
            Gear^.dX.isNegative:= not Gear^.dX.isNegative;
            Gear^.dY.isNegative:= not Gear^.dY.isNegative;
            end;

        haveCollision:= (Gear^.State and gstCollision) <> 0;

        // restore gear location
        Gear^.X:= tx;
        Gear^.Y:= ty;
        end;

    // if the attack key is pressed, lose rope contact as well
    if (Gear^.Message and gmAttack) <> 0 then
        haveCollision:= false;

    if not haveCollision then
        begin
        if (Gear^.State and gsttmpFlag) <> 0 then
            with Gear^.Hedgehog^ do
                begin
                PlaySound(sndRopeRelease);
                if CurAmmoType <> amParachute then
                    WaitCollision
                else
                    DeleteMe
                end
        end
    else
        if (Gear^.State and gsttmpFlag) = 0 then
            Gear^.State := Gear^.State or gsttmpFlag;
end;

procedure doStepRopeAttach(Gear: PGear);
var 
    HHGear: PGear;
    tx, ty, tt: hwFloat;

procedure RemoveFromAmmo;
begin
    if (Gear^.State and gstAttacked) = 0 then
    begin
        OnUsedAmmo(HHGear^.Hedgehog^);
        Gear^.State := Gear^.State or gstAttacked
    end;
    ApplyAmmoChanges(HHGear^.Hedgehog^)
end;

begin
    Gear^.X := Gear^.X - Gear^.dX;
    Gear^.Y := Gear^.Y - Gear^.dY;
    Gear^.Elasticity := Gear^.Elasticity + _1;

    HHGear := Gear^.Hedgehog^.Gear;
    DeleteCI(HHGear);

    if (HHGear^.State and gstMoving) <> 0 then
        begin
        if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX);
        if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then HHGear^.dY := _0;

        HHGear^.X := HHGear^.X + HHGear^.dX;
        Gear^.X := Gear^.X + HHGear^.dX;

        if TestCollisionYwithGear(HHGear, 1) <> 0 then
            begin
            CheckHHDamage(HHGear);
            HHGear^.dY := _0
            //HHGear^.State:= HHGear^.State and not (gstHHJumping or gstHHHJump);
            end
        else
            begin
            HHGear^.Y := HHGear^.Y + HHGear^.dY;
            Gear^.Y := Gear^.Y + HHGear^.dY;
            HHGear^.dY := HHGear^.dY + cGravity;
            if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density
            end;

        tt := Gear^.Elasticity;
        tx := _0;
        ty := _0;
        while tt > _20 do
            begin
            if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] and $FF00) <> 0) then
                begin
                Gear^.X := Gear^.X + tx;
                Gear^.Y := Gear^.Y + ty;
                Gear^.Elasticity := tt;
                Gear^.doStep := @doStepRopeWork;
                PlaySound(sndRopeAttach);
                with HHGear^ do
                    begin
                    State := State and not (gstAttacking or gstHHJumping or gstHHHJump);
                    Message := Message and not gmAttack
                    end;

                RemoveFromAmmo;

                tt := _0;
                exit
                end;
            tx := tx + Gear^.dX + Gear^.dX;
            ty := ty + Gear^.dY + Gear^.dY;
            tt := tt - _2;
            end;
        end;

    CheckCollision(Gear);

    if (Gear^.State and gstCollision) <> 0 then
        if Gear^.Elasticity < _10 then
            Gear^.Elasticity := _10000
    else
        begin
        Gear^.doStep := @doStepRopeWork;
        PlaySound(sndRopeAttach);
        with HHGear^ do
            begin
            State := State and not (gstAttacking or gstHHJumping or gstHHHJump);
            Message := Message and not gmAttack
            end;

        RemoveFromAmmo;

        exit
        end;

    if (Gear^.Elasticity > Gear^.Friction)
       or ((Gear^.Message and gmAttack) = 0)
       or ((HHGear^.State and gstHHDriven) = 0)
       or (HHGear^.Damage > 0) then
        begin
        with Gear^.Hedgehog^.Gear^ do
            begin
            State := State and not gstAttacking;
            Message := Message and not gmAttack
            end;
        DeleteGear(Gear)
        end;
    CheckGearDrowning(HHGear)
end;

procedure doStepRope(Gear: PGear);
begin
    Gear^.dX := - Gear^.dX;
    Gear^.dY := - Gear^.dY;
    Gear^.doStep := @doStepRopeAttach;
    PlaySound(sndRopeShot)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepMine(Gear: PGear);
var vg: PVisualGear;
begin
    if (Gear^.State and gstMoving) <> 0 then
    begin
        DeleteCI(Gear);
        doStepFallingGear(Gear);
        if (Gear^.State and gstMoving) = 0 then
        begin
            AddGearCI(Gear);
            Gear^.dX := _0;
            Gear^.dY := _0
        end;
        CalcRotationDirAngle(Gear);
        AllInactive := false
    end
    else
        if ((GameTicks and $3F) = 25) then
            doStepFallingGear(Gear);
    if (Gear^.Health = 0) then
        begin
            if not Gear^.dY.isNegative and (Gear^.dY > _0_2) and (TestCollisionYwithGear(Gear, 1) <> 0) then
                inc(Gear^.Damage, hwRound(Gear^.dY * _70))
            else if not Gear^.dX.isNegative and (Gear^.dX > _0_2) and TestCollisionXwithGear(Gear, 1) then
                 inc(Gear^.Damage, hwRound(Gear^.dX * _70))
            else if Gear^.dY.isNegative and (Gear^.dY < -_0_2) and (TestCollisionYwithGear(Gear, -1) <> 0) then
                 inc(Gear^.Damage, hwRound(Gear^.dY * -_70))
            else if Gear^.dX.isNegative and (Gear^.dX < -_0_2) and TestCollisionXwithGear(Gear, -1) then
                 inc(Gear^.Damage, hwRound(Gear^.dX * -_70));
        
        if (Gear^.Damage > random(30)) and ((GameTicks and $FF) = 0) then
            begin
            vg:= AddVisualGear(hwRound(Gear^.X) - 4  + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke);
            if vg <> nil then vg^.Scale:= 0.5
            end;

        if (Gear^.Damage > 35) then
            begin
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
            DeleteGear(Gear);
            exit
            end
        end;

    if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then
        if ((Gear^.State and gstAttacking) = 0) then
            begin
            if ((GameTicks and $1F) = 0) then
                if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then Gear^.State := Gear^.State or
                                                                                      gstAttacking
            end
        else // gstAttacking <> 0
            begin
            AllInactive := false;
            if (Gear^.Timer and $FF) = 0 then PlaySound(sndMineTick);
            if Gear^.Timer = 0 then
                begin
                if ((Gear^.State and gstWait) <> 0) or
                   (cMineDudPercent = 0) or
                   (getRandom(100) > cMineDudPercent) then
                    begin
                    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
                    DeleteGear(Gear)
                    end
                else
                    begin
                    vg:= AddVisualGear(hwRound(Gear^.X) - 4  + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke);
                    if vg <> nil then vg^.Scale:= 0.5;
                    PlaySound(sndVaporize);
                    Gear^.Health := 0;
                    Gear^.Damage := 0;
                    Gear^.State := Gear^.State and not gstAttacking
                    end;
                exit
                end;
            dec(Gear^.Timer);
            end
    else // gsttmpFlag = 0
        if (TurnTimeLeft = 0) or ((GameFlags and gfInfAttack) <> 0) then Gear^.State := Gear^.State or gsttmpFlag;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepSMine(Gear: PGear);
begin
    // TODO: do real calculation?
    if TestCollisionXwithGear(Gear, 2) or (TestCollisionYwithGear(Gear, -2) <> 0) or TestCollisionXwithGear(Gear, -2) or (TestCollisionYwithGear(Gear, 2) <> 0) then
    begin
        if (hwAbs(Gear^.dX) > _0) or (hwAbs(Gear^.dY) > _0) then
        begin
            PlaySound(sndRopeAttach);
            Gear^.dX:= _0;
            Gear^.dY:= _0;
            AddGearCI(Gear);
        end;
    end
    else
    begin
        DeleteCI(Gear);
        doStepFallingGear(Gear);
        AllInactive := false;
        CalcRotationDirAngle(Gear);
    end;

    if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then
    begin
        if ((Gear^.State and gstAttacking) = 0) then
        begin
            if ((GameTicks and $1F) = 0) then
                if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then Gear^.State := Gear^.State or
                                                                                      gstAttacking
        end
        else // gstAttacking <> 0
        begin
            AllInactive := false;
            if (Gear^.Timer and $FF) = 0 then PlaySound(sndMineTick);
            if Gear^.Timer = 0 then
            begin
                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
                DeleteGear(Gear);
                exit
            end;
            dec(Gear^.Timer);
        end
    end
    else // gsttmpFlag = 0
        if TurnTimeLeft = 0 then Gear^.State := Gear^.State or gsttmpFlag;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepDynamite(Gear: PGear);
begin
    doStepFallingGear(Gear);
    AllInactive := false;
    if Gear^.Timer mod 166 = 0 then inc(Gear^.Tag);
    if Gear^.Timer = 1000 then // might need better timing
        makeHogsWorry(Gear^.X, Gear^.Y, 75);
    if Gear^.Timer = 0 then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, Gear^.Hedgehog, EXPLAutoSound);
        DeleteGear(Gear);
        exit
    end;
    dec(Gear^.Timer);
end;

///////////////////////////////////////////////////////////////////////////////

(*
TODO
Increase damage as barrel smokes?
Try tweaking friction some more
*)
procedure doStepRollingBarrel(Gear: PGear);
var 
    i: LongInt;
    particle: PVisualGear;
begin
    if (Gear^.dY.QWordValue = 0) and (Gear^.dY.QWordValue = 0) and (TestCollisionYwithGear(Gear, 1) = 0) then SetLittle(Gear^.dY);
    Gear^.State := Gear^.State or gstAnimation;
    if ((Gear^.dX.QWordValue <> 0) or (Gear^.dY.QWordValue <> 0))  then
    begin
        DeleteCI(Gear);
        AllInactive := false;
        if not Gear^.dY.isNegative and (Gear^.dY > _0_2) and (TestCollisionYwithGear(Gear, 1) <> 0) then
        begin
            Gear^.State := Gear^.State or gsttmpFlag;
            inc(Gear^.Damage, hwRound(Gear^.dY * _70));
            for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do
            begin
                particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12,
                            vgtDust);
                if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
            end
        end
        else if not Gear^.dX.isNegative and (Gear^.dX > _0_2) and TestCollisionXwithGear(Gear, 1)
                 then
                 inc(Gear^.Damage, hwRound(Gear^.dX * _70))
        else if Gear^.dY.isNegative and (Gear^.dY < -_0_2) and (TestCollisionYwithGear(Gear, -1) <> 0)
                 then
                 inc(Gear^.Damage, hwRound(Gear^.dY * -_70))
        else if Gear^.dX.isNegative and (Gear^.dX < -_0_2) and TestCollisionXwithGear(Gear, -1)
                 then
                 inc(Gear^.Damage, hwRound(Gear^.dX * -_70));

        doStepFallingGear(Gear);
        CalcRotationDirAngle(Gear);
        //CheckGearDrowning(Gear)
    end
    else
    begin
        Gear^.State := Gear^.State or gsttmpFlag;
        AddGearCI(Gear)
    end;

(*
Attempt to make a barrel knock itself over an edge.  Would need more checks to avoid issues like burn damage
    begin
    x:= hwRound(Gear^.X);
    y:= hwRound(Gear^.Y);
    if (((y+1) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
        if (Land[y+1, x] = 0) then
            begin
            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
                Gear^.dX:= -_0_08
            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
                Gear^.dX:= _0_08;
            end;
    if Gear^.dX.QWordValue = 0 then AddGearCI(Gear)
    end; *)

    if not Gear^.dY.isNegative and (Gear^.dY < _0_001) and (TestCollisionYwithGear(Gear, 1) <> 0) then Gear
        ^.dY := _0;
    if hwAbs(Gear^.dX) < _0_001 then Gear^.dX := _0;

    if (Gear^.Health > 0) and ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then
        if (cBarrelHealth div Gear^.Health) > 2 then
            AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke)
    else
        AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite);
    dec(Gear^.Health, Gear^.Damage);
    Gear^.Damage := 0;
    if Gear^.Health <= 0 then Gear^.doStep := @doStepCase;
    // Hand off to doStepCase for the explosion

end;

procedure doStepCase(Gear: PGear);
var 
    i, x, y: LongInt;
    k: TGearType;
    exBoom: boolean;
    dX, dY: HWFloat;
    hog: PHedgehog;
begin
    k := Gear^.Kind;
    exBoom := false;

    if (Gear^.Message and gmDestroy) > 0 then
    begin
        DeleteGear(Gear);
        FreeActionsList;
        SetAllToActive;
        // something (hh, mine, etc...) could be on top of the case
        with CurrentHedgehog^ do
            if Gear <> nil then Gear^.Message := Gear^.Message and not (gmLJump or gmHJump);
        exit
    end;

    if k = gtExplosives then
    begin
        //if V > _0_03 then Gear^.State:= Gear^.State or gstAnimation;
        if (hwAbs(Gear^.dX) > _0_15) or ((hwAbs(Gear^.dY) > _0_15) and (hwAbs(Gear^.dX) > _0_02))
            then Gear^.doStep := @doStepRollingBarrel;

        if (Gear^.Health > 0) and ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then
            if (cBarrelHealth div Gear^.Health) > 2 then
                AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke)
        else
            AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite);
        dec(Gear^.Health, Gear^.Damage);
        Gear^.Damage := 0;
        if Gear^.Health <= 0 then
            exBoom := true;
    end;

    if (Gear^.Damage > 0) or exBoom then
    begin
        x := hwRound(Gear^.X);
        y := hwRound(Gear^.Y);
        hog:= Gear^.Hedgehog;

        DeleteGear(Gear);
        // <-- delete gear!

        if k = gtCase then
        begin
            doMakeExplosion(x, y, 25, hog, EXPLAutoSound);
            for i:= 0 to 63 do
                AddGear(x, y, gtFlame, 0, _0, _0, 0);
        end
        else if k = gtExplosives then
            begin
                doMakeExplosion(x, y, 75, hog, EXPLAutoSound);
                for i:= 0 to 31 do
                begin
                    dX := AngleCos(i * 64) * _0_5 * (getrandom + _1);
                    dY := AngleSin(i * 64) * _0_5 * (getrandom + _1);
                    AddGear(x, y, gtFlame, 0, dX, dY, 0);
                    AddGear(x, y, gtFlame, 0, -dX, -dY, 0)^.State := gsttmpFlag;
                end
            end;
        exit
    end;

    if (Gear^.dY.QWordValue <> 0) or (TestCollisionYwithGear(Gear, 1) = 0) then
    begin
        AllInactive := false;
        Gear^.dY := Gear^.dY + cGravity;
        Gear^.Y := Gear^.Y + Gear^.dY;
        if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then SetAllHHToActive;
        if (Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, -1) <> 0) then Gear^.dY := _0;
        if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then
        begin
            if (Gear^.dY > _0_2) and (k = gtExplosives) then
                inc(Gear^.Damage, hwRound(Gear^.dY * _70));

            if Gear^.dY > _0_2 then
                for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do
                    AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust)
            ;
            Gear^.dY := - Gear^.dY * Gear^.Elasticity;
            if Gear^.dY > - _0_001 then Gear^.dY := _0
            else if Gear^.dY < - _0_03 then
                     PlaySound(Gear^.ImpactSound);
        end;
        //if Gear^.dY > - _0_001 then Gear^.dY:= _0
        CheckGearDrowning(Gear);
    end;

    if (Gear^.dY.QWordValue = 0) then AddGearCI(Gear)
    else if (Gear^.dY.QWordValue <> 0) then DeleteCI(Gear)
end;

////////////////////////////////////////////////////////////////////////////////

procedure doStepTarget(Gear: PGear);
begin
    if (Gear^.Timer = 0) and (Gear^.Tag = 0) then
        PlaySound(sndWarp);

    if (Gear^.Tag = 0) and (Gear^.Timer < 1000) then
        inc(Gear^.Timer)
    else if Gear^.Tag = 1 then
        Gear^.Tag := 2
    else if Gear^.Tag = 2 then
             if Gear^.Timer > 0 then
                 dec(Gear^.Timer)
    else
    begin
        DeleteGear(Gear);
        exit;
    end;

    doStepCase(Gear)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepIdle(Gear: PGear);
begin
    AllInactive := false;
    dec(Gear^.Timer);
    if Gear^.Timer = 0 then
    begin
        DeleteGear(Gear);
        AfterAttack
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepShover(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.State := HHGear^.State or gstNoDamage;
    DeleteCI(HHGear);

    AmmoShove(Gear, 30, 115);

    HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving;
    Gear^.Timer := 250;
    Gear^.doStep := @doStepIdle
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepWhip(Gear: PGear);
var 
    HHGear: PGear;
    i: LongInt;
begin
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.State := HHGear^.State or gstNoDamage;
    DeleteCI(HHGear);

    for i:= 0 to 3 do
        begin
        AmmoShove(Gear, 30, 25);
        Gear^.X := Gear^.X + Gear^.dX * 5
        end;

    HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving;

    Gear^.Timer := 250;
    Gear^.doStep := @doStepIdle
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFlame(Gear: PGear);
var 
    gX,gY,i: LongInt;
    sticky: Boolean;
    vgt: PVisualGear;
    tdX,tdY: HWFloat;
begin
    sticky:= (Gear^.State and gsttmpFlag) <> 0;
    if not sticky then AllInactive := false;

    if TestCollisionYwithGear(Gear, 1) = 0 then
    begin
        AllInactive := false;

        if ((GameTicks mod 100) = 0) then
            begin
            vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire, gstTmpFlag);
            if vgt <> nil then
                begin
                vgt^.dx:= 0;
                vgt^.dy:= 0;
                vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2);
                end;
            end;


        if Gear^.dX.QWordValue > _0_01.QWordValue then
            Gear^.dX := Gear^.dX * _0_995;
        Gear^.dY := Gear^.dY + cGravity;
        // if sticky then Gear^.dY := Gear^.dY + cGravity;
        if Gear^.dY.QWordValue > _0_2.QWordValue then Gear^.dY := Gear^.dY * _0_995;

        //if sticky then Gear^.X := Gear^.X + Gear^.dX else
        Gear^.X := Gear^.X + Gear^.dX + cWindSpeed * 640;
        Gear^.Y := Gear^.Y + Gear^.dY;

        if (hwRound(Gear^.Y) > cWaterLine) then
        begin
            gX := hwRound(Gear^.X);
            for i:= 0 to 3 do
                AddVisualGear(gX - 16 + Random(32), cWaterLine - 16 + Random(16), vgtSteam);
            PlaySound(sndVaporize);
            DeleteGear(Gear);
            exit
        end
    end
    else
    begin
        if sticky then
            begin
            Gear^.Radius := 7;
            tdX:= Gear^.dX;
            tdY:= Gear^.dY;
            Gear^.dX.QWordValue:= 214748365;
            Gear^.dY.QWordValue:= 429496730;
            Gear^.dX.isNegative:= getrandom(2)<>1;
            Gear^.dY.isNegative:= true;
            AmmoShove(Gear, 2, 125);
            Gear^.dX:= tdX;
            Gear^.dY:= tdY;
            Gear^.Radius := 1
            end;
        if Gear^.Timer > 0 then
            begin
            dec(Gear^.Timer);
            inc(Gear^.Damage)
            end
        else
        begin
            gX := hwRound(Gear^.X);
            gY := hwRound(Gear^.Y);
            // Standard fire
            if not sticky then
                begin
                if ((GameTicks and $1) = 0) then
                    begin
                    Gear^.Radius := 7;
                    tdX:= Gear^.dX;
                    tdY:= Gear^.dY;
                    Gear^.dX.QWordValue:= 214748365;
                    Gear^.dY.QWordValue:= 429496730;
                    Gear^.dX.isNegative:= getrandom(2)<>1;
                    Gear^.dY.isNegative:= true;
                    AmmoShove(Gear, 6, 100);
                    Gear^.dX:= tdX;
                    Gear^.dY:= tdY;
                    Gear^.Radius := 1;
                    end
                else if ((GameTicks and $3) = 3) then doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage); 
                //DrawExplosion(gX, gY, 4);
                if ((GameTicks and $7) = 0) and (Random(2) = 0) then
                    for i:= 1 to Random(2)+1 do
                        AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
                if Gear^.Health > 0 then dec(Gear^.Health);
                Gear^.Timer := 450 - Gear^.Tag * 8
                end
            else
                begin
                // Modified fire
                if ((GameTicks and $7FF) = 0) and ((GameFlags and gfSolidLand) = 0) then
                    begin
                    DrawExplosion(gX, gY, 4);

                    for i:= 0 to Random(3) do
                        AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
                    end;

// 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.
                Gear^.Timer := 100 - Gear^.Tag * 3;
                if (Gear^.Damage > 3000+Gear^.Tag*1500) then Gear^.Health := 0
            end
        end
    end;
    if Gear^.Health = 0 then
    begin
        gX := hwRound(Gear^.X);
        gY := hwRound(Gear^.Y);
        if not sticky then
        begin
            if ((GameTicks and $3) = 0) and (Random(1) = 0) then
            begin
                for i:= 1 to Random(2)+1 do
                begin
                    AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
                end;
            end;
        end
        else
        begin
            for i:= 0 to Random(3) do
            begin
                AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
            end;
        end;

        DeleteGear(Gear)
    end;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFirePunchWork(Gear: PGear);
var 
    HHGear: PGear;
begin
    AllInactive := false;
    if ((Gear^.Message and gmDestroy) <> 0) then
    begin
        DeleteGear(Gear);
        AfterAttack;
        exit
    end;

    HHGear := Gear^.Hedgehog^.Gear;
    if hwRound(HHGear^.Y) <= Gear^.Tag - 2 then
    begin
        Gear^.Tag := hwRound(HHGear^.Y);
        DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y - _1, _0_5, _0, cHHRadius * 4, 2);
        HHGear^.State := HHGear^.State or gstNoDamage;
        Gear^.Y := HHGear^.Y;
        AmmoShove(Gear, 30, 40);
        HHGear^.State := HHGear^.State and not gstNoDamage
    end;

    HHGear^.dY := HHGear^.dY + cGravity;
    if not (HHGear^.dY.isNegative) then
    begin
        HHGear^.State := HHGear^.State or gstMoving;
        DeleteGear(Gear);
        AfterAttack;
        exit
    end;

    if CheckLandValue(hwRound(HHGear^.X), hwRound(HHGear^.Y + HHGear^.dY + SignAs(_6,Gear^.dY)),
       lfIndestructible) then
        HHGear^.Y := HHGear^.Y + HHGear^.dY
end;

procedure doStepFirePunch(Gear: PGear);
var 
    HHGear: PGear;
begin
    AllInactive := false;
    HHGear := Gear^.Hedgehog^.Gear;
    DeleteCI(HHGear);
    HHGear^.X := int2hwFloat(hwRound(HHGear^.X)) - _0_5;
    HHGear^.dX := SignAs(cLittle, Gear^.dX);

    HHGear^.dY := - _0_3;

    Gear^.X := HHGear^.X;
    Gear^.dX := SignAs(_0_45, Gear^.dX);
    Gear^.dY := - _0_9;
    Gear^.doStep := @doStepFirePunchWork;
    DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y + _1, _0_5, _0, cHHRadius * 4, 5);

    AddVoice(TSound(ord(sndFirePunch1) + GetRandom(6)), HHGear^.Hedgehog^.Team^.voicepack)
end;

////////////////////////////////////////////////////////////////////////////////

procedure doStepParachuteWork(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;

    inc(Gear^.Timer);

    if (TestCollisionYwithGear(HHGear, 1) <> 0)
       or ((HHGear^.State and gstHHDriven) = 0)
       or CheckGearDrowning(HHGear)
       or ((Gear^.Message and gmAttack) <> 0) then
    begin
        with HHGear^ do
        begin
            Message := 0;
            SetLittle(dX);
            dY := _0;
            State := State or gstMoving;
        end;
        DeleteGear(Gear);
        isCursorVisible := false;
        ApplyAmmoChanges(HHGear^.Hedgehog^);
        exit
    end;

    HHGear^.X := HHGear^.X + cWindSpeed * 200;

    if (Gear^.Message and gmLeft) <> 0 then HHGear^.X := HHGear^.X - cMaxWindSpeed * 80
    else if (Gear^.Message and gmRight) <> 0 then HHGear^.X := HHGear^.X + cMaxWindSpeed * 80;
    if (Gear^.Message and gmUp) <> 0 then HHGear^.Y := HHGear^.Y - cGravity * 40
    else if (Gear^.Message and gmDown) <> 0 then HHGear^.Y := HHGear^.Y + cGravity * 40;

    // don't drift into obstacles
    if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
        HHGear^.X := HHGear^.X - int2hwFloat(hwSign(HHGear^.dX));
    HHGear^.Y := HHGear^.Y + cGravity * 100;
    Gear^.X := HHGear^.X;
    Gear^.Y := HHGear^.Y
end;

procedure doStepParachute(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;

    DeleteCI(HHGear);

    AfterAttack;

    HHGear^.State := HHGear^.State and not (gstAttacking or gstAttacked or gstMoving);
    HHGear^.Message := HHGear^.Message and not gmAttack;

    Gear^.doStep := @doStepParachuteWork;

    Gear^.Message := HHGear^.Message;
    doStepParachuteWork(Gear)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepAirAttackWork(Gear: PGear);
begin
    AllInactive := false;
    Gear^.X := Gear^.X + cAirPlaneSpeed * Gear^.Tag;

    if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then
        begin
        dec(Gear^.Health);
        case Gear^.State of 
            0: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed *
                             Gear^.Tag, _0, 0);
            1: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine,    0, cBombsSpeed *
                             Gear^.Tag, _0, 0);
            2: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed *
                             Gear^.Tag, _0, 0);
            3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed *
                             Gear^.Tag, _0, Gear^.Timer + 1);
            //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed *
            //                 Gear^.Tag, _0, 5000);
            end;
        Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag);
        StopSound(Gear^.SoundChannel, 4000);
        end;

    if (GameTicks and $3F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);

    if (hwRound(Gear^.X) > (LAND_WIDTH+2048)) or (hwRound(Gear^.X) < -2048) then
    begin
        // avoid to play forever (is this necessary?)
        StopSound(Gear^.SoundChannel);
        DeleteGear(Gear)
    end;
end;

procedure doStepAirAttack(Gear: PGear);
begin
    AllInactive := false;

    if Gear^.X.QWordValue = 0 then
    begin
        Gear^.Tag :=  1;
        Gear^.X := -_2048;
    end
    else
    begin
        Gear^.Tag := -1;
        Gear^.X := int2hwFloat(LAND_WIDTH + 2048);
    end;

    Gear^.Y := int2hwFloat(topY-300);
    Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15);

    // calcs for Napalm Strike, so that it will hit the target (without wind at least :P)
    if (Gear^.State = 2) then
        Gear^.dX := Gear^.dX - cBombsSpeed * Gear^.Tag * 900 
    // calcs for regular falling gears
    else if (int2hwFloat(Gear^.Target.Y) - Gear^.Y > _0) then
            Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 /
                cGravity) * Gear^.Tag;

    Gear^.Health := 6;
    Gear^.doStep := @doStepAirAttackWork;
    Gear^.SoundChannel := LoopSound(sndPlane, 4000);

end;

////////////////////////////////////////////////////////////////////////////////

procedure doStepAirBomb(Gear: PGear);
begin
    AllInactive := false;
    doStepFallingGear(Gear);
    if (Gear^.State and gstCollision) <> 0 then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
        DeleteGear(Gear);
        performRumble();
        exit
    end;
    if (GameTicks and $3F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
end;

////////////////////////////////////////////////////////////////////////////////

procedure doStepGirder(Gear: PGear);
var 
    HHGear: PGear;
    x, y, tx, ty: hwFloat;
begin
    AllInactive := false;

    HHGear := Gear^.Hedgehog^.Gear;
    tx := int2hwFloat(Gear^.Target.X);
    ty := int2hwFloat(Gear^.Target.Y);
    x := HHGear^.X;
    y := HHGear^.Y;

    if (Distance(tx - x, ty - y) > _256) 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
    begin
        PlaySound(sndDenied);
        HHGear^.Message := HHGear^.Message and not gmAttack;
        HHGear^.State := HHGear^.State and not gstAttacking;
        HHGear^.State := HHGear^.State or gstHHChooseTarget;
        isCursorVisible := true;
        DeleteGear(Gear)
    end
    else 
    begin
        PlaySound(sndPlaced);
        DeleteGear(Gear);
        AfterAttack;
    end;

    HHGear^.State := HHGear^.State and not (gstAttacking or gstAttacked);
    HHGear^.Message := HHGear^.Message and not gmAttack;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepTeleportAfter(Gear: PGear);
var 
    HHGear: PGear;
begin
    Gear^.Hedgehog^.Unplaced := false;
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.Y := HHGear^.Y + HHGear^.dY;
    HHGear^.X := HHGear^.X + HHGear^.dX;
    // hedgehog falling to collect cases
    HHGear^.dY := HHGear^.dY + cGravity;
    if (TestCollisionYwithGear(HHGear, 1) <> 0)
       or CheckGearDrowning(HHGear) then
    begin
        DeleteGear(Gear);
        AfterAttack
    end
end;

procedure doStepTeleportAnim(Gear: PGear);
begin
    inc(Gear^.Timer);
    if Gear^.Timer = 65 then
    begin
        Gear^.Timer := 0;
        inc(Gear^.Pos);
        if Gear^.Pos = 11 then
            Gear^.doStep := @doStepTeleportAfter
    end;
end;

procedure doStepTeleport(Gear: PGear);
var 
    HHGear: PGear;
begin
    AllInactive := false;

    HHGear := Gear^.Hedgehog^.Gear;
    if not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprHHTelepMask].Width div 2,
       Gear^.Target.Y - SpritesData[sprHHTelepMask].Height div 2,
       sprHHTelepMask, 0, false, false) then
    begin
        HHGear^.Message := HHGear^.Message and not gmAttack;
        HHGear^.State := HHGear^.State and not gstAttacking;
        HHGear^.State := HHGear^.State or gstHHChooseTarget;
        DeleteGear(Gear);
        isCursorVisible := true;
        PlaySound(sndDenied)
    end
    else
    begin
        DeleteCI(HHGear);
        SetAllHHToActive;
        Gear^.doStep := @doStepTeleportAnim;

  // copy old HH position and direction to Gear (because we need them for drawing the vanishing hog)
        Gear^.dX := HHGear^.dX;
        // retrieve the cursor direction (it was previously copied to X so it doesn't get lost)
        HHGear^.dX.isNegative := (Gear^.X.QWordValue <> 0);
        Gear^.X := HHGear^.X;
        Gear^.Y := HHGear^.Y;
        HHGear^.X := int2hwFloat(Gear^.Target.X);
        HHGear^.Y := int2hwFloat(Gear^.Target.Y);
        HHGear^.State := HHGear^.State or gstMoving;
        playSound(sndWarp)
    end;
    Gear^.Target.X:= NoPointX
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepSwitcherWork(Gear: PGear);
var 
    HHGear: PGear;
    Msg, State: Longword;
begin
    AllInactive := false;

    if ((Gear^.Message and not gmSwitch) <> 0) or (TurnTimeLeft = 0) then
    begin
        HHGear := Gear^.Hedgehog^.Gear;
        Msg := Gear^.Message and not gmSwitch;
        DeleteGear(Gear);
        ApplyAmmoChanges(HHGear^.Hedgehog^);

        HHGear := CurrentHedgehog^.Gear;
        ApplyAmmoChanges(HHGear^.Hedgehog^);
        HHGear^.Message := Msg;
        exit
    end;

    if (Gear^.Message and gmSwitch) <> 0 then
    begin
        HHGear := CurrentHedgehog^.Gear;
        HHGear^.Message := HHGear^.Message and not gmSwitch;
        Gear^.Message := Gear^.Message and not gmSwitch;
        State := HHGear^.State;
        HHGear^.State := 0;
        HHGear^.Active := false;
        HHGear^.Z := cHHZ;
        RemoveGearFromList(HHGear);
        InsertGearToList(HHGear);

        PlaySound(sndSwitchHog);

        repeat
            CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.
                                         HedgehogsNumber);
        until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0);

        CurrentHedgehog := @CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog];

        HHGear := CurrentHedgehog^.Gear;
        HHGear^.State := State;
        HHGear^.Active := true;
        FollowGear := HHGear;
        HHGear^.Z := cCurrHHZ;
        RemoveGearFromList(HHGear);
        InsertGearToList(HHGear);
        Gear^.X := HHGear^.X;
        Gear^.Y := HHGear^.Y
    end;
end;

procedure doStepSwitcher(Gear: PGear);
var 
    HHGear: PGear;
begin
    Gear^.doStep := @doStepSwitcherWork;

    HHGear := Gear^.Hedgehog^.Gear;
    OnUsedAmmo(HHGear^.Hedgehog^);
    with HHGear^ do
    begin
        State := State and not gstAttacking;
        Message := Message and not gmAttack
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepMortar(Gear: PGear);
var 
    dX, dY: hwFloat;
    i: LongInt;
    dxn, dyn: boolean;
begin
    AllInactive := false;
    dxn := Gear^.dX.isNegative;
    dyn := Gear^.dY.isNegative;

    doStepFallingGear(Gear);
    if (Gear^.State and gstCollision) <> 0 then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);

        Gear^.dX.isNegative := not dxn;
        Gear^.dY.isNegative := not dyn;
        for i:= 0 to 4 do
        begin
            dX := Gear^.dX + (GetRandom - _0_5) * _0_03;
            dY := Gear^.dY + (GetRandom - _0_5) * _0_03;
            AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtCluster, 0, dX, dY, 25);
        end;

        DeleteGear(Gear);
        exit
    end;

    if (GameTicks and $3F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepKamikazeWork(Gear: PGear);

const upd: Longword =   0;
var 
    i: LongWord;
    HHGear: PGear;
    sparkles: PVisualGear;
    hasWishes: boolean;
begin
    AllInactive := false;
    hasWishes:= ((Gear^.Message and (gmPrecise or gmSwitch)) = (gmPrecise or gmSwitch));
    if hasWishes then Gear^.AdvBounce:= 1;

    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.State := HHGear^.State or gstNoDamage;
    DeleteCI(HHGear);

    Gear^.X := HHGear^.X;
    Gear^.Y := HHGear^.Y;
    if (GameTicks mod 2 = 0) and hasWishes then
        begin
        sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1);
        if sparkles <> nil then 
            begin
            sparkles^.Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF;
            sparkles^.Angle:= random * 360;
            end
        end;

    i := 2;
    repeat
        
        Gear^.X := Gear^.X + HHGear^.dX;
        Gear^.Y := Gear^.Y + HHGear^.dY;
        HHGear^.X := Gear^.X;
        HHGear^.Y := Gear^.Y;

        inc(Gear^.Damage, 2);

        //  if TestCollisionXwithGear(HHGear, hwSign(Gear^.dX))
        //      or TestCollisionYwithGear(HHGear, hwSign(Gear^.dY)) then inc(Gear^.Damage, 3);

        dec(i)
    until (i = 0) or (Gear^.Damage > Gear^.Health);

    inc(upd);
    if upd > 3 then
    begin
        if Gear^.Health < 1500 then
        begin
            if Gear^.AdvBounce <> 0 then
                Gear^.Pos := 3
            else
                Gear^.Pos := 2;
        end;

        AmmoShove(Gear, 30, 40);

        DrawTunnel(HHGear^.X - HHGear^.dX * 10,
                   HHGear^.Y - _2 - HHGear^.dY * 10 + hwAbs(HHGear^.dY) * 2,
        HHGear^.dX,
        HHGear^.dY,
        20 + cHHRadius * 2,
        cHHRadius * 2 + 6);

        upd := 0
    end;

    if Gear^.Health < Gear^.Damage then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
        for i:= 0 to 31 do
            begin
            sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
            if sparkles <> nil then
                with sparkles^ do
                    begin
                    Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF;
                    Angle:= random * 360;
                    dx:= 0.001 * (random(200));
                    dy:= 0.001 * (random(200));
                    if random(2) = 0 then dx := -dx;
                    if random(2) = 0 then dy := -dy;
                    FrameTicks:= random(400) + 250
                    end;
            end;
        AfterAttack;
        DeleteGear(Gear);
        DeleteGear(HHGear);
    end
    else
    begin
        dec(Gear^.Health, Gear^.Damage);
        Gear^.Damage := 0
    end
end;

procedure doStepKamikazeIdle(Gear: PGear);
begin
    AllInactive := false;
    dec(Gear^.Timer);
    if Gear^.Timer = 0 then
        begin
        Gear^.Pos := 1;
        PlaySound(sndKamikaze, Gear^.Hedgehog^.Team^.voicepack);
        Gear^.doStep := @doStepKamikazeWork
        end
end;

procedure doStepKamikaze(Gear: PGear);
var 
    HHGear: PGear;
begin
    AllInactive := false;

    HHGear := Gear^.Hedgehog^.Gear;

    HHGear^.dX := Gear^.dX;
    HHGear^.dY := Gear^.dY;

    Gear^.dX := SignAs(_0_45, Gear^.dX);
    Gear^.dY := - _0_9;

    Gear^.Timer := 550;

    Gear^.doStep := @doStepKamikazeIdle
end;

////////////////////////////////////////////////////////////////////////////////

const cakeh =   27;
    cakeDmg =   75;
var 
    CakePoints: array[0..Pred(cakeh)] of record
        x, y: hwFloat;
    end;
    CakeI: Longword;

procedure doStepCakeExpl(Gear: PGear);
begin
    AllInactive := false;

    inc(Gear^.Tag);
    if Gear^.Tag < 2250 then exit;

    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound);
    AfterAttack;
    DeleteGear(Gear)
end;

procedure doStepCakeDown(Gear: PGear);
var 
    gi: PGear;
    dmg: LongInt;
begin
    AllInactive := false;

    inc(Gear^.Tag);
    if Gear^.Tag < 100 then exit;
    Gear^.Tag := 0;

    if Gear^.Pos = 0 then
    begin
        gi := GearsList;
        while gi <> nil do
        begin
            dmg := cakeDmg * 2 - hwRound(Distance(gi^.X - Gear^.X, gi^.Y - Gear^.Y));
            if (dmg > 1) and (gi^.Kind = gtHedgehog) then
                if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then
                    gi^.State := gi^.State or gstLoser
            else
                gi^.State := gi^.State or gstWinner;
            gi := gi^.NextGear
        end;
        Gear^.doStep := @doStepCakeExpl;
        PlaySound(sndCake)
    end
    else dec(Gear^.Pos)
end;


procedure doStepCakeWork(Gear: PGear);

const dirs: array[0..3] of TPoint =   ((x: 0; y: -1), (x: 1; y: 0),(x: 0; y: 1),(x: -1; y: 0));
var 
    xx, yy, xxn, yyn: LongInt;
    da: LongInt;
    tdx, tdy: hwFloat;

procedure PrevAngle;
begin
    Gear^.Angle := (LongInt(Gear^.Angle) + 4 - dA) mod 4
end;

procedure NextAngle;
begin
    Gear^.Angle := (LongInt(Gear^.Angle) + 4 + dA) mod 4
end;

begin
    AllInactive := false;

    inc(Gear^.Tag);
    if Gear^.Tag < 7 then exit;

    dA := hwSign(Gear^.dX);
    xx := dirs[Gear^.Angle].x;
    yy := dirs[Gear^.Angle].y;
    xxn := dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].x;
    yyn := dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].y;

    if (xx = 0) then
        if TestCollisionYwithGear(Gear, yy) <> 0 then
            PrevAngle
    else
    begin
        Gear^.Tag := 0;
        Gear^.Y := Gear^.Y + int2hwFloat(yy);
        if not TestCollisionXwithGear(Gear, xxn) then
        begin
            Gear^.X := Gear^.X + int2hwFloat(xxn);
            NextAngle
        end;
    end;

    if (yy = 0) then
        if TestCollisionXwithGear(Gear, xx) then
            PrevAngle
    else
    begin
        Gear^.Tag := 0;
        Gear^.X := Gear^.X + int2hwFloat(xx);
        if not TestCollisionY(Gear, yyn) then
        begin
            Gear^.Y := Gear^.Y + int2hwFloat(yyn);
            NextAngle
        end;
    end;

    if Gear^.Tag = 0 then
    begin
        CakeI := (CakeI + 1) mod cakeh;
        tdx := CakePoints[CakeI].x - Gear^.X;
        tdy := - CakePoints[CakeI].y + Gear^.Y;
        CakePoints[CakeI].x := Gear^.X;
        CakePoints[CakeI].y := Gear^.Y;
        Gear^.DirAngle := DxDy2Angle(tdx, tdy);
    end;

    dec(Gear^.Health);
    Gear^.Timer := Gear^.Health*10;
    if Gear^.Health mod 100 = 0 then Gear^.PortalCounter:= 0;
    // This is not seconds, but at least it is *some* feedback
    if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then
    begin
        FollowGear := Gear;
        Gear^.RenderTimer := false;
        Gear^.doStep := @doStepCakeDown
    end
end;

procedure doStepCakeUp(Gear: PGear);
var 
    i: Longword;
begin
    AllInactive := false;

    inc(Gear^.Tag);
    if Gear^.Tag < 100 then exit;
    Gear^.Tag := 0;

    if Gear^.Pos = 6 then
    begin
        for i:= 0 to Pred(cakeh) do
        begin
            CakePoints[i].x := Gear^.X;
            CakePoints[i].y := Gear^.Y
        end;
        CakeI := 0;
        Gear^.doStep := @doStepCakeWork
    end
    else inc(Gear^.Pos)
end;

procedure doStepCakeFall(Gear: PGear);
begin
    AllInactive := false;

    Gear^.dY := Gear^.dY + cGravity;
    if TestCollisionYwithGear(Gear, 1) <> 0 then Gear^.doStep := @doStepCakeUp
    else
        begin
        Gear^.Y := Gear^.Y + Gear^.dY;
        if CheckGearDrowning(Gear) then AfterAttack
        end
end;

procedure doStepCake(Gear: PGear);
var 
    HHGear: PGear;
begin
    AllInactive := false;

    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.Message := HHGear^.Message and (not gmAttack);
    DeleteCI(HHGear);
    Gear^.IntersectGear:= nil;

    FollowGear := Gear;

    Gear^.doStep := @doStepCakeFall
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepSeductionWork(Gear: PGear);
var i: LongInt;
    hogs: TPGearArray;
begin
    AllInactive := false;
    hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius);
    if Length(hogs) > 0 then
        begin
        for i:= 0 to High(hogs) do
            begin
            if hogs[i] <> CurrentHedgehog^.Gear then
                begin
                //d:= Distance(Gear^.X - hogs[i]^.X, Gear^.Y - hogs[i]^.Y);
                hogs[i]^.dX:= _50 * cGravity * (Gear^.X - hogs[i]^.X) / _25;
                //if Gear^.X < hogs[i]^.X then hogs[i]^.dX.isNegative:= true;
                hogs[i]^.dY:= -_450 * cGravity;
                hogs[i]^.Active:= true;
                end
            end;
        end ;
        AfterAttack;
        DeleteGear(Gear);
(*
    Gear^.X := Gear^.X + Gear^.dX;
    Gear^.Y := Gear^.Y + Gear^.dY;
    x := hwRound(Gear^.X);
    y := hwRound(Gear^.Y);

    if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
        if (Land[y, x] <> 0) then
            begin
            Gear^.dX.isNegative := not Gear^.dX.isNegative;
            Gear^.dY.isNegative := not Gear^.dY.isNegative;
            Gear^.dX := Gear^.dX * _1_5;
            Gear^.dY := Gear^.dY * _1_5 - _0_3;
            AmmoShove(Gear, 0, 40);
            AfterAttack;
            DeleteGear(Gear)
            end
        else
    else
        begin
        AfterAttack;
        DeleteGear(Gear)
        end*)
end;

procedure doStepSeductionWear(Gear: PGear);
var heart: PVisualGear;
begin
    AllInactive := false;
    inc(Gear^.Timer);
    if Gear^.Timer > 250 then
        begin
        Gear^.Timer := 0;
        inc(Gear^.Pos);
        if Gear^.Pos = 5 then
            PlaySound(sndYoohoo, Gear^.Hedgehog^.Team^.voicepack)
        end;

    if (Gear^.Pos = 14) and (RealTicks and $3 = 0) then 
        begin
        heart:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
        if heart <> nil then 
            with heart^ do
                begin
                dx:= 0.001 * (random(200));
                dy:= 0.001 * (random(200));
                if random(2) = 0 then dx := -dx;
                if random(2) = 0 then dy := -dy;
                FrameTicks:= random(750) + 1000;
                State:= ord(sprSeduction)
                end;
        end;

    if Gear^.Pos = 15 then
        Gear^.doStep := @doStepSeductionWork
end;

procedure doStepSeduction(Gear: PGear);
begin
    AllInactive := false;
    //DeleteCI(Gear^.Hedgehog^.Gear);
    Gear^.doStep := @doStepSeductionWear
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepWaterUp(Gear: PGear);
var 
    i: LongWord;
begin
    if (Gear^.Tag = 0) or (cWaterLine = 0) then
        begin
            DeleteGear(Gear);
            exit
        end;

    AllInactive := false;

    inc(Gear^.Timer);
    if Gear^.Timer = 17 then
        Gear^.Timer := 0
    else
        exit;

    if cWaterLine > 0 then
    begin
        dec(cWaterLine);
        for i:= 0 to LAND_WIDTH - 1 do
            Land[cWaterLine, i] := 0;
        SetAllToActive
    end;

    dec(Gear^.Tag);
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepDrill(Gear: PGear);
forward;

procedure doStepDrillDrilling(Gear: PGear);
var 
    t: PGearArray;
    ox, oy: hwFloat;
begin
    AllInactive := false;

    if (Gear^.Timer > 0) and ((Gear^.Timer mod 10) = 0) then
    begin
        ox := Gear^.X;
        oy := Gear^.Y;
        Gear^.X := Gear^.X + Gear^.dX;
        Gear^.Y := Gear^.Y + Gear^.dY;
        DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 2, 6);
        if (Gear^.Timer mod 30) = 0 then
            AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust);
        if (CheckGearDrowning(Gear)) then
        begin
            StopSound(Gear^.SoundChannel);
            exit
        end
    end;

    t := CheckGearsCollision(Gear);
    //fixes drill not exploding when touching HH bug
    if (Gear^.Timer = 0) or (t^.Count <> 0) or 
       ( ((Gear^.State and gsttmpFlag) = 0) and
         (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0)
         and not TestCollisionXWithGear(Gear, hwSign(Gear^.dX)))
// CheckLandValue returns true if the type isn't matched
       or not CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y), lfIndestructible) then
        begin
        //out of time or exited ground
        StopSound(Gear^.SoundChannel);
        if (Gear^.State and gsttmpFlag) <> 0 then
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
        else
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
        DeleteGear(Gear);
        exit
        end
    else if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and not TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) then
        begin
        StopSound(Gear^.SoundChannel);
        Gear^.Tag := 1;
        Gear^.doStep := @doStepDrill
        end;

    dec(Gear^.Timer);
end;

procedure doStepDrill(Gear: PGear);
var 
    t: PGearArray;
    oldDx, oldDy: hwFloat;
    t2: hwFloat;
begin
    AllInactive := false;

    if (Gear^.State and gsttmpFlag) = 0 then
        Gear^.dX := Gear^.dX + cWindSpeed;

    oldDx := Gear^.dX;
    oldDy := Gear^.dY;

    doStepFallingGear(Gear);

    if (GameTicks and $3F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);

    if ((Gear^.State and gstCollision) <> 0) then
    begin
        //hit
        Gear^.dX := oldDx;
        Gear^.dY := oldDy;

        t := CheckGearsCollision(Gear);
        if (t^.Count = 0) then
        begin
            //hit the ground not the HH
            t2 := _0_5 / Distance(Gear^.dX, Gear^.dY);
            Gear^.dX := Gear^.dX * t2;
            Gear^.dY := Gear^.dY * t2;
        end
        else
        begin
            //explode right on contact with HH
            if (Gear^.State and gsttmpFlag) <> 0 then
                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
            else
                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
            DeleteGear(Gear);
            exit;
        end;

        Gear^.SoundChannel := LoopSound(sndDrillRocket);
        Gear^.doStep := @doStepDrillDrilling;
        if (Gear^.State and gsttmpFlag) <> 0 then
            gear^.RenderTimer:= true;
        dec(Gear^.Timer)
    end
    else if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Tag <> 0) then
         begin
            if Gear^.Timer = 0 then
            begin
                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
                DeleteGear(Gear);
            end
            else
                dec(Gear^.Timer);
         end;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepBallgunWork(Gear: PGear);
var 
    HHGear: PGear;
    rx, ry: hwFloat;
    gX, gY: LongInt;
begin
    AllInactive := false;
    dec(Gear^.Timer);
    HHGear := Gear^.Hedgehog^.Gear;
    HedgehogChAngle(HHGear);
    gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
    gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
    if (Gear^.Timer mod 100) = 0 then
    begin
        rx := rndSign(getRandom * _0_1);
        ry := rndSign(getRandom * _0_1);

        AddGear(gx, gy, gtBall, 0,
                SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx,
        AngleCos(HHGear^.Angle) * ( - _0_8) + ry,
        0);

        PlaySound(sndGun);
    end;

    if (Gear^.Timer = 0) or (HHGear^.Damage <> 0) then
    begin
        DeleteGear(Gear);
        AfterAttack
    end
end;

procedure doStepBallgun(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.Message := HHGear^.Message and not (gmUp or gmDown);
    HHGear^.State := HHGear^.State or gstNotKickable;
    Gear^.doStep := @doStepBallgunWork
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepRCPlaneWork(Gear: PGear);

const cAngleSpeed =   3;
var 
    HHGear: PGear;
    i: LongInt;
    dX, dY: hwFloat;
    fChanged: boolean;
    trueAngle: Longword;
    t: PGear;
begin
    AllInactive := false;

    HHGear := Gear^.Hedgehog^.Gear;
    FollowGear := Gear;

    if Gear^.Timer > 0 then dec(Gear^.Timer);

    fChanged := false;
    if ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then
    begin
        fChanged := true;
        if Gear^.Angle > 2048 then dec(Gear^.Angle)
        else
            if Gear^.Angle < 2048 then inc(Gear^.Angle)
        else fChanged := false
    end
    else
    begin
        if ((Gear^.Message and gmLeft) <> 0) then
        begin
            fChanged := true;
            Gear^.Angle := (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096
        end;

        if ((Gear^.Message and gmRight) <> 0) then
        begin
            fChanged := true;
            Gear^.Angle := (Gear^.Angle + cAngleSpeed) mod 4096
        end
    end;

    if fChanged then
    begin
        Gear^.dX.isNegative := (Gear^.Angle > 2048);
        if Gear^.dX.isNegative then
            trueAngle := 4096 - Gear^.Angle
        else
            trueAngle := Gear^.Angle;

        Gear^.dX := SignAs(AngleSin(trueAngle), Gear^.dX) * _0_25;
        Gear^.dY := AngleCos(trueAngle) * -_0_25;
    end;

    Gear^.X := Gear^.X + Gear^.dX;
    Gear^.Y := Gear^.Y + Gear^.dY;

    if (GameTicks and $FF) = 0 then
        if Gear^.Timer < 3500 then
            AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace)
    else
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);

    if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then
    begin
        HHGear^.Message := HHGear^.Message and not gmAttack;
        AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, Gear^.dX * _0_5, Gear^.dY *
        _0_5, 0);
        dec(Gear^.Health)
    end;

    if ((HHGear^.Message and gmLJump) <> 0)
       and ((Gear^.State and gsttmpFlag) = 0) then
    begin
        Gear^.State := Gear^.State or gsttmpFlag;
        PauseMusic;
        playSound(sndRideOfTheValkyries);
    end;

    // pickup bonuses
    t := CheckGearNear(Gear, gtCase, 36, 36);
    if t <> nil then
        PickUp(HHGear, t);

    CheckCollision(Gear);

    if ((Gear^.State and gstCollision) <> 0) or CheckGearDrowning(Gear) then
    begin
        StopSound(Gear^.SoundChannel);
        StopSound(sndRideOfTheValkyries);
        ResumeMusic;

        if ((Gear^.State and gstCollision) <> 0) then
        begin
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound);
            for i:= 0 to 15 do
            begin
                dX := AngleCos(i * 64) * _0_5 * (GetRandom + _1);
                dY := AngleSin(i * 64) * _0_5 * (GetRandom + _1);
                AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, dY, 0);
                AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, -dY, 0);
            end;
            DeleteGear(Gear)
        end;

        AfterAttack;
        CurAmmoGear := nil;
        if (GameFlags and gfInfAttack) = 0 then
            begin
            if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
            TurnTimeLeft:= 14 * 125;
            end;

        HHGear^.Message := 0;
        ParseCommand('/taunt '#1, true)
    end
end;

procedure doStepRCPlane(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.Message := 0;
    HHGear^.State := HHGear^.State or gstNotKickable;
    Gear^.Angle := HHGear^.Angle;
    Gear^.Tag := hwSign(HHGear^.dX);
    if HHGear^.dX.isNegative then Gear^.Angle := 4096 - Gear^.Angle;
    Gear^.doStep := @doStepRCPlaneWork
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepJetpackWork(Gear: PGear);
var 
    HHGear: PGear;
    fuel, i: LongInt;
    move: hwFloat;
    isUnderwater: Boolean;
    bubble: PVisualGear;
begin
    isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius;
    if Gear^.Pos > 0 then dec(Gear^.Pos);
    AllInactive := false;
    HHGear := Gear^.Hedgehog^.Gear;
    //dec(Gear^.Timer);
    move := _0_2;
    fuel := 50;
(*if (HHGear^.Message and gmPrecise) <> 0 then
    begin
    move:= _0_02;
    fuel:= 5;
    end;*)

    if Gear^.Health > 0 then
        begin
        if (HHGear^.Message and gmUp) <> 0 then
            begin
            if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then
                begin
                if isUnderwater then
                    begin
                    HHGear^.dY := HHGear^.dY - (move * _0_7);
                    for i:= random(10)+10 downto 0 do
                        begin
                        bubble := AddVisualGear(hwRound(HHGear^.X) - 8 + random(16), hwRound(HHGear^.Y) + 16 + random(8), vgtBubble);
                        if bubble <> nil then bubble^.dY:= random(20)/10+0.1;
                        end
                    end
                else HHGear^.dY := HHGear^.dY - move;
                end;
            dec(Gear^.Health, fuel);
            Gear^.MsgParam := Gear^.MsgParam or gmUp;
            Gear^.Timer := GameTicks
            end;
        move.isNegative := (HHGear^.Message and gmLeft) <> 0;
        if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
            begin
            HHGear^.dX := HHGear^.dX + (move * _0_1);
            if isUnderwater then
                begin
                for i:= random(5)+5 downto 0 do
                    begin
                    bubble := AddVisualGear(hwRound(HHGear^.X)+random(8), hwRound(HHGear^.Y) - 8 + random(16), vgtBubble);
                    if bubble <> nil then 
                        begin
                        bubble^.dX:= (random(10)/10 + 0.02) * -1;
                        if (move.isNegative) then
                            begin
                            bubble^.X := bubble^.X + 28;
                            bubble^.dX:= bubble^.dX * (-1)
                            end
                        else bubble^.X := bubble^.X - 28;
                        end;
                    end
                end;
            dec(Gear^.Health, fuel div 5);
            Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
            Gear^.Timer := GameTicks
            end
        end;

    // erases them all at once :-/
    if (Gear^.Timer <> 0) and (GameTicks - Gear^.Timer > 250) then
        begin
        Gear^.Timer := 0;
        Gear^.MsgParam := 0
        end;

    if Gear^.Health < 0 then Gear^.Health := 0;
    i:= Gear^.Health div 20;
    if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
        begin
        Gear^.Damage:= i;
        //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate);
        if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
        Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) +
                     '%', cWhiteColor, fntSmall)
        end;

    if HHGear^.Message and (gmAttack or gmUp or gmPrecise or gmLeft or gmRight) <> 0 then Gear^
        .State := Gear^.State and not gsttmpFlag;
    HHGear^.Message := HHGear^.Message and not (gmUp or gmPrecise or gmLeft or gmRight);
    HHGear^.State := HHGear^.State or gstMoving;

    Gear^.X := HHGear^.X;
    Gear^.Y := HHGear^.Y;
    // For some reason I need to reapply followgear here, something else grabs it otherwise.
    if not bShowAmmoMenu and not CurrentTeam^.ExtDriven then FollowGear := HHGear;

    if not isUnderWater and hasBorder and ((HHGear^.X < _0) or (hwRound(HHGear^.X) > LAND_WIDTH)) then HHGear^.dY.isNegative:= false;
    if ((Gear^.State and gsttmpFlag) = 0) or (HHGear^.dY < _0) then doStepHedgehogMoving(HHGear);

    if // (Gear^.Health = 0)
        (HHGear^.Damage <> 0)
        //or CheckGearDrowning(HHGear)
        or (cWaterLine + 512 < hwRound(HHGear^.Y))
        or (TurnTimeLeft = 0)
        // allow brief ground touches - to be fair on this, might need another counter
        or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0))
        or ((Gear^.Message and gmAttack) <> 0) then
        begin
        with HHGear^ do
            begin
            Message := 0;
            Active := true;
            State := State or gstMoving
            end;
        DeleteGear(Gear);
        isCursorVisible := false;
        ApplyAmmoChanges(HHGear^.Hedgehog^);
        //    if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);

//    Gear^.Tex:= RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) + '%', cWhiteColor, fntSmall)

//AddCaption(trmsg[sidFuel]+': '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate);
        end
end;

procedure doStepJetpack(Gear: PGear);
var 
    HHGear: PGear;
begin
    Gear^.Pos:= 0;
    Gear^.doStep := @doStepJetpackWork;

    HHGear := Gear^.Hedgehog^.Gear;
    FollowGear := HHGear;
    AfterAttack;
    with HHGear^ do
    begin
        State := State and not gstAttacking;
        Message := Message and not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight);
        if (dY < _0_1) and (dY > -_0_1) then
        begin
            Gear^.State := Gear^.State or gsttmpFlag;
            dY := dY - _0_2
        end
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepBirdyDisappear(Gear: PGear);
begin
    AllInactive := false;
    Gear^.Pos := 0;
    if Gear^.Timer < 2000 then
        inc(Gear^.Timer, 1)
    else
    begin
        DeleteGear(Gear);
    end;
end;

procedure doStepBirdyFly(Gear: PGear);
var 
    HHGear: PGear;
    fuel, i: LongInt;
    move: hwFloat;
begin
    HHGear := CurrentHedgehog^.Gear;

    move := _0_2;
    fuel := 50;

    if Gear^.Pos > 0 then
        dec(Gear^.Pos, 1)
    else if (HHGear^.Message and (gmLeft or gmRight or gmUp)) <> 0 then
             Gear^.Pos := 500;

    if HHGear^.dX.isNegative then
        Gear^.Tag := -1
    else
        Gear^.Tag := 1;

    if (HHGear^.Message and gmUp) <> 0 then
        begin
        if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then
            HHGear^.dY := HHGear^.dY - move;
        dec(Gear^.Health, fuel);
        Gear^.MsgParam := Gear^.MsgParam or gmUp;
        end;
    if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true;
    if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
        begin
        HHGear^.dX := HHGear^.dX + (move * _0_1);
        dec(Gear^.Health, fuel div 5);
        Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
        end;

    if Gear^.Health < 0 then Gear^.Health := 0;
    if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then
        for i:= ((500-Gear^.Health) div 250) downto 0 do
            AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather);

    if (HHGear^.Message and gmAttack <> 0) then
        begin
        HHGear^.Message := HHGear^.Message and not gmAttack;
        if Gear^.FlightTime > 0 then
            begin
            AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0);
            PlaySound(sndBirdyLay);
            dec(Gear^.FlightTime)
            end;
        end;

    if HHGear^.Message and (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then 
        Gear^.State := Gear^.State and not gsttmpFlag;
    HHGear^.Message := HHGear^.Message and not (gmUp or gmPrecise or gmLeft or gmRight);
    HHGear^.State := HHGear^.State or gstMoving;

    Gear^.X := HHGear^.X;
    Gear^.Y := HHGear^.Y - int2hwFloat(32);
    // For some reason I need to reapply followgear here, something else grabs it otherwise.
    if not bShowAmmoMenu then FollowGear := HHGear;

    if ((Gear^.State and gsttmpFlag) = 0) or (HHGear^.dY < _0) then doStepHedgehogMoving(HHGear);

    if  (Gear^.Health = 0)
       or (HHGear^.Damage <> 0)
       or CheckGearDrowning(HHGear)
       or (TurnTimeLeft = 0)
       // allow brief ground touches - to be fair on this, might need another counter
       or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0))
       or ((Gear^.Message and gmAttack) <> 0) then
        begin
        with HHGear^ do
            begin
            Message := 0;
            Active := true;
            State := State or gstMoving
            end;
        Gear^.State := Gear^.State or gstAnimation or gstTmpFlag;
        if HHGear^.dY < _0 then
            begin
            Gear^.dX := HHGear^.dX;
            Gear^.dY := HHGear^.dY;
            end;
        Gear^.Timer := 0;
        Gear^.doStep := @doStepBirdyDisappear;
        CurAmmoGear := nil;
        isCursorVisible := false;
        AfterAttack;
        end
end;

procedure doStepBirdyDescend(Gear: PGear);
var 
    HHGear: PGear;
begin
    if Gear^.Timer > 0 then
        dec(Gear^.Timer, 1)
    else if CurrentHedgehog = nil then
        begin
            DeleteGear(Gear);
            AfterAttack;
            exit
        end;
    HHGear := CurrentHedgehog^.Gear;
    HHGear^.Message := HHGear^.Message and not (gmUp or gmPrecise or gmLeft or gmRight);
    if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then
    begin
        if Gear^.Timer = 0 then
            Gear^.Y := Gear^.Y + _0_1
    end
    else if Gear^.Timer = 0 then
        begin
            Gear^.doStep := @doStepBirdyFly;
            HHGear^.dY := -_0_2
        end
end;

procedure doStepBirdyAppear(Gear: PGear);
begin
    Gear^.Pos := 0;
    if Gear^.Timer < 2000 then
        inc(Gear^.Timer, 1)
    else
    begin
        Gear^.Timer := 500;
        Gear^.dX := _0;
        Gear^.dY := _0;
        Gear^.State :=  Gear^.State and not gstAnimation;
        Gear^.doStep := @doStepBirdyDescend;
    end
end;

procedure doStepBirdy(Gear: PGear);
var 
    HHGear: PGear;
begin
    gear^.State :=  gear^.State or gstAnimation and not gstTmpFlag;
    Gear^.doStep := @doStepBirdyAppear;
    if CurrentHedgehog = nil then
    begin
        DeleteGear(Gear);
        exit
    end;

    HHGear := CurrentHedgehog^.Gear;

    if HHGear^.dX.isNegative then
        Gear^.Tag := -1
    else
        Gear^.Tag := 1;
    Gear^.Pos := 0;
    AllInactive := false;
    FollowGear := HHGear;
    with HHGear^ do
        begin
        State := State and not gstAttacking;
        Message := Message and not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)
        end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepEggWork(Gear: PGear);
var 
    vg: PVisualGear;
    i: LongInt;
begin
    AllInactive := false;
    Gear^.dX := Gear^.dX;
    doStepFallingGear(Gear);
    //    CheckGearDrowning(Gear); // already checked for in doStepFallingGear
    CalcRotationDirAngle(Gear);

    if (Gear^.State and gstCollision) <> 0 then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0);
        PlaySound(sndEggBreak);
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
        vg := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
        if vg <> nil then vg^.Frame := 2;

        for i:= 10 downto 0 do
        begin
            vg := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), hwRound(Gear^.Y) - 3 + Random(6),
                  vgtDust);
            if vg <> nil then vg^.dX := vg^.dX + (Gear^.dX.QWordValue / 21474836480);
        end;

        DeleteGear(Gear);
        exit
    end;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doPortalColorSwitch();
var CurWeapon: PAmmo;
begin
    if (CurrentHedgehog <> nil)
       and (CurrentHedgehog^.Gear <> nil)
       and ((CurrentHedgehog^.Gear^.Message and gmSwitch) <> 0) then
        With CurrentHedgehog^ do
            if (CurAmmoType = amPortalGun) then
            begin
                CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and not gmSwitch;
                
                CurWeapon:= GetAmmoEntry(CurrentHedgehog^);
                if CurWeapon^.Pos <> 0 then
                    CurWeapon^.Pos := 0
                else
                    CurWeapon^.Pos := 1;
            end;
end;

procedure doStepPortal(Gear: PGear);
var 
    iterator, conPortal: PGear;
    s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed,
    resetx, resety, resetdx, resetdy: hwFloat;
    sx, sy, rh, resetr: LongInt;
    hasdxy, isbullet, iscake, isCollision: Boolean;
begin
    doPortalColorSwitch();

    // destroy portal if ground it was attached too is gone
    if ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)] and $FF00) = 0)
       or (Gear^.Timer < 1)
       or (Gear^.Hedgehog <> CurrentHedgehog)
       or (hwRound(Gear^.Y) > cWaterLine) then
    begin
        deleteGear(Gear);
        EXIT;
    end;

    if (TurnTimeLeft < 1)
       or (Gear^.Health < 1) then
        dec(Gear^.Timer);

    if Gear^.Timer < 10000 then
        gear^.RenderTimer := true;

    // abort if there is no other portal connected to this one
    if (Gear^.IntersectGear = nil) then
        exit;
    if ((Gear^.IntersectGear^.Tag and 1) = 0) then // or if it's still moving;
        exit;

    conPortal := Gear^.IntersectGear;

    // check all gears for stuff to port through
    iterator := nil;
    while true do
    begin

        // iterate through GearsList
        if iterator = nil then
            iterator := GearsList
        else
            iterator := iterator^.NextGear;

        // end of list?
        if iterator = nil then
            break;

        // don't port portals or other gear that wouldn't make sense
        if (iterator^.Kind in [gtPortal, gtRope, gtRCPlane])
            or (iterator^.PortalCounter > 32) then
             continue;

        // don't port hogs on rope
        // TODO: this will also prevent hogs while falling after rope use from
        //       falling through portals... fix that!
        if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil)
           and (iterator = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.Kind =
           gtRope) then
            continue;

        // check if gear fits through portal
        if (iterator^.Radius > Gear^.Radius) then
            continue;

        // this is the max range we accept incoming gears in
        r := Int2hwFloat(iterator^.Radius+Gear^.Radius);

        // too far away?
        if (iterator^.X < Gear^.X - r)
           or (iterator^.X > Gear^.X + r)
           or (iterator^.Y < Gear^.Y - r)
           or (iterator^.Y > Gear^.Y + r) then
            continue;

        hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0))
                    or ((iterator^.State or gstMoving) = 0));

        // in case the object is not moving, let's asume it's falling towards the portal
        if not hasdxy then
        begin
            if Gear^.Y < iterator^.Y then
                continue;
            ox:= Gear^.X - iterator^.X;
            oy:= Gear^.Y - iterator^.Y;
        end
        else
        begin
            ox:= iterator^.dX;
            oy:= iterator^.dY;
        end;

        // cake will need extra treatment... it's so delicious and moist!
        iscake:= (iterator^.Kind = gtCake);

        // won't port stuff that does not move towards the front/portal entrance
        if iscake then
        begin
            if not ((iterator^.X - Gear^.X)*ox + (iterator^.Y - Gear^.Y)*oy).isNegative then
                continue;
        end
        else
            if not (Gear^.dX*ox + Gear^.dY*oy).isNegative then
                continue;

        isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]);

        r:= int2hwFloat(iterator^.Radius);

        if not (isbullet or iscake) then
        begin
            // wow! good candidate there, let's see if the distance and direction is okay!
            if hasdxy then
            begin
                s := r / Distance(iterator^.dX, iterator^.dY);
                ox:= iterator^.X + s * iterator^.dX;
                oy:= iterator^.Y + s * iterator^.dY;
            end
            else
            begin
                ox:= iterator^.X;
                oy:= iterator^.Y + r;
            end;

            if (hwRound(Distance(Gear^.X-ox,Gear^.Y-oy)) > Gear^.Radius + 1 ) then
                continue;
        end;

        // draw bullet trail
        if isbullet then
            spawnBulletTrail(iterator);

        // calc gear offset in portal vector direction
        ox := (iterator^.X - Gear^.X);
        oy := (iterator^.Y - Gear^.Y);
        poffs:= (Gear^.dX * ox + Gear^.dY * oy);

        if not isBullet and poffs.isNegative then
            continue;

        // only port bullets close to the portal
        if isBullet and not (hwAbs(poffs) < _3) then
            continue;

        //
        // gears that make it till here will definately be ported
        //
        // (but old position/movement vector might be restored in case there's
        // not enough space on the other side)
        //

        resetr  := iterator^.Radius;
        resetx  := iterator^.X;
        resety  := iterator^.Y;
        resetdx := iterator^.dX;
        resetdy := iterator^.dY;

        // create a normal of the portal vector, but ...
        nx := Gear^.dY;
        ny := Gear^.dX;
        // ... decide where the top is based on the hog's direction when firing the portal
        if Gear^.Elasticity.isNegative then
            nx.isNegative := not nx.isNegative
        else
            ny.isNegative := not ny.isNegative;

        // calc gear offset in portal normal vector direction
        noffs:= (nx * ox + ny * oy);

        if isBullet and (hwRound(hwAbs(noffs)) >= Gear^.Radius) then
            continue;

        // avoid gravity related loops of not really moving gear
        if not (iscake or isbullet) and (Gear^.dY.isNegative) and (conPortal^.dY.isNegative)
            and ((iterator^.dX.QWordValue + iterator^.dY.QWordValue) < _0_08.QWordValue)
            and (iterator^.PortalCounter > 0) then
             continue;

        // calc gear speed along to the vector and the normal vector of the portal
        if hasdxy then
        begin
            pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY);
            nspeed:= (nx * iterator^.dX + ny * iterator^.dY);
        end
        else
        begin
            pspeed:= hwAbs(cGravity * oy);
            nspeed:= _0;
        end;

        // creating normal vector of connected (exit) portal
        nx := conPortal^.dY;
        ny := conPortal^.dX;
        if conPortal^.Elasticity.isNegative then
            nx.isNegative := not nx.isNegative
        else
            ny.isNegative := not ny.isNegative;

        // inverse cake's normal movement direction,
        // as if it just walked through a hole
        //if iscake then nspeed.isNegative:= not nspeed.isNegative;

//AddFileLog('poffs:'+cstr(poffs)+' noffs:'+cstr(noffs)+' pspeed:'+cstr(pspeed)+' nspeed:'+cstr(nspeed));
        iterator^.dX := -pspeed * conPortal^.dX + nspeed * nx;
        iterator^.dY := -pspeed * conPortal^.dY + nspeed * ny;

        // make the gear's exit position close to the portal while
        // still respecting the movement direction

         // determine the distance (in exit vector direction)
         // that we want the gear at
         if iscake then
             ox:= (r - _0_7)
         else
             ox:= (r * _1_5);
         s:= ox / poffs;
         poffs:= ox;
         if (nspeed.QWordValue <> 0) and (pspeed > _0) then
             noffs:= noffs * s * (nspeed / pspeed);

        // move stuff with high normal offset closer to the portal's center
        if not isbullet then
        begin
            s := hwAbs(noffs) + r - int2hwFloat(Gear^.Radius);
            if s > _0 then
                noffs:= noffs - SignAs(s,noffs)
        end;

        iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx;
        iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny;

        if not hasdxy and not (conPortal^.dY.isNegative) then
        begin
            iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y))
        end;

        // see if the space on the exit side actually is enough

        if not (isBullet or isCake) then
        begin
            // TestCollisionXwithXYShift requires a hwFloat for xShift
            ox.QWordValue := _1.QWordValue;
            ox.isNegative := not iterator^.dX.isNegative;

            sx := hwSign(iterator^.dX);
            sy := hwSign(iterator^.dY);

            if iterator^.Radius > 1 then
                iterator^.Radius := iterator^.Radius - 1;

            // check front
            isCollision := TestCollisionY(iterator, sy)
                        or TestCollisionX(iterator, sx);

            if not isCollision then
            begin
                // check center area (with half the radius so that the
                // the square check won't check more pixels than we want to)
                iterator^.Radius := 1 + resetr div 2;
                rh := resetr div 4;
                isCollision := TestCollisionYwithXYShift(iterator,       0, -sy * rh, sy, false)
                            or TestCollisionXwithXYShift(iterator, ox * rh,        0, sx, false);
            end;

            iterator^.Radius := resetr;

            if isCollision then
            begin
                // collision! oh crap! go back!
                iterator^.X  := resetx;
                iterator^.Y  := resety;
                iterator^.dX := resetdx;
                iterator^.dY := resetdy;
                continue;
            end;
        end;

        //
        // You're now officially portaled!
        //

        // Until loops are reliably broken
        if iscake then iterator^.PortalCounter:= 33
        else
            begin
            inc(iterator^.PortalCounter);
            iterator^.State:= iterator^.State and not gstHHHJump
            end;

        if not isbullet and (iterator^.Kind <> gtFlake) then
            FollowGear := iterator;

        // store X/Y values of exit for net bullet trail
        if isbullet then
        begin
            iterator^.Elasticity:= iterator^.X;
            iterator^.Friction  := iterator^.Y;
        end;

        // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
        iterator:= GearsList;
        while iterator <> nil do
            begin
            if iterator^.Kind <> gtPortal then
                begin
                iterator^.Active:= true;
                if iterator^.dY.QWordValue = _0.QWordValue then iterator^.dY.isNegative:= false;
                iterator^.State:= iterator^.State or gstMoving;
                DeleteCI(iterator);
                //inc(iterator^.dY.QWordValue,10);
                end;
            iterator:= iterator^.NextGear
            end;

        if Gear^.Health > 1 then dec(Gear^.Health);
    end;
end;

procedure doStepMovingPortal_real(Gear: PGear);
var 
    x, y, tx, ty: LongInt;
    s: hwFloat;

procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean);
var
    CurWeapon: PAmmo;
begin
    if CurrentHedgehog <> nil then
        with CurrentHedgehog^ do
            begin
            CurWeapon:= GetAmmoEntry(CurrentHedgehog^);
            if (CurAmmoType = amPortalGun) then
                begin
                if not destroyGear then
                    begin
                    // switch color of ball to opposite of oldPortal
                    if (oldPortal^.Tag and 2) = 0 then
                        CurWeapon^.Pos:= 1
                    else
                        CurWeapon^.Pos:= 0;
                    end;

                // make the ball visible
                CurWeapon^.Timer := 0;
                end
            end;
    if destroyGear then oldPortal^.Timer:= 0;
end;

begin
    x := hwRound(Gear^.X);
    y := hwRound(Gear^.Y);
    tx := 0;
    ty := 0;
    // avoid compiler hints

    if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then
    begin
        Gear^.State := Gear^.State or gstCollision;
        Gear^.State := Gear^.State and not gstMoving;
        if not CalcSlopeTangent(Gear, x, y, tx, ty, 255)
           or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain
        begin
            loadNewPortalBall(Gear, true);
            EXIT;
        end;

        // making a normalized normal vector
        s := _1/DistanceI(tx,ty);
        Gear^.dX :=  s * ty;
        Gear^.dY := -s * tx;

        Gear^.DirAngle := DxDy2Angle(-Gear^.dY,Gear^.dX);
        if not Gear^.dX.isNegative then Gear^.DirAngle := 180-Gear^.DirAngle;

        if ((Gear^.IntersectGear = nil)
           or (hwRound(Distance(Gear^.X - Gear^.IntersectGear^.X,Gear^.Y-Gear^.IntersectGear^.Y)) >=
           Gear^.Radius*2))
            then
        begin
            loadNewPortalBall(Gear, false);
            inc(Gear^.Tag);
            Gear^.doStep := @doStepPortal;
        end
        else
            loadNewPortalBall(Gear, true);
    end
    else if (y > cWaterLine) or (y < -LAND_WIDTH)
            or (x > 2*LAND_WIDTH) or (x < -LAND_WIDTH) then
             loadNewPortalBall(Gear, true);
end;

procedure doStepMovingPortal(Gear: PGear);
begin
    doPortalColorSwitch();
    doStepPerPixel(Gear, @doStepMovingPortal_real, true);
    if (Gear^.Timer < 1)
       or (Gear^.Hedgehog <> CurrentHedgehog) then
            deleteGear(Gear);
end;

procedure doStepPortalShot(newPortal: PGear);
var 
    iterator: PGear;
    s: hwFloat;
    CurWeapon: PAmmo;
begin
    s:= Distance (newPortal^.dX, newPortal^.dY);

    // Adds the hog speed (only that part in/directly against shot direction)
    // to the shot speed (which we triple previously btw)
    // (This is done my projecting the hog movement vector onto the shot movement vector and then adding the resulting length
    // to the scaler)
    s := (_2 * s + (newPortal^.dX * CurrentHedgehog^.Gear^.dX + newPortal^.dY * CurrentHedgehog^.Gear^.dY ) / s) / s;
    newPortal^.dX := newPortal^.dX * s;
    newPortal^.dY := newPortal^.dY * s;

    newPortal^.IntersectGear := nil;

    if CurrentHedgehog <> nil then
        With CurrentHedgehog^ do
        begin
            CurWeapon:= GetAmmoEntry(CurrentHedgehog^);
            // let's save the HH's dX's direction so we can decide where the "top" of the portal hole
            newPortal^.Elasticity.isNegative := CurrentHedgehog^.Gear^.dX.isNegative;
            // when doing a backjump the dx is the opposite of the facing direction
            if ((Gear^.State and gstHHHJump) <> 0) and not cArtillery then
                newPortal^.Elasticity.isNegative := not newPortal^.Elasticity.isNegative;

            // make portal gun look unloaded
            if (CurWeapon <> nil) and (CurAmmoType = amPortalGun) then
                CurWeapon^.Timer := CurWeapon^.Timer or 2;

            iterator := GearsList;
            while iterator <> nil do
            begin
                if (iterator^.Kind = gtPortal) then
                    if (iterator <> newPortal) and (iterator^.Timer > 0) then
                    begin
                        if (iterator^.Tag and 2) = (newPortal^.Tag and 2) then
                        begin
                            iterator^.Timer:= 0;
                        end
                        else
                        begin
                            // link portals with each other
                            newPortal^.IntersectGear := iterator;
                            iterator^.IntersectGear := newPortal;
                            iterator^.Health := newPortal^.Health;
                        end;
                    end;
                iterator^.PortalCounter:= 0;
                iterator := iterator^.NextGear
            end;
        end;
    newPortal^.State := newPortal^.State and not gstCollision;
    newPortal^.State := newPortal^.State or gstMoving;
    newPortal^.doStep := @doStepMovingPortal;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepPiano(Gear: PGear);
var 
    r0, r1: LongInt;
    odY: hwFloat;
begin
    AllInactive := false;
    if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.
       Message and gmSlot) <> 0) then
    begin
        case CurrentHedgehog^.Gear^.MsgParam of 
            0: PlaySound(sndPiano0);
            1: PlaySound(sndPiano1);
            2: PlaySound(sndPiano2);
            3: PlaySound(sndPiano3);
            4: PlaySound(sndPiano4);
            5: PlaySound(sndPiano5);
            6: PlaySound(sndPiano6);
            7: PlaySound(sndPiano7);
            else PlaySound(sndPiano8);
        end;
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote);
        CurrentHedgehog^.Gear^.MsgParam := 0;
        CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and not gmSlot;
    end;

    if (*((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or*) (Gear^.Pos = 5) then
        // bounce up to 10 times (3 times on gameflagged solid land) before dropping past landscape
    begin
        Gear^.dY := Gear^.dY + cGravity * 2;
        Gear^.Y := Gear^.Y + Gear^.dY;
        CheckGearDrowning(Gear);
        if (Gear^.State and gstDrowning) <> 0 then
        begin
            if CurrentHedgehog^.Gear <> nil then
            begin
                // Drown the hedgehog.  Could also just delete it, but hey, this gets a caption
                CurrentHedgehog^.Gear^.Active := true;
                CurrentHedgehog^.Gear^.X := Gear^.X;
                CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128;
                CurrentHedgehog^.Unplaced := false;
                if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
                TurnTimeLeft:= 0
            end;
            ResumeMusic
        end;
        exit
    end;

    odY:= Gear^.dY;
    doStepFallingGear(Gear);

    if (Gear^.State and gstDrowning) <> 0 then
    begin
        if CurrentHedgehog^.Gear <> nil then
        begin
            // Drown the hedgehog.  Could also just delete it, but hey, this gets a caption
            CurrentHedgehog^.Gear^.Active := true;
            CurrentHedgehog^.Gear^.X := Gear^.X;
            CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128;
            CurrentHedgehog^.Unplaced := false;
            if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
            TurnTimeLeft:= 0
        end;
        ResumeMusic
    end
    else if (Gear^.State and gstCollision) <> 0 then
        begin
            r0 := GetRandom(21);
            r1 := GetRandom(21);
            doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0);
            doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0);
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound);
            for r0:= 0 to 4 do
                AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote);
            Gear^.dY := odY * -1 + cGravity * 2;
            Gear^.Pos := Gear^.Pos + 1;
        end
    else
        Gear^.dY := Gear^.dY + cGravity * 2;
    // let it fall faster so itdoesn't take too long for the whole attack
end;


////////////////////////////////////////////////////////////////////////////////
procedure doStepSineGunShotWork(Gear: PGear);
var 
    x, y, rX, rY, t, tmp, initHealth: LongInt;
    oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat;
    justCollided: boolean;
begin
    AllInactive := false;
    initHealth := Gear^.Health;
    lX := Gear^.X;
    lY := Gear^.Y;
    ldX := Gear^.dX;
    ldY := Gear^.dY;
    sdy := _0_5/Distance(Gear^.dX,Gear^.dY);
    ldX := ldX * sdy;
    ldY := ldY * sdy;
    sdY := hwAbs(ldX) + hwAbs(ldY);
    sdX := _1 - hwAbs(ldX/sdY);
    sdY := _1 - hwAbs(ldY/sdY);
    if (ldX.isNegative = ldY.isNegative) then sdY := -sdY;

    // initial angle depends on current GameTicks
    t := GameTicks mod 4096;


    // used for a work-around detection of area that is within land array, but outside borders
    justCollided := false;

    repeat
        lX := lX + ldX;
        lY := lY + ldY;
        oX := Gear^.X;
        oY := Gear^.Y;
        rX := hwRound(oX);
        rY := hwRound(oY);
        tmp := t mod 4096;
        amp := _128 * (_1 - hwSqr(int2hwFloat(Gear^.Health)/initHealth));
        sine := amp * AngleSin(tmp mod 2048);
        sine.isNegative := (tmp < 2048);
        inc(t,Gear^.Health div 313);
        Gear^.X := lX + (sine * sdX);
        Gear^.Y := ly + (sine * sdY);
        Gear^.dX := Gear^.X - oX;
        Gear^.dY := Gear^.Y - oY;

        x := hwRound(Gear^.X);
        y := hwRound(Gear^.Y);

        // if borders are on, stop outside land array
        if hasBorder and (((x and LAND_WIDTH_MASK) <> 0) or ((y and LAND_HEIGHT_MASK) <> 0)) then
        begin
            Gear^.Damage := 0;
            Gear^.Health := 0;
        end
        else
        begin
            if (rY <= cWaterLine) or (y <= cWaterLine) then
            begin
                if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0)
                   and (Land[y, x] <> 0) then
                begin
                    if justCollided then
                    begin
                        Gear^.Damage := 0;
                        Gear^.Health := 0;
                    end
                    else
                    begin
                        inc(Gear^.Damage,3);
                        justCollided := true;
                    end;
                end
                else
                    justCollided := false;

                // kick nearby hogs, dig tunnel and add some fire
                // if at least 5 collisions occured
                if Gear^.Damage > 0 then
                begin
                    DrawExplosion(rX,rY,Gear^.Radius);

                    // kick nearby hogs
                    AmmoShove(Gear, 35, 50);

                    dec(Gear^.Health, Gear^.Damage);
                    Gear^.Damage := 0;

                    // add some fire to the tunnel
                    if getRandom(6) = 0 then
                        AddGear(x - Gear^.Radius + LongInt(getRandom(2 * Gear^.Radius)), y -
                        getRandom(Gear^.Radius + 1), gtFlame, gsttmpFlag, _0, _0, 0);
                end;

                if getRandom(100) = 0 then
                    AddVisualGear(x, y, vgtSmokeTrace); 
            end
            else dec(Gear^.Health, 5); // if underwater get additional damage
        end;

        dec(Gear^.Health);

        // decrease bullet size towards the end
        if (Gear^.Radius > 4) then begin
            if (Gear^.Health <= (initHealth div 3)) then dec(Gear^.Radius) end
        else if (Gear^.Radius > 3) then begin
            if (Gear^.Health <= (initHealth div 4)) then dec(Gear^.Radius) end
        else if (Gear^.Radius > 2) then begin
            if (Gear^.Health <= (initHealth div 5)) then dec(Gear^.Radius) end
        else if (Gear^.Radius > 1) then begin
            if (Gear^.Health <= (initHealth div 6)) then dec(Gear^.Radius) end;

    until (Gear^.Health <= 0);

    DeleteGear(Gear);
    AfterAttack;
end;

procedure doStepSineGunShot(Gear: PGear);
var 
    HHGear: PGear;
begin
    PlaySound(sndSineGun); 

    // push the shooting Hedgehog back
    HHGear := CurrentHedgehog^.Gear;
    Gear^.dX.isNegative := not Gear^.dX.isNegative;
    Gear^.dY.isNegative := not Gear^.dY.isNegative;
    HHGear^.dX := Gear^.dX;
    HHGear^.dY := Gear^.dY;
    AmmoShove(Gear, 0, 80);
    Gear^.dX.isNegative := not Gear^.dX.isNegative;
    Gear^.dY.isNegative := not Gear^.dY.isNegative;

    Gear^.doStep := @doStepSineGunShotWork;
    performRumble();
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFlamethrowerWork(Gear: PGear);
var 
    HHGear: PGear;
    rx, ry, speed: hwFloat;
    i, gX, gY: LongInt;
    Fire: PGear;
begin
    AllInactive := false;
    HHGear := Gear^.Hedgehog^.Gear;
    HedgehogChAngle(HHGear);
    gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
    gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
    
    if (GameTicks and $FF) = 0 then
        begin
        if (HHGear^.Message and gmRight) <> 0 then
            begin
            if HHGear^.dX.isNegative and (Gear^.Tag < 20) then inc(Gear^.Tag)
            else if Gear^.Tag > 5 then dec(Gear^.Tag);
            end
        else if (HHGear^.Message and gmLeft) <> 0 then
            begin
            if HHGear^.dX.isNegative and (Gear^.Tag > 5) then dec(Gear^.Tag)
            else if Gear^.Tag < 20 then inc(Gear^.Tag);
            end
        end;
    
    dec(Gear^.Timer);
    if Gear^.Timer = 0 then
        begin
        dec(Gear^.Health);
        if (Gear^.Health mod 5) = 0 then
            begin
            rx := rndSign(getRandom * _0_1);
            ry := rndSign(getRandom * _0_1);
            speed := _0_5 * (_10 / Gear^.Tag);
    
            Fire := AddGear(gx, gy, gtFlame, 0,
                        SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
                AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
            Fire^.State := Fire^.State or gsttmpFlag;
            
            if (Gear^.Health mod 30) = 0 then 
                Fire := AddGear(gx, gy, gtFlame, 0,
                            SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
                    AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
            end;
        Gear^.Timer:= Gear^.Tag
        end;

    if (Gear^.Health = 0) or (HHGear^.Damage <> 0) then
        begin
        DeleteGear(Gear);
        AfterAttack
        end
    else
        begin
        i:= Gear^.Health div 5;
        if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
            begin
            Gear^.Damage:= i;
            if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
            Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) +
                         '%', cWhiteColor, fntSmall)
            end
        end
end;

procedure doStepFlamethrower(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.Message := HHGear^.Message and not (gmUp or gmDown or gmLeft or gmRight);
    HHGear^.State := HHGear^.State or gstNotKickable;
    Gear^.doStep := @doStepFlamethrowerWork
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepLandGunWork(Gear: PGear);
var 
    HHGear: PGear;
    rx, ry, speed: hwFloat;
    i, gX, gY: LongInt;
    Flake: PGear;
begin
    AllInactive := false;
    HHGear := Gear^.Hedgehog^.Gear;
    HedgehogChAngle(HHGear);
    gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
    gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
    
    if (GameTicks and $FF) = 0 then
        begin
        if (HHGear^.Message and gmRight) <> 0 then
            begin
            if HHGear^.dX.isNegative and (Gear^.Tag < 20) then inc(Gear^.Tag)
            else if Gear^.Tag > 5 then dec(Gear^.Tag);
            end
        else if (HHGear^.Message and gmLeft) <> 0 then
            begin
            if HHGear^.dX.isNegative and (Gear^.Tag > 5) then dec(Gear^.Tag)
            else if Gear^.Tag < 20 then inc(Gear^.Tag);
            end
        end;
    
    dec(Gear^.Timer);
    if Gear^.Timer = 0 then
        begin
        dec(Gear^.Health);

        rx := rndSign(getRandom * _0_1);
        ry := rndSign(getRandom * _0_1);
        speed := (_3 / Gear^.Tag);

        Flake := AddGear(gx, gy, gtFlake, 0, _0, _0, 0);
        Flake^.dX:= SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx;
        Flake^.dY:= AngleCos(HHGear^.Angle) * ( - speed) + ry;
        Flake^.State := Flake^.State or gsttmpFlag;
            
        Gear^.Timer:= Gear^.Tag
        end;

    if (Gear^.Health = 0) or (HHGear^.Damage <> 0) or ((HHGear^.Message and gmAttack) <> 0) then
        begin
        HHGear^.Message:= HHGear^.Message and not gmAttack;
        DeleteGear(Gear);
        AfterAttack
        end
    else
        begin
        i:= Gear^.Health div 10;
        if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
            begin
            Gear^.Damage:= i;
            if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
            Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) +
                         '%', cWhiteColor, fntSmall)
            end
        end
end;

procedure doStepLandGun(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := Gear^.Hedgehog^.Gear;
    HHGear^.Message := HHGear^.Message and not (gmUp or gmDown or gmLeft or gmRight or gmAttack);
    HHGear^.State := HHGear^.State or gstNotKickable;
    Gear^.doStep := @doStepLandGunWork
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepPoisonCloud(Gear: PGear);
begin
    if Gear^.Timer = 0 then
    begin
        DeleteGear(Gear);
        exit
    end;
    dec(Gear^.Timer);
    Gear^.X:= Gear^.X + Gear^.dX;
    Gear^.Y:= Gear^.Y + Gear^.dY;
    Gear^.dX := Gear^.dX + cWindSpeed / 4;
    Gear^.dY := Gear^.dY + cGravity / 100;
    if (GameTicks and $FF) = 0 then
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned);
    AllInactive:= false;
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepHammer(Gear: PGear);
var HHGear, tmp, tmp2: PGear;
         t: PGearArray;
         i: LongInt;
begin
HHGear:= Gear^.Hedgehog^.Gear;
HHGear^.State:= HHGear^.State or gstNoDamage;
DeleteCI(HHGear);

t:= CheckGearsCollision(Gear);

for i:= 5 downto 0 do
    AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);

i:= t^.Count;
while i > 0 do
    begin
    dec(i);
    tmp:= t^.ar[i];
    if (tmp^.State and gstNoDamage) = 0 then
        if (tmp^.Kind = gtHedgehog) or (tmp^.Kind = gtMine) or (tmp^.Kind = gtExplosives) then
            begin
            //tmp^.State:= tmp^.State or gstFlatened;
            ApplyDamage(tmp, CurrentHedgehog, tmp^.Health div 3, dsUnknown);
            //DrawTunnel(tmp^.X, tmp^.Y - _1, _0, _0_5, cHHRadius * 6, cHHRadius * 3);
            tmp2:= AddGear(hwRound(tmp^.X), hwRound(tmp^.Y), gtHammerHit, 0, _0, _0, 0);
            tmp2^.IntersectGear:= tmp;
            SetAllToActive
            end
        else
            begin
            end
    end;

HHGear^.State:= HHGear^.State and not gstNoDamage;
Gear^.Timer:= 250;
Gear^.doStep:= @doStepIdle
end;

procedure doStepHammerHitWork(Gear: PGear);
var 
    i, j, ei: LongInt;
    HitGear: PGear;
begin
    AllInactive := false;
    HitGear := Gear^.IntersectGear;
    dec(Gear^.Timer);
    if (HitGear = nil) or (Gear^.Timer = 0) or ((Gear^.Message and gmDestroy) <> 0) then
        begin
        DeleteGear(Gear);
        exit
        end;

    if (Gear^.Timer mod 5) = 0 then
        begin
        AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);

        i := hwRound(Gear^.X) - HitGear^.Radius + 2;
        ei := hwRound(Gear^.X) + HitGear^.Radius - 2;
        for j := 1 to 4 do DrawExplosion(i - GetRandom(5), hwRound(Gear^.Y) + 6*j, 3);
        for j := 1 to 4 do DrawExplosion(ei + LongInt(GetRandom(5)), hwRound(Gear^.Y) + 6*j, 3);
        while i <= ei do
            begin
            for j := 1 to 11 do DrawExplosion(i, hwRound(Gear^.Y) + 3*j, 3);
            inc(i, 1)
            end;

        if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9)
           , lfIndestructible) then
            begin
            Gear^.X := Gear^.X + Gear^.dX;
            Gear^.Y := Gear^.Y + _1_9;
            end;
        end;
    if TestCollisionYwithGear(Gear, 1) <> 0 then
        begin
        Gear^.dY := _0;
        SetLittle(HitGear^.dX);
        HitGear^.dY := _0;
        end
    else
        begin
        Gear^.dY := Gear^.dY + cGravity;
        Gear^.Y := Gear^.Y + Gear^.dY;
        if hwRound(Gear^.Y) > cWaterLine then Gear^.Timer := 1
        end;

    Gear^.X := Gear^.X + HitGear^.dX;
    HitGear^.X := Gear^.X;
    SetLittle(HitGear^.dY);
    HitGear^.Active:= true;
end;

procedure doStepHammerHit(Gear: PGear);
var 
    i, y: LongInt;
    ar: TRangeArray;
    HHGear: PGear;
begin
    i := 0;
    HHGear := Gear^.Hedgehog^.Gear;

    y := hwRound(Gear^.Y) - cHHRadius * 2;
    while y < hwRound(Gear^.Y) do
    begin
        ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2));
        ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
        inc(y, 2);
        inc(i)
    end;

    DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i));
    Gear^.dY := HHGear^.dY;
    DeleteCI(HHGear);

    doStepHammerHitWork(Gear);
    Gear^.doStep := @doStepHammerHitWork
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepResurrectorWork(Gear: PGear);
var
    graves: TPGearArray;
    resgear: PGear;
    hh: PHedgehog;
    i: LongInt;
begin
    if (TurnTimeLeft > 0) then
        dec(TurnTimeLeft);

    AllInactive := false;
    hh := Gear^.Hedgehog;

    // no, you can't do that here
    {DrawCentered(hwRound(hh^.Gear^.X) + WorldDx, hwRound(hh^.Gear^.Y) + WorldDy -
            cHHRadius - 14 - hh^.HealthTagTex^.h, hh^.HealthTagTex);
    }
    (*DrawCircle(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, 1.5, 0, 0, $FF,
            $FF);*)

    if ((Gear^.Message and gmUp) <> 0) then 
        begin
        if (GameTicks and $F) <> 0 then exit;
        end 
    else if (GameTicks and $1FF) <> 0 then exit;

    if Gear^.Power < 45 then 
        begin
        inc(Gear^.Power);
        if TestCollisionYwithGear(hh^.Gear, -1) = 0 then hh^.Gear^.Y := hh^.Gear^.Y - _1;
        end;

    graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius);

    if Length(graves) = 0 then 
        begin
        StopSound(Gear^.SoundChannel);
        Gear^.Timer := 250;
        Gear^.doStep := @doStepIdle;
        exit;
        end;

    if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then 
        begin
        if Length(graves) <= Gear^.Tag then Gear^.Tag:= 0;
        dec(hh^.Gear^.Health);
        if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then
            hh^.Gear^.Damage:= 1;
        RenderHealth(hh^);
        inc(graves[Gear^.Tag]^.Health);
        inc(Gear^.Tag)
{-for i:= 0 to High(graves) do begin
            if hh^.Gear^.Health > 0 then begin
                dec(hh^.Gear^.Health);
                inc(graves[i]^.Health);
            end;
        end; -}
        end 
    else 
        begin
        // now really resurrect the hogs with the hp saved in the graves
        for i:= 0 to High(graves) do
            if graves[i]^.Health > 0 then
                begin
                resgear := AddGear(hwRound(graves[i]^.X), hwRound(graves[i]^.Y),
                        gtHedgehog, gstWait, _0, _0, 0);
                resgear^.Hedgehog := graves[i]^.Hedgehog;
                resgear^.Health := graves[i]^.Health;
                PHedgehog(graves[i]^.Hedgehog)^.Gear := resgear;
                DeleteGear(graves[i]);
                RenderHealth(resgear^.Hedgehog^);
                RecountTeamHealth(resgear^.Hedgehog^.Team);
                resgear^.Hedgehog^.Effects[heResurrected]:= true;
                // only make hat-less hedgehogs look like zombies, preserve existing hats
                if resgear^.Hedgehog^.Hat = 'NoHat' then
                    LoadHedgehogHat(resgear, 'Reserved/Zombie');
                end;

        hh^.Gear^.dY := _0;
        hh^.Gear^.dX := _0;
        doStepHedgehogMoving(hh^.Gear);
        StopSound(Gear^.SoundChannel);
        Gear^.Timer := 250;
        Gear^.doStep := @doStepIdle;
        end
    //if hh^.Gear^.Health = 0 then doStepHedgehogFree(hh^.Gear);
end;

procedure doStepResurrector(Gear: PGear);
var
    graves: TPGearArray;
    i: LongInt;
begin
    AllInactive := false;
    graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius);

    if Length(graves) > 0 then
        begin
        for i:= 0 to High(graves) do
            begin
            PHedgehog(graves[i]^.Hedgehog)^.Gear := nil;
            graves[i]^.Health := 0;
            end;
        Gear^.doStep := @doStepResurrectorWork;
        end 
    else 
        begin
        StopSound(Gear^.SoundChannel);
        Gear^.Timer := 250;
        Gear^.doStep := @doStepIdle;
        end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepNapalmBomb(Gear: PGear);
var
    i, gX, gY: LongInt;
    dX, dY: hwFloat;
begin
    AllInactive := false;
    doStepFallingGear(Gear);
    if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound);
        gX := hwRound(Gear^.X);
        gY := hwRound(Gear^.Y); 
        for i:= 0 to 10 do
        begin
            dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandom + _1);
            dY := AngleSin(i * 8) * _0_5 * (GetRandom + _1);
            AddGear(gX, gY, gtFlame, 0, dX, dY, 0);
            AddGear(gX, gY, gtFlame, 0, dX, -dY, 0);
            AddGear(gX, gY, gtFlame, 0, -dX, dY, 0);
            AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0);
        end;
        DeleteGear(Gear);
        exit
    end;
    if (Gear^.Timer = 0) then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound);
        for i:= -19 to 19 do
           FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0);
        DeleteGear(Gear);
        exit
    end;
    if (GameTicks and $3F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
    dec(Gear^.Timer)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepStructure(Gear: PGear);
var 
    x, y: LongInt;
begin
    if (Gear^.State and gstMoving) <> 0 then
        begin
        AddGearCI(Gear);
        Gear^.dX:= _0;
        Gear^.dY:= _0;
        Gear^.State:= Gear^.State and not gstMoving;
        end;

    if CurAmmoGear = Gear then
        begin
        if (CurrentHedgehog = nil) or (CurrentHedgehog^.Gear = nil) then 
            begin
            DeleteGear(Gear);
            exit
            end;
        if Gear = CurAmmoGear then CurAmmoGear := nil;
        Gear^.Hedgehog:= CurrentHedgehog;
        RemoveGearFromList(CurrentHedgehog^.Gear);
        CurrentHedgehog^.Gear^.Z := cHHZ;
        CurrentHedgehog^.Gear^.Active := false;
        CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State and not gstHHDriven;
        CurrentHedgehog^.GearHidden:= CurrentHedgehog^.Gear;
        CurrentHedgehog^.Gear:= nil;
        Gear^.Tag:= TotalRounds + Gear^.Tag;
        AddGearCI(Gear);
        end;

    dec(Gear^.Health, Gear^.Damage);
    Gear^.Damage := 0;
        
    if (Gear^.Tag = TotalRounds) or (Gear^.Health <= 0) then
        begin
        if Gear^.Hedgehog <> nil then
            begin
            Gear^.Hedgehog^.Gear:= Gear^.Hedgehog^.GearHidden;
            Gear^.Hedgehog^.GearHidden:= nil;
            Gear^.Hedgehog^.Gear^.X:= Gear^.X;
            Gear^.Hedgehog^.Gear^.Y:= Gear^.Y - int2hwFloat(Gear^.Radius + cHHRadius);
            InsertGearToList(Gear^.Hedgehog^.Gear);
            Gear^.Hedgehog:= nil;
            SetAllHHToActive;
            end;
        end;
        
    if Gear^.Health <= 0 then
        begin
        x := hwRound(Gear^.X);
        y := hwRound(Gear^.Y);

        DeleteCI(Gear);
        DeleteGear(Gear);

        doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound);
        end;
end;

////////////////////////////////////////////////////////////////////////////////
(*
 TARDIS needs 
 Warp in.  Pos = 1
 Pause.    Pos = 2
 Hide gear  (TARDIS hedgehog was nil)
 Warp out. Pos = 3
 ... idle active for some time period ...  Pos = 4
 Warp in.  Pos = 1
 Pause.    Pos = 2
 Restore gear  (TARDIS hedgehog was not nil)
 Warp out. Pos = 3
*)

procedure doStepTardisWarp(Gear: PGear);
var HH: PHedgehog;
    i,j,cnt: LongWord;
begin
HH:= Gear^.Hedgehog;
if Gear^.Pos = 2 then
    begin
    StopSound(Gear^.SoundChannel);
    if (Gear^.Timer = 0) then
        begin
        if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible = 0) then
            begin
            AfterAttack;
            if Gear = CurAmmoGear then CurAmmoGear := nil;
            if (HH^.Gear^.Damage = 0) and  (HH^.Gear^.Health > 0) and 
            ((Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then
                HideHog(HH)
            end
        //else if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible <> 0) then
        else if (HH^.GearHidden <> nil) then// and (HH^.Gear^.State and gstInvisible <> 0) then
            RestoreHog(HH)
        end;

    inc(Gear^.Timer);
    if (Gear^.Timer > 2000) and ((GameTicks mod 2000) = 1000) then
        begin
        Gear^.SoundChannel := LoopSound(sndTardis);
        Gear^.Pos:= 3
        end
    end;

if (Gear^.Pos = 1) and (GameTicks and $1F = 0) and (Gear^.Power < 255) then 
    begin
    inc(Gear^.Power);
    if (Gear^.Power = 172) and (HH^.Gear <> nil) and 
       (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and
       ((HH^.Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then
        with HH^.Gear^ do
            begin
	        State:= State or gstAnimation;
	        Tag:= 2;
	        Timer:= 0;
	        Pos:= 0
            end
    end;
if (Gear^.Pos = 3) and (GameTicks and $1F = 0) and (Gear^.Power > 0) then dec(Gear^.Power);
if (Gear^.Pos = 1) and (Gear^.Power = 255) and ((GameTicks mod 2000) = 1000) then Gear^.Pos:= 2;
if (Gear^.Pos = 3) and (Gear^.Power = 0) then
    begin
    StopSound(Gear^.SoundChannel);
    if HH^.GearHidden = nil then
        begin
        DeleteGear(Gear);
        exit
        end;
    Gear^.Pos:= 4;
    // This condition might need tweaking
    Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime
    end;

if (Gear^.Pos = 4) then
    begin
    cnt:= 0;
    for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do
        for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do
            if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) and
               ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) and
               (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health >
                HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then inc(cnt);
    if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then
        begin
        Gear^.SoundChannel := LoopSound(sndTardis);
        Gear^.Pos:= 1;
        Gear^.Power:= 0;
        Gear^.Timer:= 0;
        if HH^.GearHidden <> nil then FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH,true);
        if HH^.GearHidden <> nil then 
            begin
            Gear^.X:= HH^.GearHidden^.X;
            Gear^.Y:= HH^.GearHidden^.Y;
            //HH^.Gear:=HH^.GearHidden;
            //HH^.GearHidden:= nil;
            //HH^.Gear^.State:= HH^.Gear^.State or gstInvisible;
            end
        end
    else dec(Gear^.Timer);
    end;

end;

procedure doStepTardis(Gear: PGear);
var i,j,cnt: LongWord;
    HH: PHedgehog;
begin
(*
    Conditions for not activating.
    1. Hog is last of his clan
    2. Sudden Death is in play
    3. Hog is a king
*)
    HH:= Gear^.Hedgehog;
    if HH^.Gear <> nil then 
    if (HH^.Gear = nil) or (HH^.King) or (SuddenDeathDmg) then
        begin
        if HH^.Gear <> nil then 
            begin
            HH^.Gear^.Message := HH^.Gear^.Message and not gmAttack;
            HH^.Gear^.State:= HH^.Gear^.State and not gstAttacking;
            end;
        PlaySound(sndDenied);
        DeleteGear(gear);
        exit
        end;
    cnt:= 0;
    for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do
        for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do
            if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) and
               ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) and
               (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health >
                HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then inc(cnt);
    if cnt < 2 then
        begin
        if HH^.Gear <> nil then 
            begin
            HH^.Gear^.Message := HH^.Gear^.Message and not gmAttack;
            HH^.Gear^.State:= HH^.Gear^.State and not gstAttacking;
            end;
            PlaySound(sndDenied);
            DeleteGear(gear);
            exit
        end;
    Gear^.SoundChannel := LoopSound(sndTardis);
    Gear^.doStep:= @doStepTardisWarp
end;

////////////////////////////////////////////////////////////////////////////////