hedgewars/GSHandlers.inc
author nemo
Sun, 10 Oct 2010 20:16:17 -0400
changeset 3951 c9a63db3e603
parent 3915 c05855146440
child 3953 fd7ced2071a1
permissions -rw-r--r--
Correct another bug in slot switching, adjust width of theme list, really truly fix reset of weps (I hope) should also fix infinite teleport bug in place hogs mode. Slow update of health to 5s for inf attack mode.

(*
 * Hedgewars, a free turn based strategy game
 * Copyright (c) 2004-2010 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
 *)

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, PHedgehog(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, PHedgehog(gi^.Hedgehog)^.Team^.voicepack)
                    else
                        PlaySound(sndUhOh, PHedgehog(gi^.Hedgehog)^.Team^.voicepack);
                end;
            end;
        end;
        gi := gi^.NextGear
    end;
end;
////////////////////////////////////////////////////////////////////////////////
procedure doStepDrowningGear(Gear: PGear);
forward;

function CheckGearDrowning(Gear: PGear): boolean;
var 
    skipSpeed, skipAngle, skipDecay: hwFloat;
    i, maxDrops: LongInt;
    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
    if cWaterLine < hwRound(Gear^.Y) + Gear^.Radius then
    begin
        skipSpeed := _0_25;
        skipAngle := _1_9;
        skipDecay := _0_87;
        // 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 PHedgehog(Gear^.Hedgehog)^.Effects[heResurrectable] then
                            ResurrectHedgehog(Gear)
                        else
                            begin
                            Gear^.doStep := @doStepDrowningGear;
                            Gear^.State := Gear^.State and (not gstHHDriven);
                            AddCaption(Format(GetEventString(eidDrowned), PHedgehog(Gear^.Hedgehog)^.Name), cWhiteColor, capgrpMessage);
                            end
                        end
                    else
                        Gear^.doStep := @doStepDrowningGear
            end;
            if ((not isSubmersible) and (hwRound(Gear^.Y) < cWaterLine + 64 + Gear^.Radius)) or
               (isSubmersible and (hwRound(Gear^.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 (hwRound(Gear^.Y) < cWaterLine + 64 + Gear^.Radius)) or
             (isSubmersible and (hwRound(Gear^.Y) < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0) and (CurAmmoGear^.dY < _0_01)))) then
        begin
            AddVisualGear(hwRound(Gear^.X), cWaterLine, vgtSplash);

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

procedure CheckCollision(Gear: PGear);
begin
    if TestCollisionXwithGear(Gear, hwSign(Gear^.X)) or TestCollisionYwithGear(Gear, hwSign(Gear^.Y)
       )
        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, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack)
        //else
        //    PlaySound(sndOw1, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack);

        ApplyDamage(Gear, 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;
    if (cWaterOpacity > $FE) or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then
        DeleteGear(Gear);
    // Create some bubbles (0.5% might be better but causes too few bubbles sometimes)
    if (cWaterOpacity < $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)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFallingGear(Gear: PGear);
var 
    isFalling: boolean;
    //tmp: QWord;
    tdX, tdY: hwFloat;
    collV, collH: LongInt;
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;
        if TestCollisionYwithGear(Gear, -1) then
        begin
            collV := -1;
            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) then collV := 1;
    end
    else if TestCollisionYwithGear(Gear, 1) then
        begin
            collV := 1;
            isFalling := false;
            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) then
            collV := -1;
    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 Gear^.dY := Gear^.dY + cGravity;

    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) then
        if ((Gear^.Damage <> 0) or ((Gear^.State and (gstCollision or gstMoving)) = (gstCollision or
           gstMoving))) 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 
            gtAmmo_Bomb: 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, 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 
            gtAmmo_Bomb: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
            gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, EXPLAutoSound);
            gtClusterBomb: 
                begin
                    x := hwRound(Gear^.X);
                    y := hwRound(Gear^.Y);
                    doMakeExplosion(x, y, 20, 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, 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, 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, EXPLAutoSound);
                for i:= 0 to 2 do
                    AddGear(hwRound(Gear^.X) - 30 + GetRandom(60), hwRound(Gear^.Y) - 20 + GetRandom(40), gtPoisonCloud, 0, _0, _0, 0);
                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 
    i, gX, gY: LongInt;
    dX, dY: hwFloat;
    Fire: PGear;
begin
    AllInactive := false;

    doStepFallingGear(Gear);
    CalcRotationDirAngle(Gear);

    if (Gear^.State and gstCollision) <> 0 then
    begin
        PlaySound(sndMolotov);
        gX := hwRound(Gear^.X);
        gY := hwRound(Gear^.Y);
        //doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 5, EXPLAutoSound);
        for i:= 0 to 20 do
        begin
            dX := AngleCos(i * 2) * ((_0_1*(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 doStepWatermelon(Gear: PGear);
begin
    AllInactive := false;
    Gear^.doStep := @doStepBomb
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, 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 doStepGrenade(Gear: PGear);
begin
    AllInactive := false;
    Gear^.dX := Gear^.dX + cWindSpeed;
    doStepFallingGear(Gear);
    if (Gear^.State and gstCollision) <> 0 then
    begin
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
        DeleteGear(Gear);
        exit
    end;
    if (GameTicks and $3F) = 0 then
        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
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: LongInt;
    nuw: boolean;

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 * (TargetPoint.X - gX));
        Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (TargetPoint.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, EXPLAutoSound);
        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, EXPLAutoSound);
        DeleteGear(Gear);
        exit
    end;
    dec(Gear^.Timer);
    if Gear^.Timer = 0 then
    begin
        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 doStepBulletWork(Gear: PGear);
var 
    i, x, y: LongWord;
    oX, oY: hwFloat;
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);
        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 (cWaterOpacity < $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;
        Gear^.doStep := @doStepShotIdle
    end;
end;

procedure doStepDEagleShot(Gear: PGear);
begin
    PlaySound(sndGun);
    Gear^.doStep := @doStepBulletWork
end;

procedure doStepSniperRifleShot(Gear: PGear);
var 
    HHGear: PGear;
    shell: PVisualGear;
begin
    cArtillery := true;
    HHGear := PHedgehog(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);
        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;
    gtATSmoothWindCh: 
begin
    if Gear^.Timer = 0 then
    begin
        if WindBarWidth < Gear^.Tag then inc(WindBarWidth)
        else if WindBarWidth > Gear^.Tag then dec(WindBarWidth);
        if WindBarWidth <> Gear^.Tag then Gear^.Timer := 10;
    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: LongInt;
    HHGear: PGear;
begin
    AllInactive := false;
    HHGear := PHedgehog(Gear^.Hedgehog)^.Gear;
    dec(Gear^.Timer);
    if (Gear^.Timer = 0)or((Gear^.Message and gmDestroy) <> 0)or((HHGear^.State and gstHHDriven) =
       0) then
    begin
        StopSound(Gear^.SoundChannel);
        DeleteGear(Gear);
        AfterAttack;
        exit
    end;

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

    if (Gear^.Timer mod 47) = 0 then
    begin
        for i:= 0 to 1 do
            AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
        i := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2));
        ei := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
        while i <= ei do
        begin
            DrawExplosion(i, hwRound(Gear^.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) 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 := PHedgehog(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);
    HHGear := PHedgehog(Gear^.Hedgehog)^.Gear;

    HedgehogChAngle(HHGear);

    b := false;

    if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7  then
    begin
        Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, HHGear^.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 (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 := PHedgehog(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 := PHedgehog(Gear^.Hedgehog)^.Gear;
    if ((HHGear^.State and gstHHDriven) = 0)
       or (CheckGearDrowning(HHGear))
       or TestCollisionYwithGear(HHGear, 1) then
    begin
        DeleteGear(Gear);
        isCursorVisible := false;
        ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^);
        exit
    end;

    HedgehogChAngle(HHGear);

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

    if HHGear^.dY.isNegative and TestCollisionYwithGear(HHGear, -1) then HHGear^.dY := _0;
    HHGear^.X := HHGear^.X + HHGear^.dX;
    HHGear^.Y := HHGear^.Y + HHGear^.dY;
    HHGear^.dY := HHGear^.dY + cGravity;

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

        ApplyAngleBounds(PHedgehog(Gear^.Hedgehog)^, amRope);

        Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX);
        Gear^.dY := -AngleCos(HHGear^.Angle);
        Gear^.Friction := _450;
        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: 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 := PHedgehog(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) then HHGear^.dX := HHGear^.dX - _0_0002
    else
        if (Gear^.Message and gmRight <> 0) then HHGear^.dX := HHGear^.dX + _0_0002;

    if not TestCollisionYwithGear(HHGear, 1) then HHGear^.dY := HHGear^.dY + cGravity;

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

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

    Gear^.dX := mdX;
    // for visual purposes only
    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))) 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))) 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] <> 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)) 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;


    if (Gear^.Message and gmAttack) <> 0 then
        if (Gear^.State and gsttmpFlag) <> 0 then
            with PHedgehog(Gear^.Hedgehog)^ do
            begin
                PlaySound(sndRopeRelease);
                if CurAmmoType <> amParachute then
                    WaitCollision
                else
                    DeleteMe
            end
    else
    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(PHedgehog(HHGear^.Hedgehog)^);
        Gear^.State := Gear^.State or gstAttacked
    end;
    ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^)
end;

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

    HHGear := PHedgehog(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) then HHGear^.dY := _0;

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

        if TestCollisionYwithGear(HHGear, 1) 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;
        end;

        tt := Gear^.Elasticity;
        tx := _0;
        ty := _0;
        while tt > _20 do
        begin
            if  TestCollisionXwithXYShift(Gear, tx, hwRound(ty), -hwSign(Gear^.dX))
               or TestCollisionYwithXYShift(Gear, hwRound(tx), hwRound(ty), -hwSign(Gear^.dY)) then
            begin
                Gear^.X := Gear^.X + tx;
                Gear^.Y := Gear^.Y + ty;
                Gear^.Elasticity := tt;
                Gear^.doStep := @doStepRopeWork;
                PlaySound(sndRopeAttach);
                with HHGear^ do
                    State := State and not (gstAttacking or gstHHJumping or gstHHHJump);

                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
            State := State and not (gstAttacking or gstHHJumping or gstHHHJump);

        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 PHedgehog(Gear^.Hedgehog)^.Gear^ do
        begin
            State := State and not gstAttacking;
            Message := Message and not gmAttack
        end;
        DeleteGear(Gear)
    end
end;

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

////////////////////////////////////////////////////////////////////////////////
procedure doStepMine(Gear: PGear);
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^.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, EXPLAutoSound);
                DeleteGear(Gear)
            end
            else
            begin
                AddVisualGear(hwRound(Gear^.X) - 4  + Random(8), hwRound(Gear^.Y) - 4 - Random(4),
                vgtSmoke);
                PlaySound(sndVaporize);
                Gear^.Health := 0;
            end;
            exit
        end;
        dec(Gear^.Timer);
    end
    else // gsttmpFlag = 0
        if TurnTimeLeft = 0 then Gear^.State := Gear^.State or gsttmpFlag;
end;

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

    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
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, EXPLAutoSound);
            DeleteGear(Gear);
            exit
        end;
        dec(Gear^.Timer);
    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, 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
    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) 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)
                 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) then Gear
        ^.dY := _0;
    if hwAbs(Gear^.dX) < _0_001 then Gear^.dX := _0;

    if ((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;
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 * 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);
        DeleteGear(Gear);
        // <-- delete gear!

        if k = gtCase then
        begin
            doMakeExplosion(x, y, 25, 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, 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 (not TestCollisionYwithGear(Gear, 1)) 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) then Gear^.dY := _0;
        if (not Gear^.dY.isNegative) and TestCollisionYwithGear(Gear, 1) 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
        begin
            Gear^.Tag := 2;
            if (TrainingFlags and tfTimeTrial) <> 0 then
            begin
                inc(TurnTimeLeft, TrainingTimeInc);

                if TrainingTimeInc > TrainingTimeInM then
                    dec(TrainingTimeInc, TrainingTimeInD);
                if TurnTimeLeft > TrainingTimeMax then
                    TurnTimeLeft := TrainingTimeMax;
            end;
        end
    else if Gear^.Tag = 2 then
             if Gear^.Timer > 0 then
                 dec(Gear^.Timer)
    else
    begin
        if (TrainingFlags and tfTargetRespawn) <> 0 then
        begin
            TrainingTargetGear := AddGear(0, 0, gtTarget, 0, _0, _0, 0);
            FindPlace(TrainingTargetGear, false, 0, LAND_WIDTH);
        end;
        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 := PHedgehog(Gear^.Hedgehog)^.Gear;
    HHGear^.State := HHGear^.State or gstNoDamage;
    DeleteCI(HHGear);

    AmmoShove(Gear, 30, 115);

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

////////////////////////////////////////////////////////////////////////////////
procedure doStepWhip(Gear: PGear);
var 
    HHGear: PGear;
    i: LongInt;
begin
    HHGear := PHedgehog(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;
    Gear^.Timer := 250;
    Gear^.doStep := @doStepIdle
end;

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

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

        if ((GameTicks mod 100) = 0) then
            begin
            vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire);
            if vgt <> nil then
                begin
                vgt^.dx:= 0;
                vgt^.dy:= 0;
                vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2);
                vgt^.State:= gstTmpFlag;
                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;
            AmmoShove(Gear, 2, 30);
            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;
                    AmmoShove(Gear, 4, 150);
                    Gear^.Radius := 1;
                    end
                else if ((GameTicks and $3) = 3) then doMakeExplosion(gX, gY, 6, 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 := PHedgehog(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 := PHedgehog(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);

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

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

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

    inc(Gear^.Timer);

    if TestCollisionYwithGear(HHGear, 1)
       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(PHedgehog(HHGear^.Hedgehog)^);
        exit
    end;

    if not TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
        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;

    HHGear^.Y := HHGear^.Y + cGravity * 100;
    Gear^.X := HHGear^.X;
    Gear^.Y := HHGear^.Y
end;

procedure doStepParachute(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := PHedgehog(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);
var 
    i: Longint;
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: for i:= -19 to 19 do
                   FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0,
                                 _0_001 * i, _0, 0);
        end;
        Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag)
    end;

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

    if (hwRound(Gear^.X) > (LAND_WIDTH+1024)) or (hwRound(Gear^.X) < -1024) then DeleteGear(Gear)
end;

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

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

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

    if (int2hwFloat(TargetPoint.Y) - Gear^.Y > _0) and (Gear^.State <> 2) then
        Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(TargetPoint.Y) - Gear^.Y) * 2 /
                    cGravity) * Gear^.Tag;

    Gear^.Health := 6;
    Gear^.doStep := @doStepAirAttackWork;
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, EXPLAutoSound);
        DeleteGear(Gear);
        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 := PHedgehog(Gear^.Hedgehog)^.Gear;
    tx := int2hwFloat(TargetPoint.X);
    ty := int2hwFloat(TargetPoint.Y);
    x := HHGear^.X;
    y := HHGear^.Y;

    if (Distance(tx - x, ty - y) > _256) or
       not TryPlaceOnLand(TargetPoint.X - SpritesData[sprAmGirder].Width div 2,
       TargetPoint.Y - SpritesData[sprAmGirder].Height div 2,
       sprAmGirder, Gear^.State, true) 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;
    TargetPoint.X := NoPointX
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepTeleportAfter(Gear: PGear);
var 
    HHGear: PGear;
begin
    PHedgehog(Gear^.Hedgehog)^.Unplaced := false;
    HHGear := PHedgehog(Gear^.Hedgehog)^.Gear;
    HHGear^.Y := HHGear^.Y + HHGear^.dY;
    // hedgehog falling to collect cases
    HHGear^.dY := HHGear^.dY + cGravity;
    if TestCollisionYwithGear(HHGear, 1)
       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 := PHedgehog(Gear^.Hedgehog)^.Gear;
    if not TryPlaceOnLand(TargetPoint.X - SpritesData[sprHHTelepMask].Width div 2,
       TargetPoint.Y - SpritesData[sprHHTelepMask].Height div 2,
       sprHHTelepMask, 0, 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(TargetPoint.X);
        HHGear^.Y := int2hwFloat(TargetPoint.Y);
        HHGear^.State := HHGear^.State or gstMoving;
        playSound(sndWarp)
    end;
    TargetPoint.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 := PHedgehog(Gear^.Hedgehog)^.Gear;
        Msg := Gear^.Message and not gmSwitch;
        DeleteGear(Gear);
        OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^);
        ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^);

        HHGear := CurrentHedgehog^.Gear;
        ApplyAmmoChanges(PHedgehog(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);

        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 := PHedgehog(Gear^.Hedgehog)^.Gear;
    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, 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;
begin
    AllInactive := false;

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

    Gear^.X := HHGear^.X;
    Gear^.Y := HHGear^.Y;

    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 Gear^.Pos := 2;

        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, EXPLAutoSound);
        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, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack);
        Gear^.doStep := @doStepKamikazeWork
    end
end;

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

    HHGear := PHedgehog(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, 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) 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;
    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) 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 := PHedgehog(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 
    x, y: LongInt;
begin
    AllInactive := false;

    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);
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, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack)
    end;

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

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

////////////////////////////////////////////////////////////////////////////////
procedure doStepWaterUp(Gear: PGear);
var 
    i: LongWord;
begin
    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;

    inc(Gear^.Tag);
    if (Gear^.Tag = 47) or (cWaterLine = 0) then
        DeleteGear(Gear)
end;

////////////////////////////////////////////////////////////////////////////////
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 (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 (not TestCollisionYWithGear(Gear, hwSign(Gear^.dY))
       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);
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
        DeleteGear(Gear);
        exit
    end;

    dec(Gear^.Timer);
end;

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

    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
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
            DeleteGear(Gear);
            exit;
        end;

        Gear^.SoundChannel := LoopSound(sndDrillRocket);
        Gear^.doStep := @doStepDrillDrilling;
        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 := PHedgehog(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 := PHedgehog(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;

    if ((TrainingFlags and tfRCPlane) = 0) and (Gear^.Timer > 0) then dec(Gear^.Timer);

    if ((TrainingFlags and tfRCPlane) <> 0) and ((TrainingFlags and tfTimeTrial) <> 0 ) and (
       TimeTrialStartTime = 0) then TimeTrialStartTime := RealTicks;

    HHGear := PHedgehog(Gear^.Hedgehog)^.Gear;
    FollowGear := Gear;

    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 (TrainingFlags and tfRCPlane) = 0 then
    begin
        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);
    end
    else
    begin
        if (GameTicks and $FF) = 0 then
            AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);

        // pickup targets
        t := CheckGearNear(Gear, gtTarget, 36, 36);
        if t <> nil then
        begin
            if t^.Tag <> 0 then // collect it only once
                exit;
            PlaySound(sndShotgunReload);
            t^.Tag := 1;
            TrainingTargetGear := nil;
            // remove target cursor
            exit;
        end;

        if (TurnTimeLeft > 0) then
            dec(TurnTimeLeft)
    end;

    CheckCollision(Gear);

    if ((Gear^.State and gstCollision) <> 0) or (((TrainingFlags and tfRCPlane) <> 0) and (
       TurnTimeLeft = 0))
       or CheckGearDrowning(Gear) then
    begin
        if ((TrainingFlags and tfRCPlane) <> 0) and ((TrainingFlags and tfTimeTrial) <> 0 ) and (
           TimeTrialStopTime = 0) then TimeTrialStopTime := RealTicks;
        StopSound(Gear^.SoundChannel);
        StopSound(sndRideOfTheValkyries);
        ResumeMusic;

        if ((Gear^.State and gstCollision) <> 0) or (((TrainingFlags and tfRCPlane) <> 0) and (
           TurnTimeLeft = 0)) then
        begin
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, EXPLAutoSound);
            for i:= 0 to 32 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;
        TurnTimeLeft := 14 * 125;

        if (TrainingFlags and tfRCPlane) <> 0 then
            TurnTimeLeft := 0;
        // HACK: RCPlane training allows unlimited plane starts in last 2 seconds

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

procedure doStepRCPlane(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := PHedgehog(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 := PHedgehog(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 *= -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;
    if (GameTicks and $3F) = 0 then
        begin
        //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate);
        if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
        Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) +
                     '%', 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 then FollowGear := HHGear;

    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))
        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(PHedgehog(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 := PHedgehog(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))
       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), 11, 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 flags: LongWord;
    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^);
                flags := CurWeapon^.Timer and not 2;
                if (flags and 1) = 0 then
                    CurWeapon^.Timer := flags or 1
                else
                    CurWeapon^.Timer := flags and not 1;
            end;
end;

procedure doStepPortal(Gear: PGear);
var 
    iterator, conPortal: PGear;
    s, acptRadius, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed: hwFloat;
    noTrap, hasdxy: 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 (PHedgehog(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 = gtPortal) or (iterator^.Kind = gtRope) or (iterator^.PortalCounter > 20) then
            continue;

        // don't port hogs on rope
        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
        acptRadius := Int2hwFloat(iterator^.Radius+Gear^.Radius);

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

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

        // won't port stuff that moves away from me!
        if hasdxy and not (Gear^.dX*iterator^.dX + Gear^.dY*iterator^.dY).isNegative then
                continue;

        if (iterator^.Kind <> gtCake) then
        begin
            // wow! good candidate there, let's see if the distance and direction is okay!
            if hasdxy then
            begin
                s := int2hwFloat(iterator^.Radius) / 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 + Int2hwFloat(iterator^.Radius);
            end;

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

(*
        noTrap := ((not Gear^.dY.isNegative or (Gear^.dY.QWordValue = 0))
                  // can't be entered from above
                  or ((conPortal^.dY.isNegative and not (conPortal^.dY.QWordValue = 0))));
        // can't be left downwards; 

        // prevent getting stuck in a ground portal loop       
        if noTrap and (iterator^.dY.QWordValue < _0_08.QWordValue) then
            continue; *)

        iterator^.Active := true;
        iterator^.State := iterator^.State or gstMoving;
        DeleteCI(iterator);

        // Until loops are reliably broken
        inc(iterator^.PortalCounter);

        // find out how much speed parallel to the portal vector
        // the gear has, also get the vector offset
        pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY);
        ox := (iterator^.X - Gear^.X);
        oy := (iterator^.Y - Gear^.Y);
        poffs:= (Gear^.dX * ox + Gear^.dY * oy);
        // 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;

        // now let's find out how much speed the gear has in the
        // direction of that normal
        nspeed:= (nx * iterator^.dX + ny * iterator^.dY);
        noffs:= (nx * ox + ny * oy);

        // now let's project those back to the connected portal's vectors
        nx := conPortal^.dY;
        ny := conPortal^.dX;
        if conPortal^.Elasticity.isNegative then
            nx.isNegative := not nx.isNegative
        else
            ny.isNegative := not ny.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;
        if iterator^.Kind = gtCake then
            poffs := poffs * _0_5;
        iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx;
        iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny;

        FollowGear := iterator;
//AddFileLog('portal''d');

{
        s := _0_2 + _0_008 * Gear^.Health;
        iterator^.dX := s * iterator^.dX;
        iterator^.dY := s * iterator^.dY;
}

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

{        // breaks (some) loops
        if Distance(iterator^.dX, iterator^.dY) > _0_96 then
            begin
            iterator^.dX := iterator^.dX + signAs(cGravity * getRandom(1000),iterator^.dX);
            iterator^.dY := iterator^.dY + signAs(cGravity * getRandom(1000),iterator^.dY);
            s := _0_96 / Distance(iterator^.dX, iterator^.dY);
            iterator^.dX := s * iterator^.dX;
            iterator^.dY := s * iterator^.dX;
            end;
}
    end;
end;

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

procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean);
var 
    flags: LongWord;
    CurWeapon: PAmmo;
begin
    if CurrentHedgehog <> nil then
        with CurrentHedgehog^ do
            begin
            CurWeapon:= GetAmmoEntry(CurrentHedgehog^);
            if (CurAmmoType = amPortalGun) then
                begin
                flags := CurWeapon^.Timer;

                if destroyGear xor ((oldPortal^.Tag and 2) = 0) then
                    flags := flags or 1
                else
                    flags := flags and not 1;

                CurWeapon^.Timer := flags and not 2;
                // make the ball visible
                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 (PHedgehog(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
            CurWeapon^.Timer := CurWeapon^.Timer or 2;

            // set portal to the currently chosen color
            if ((CurWeapon^.Timer and 1) <> 0) then
                newPortal^.Tag := newPortal^.Tag 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
            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
        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, 0);
            doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, 0);
            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, 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; 
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFlamethrowerWork(Gear: PGear);
var 
    HHGear: PGear;
    rx, ry, speed: hwFloat;
    gX, gY: LongInt;
    Fire: PGear;
begin
    AllInactive := false;
    HHGear := PHedgehog(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 10) = 0 then
        begin
            rx := rndSign(getRandom * _0_1);
            ry := rndSign(getRandom * _0_1);
            speed := _0_8 * (_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 20) = 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
        if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
        Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 5)) +
                     '%', cWhiteColor, fntSmall) 
    end
end;

procedure doStepFlamethrower(Gear: PGear);
var 
    HHGear: PGear;
begin
    HHGear := PHedgehog(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 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 mod 250) = 0 then
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, 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:= PHedgehog(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) then
            begin
            //tmp^.State:= tmp^.State or gstFlatened;
            ApplyDamage(tmp, 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^.Hedgehog:= tmp^.Hedgehog;
            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, ei: LongInt;
    HHGear: PGear;
begin
    AllInactive := false;
    HHGear := PHedgehog(Gear^.Hedgehog)^.Gear;
    dec(Gear^.Timer);
    if (HHGear = 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) - Gear^.Radius - LongInt(GetRandom(2));
        ei := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
        while i <= ei do
        begin
            DrawExplosion(i, hwRound(Gear^.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) 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);
end;

procedure doStepHammerHit(Gear: PGear);
var 
    i, y: LongInt;
    ar: TRangeArray;
    HHGear: PGear;
begin
    i := 0;
    HHGear := PHedgehog(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;