hedgewars/uGearsHedgehog.pas
author blackmetalowiec
Wed, 07 Dec 2011 12:19:36 +0100
changeset 6513 677b96d13e1f
parent 6468 da1e7fe7cff7
child 6515 74a04089bb56
permissions -rw-r--r--
Auto refresh room list after leaving room. Fixes issue #320 for voluntarily and involuntarily coming to room list.

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

{$INCLUDE "options.inc"}

unit uGearsHedgehog;
interface
uses uTypes;

procedure doStepHedgehog(Gear: PGear);
procedure AfterAttack; 
procedure HedgehogStep(Gear: PGear); 
procedure doStepHedgehogMoving(Gear: PGear); 
procedure HedgehogChAngle(HHGear: PGear); 

implementation
uses uConsts, uVariables, uFloat, uAmmos, uSound, uCaptions, uMisc, 
    uCommands, uLocale, uUtils, uVisualGears, uStats, uIO, uScript,
    uGearsList, uGears, uCollisions, uRandom, uStore, uTeams, 
    uGearsUtils;

// Shouldn't more of this ammo switching stuff be moved to uAmmos ?
function ChangeAmmo(HHGear: PGear): boolean;
var slot, i: Longword;
    ammoidx: LongInt;
begin
ChangeAmmo:= false;
slot:= HHGear^.MsgParam;

with HHGear^.Hedgehog^ do
    begin
    HHGear^.Message:= HHGear^.Message and (not gmSlot);
    ammoidx:= 0;
    if ((HHGear^.State and (gstAttacking or gstAttacked)) <> 0) or
       ((MultiShootAttacks > 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) = 0)) or
       ((HHGear^.State and gstHHDriven) = 0) then exit;
    ChangeAmmo:= true;

    while (ammoidx < cMaxSlotAmmoIndex) and (Ammo^[slot, ammoidx].AmmoType <> CurAmmoType) do inc(ammoidx);

    if ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) and (MultiShootAttacks > 0) then OnUsedAmmo(HHGear^.Hedgehog^);

    MultiShootAttacks:= 0;
    HHGear^.Message:= HHGear^.Message and (not (gmLJump or gmHJump));
    
    if Ammoz[CurAmmoType].Slot = slot then
        begin
        i:= 0;
        repeat
        inc(ammoidx);
        if (ammoidx > cMaxSlotAmmoIndex) then
            begin
            inc(i);
            CurAmmoType:= amNothing;
            ammoidx:= -1;
            //TryDo(i < 2, 'Engine bug: no ammo in current slot', true)
            end;
        until (i = 1) or ((Ammo^[slot, ammoidx].Count > 0) and (Team^.Clan^.TurnNumber > Ammoz[Ammo^[slot, ammoidx].AmmoType].SkipTurns))
        end 
    else
        begin
        i:= 0;
        // check whether there is ammo in slot
        while (i <= cMaxSlotAmmoIndex)
          and ((Ammo^[slot, i].Count = 0)
               or (Team^.Clan^.TurnNumber <= Ammoz[Ammo^[slot, i].AmmoType].SkipTurns)) do inc(i);

        if i <= cMaxSlotAmmoIndex then ammoidx:= i
        else ammoidx:= -1
        end;
        if ammoidx >= 0 then CurAmmoType:= Ammo^[slot, ammoidx].AmmoType;
    end
end;

procedure HHSetWeapon(HHGear: PGear);
var t: LongInt;
    weap: TAmmoType;
    Hedgehog: PHedgehog;
    s: boolean;
begin
s:= false;

weap:= TAmmoType(HHGear^.MsgParam);
Hedgehog:= HHGear^.Hedgehog;

if Hedgehog^.Team^.Clan^.TurnNumber <= Ammoz[weap].SkipTurns then exit; // weapon is not activated yet

HHGear^.MsgParam:= Ammoz[weap].Slot;

t:= cMaxSlotAmmoIndex;

HHGear^.Message:= HHGear^.Message and (not gmWeapon);

with Hedgehog^ do
    while (CurAmmoType <> weap) and (t >= 0) do
        begin
        s:= ChangeAmmo(HHGear);
        dec(t)
        end;

if s then ApplyAmmoChanges(HHGear^.Hedgehog^)
end;

procedure HHSetTimer(Gear: PGear);
var CurWeapon: PAmmo;
    color: LongWord;
begin
Gear^.Message:= Gear^.Message and (not gmTimer);
CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);
with Gear^.Hedgehog^ do
    if ((Gear^.Message and gmPrecise) <> 0) and ((CurWeapon^.Propz and ammoprop_SetBounce) <> 0) then
        begin
        color:= Gear^.Hedgehog^.Team^.Clan^.Color;
        case Gear^.MsgParam of
            1: begin
               AddCaption(format(trmsg[sidBounce], trmsg[sidBounce1]), color, capgrpAmmostate);
               CurWeapon^.Bounciness:= 350;
               end;
            2: begin
               AddCaption(format(trmsg[sidBounce], trmsg[sidBounce2]), color, capgrpAmmostate);
               CurWeapon^.Bounciness:= 700;
               end;
            3: begin
               AddCaption(format(trmsg[sidBounce], trmsg[sidBounce3]), color, capgrpAmmostate);
               CurWeapon^.Bounciness:= 1000;
               end;
            4: begin
               AddCaption(format(trmsg[sidBounce], trmsg[sidBounce4]), color, capgrpAmmostate);
               CurWeapon^.Bounciness:= 2000;
               end;
            5: begin
               AddCaption(format(trmsg[sidBounce], trmsg[sidBounce5]), color, capgrpAmmostate);
               CurWeapon^.Bounciness:= 4000;
               end
            end
        end
    else if (CurWeapon^.Propz and ammoprop_Timerable) <> 0 then
        begin
        CurWeapon^.Timer:= 1000 * Gear^.MsgParam;
        with CurrentTeam^ do
            ApplyAmmoChanges(Hedgehogs[CurrHedgehog]);
        end;
end;


procedure Attack(Gear: PGear);
var xx, yy, newDx, newDy, lx, ly: hwFloat;
    speech: PVisualGear;
    newGear:  PGear;
    CurWeapon: PAmmo;
    altUse: boolean;
    elastic: hwFloat;
begin
newGear:= nil;
bShowFinger:= false;
CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);
with Gear^,
     Gear^.Hedgehog^ do
     begin
     if ((State and gstHHDriven) <> 0)and
        ((State and (gstAttacked or gstHHChooseTarget)) = 0) and
        (((State and gstMoving) = 0) or
            (Power > 0) or
            (CurAmmoType = amTeleport) or 
            // Allow attacks while moving on ammo with AltAttack
            ((CurAmmoGear <> nil) and ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0)) or
            ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackInMove) <> 0)) and
        ((TargetPoint.X <> NoPointX) or ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) = 0)) then
        begin
        State:= State or gstAttacking;
        if Power = cMaxPower then Message:= Message and (not gmAttack)
        else if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) = 0 then Message:= Message and (not gmAttack)
        else begin
             if Power = 0 then
                begin
                AttackBar:= CurrentTeam^.AttackBar;
                PlaySound(sndThrowPowerUp)
                end;
             inc(Power)
             end;
        if ((Message and gmAttack) <> 0) then exit;

        if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0 then
           begin
           StopSound(sndThrowPowerUp);
           PlaySound(sndThrowRelease);
           end;

        xx:= SignAs(AngleSin(Angle), dX);
        yy:= -AngleCos(Angle);

        lx:= X + int2hwfloat(round(GetLaunchX(CurAmmoType, hwSign(dX), Angle)));
        ly:= Y + int2hwfloat(round(GetLaunchY(CurAmmoType, Angle)));

        if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then xx:= - xx;
        if Ammoz[CurAmmoType].Ammo.AttackVoice <> sndNone then
           AddVoice(Ammoz[CurAmmoType].Ammo.AttackVoice, CurrentTeam^.voicepack);

// Initiating alt attack
        if  (CurAmmoGear <> nil) and
            ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0) and
            ((Gear^.Message and gmLJump) <> 0) and
            ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
            begin
            newDx:= dX / _2; 
            newDy:= dY / _2;
            altUse:= true;
            end
        else
            begin
            newDx:= xx*Power/cPowerDivisor;
            newDy:= yy*Power/cPowerDivisor;
            altUse:= false
            end;

             case CurAmmoType of
                      amGrenade: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGrenade,         0, newDx, newDy, CurWeapon^.Timer);
                      amMolotov: newGear:= AddGear(hwRound(lx), hwRound(ly), gtMolotov,      0, newDx, newDy, 0);
                  amClusterBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtClusterBomb,  0, newDx, newDy, CurWeapon^.Timer);
                      amGasBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGasBomb,      0, newDx, newDy, CurWeapon^.Timer);
                      amBazooka: newGear:= AddGear(hwRound(lx), hwRound(ly), gtShell,        0, newDx, newDy, 0);
                     amSnowball: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSnowball,     0, newDx, newDy, 0);
                          amBee: newGear:= AddGear(hwRound(lx), hwRound(ly), gtBee,          0, newDx, newDy, 0);
                      amShotgun: begin
                                 PlaySound(sndShotgunReload);
                                 newGear:= AddGear(hwRound(lx), hwRound(ly), gtShotgunShot,  0, xx * _0_5, yy * _0_5, 0);
                                 end;
                   amPickHammer: newGear:= AddGear(hwRound(lx), hwRound(ly) + cHHRadius, gtPickHammer, 0, _0, _0, 0);
                         amSkip: ParseCommand('/skip', true);
                         amRope: newGear:= AddGear(hwRound(lx), hwRound(ly), gtRope, 0, xx, yy, 0);
                         amMine: if altUse then
                                    newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, newDx, newDy, 3000)
                                 else
                                    newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
                        amSMine: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSMine,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
                       amDEagle: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0);
                      amSineGun: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0);
                    amPortalGun: begin
                                 newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtPortal, 0, xx * _0_6, yy * _0_6, 
                                 // set selected color
                                 CurWeapon^.Pos);
                                 end;
                  amSniperRifle: begin
                                 PlaySound(sndSniperReload);
                                 newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSniperRifleShot, 0, xx * _0_5, yy * _0_5, 0);
                                 end;
                     amDynamite: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtDynamite, 0, SignAs(_0_03, dX), _0, 5000);
                    amFirePunch: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtFirePunch, 0, xx, _0, 0);
                         amWhip: begin
                                 newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtWhip, 0, SignAs(_1, dX), - _0_8, 0);
                                 PlaySound(sndWhipCrack)
                                 end;
                       amHammer: begin
                                 newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtHammer, 0, SignAs(_1, dX), - _0_8, 0);
                                 PlaySound(sndWhack)
                                 end;
                  amBaseballBat: begin
                                 newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtShover, gsttmpFlag, xx * _0_5, yy * _0_5, 0);
                                 PlaySound(sndBaseballBat) // TODO: Only play if something is hit?
                                 end;
                    amParachute: begin
                                 newGear:= AddGear(hwRound(lx), hwRound(ly), gtParachute, 0, _0, _0, 0);
                                 PlaySound(sndParachute)
                                 end;
                    // we save CurWeapon^.Pos (in this case: cursor direction) by using it as (otherwise irrelevant) X value of the new gear.
                    amAirAttack: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 0, _0, _0, 0);
                   amMineStrike: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 1, _0, _0, 0);
                  amDrillStrike: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 3, _0, _0, CurWeapon^.Timer);
                       amNapalm: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 2, _0, _0, 0);
                    amBlowTorch: newGear:= AddGear(hwRound(lx), hwRound(ly), gtBlowTorch, 0, SignAs(_0_5, dX), _0, 0);
                       amGirder: newGear:= AddGear(0, 0, gtGirder, CurWeapon^.Pos, _0, _0, 0);
                     amTeleport: newGear:= AddGear(CurWeapon^.Pos, 0, gtTeleport, 0, _0, _0, 0);
                       amSwitch: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSwitcher, 0, _0, _0, 0);
                       amMortar: begin
                                 playSound(sndMortar);
                                 newGear:= AddGear(hwRound(lx), hwRound(ly), gtMortar,  0, xx*cMaxPower/cPowerDivisor, yy*cMaxPower/cPowerDivisor, 0);
                                 end;
                      amRCPlane: begin
                                 newGear:= AddGear(hwRound(lx), hwRound(ly), gtRCPlane,  0, xx * cMaxPower / cPowerDivisor / 4, yy * cMaxPower / cPowerDivisor / 4, 0);
                                 newGear^.SoundChannel:= LoopSound(sndRCPlane, nil)
                                 end;
                       amKamikaze: newGear:= AddGear(hwRound(lx), hwRound(ly), gtKamikaze, 0, xx * _0_5, yy * _0_5, 0);
                         amCake: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 3, hwRound(ly), gtCake, 0, xx, _0, 0);
                    amSeduction: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSeduction, 0, _0, _0, 0);
                   amWatermelon: newGear:= AddGear(hwRound(lx), hwRound(ly), gtWatermelon,  0, newDx, newDy, CurWeapon^.Timer);
                  amHellishBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtHellishBomb,    0, newDx, newDy, 0);
                        amDrill: newGear:= AddGear(hwRound(lx), hwRound(ly), gtDrill, 0, newDx, newDy, 0);
                      amBallgun: newGear:= AddGear(hwRound(X), hwRound(Y), gtBallgun,  0, xx * _0_5, yy * _0_5, 0);
                    amJetpack: newGear:= AddGear(hwRound(lx), hwRound(ly), gtJetpack, 0, _0, _0, 0);
                    amBirdy: begin
                             PlaySound(sndWhistle);
                             newGear:= AddGear(hwRound(lx), hwRound(ly) - 32, gtBirdy, 0, _0, _0, 0);
                             end;
                      amLowGravity: begin
                                    PlaySound(sndLowGravity);
                                    cGravity:= cMaxWindSpeed;
                                    cGravityf:= 0.00025
                                    end;
                      amExtraDamage:begin 
                                    PlaySound(sndHellishImpact4);
                                    cDamageModifier:= _1_5
                                    end;
                      amInvulnerable: Invulnerable:= true;
                      amExtraTime:  begin
                                    PlaySound(sndSwitchHog);
                                    TurnTimeLeft:= TurnTimeLeft + 30000
                                    end;
                      amLaserSight: cLaserSighting:= true;
                      amVampiric: begin
                                  PlaySound(sndOw1, Team^.voicepack);
                                  cVampiric:= true;
                                  end;
                      amPiano: begin
                               // Tuck the hedgehog away until the piano attack is completed
                               Unplaced:= true;
                               X:= _0;
                               Y:= _0;
                               newGear:= AddGear(TargetPoint.X, 0, gtPiano, 0, _0, _0, 0);
                               PauseMusic
                               end;
                      amFlamethrower: newGear:= AddGear(hwRound(X), hwRound(Y), gtFlamethrower,  0, xx * _0_5, yy * _0_5, 0);
                      amLandGun: newGear:= AddGear(hwRound(X), hwRound(Y), gtLandGun,  0, xx * _0_5, yy * _0_5, 0);
                    amResurrector: begin
                        newGear:= AddGear(hwRound(lx), hwRound(ly),
                                gtResurrector, 0, _0, _0, 0);
                        newGear^.SoundChannel := LoopSound(sndResurrector);
                    end;
                   //amMelonStrike: AddGear(CurWeapon^.Pos, 0, gtAirAttack, 4, _0, _0, 0);
                     amStructure: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtStructure, gstWait, SignAs(_0_02, dX), _0, 3000);
                        amTardis: newGear:= AddGear(hwRound(X), hwRound(Y), gtTardis, 0, _0, _0, 5000);
                  end;
             case CurAmmoType of
                      amGrenade, amMolotov, 
                  amClusterBomb, amGasBomb, 
                      amBazooka, amSnowball, 
                          amBee, amSMine,
                       amMortar, amWatermelon,
                  amHellishBomb, amDrill: FollowGear:= newGear;

                      amShotgun, amPickHammer,
                         amRope, amDEagle,
                      amSineGun, amSniperRifle,
                    amFirePunch, amWhip,
                       amHammer, amBaseballBat,
                    amParachute, amBlowTorch,
                       amGirder, amTeleport,
                       amSwitch, amRCPlane,
                     amKamikaze, amCake,
                    amSeduction, amBallgun,
                      amJetpack, amBirdy,
                 amFlamethrower, amLandGun,
                  amResurrector, amStructure,
                       amTardis, amPiano: CurAmmoGear:= newGear;
                  end;
              if (CurAmmoType = amMine) or (CurAmmoType = amSMine) and (GameFlags and gfInfAttack <> 0) then newGear^.FlightTime:= GameTicks + 1000;
        if Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0 then
            begin
            newGear^.Target.X:= TargetPoint.X;
            newGear^.Target.Y:= TargetPoint.Y
            end;

        // Clear FollowGear if using on a rope/parachute/saucer etc so focus stays with the hog's movement
        if altUse then FollowGear:= nil;

        if (newGear <> nil) and ((Ammoz[newGear^.AmmoType].Ammo.Propz and ammoprop_SetBounce) <> 0) then
            begin
            elastic:=  int2hwfloat(CurWeapon^.Bounciness) / _1000;

            if elastic < _1 then newGear^.Elasticity:= newGear^.Elasticity * elastic
            else if elastic > _1 then newGear^.Elasticity:= _1 - ((_1-newGear^.Elasticity) / elastic);
(* Experimented with friction modifier. Didn't seem helpful 
            fric:= int2hwfloat(CurWeapon^.Bounciness) / _250;
            if fric < _1 then newGear^.Friction:= newGear^.Friction * fric
            else if fric > _1 then newGear^.Friction:= _1 - ((_1-newGear^.Friction) / fric)*)
            end;


        uStats.AmmoUsed(CurAmmoType);

        if not (SpeechText = '') then
            begin
            speech:= AddVisualGear(0, 0, vgtSpeechBubble);
            if speech <> nil then
               begin
               speech^.Text:= SpeechText;
               speech^.Hedgehog:= Gear^.Hedgehog;
               speech^.FrameTicks:= SpeechType;
               end;
            SpeechText:= ''
            end;

        Power:= 0;
        if (CurAmmoGear <> nil)
           and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) = 0){check for dropping ammo from rope} then
           begin
           Message:= Message or gmAttack;
           CurAmmoGear^.Message:= Message
           end else begin
           if not CurrentTeam^.ExtDriven and
             ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0) then SendIPC('a');
           AfterAttack;
           end
        end else Message:= Message and (not gmAttack);
     end;
     TargetPoint.X := NoPointX;
     ScriptCall('onHogAttack');
end;

procedure AfterAttack;
var s: shortstring;
    a: TAmmoType;
begin
with CurrentHedgehog^.Gear^,
        CurrentHedgehog^ do
    begin
    a:= CurAmmoType;
    State:= State and (not gstAttacking);
    if (Ammoz[a].Ammo.Propz and ammoprop_Effect) = 0 then
        begin
        Inc(MultiShootAttacks);
        
        if (Ammoz[a].Ammo.NumPerTurn >= MultiShootAttacks) then
            begin
            s:= inttostr(Ammoz[a].Ammo.NumPerTurn - MultiShootAttacks + 1);
            AddCaption(format(trmsg[sidRemaining], s), cWhiteColor, capgrpAmmostate);
            end;
        
        if (Ammoz[a].Ammo.NumPerTurn >= MultiShootAttacks) or
            ((GameFlags and gfMultiWeapon) <> 0) then
            begin
            isInMultiShoot:= true
            end
        else
            begin
            OnUsedAmmo(CurrentHedgehog^);
            if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) and (((GameFlags and gfInfAttack) = 0) or PlacingHogs) then
                begin
                if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
                TurnTimeLeft:=(Ammoz[a].TimeAfterTurn * cGetAwayTime) div 100;
                end;
            if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) then State:= State or gstAttacked;
            if (Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) <> 0 then ApplyAmmoChanges(CurrentHedgehog^)
            end;
        end
    else
        begin
        OnUsedAmmo(CurrentHedgehog^);
        ApplyAmmoChanges(CurrentHedgehog^);
        end;
    AttackBar:= 0
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepHedgehogDead(Gear: PGear);
const frametime = 200;
      timertime = frametime * 6;
begin
if Gear^.Hedgehog^.Unplaced then exit;
if Gear^.Timer > 1 then
    begin
    AllInactive:= false;
    dec(Gear^.Timer);
    if (Gear^.Timer mod frametime) = 0 then inc(Gear^.Pos)
    end 
else if Gear^.Timer = 1 then
    begin
    Gear^.State:= Gear^.State or gstNoDamage;
    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, CurrentHedgehog, EXPLAutoSound);
    AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtGrave, 0, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
    DeleteGear(Gear);
    SetAllToActive
    end 
else // Gear^.Timer = 0
    begin
    AllInactive:= false;
    Gear^.Z:= cCurrHHZ;
    RemoveGearFromList(Gear);
    InsertGearToList(Gear);
    PlaySound(sndByeBye, Gear^.Hedgehog^.Team^.voicepack);
    Gear^.Pos:= 0;
    Gear^.Timer:= timertime
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepHedgehogGone(Gear: PGear);
const frametime = 65;
      timertime = frametime * 11;
begin
if Gear^.Hedgehog^.Unplaced then exit;
if Gear^.Timer > 1 then
    begin
    AllInactive:= false;
    dec(Gear^.Timer);
    if (Gear^.Timer mod frametime) = 0 then inc(Gear^.Pos)
    end else
if Gear^.Timer = 1 then
    begin
    DeleteGear(Gear);
    SetAllToActive
    end else // Gear^.Timer = 0
    begin
    AllInactive:= false;
    Gear^.Z:= cCurrHHZ;
    RemoveGearFromList(Gear);
    InsertGearToList(Gear);
    PlaySound(sndByeBye, Gear^.Hedgehog^.Team^.voicepack);
    PlaySound(sndWarp);
    Gear^.Pos:= 0;
    Gear^.Timer:= timertime
    end
end;

////////////////////////////////////////////////////////////////////////////////
procedure PickUp(HH, Gear: PGear);
var s: shortstring;
    a: TAmmoType;
    i: LongInt;
    vga: PVisualGear;
begin
Gear^.Message:= gmDestroy;
PlaySound(sndShotgunReload);
if (Gear^.Pos and posCaseExplode) <> 0 then
    if (Gear^.Pos and posCasePoison) <> 0 then
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound + EXPLPoisoned)
    else
        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound)
else if (Gear^.Pos and posCasePoison) <> 0 then
    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound + EXPLPoisoned + EXPLNoDamage)
else
case Gear^.Pos of
       posCaseUtility,
       posCaseAmmo: begin
                    if Gear^.AmmoType <> amNothing then a:= Gear^.AmmoType 
                    else
                        begin
                        for i:= 0 to GameTicks and $7F do GetRandom(2); // Burn some random numbers
                        if Gear^.Pos = posCaseUtility then a:= GetUtility
                        else a:= GetAmmo
                        end;
                    AddAmmo(HH^.Hedgehog^, a);
// Possibly needs to check shared clan ammo game flag once added.
// On the other hand, no obvious reason that clan members shouldn't know what ammo another clan member picked up
                    if (not (HH^.Hedgehog^.Team^.ExtDriven 
                      or (HH^.Hedgehog^.BotLevel > 0)))
                      or (HH^.Hedgehog^.Team^.Clan^.ClanIndex = LocalClan)
                      or (GameType = gmtDemo)  then
                        begin
                        s:= trammo[Ammoz[a].NameId] + ' (+' + IntToStr(Ammoz[a].NumberInCase) + ')';
                        AddCaption(s, HH^.Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);

                        // show ammo icon
                        vga:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtAmmo);
                        if vga <> nil then
                            vga^.Frame:= Longword(a);
                        end;

                    end;
     posCaseHealth: begin
                    inc(HH^.Health, Gear^.Health);
                    HH^.Hedgehog^.Effects[hePoisoned] := false;
                    str(Gear^.Health, s);
                    s:= '+' + s;
                    AddCaption(s, HH^.Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
                    RenderHealth(HH^.Hedgehog^);
                    RecountTeamHealth(HH^.Hedgehog^.Team);

                    i:= 0;
                    while i < Gear^.Health do
                        begin
                        vga:= AddVisualGear(hwRound(HH^.X), hwRound(HH^.Y), vgtStraightShot);
                        if vga <> nil then
                            with vga^ do
                                begin
                                Tint:= $00FF00FF;
                                State:= ord(sprHealth)
                                end;
                        inc(i, 5);
                        end;
                    end;
     end
end;

const StepTicks: LongWord = 0;

procedure HedgehogStep(Gear: PGear);
var PrevdX: LongInt;
    CurWeapon: PAmmo;
begin
CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);
if ((Gear^.State and (gstAttacking or gstMoving)) = 0) then
   begin
   if isCursorVisible then
      with Gear^.Hedgehog^ do
        with CurWeapon^ do
          begin
          if (Gear^.Message and gmLeft  ) <> 0 then
             Pos:= (Pos - 1 + Ammoz[AmmoType].PosCount) mod Ammoz[AmmoType].PosCount
          else
          if (Gear^.Message and gmRight ) <> 0 then
             Pos:= (Pos + 1) mod Ammoz[AmmoType].PosCount
          else exit;
          StepTicks:= 200;
          exit
          end;

    if ((Gear^.Message and gmAnimate) <> 0) then
        begin
        Gear^.Message:= 0;
        Gear^.State:= Gear^.State or gstAnimation;
        Gear^.Tag:= Gear^.MsgParam;
        Gear^.Timer:= 0;
        Gear^.Pos:= 0
        end;

   if ((Gear^.Message and gmLJump ) <> 0) then
      begin
      Gear^.Message:= Gear^.Message and (not gmLJump);
      DeleteCI(Gear);
      if TestCollisionYwithGear(Gear, -1) = 0 then
         if not TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) then Gear^.Y:= Gear^.Y - _2 else
         if not TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) then Gear^.Y:= Gear^.Y - _1;
      if not (TestCollisionXwithGear(Gear, hwSign(Gear^.dX))
         or   (TestCollisionYwithGear(Gear, -1) <> 0)) then
         begin
         Gear^.dY:= -_0_15;
         if not cArtillery then Gear^.dX:= SignAs(_0_15, Gear^.dX);
         Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
         PlaySound(sndJump1, Gear^.Hedgehog^.Team^.voicepack);
         exit
         end;
      end;

   if ((Gear^.Message and gmHJump ) <> 0) then
      begin
      DeleteCI(Gear);
      Gear^.Message:= Gear^.Message and (not gmHJump);

      Gear^.dY:= -_0_2;
      SetLittle(Gear^.dX);
      Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
      PlaySound(sndJump3, Gear^.Hedgehog^.Team^.voicepack);
      exit
      end;

   PrevdX:= hwSign(Gear^.dX);
   if (Gear^.Message and gmLeft  )<>0 then Gear^.dX:= -cLittle else
   if (Gear^.Message and gmRight )<>0 then Gear^.dX:=  cLittle else exit;

   if (Gear^.Message and (gmLeft or gmRight)) <> 0 then
      begin
      StepSoundTimer:= cHHStepTicks;
      end;
   
   StepTicks:= cHHStepTicks;
   if PrevdX <> hwSign(Gear^.dX) then
      begin
      FollowGear:= Gear;
      exit
      end;
   DeleteCI(Gear); // must be after exit!! (see previous line)

   Gear^.Hedgehog^.visStepPos:= (Gear^.Hedgehog^.visStepPos + 1) and 7;
   if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
      begin
      if not (TestCollisionXwithXYShift(Gear, _0, -6, hwSign(Gear^.dX))
         or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
      if not (TestCollisionXwithXYShift(Gear, _0, -5, hwSign(Gear^.dX))
         or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
      if not (TestCollisionXwithXYShift(Gear, _0, -4, hwSign(Gear^.dX))
         or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
      if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX))
         or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
      if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX))
         or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
      if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX))
         or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
      end;

   if (not cArtillery) and ((Gear^.Message and gmPrecise) = 0) and (not TestCollisionXwithGear(Gear, hwSign(Gear^.dX))) then
      Gear^.X:= Gear^.X + SignAs(_1, Gear^.dX);

   SetAllHHToActive;

   if TestCollisionYwithGear(Gear, 1) = 0 then
   begin
   Gear^.Y:= Gear^.Y + _1;
   if TestCollisionYwithGear(Gear, 1) = 0 then
   begin
   Gear^.Y:= Gear^.Y + _1;
   if TestCollisionYwithGear(Gear, 1) = 0 then
   begin
   Gear^.Y:= Gear^.Y + _1;
   if TestCollisionYwithGear(Gear, 1) = 0 then
   begin
   Gear^.Y:= Gear^.Y + _1;
   if TestCollisionYwithGear(Gear, 1) = 0 then
   begin
   Gear^.Y:= Gear^.Y + _1;
   if TestCollisionYwithGear(Gear, 1) = 0 then
   begin
   Gear^.Y:= Gear^.Y + _1;
   if TestCollisionYwithGear(Gear, 1) = 0 then
      begin
      Gear^.Y:= Gear^.Y - _6;
      Gear^.dY:= _0;
      Gear^.State:= Gear^.State or gstMoving;
      exit
      end;
   end
   end
   end
   end
   end
   end;
   AddGearCI(Gear)
   end
end;

procedure HedgehogChAngle(HHGear: PGear);
var da: LongWord;
begin
with HHGear^.Hedgehog^ do
    if ((CurAmmoType = amRope) and 
        ((HHGear^.State and (gstMoving or gstHHJumping)) = gstMoving)) or
       ((CurAmmoType = amPortalGun) and 
        ((HHGear^.State and gstMoving) <> 0)) then da:= 2
    else da:= 1;

if (((HHGear^.Message and gmPrecise) = 0) or ((GameTicks mod 5) = 1)) then
    if ((HHGear^.Message and gmUp) <> 0) and (HHGear^.Angle >= CurMinAngle + da) then dec(HHGear^.Angle, da)
    else
    if ((HHGear^.Message and gmDown) <> 0) and (HHGear^.Angle + da <= CurMaxAngle) then inc(HHGear^.Angle, da)
end;


////////////////////////////////////////////////////////////////////////////////
procedure doStepHedgehogMoving(Gear: PGear);
var isFalling, isUnderwater: boolean;
    land: Word;
begin
land:= 0;
isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius;
if Gear^.dX.QWordValue > 8160437862 then Gear^.dX.QWordValue:= 8160437862;
if Gear^.dY.QWordValue > 8160437862 then Gear^.dY.QWordValue:= 8160437862;

if Gear^.Hedgehog^.Unplaced then
   begin
   Gear^.dY:= _0;
   Gear^.dX:= _0;
   Gear^.State:= Gear^.State and (not gstMoving);
   exit
   end;
isFalling:= (Gear^.dY.isNegative) or not TestCollisionYKick(Gear, 1);
if isFalling then
   begin
   if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then Gear^.dY:= _0;
   Gear^.State:= Gear^.State or gstMoving;
   if (CurrentHedgehog^.Gear = Gear)
        and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then 
        begin
        FollowGear:= Gear;
        end;
   if isUnderwater then Gear^.dY:= Gear^.dY + cGravity / _2
   else
       begin
       Gear^.dY:= Gear^.dY + cGravity;
// this set of circumstances could be less complex if jumping was more clearly identified
       if ((GameFlags and gfMoreWind) <> 0) and 
          (((Gear^.Damage <> 0) or
          ((CurAmmoGear <> nil) and
            ((CurAmmoGear^.AmmoType = amJetpack) or
            (CurAmmoGear^.AmmoType = amBirdy))) or
          ((Gear^.dY.QWordValue + Gear^.dX.QWordValue) > _0_55.QWordValue)))
          then Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density
       end
   end 
else
   begin
   land:= TestCollisionYwithGear(Gear, 1);
   if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_55.QWordValue) and ((land and lfIce) = 0)
      and ((Gear^.State and gstHHJumping) <> 0) then SetLittle(Gear^.dX);

   if not Gear^.dY.isNegative then
      begin
      CheckHHDamage(Gear);

      if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) and
         (Gear^.dX.QWordValue < _0_02.QWordValue) then Gear^.dX.isNegative:= not Gear^.dX.isNegative; // landing after high jump

      Gear^.State:= Gear^.State and (not (gstHHJumping or gstHHHJump));
      Gear^.dY:= _0;
      end else Gear^.dY:= Gear^.dY + cGravity;

   if ((Gear^.State and gstMoving) <> 0) then
       begin
       if land and lfIce <> 0 then
           begin
           Gear^.dX:= Gear^.dX * (_1 - (_1 - Gear^.Friction) / _2)
           end
       else Gear^.dX:= Gear^.dX * Gear^.Friction;
       end
   end;

if (Gear^.State <> 0) then DeleteCI(Gear);

if isUnderwater then
   begin
   Gear^.dY:= Gear^.dY * _0_999;
   Gear^.dX:= Gear^.dX * _0_999;
   end;

if (Gear^.State and gstMoving) <> 0 then
   if TestCollisionXKick(Gear, hwSign(Gear^.dX)) then
      if not isFalling then
         if hwAbs(Gear^.dX) > _0_01 then
            if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -1, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_96; Gear^.Y:= Gear^.Y - _1 end else
            if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -2, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_93; Gear^.Y:= Gear^.Y - _2 end else
            if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -3, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_9 ; Gear^.Y:= Gear^.Y - _3 end else
            if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -4, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_87; Gear^.Y:= Gear^.Y - _4 end else
            if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -5, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_84; Gear^.Y:= Gear^.Y - _5 end else
            if hwAbs(Gear^.dX) > _0_02 then Gear^.dX:= -Gear^.Elasticity * Gear^.dX
                                   else begin
                                        Gear^.State:= Gear^.State and (not gstMoving);
                                        while TestCollisionYWithGear(Gear,1) = 0 do Gear^.Y:= Gear^.Y+_1;
                                        SetLittle(Gear^.dX)
                                        end
            else begin
                 Gear^.State:= Gear^.State and (not gstMoving);
                 while TestCollisionYWithGear(Gear,1) = 0 do Gear^.Y:= Gear^.Y+_1;
                 SetLittle(Gear^.dX)
                 end
         else if (hwAbs(Gear^.dX) > cLittle)
                and ((Gear^.State and gstHHJumping) = 0)
                then Gear^.dX:= -Gear^.Elasticity * Gear^.dX
                else SetLittle(Gear^.dX);

if (not isFalling) and
   (hwAbs(Gear^.dX) + hwAbs(Gear^.dY) < _0_03) then
   begin
   Gear^.State:= Gear^.State and (not gstWinner);
   Gear^.State:= Gear^.State and (not gstMoving);
   while TestCollisionYWithGear(Gear,1) = 0 do Gear^.Y:= Gear^.Y+_1;
   SetLittle(Gear^.dX);
   Gear^.dY:= _0
   end else Gear^.State:= Gear^.State or gstMoving;

if (Gear^.State and gstMoving) <> 0 then
   begin
   Gear^.State:= Gear^.State and (not gstAnimation);
// ARTILLERY but not being moved by explosions
   Gear^.X:= Gear^.X + Gear^.dX;
   Gear^.Y:= Gear^.Y + Gear^.dY;
   if (not Gear^.dY.isNegative) and
      (not TestCollisionYKick(Gear, 1)) and
       TestCollisionYwithXYShift(Gear, 0, 1, 1) then
      begin
      CheckHHDamage(Gear);
      Gear^.dY:= _0;
      Gear^.Y:= Gear^.Y + _1
      end;
   CheckGearDrowning(Gear);
   // hide target cursor if current hog is drowning
   if (Gear^.State and gstDrowning) <> 0 then
       if (CurrentHedgehog^.Gear = Gear) then
          isCursorVisible:= false
   end;

if (hwAbs(Gear^.dY) > _0) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then
    begin
    inc(Gear^.FlightTime);
    if Gear^.FlightTime = 3000 then
        begin
        AddCaption(GetEventString(eidHomerun), cWhiteColor, capgrpMessage);
        PlaySound(sndHomerun)
        end;
    end
else
    begin
    uStats.hedgehogFlight(Gear, Gear^.FlightTime);
    Gear^.FlightTime:= 0;
    end;

end;

procedure doStepHedgehogDriven(HHGear: PGear);
var t: PGear;
    wasJumping: boolean;
    Hedgehog: PHedgehog;
begin
Hedgehog:= HHGear^.Hedgehog;
if isInMultiShoot then
   HHGear^.Message:= 0;

if ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_Utility) <> 0) and isInMultiShoot then 
    AllInactive:= true
else if not isInMultiShoot then AllInactive:= false;

if (TurnTimeLeft = 0) or (HHGear^.Damage > 0) then
    begin
    if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
    TurnTimeLeft:= 0;
    isCursorVisible:= false;
    HHGear^.State:= HHGear^.State and (not (gstHHDriven or gstAnimation or gstAttacking));
    AttackBar:= 0;
    if HHGear^.Damage > 0 then
        HHGear^.State:= HHGear^.State and (not (gstHHJumping or gstHHHJump));
    exit
    end;

if (HHGear^.State and gstAnimation) <> 0 then
    begin
    HHGear^.Message:= 0;
    if (HHGear^.Pos = Wavez[TWave(HHGear^.Tag)].VoiceDelay) and (HHGear^.Timer = 0) then PlaySound(Wavez[TWave(HHGear^.Tag)].Voice, Hedgehog^.Team^.voicepack);
    inc(HHGear^.Timer);
    if HHGear^.Timer = Wavez[TWave(HHGear^.Tag)].Interval then
        begin
        HHGear^.Timer:= 0;
        inc(HHGear^.Pos);
        if HHGear^.Pos = Wavez[TWave(HHGear^.Tag)].FramesCount then
            HHGear^.State:= HHGear^.State and (not gstAnimation)
        end;
    exit
    end;

if ((HHGear^.State and gstMoving) <> 0)
    or (StepTicks = cHHStepTicks)
    or (CurAmmoGear <> nil) then // we are moving
    begin
    with Hedgehog^ do
        if (CurAmmoGear = nil)
        and (HHGear^.dY > _0_39)
        and (CurAmmoType = amParachute) then HHGear^.Message:= HHGear^.Message or gmAttack;
    // check for case with ammo
    t:= CheckGearNear(HHGear, gtCase, 36, 36);
    if t <> nil then
        PickUp(HHGear, t)
    end;

if (CurAmmoGear = nil) then
    if (((HHGear^.Message and gmAttack) <> 0)
        or ((HHGear^.State and gstAttacking) <> 0)) then
        Attack(HHGear) // should be before others to avoid desync with '/put' msg and changing weapon msgs
    else
else 
    with Hedgehog^ do
        if ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0)
            and ((HHGear^.Message and gmLJump) <> 0)
            and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
            begin
            Attack(HHGear);
            HHGear^.Message:= HHGear^.Message and (not gmLJump)
            end;

if (CurAmmoGear = nil)
    or ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0) 
    or ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) then
    begin
    if ((HHGear^.Message and gmSlot) <> 0) then
        if ChangeAmmo(HHGear) then ApplyAmmoChanges(Hedgehog^);

    if ((HHGear^.Message and gmWeapon) <> 0) then HHSetWeapon(HHGear);

    if ((HHGear^.Message and gmTimer) <> 0) then HHSetTimer(HHGear);
    end;

if CurAmmoGear <> nil then
   begin
   CurAmmoGear^.Message:= HHGear^.Message;
   exit
   end;

if not isInMultiShoot then
   HedgehogChAngle(HHGear);

if (HHGear^.State and gstMoving) <> 0 then
    begin
    wasJumping:= ((HHGear^.State and gstHHJumping) <> 0);

    if ((HHGear^.Message and gmHJump) <> 0) and
        wasJumping and
        ((HHGear^.State and gstHHHJump) = 0) then
        if (not (hwAbs(HHGear^.dX) > cLittle)) and (HHGear^.dY < -_0_02) then
            begin
            HHGear^.State:= HHGear^.State or gstHHHJump;
            HHGear^.dY:= -_0_25;
            if not cArtillery then HHGear^.dX:= -SignAs(_0_02, HHGear^.dX);
            PlaySound(sndJump2, Hedgehog^.Team^.voicepack)
            end;

    HHGear^.Message:= HHGear^.Message and (not (gmLJump or gmHJump));

    if (not cArtillery) and wasJumping and
        TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX);

    if Hedgehog^.Gear <> nil then doStepHedgehogMoving(HHGear);

    if ((HHGear^.State and (gstMoving or gstDrowning)) = 0) then
        begin
        AddGearCI(HHGear);
        if wasJumping then
            StepTicks:= 410
        else
            StepTicks:= 95
        end;
    exit
    end;

    if not isInMultiShoot and (Hedgehog^.Gear <> nil) then
        begin
        if StepTicks > 0 then dec(StepTicks);
        if (StepTicks = 0) then HedgehogStep(HHGear)
        end
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepHedgehogFree(Gear: PGear);
var prevState: Longword;
begin
prevState:= Gear^.State;

doStepHedgehogMoving(Gear);

if (Gear^.State and (gstMoving or gstDrowning)) <> 0 then
    begin
    if Gear^.Damage > 0 then CalcRotationDirAngle(Gear);
    AllInactive:= false;
    exit
    end;

if (Gear^.Health = 0) then
    begin
    if PrvInactive or ((GameFlags and gfInfAttack) <> 0) then
        begin
        Gear^.Timer:= 0;
        FollowGear:= Gear;
        PrvInactive:= false;
        AllInactive:= false;

        if (Gear^.State and gstHHGone) = 0 then
            begin
            Gear^.Hedgehog^.Effects[hePoisoned] := false;
            if Gear^.Hedgehog^.Effects[heResurrectable] then begin
                ResurrectHedgehog(Gear);
            end else 
                begin
                Gear^.State:= (Gear^.State or gstHHDeath) and (not gstAnimation);
                Gear^.doStep:= @doStepHedgehogDead;
                // Death message
                AddCaption(Format(GetEventString(eidDied), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
                end;
            end
        else
            begin
            Gear^.State:= Gear^.State and (not gstAnimation);
            Gear^.doStep:= @doStepHedgehogGone;

            // Gone message
            AddCaption(Format(GetEventString(eidGone), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
            end
        end;
    exit
    end;

if ((Gear^.State and gstWait) = 0) and
    (prevState <> Gear^.State) then
    begin
    Gear^.State:= Gear^.State or gstWait;
    Gear^.Timer:= 150
    end else
    begin
    if Gear^.Timer = 0 then
        begin
        Gear^.State:= Gear^.State and (not (gstWait or gstLoser or gstWinner or gstAttacked or gstNotKickable or gstHHChooseTarget));
        Gear^.Active:= false;
        AddGearCI(Gear);
        exit
        end else dec(Gear^.Timer)
    end;

AllInactive:= false
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepHedgehog(Gear: PGear);
(*
var x,y,tx,ty: LongInt;
    tdX, tdY, slope: hwFloat; 
    land: Word; *)
var slope: hwFloat; 
begin
if (Gear^.Message and gmDestroy) <> 0 then
    begin
    DeleteGear(Gear);
    exit
    end;

if (Gear^.State and gstHHDriven) = 0 then
    doStepHedgehogFree(Gear)
else
    begin
    with Gear^.Hedgehog^ do
        if Team^.hasGone then
            TeamGoneEffect(Team^)
        else
            doStepHedgehogDriven(Gear)
    end;
if (Gear^.Message and (gmAllStoppable or gmLJump or gmHJump) = 0) and
   (Gear^.State and (gstHHJumping or gstHHHJump or gstAttacking) = 0) and
   (not Gear^.dY.isNegative) and
   (GameTicks mod (100*LongWOrd(hwRound(cMaxWindSpeed*2/cGravity))) = 0) and
   (TestCollisionYwithGear(Gear, 1) and lfIce <> 0) then
    begin
    slope:= CalcSlopeBelowGear(Gear);
    Gear^.dX:=Gear^.dX+slope*_0_07;
    if slope.QWordValue <> 0 then Gear^.State:= Gear^.State or gstMoving;
(*
    x:= hwRound(Gear^.X);
    y:= hwRound(Gear^.Y);
    AddVisualGear(x, y, vgtSmokeTrace);
    AddVisualGear(x - hwRound(_5*slope), y + hwRound(_5*slope), vgtSmokeTrace);
    AddVisualGear(x + hwRound(_5*slope), y - hwRound(_5*slope), vgtSmokeTrace);
    AddVisualGear(x - hwRound(_20 * slope), y + hwRound(_20 * slope), vgtSmokeTrace);
    AddVisualGear(x + hwRound(_20 * slope), y - hwRound(_20 * slope), vgtSmokeTrace);
    AddVisualGear(x - hwRound(_30 * slope), y + hwRound(_30 * slope), vgtSmokeTrace);
    AddVisualGear(x + hwRound(_30 * slope), y - hwRound(_30 * slope), vgtSmokeTrace);
    AddVisualGear(x - hwRound(_40 * slope), y + hwRound(_40 * slope), vgtSmokeTrace);
    AddVisualGear(x + hwRound(_40 * slope), y - hwRound(_40 * slope), vgtSmokeTrace);
    AddVisualGear(x - hwRound(_50 * slope), y + hwRound(_50 * slope), vgtSmokeTrace);
    AddVisualGear(x + hwRound(_50 * slope), y - hwRound(_50 * slope), vgtSmokeTrace); *)
    end
end;

end.