hedgewars/GSHandlers.inc
author unc0rr
Mon, 29 Sep 2008 22:14:23 +0000
changeset 1301 c6fe8a4bfd34
parent 1298 18fdc25fe65d
child 1343 7a47a80b20ad
permissions -rw-r--r--
Fix a bug screwing team selection up in network game (REMOVETEAM message doesn't have teamID, and after removing the team QMap still contains old info, when add and remove team with the same name, total hedgehogs number will be decreased by first team hh number)

(*
 * Hedgewars, a free turn based strategy game
 * Copyright (c) 2004-2008 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 doStepDrowningGear(Gear: PGear); forward;

function CheckGearDrowning(Gear: PGear): boolean;
begin
if cWaterLine < hwRound(Gear^.Y) + Gear^.Radius then
	begin
	CheckGearDrowning:= true;
	Gear^.State:= gstDrowning;
	Gear^.doStep:= @doStepDrowningGear;
	PlaySound(sndSplash, false)
	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;
begin
if _0_4 < Gear^.dY then
	begin
	if _0_6 < Gear^.dY then
		PlaySound(sndOw4, false)
	else
		PlaySound(sndOw1, false);

	dmg:= 1 + hwRound((hwAbs(Gear^.dY) - _0_4) * 70);
	inc(Gear^.Damage, dmg);
	AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y) + cHHRadius, dmg, Gear);
	end
end;

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
procedure CalcRotationDirAngle(Gear: PGear);
var dAngle: real;
begin
dAngle:= (hwAbs(Gear^.dX) + hwAbs(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;
if hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater then DeleteGear(Gear)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFallingGear(Gear: PGear);
var isFalling: boolean;
begin
Gear^.State:= Gear^.State and not gstCollision;

if Gear^.dY.isNegative then
	begin
	isFalling:= true;
	if TestCollisionYwithGear(Gear, -1) then
		begin
		Gear^.dX:=   Gear^.dX * Gear^.Friction;
		Gear^.dY:= - Gear^.dY * Gear^.Elasticity;
		Gear^.State:= Gear^.State or gstCollision
		end
	end else
	if TestCollisionYwithGear(Gear, 1) then
		begin
		isFalling:= false;
		Gear^.dX:=   Gear^.dX * Gear^.Friction;
		Gear^.dY:= - Gear^.dY * Gear^.Elasticity;
		Gear^.State:= Gear^.State or gstCollision
		end else isFalling:= true;

if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
	begin
	Gear^.dX:= - Gear^.dX * Gear^.Elasticity;
	Gear^.dY:=   Gear^.dY * Gear^.Elasticity;
	Gear^.State:= Gear^.State or gstCollision
	end;

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
	(not isFalling) then
	Gear^.State:= Gear^.State and not gstMoving
else
	Gear^.State:= Gear^.State or      gstMoving
end;

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

doStepFallingGear(Gear);

dec(Gear^.Timer);
if Gear^.Timer = 0 then
	begin
	case Gear^.Kind of
		gtAmmo_Bomb: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
		gtClusterBomb: begin
				doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, EXPLAutoSound);
				for i:= 0 to 4 do
					begin
					dX:= rndSign(GetRandom * _0_1);
					dY:= (GetRandom - _3) * _0_08;
					AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtCluster, 0, dX, dY, 25);
					end
				end;
		gtWatermelon: begin
				doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, EXPLAutoSound);
				for i:= 0 to 5 do
					begin
					dX:= rndSign(GetRandom * _0_1);
					dY:= (GetRandom - _2) * _0_2;
					AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMelonPiece, 0, dX, dY, 75)^.DirAngle:= i * 60;
					end
				end;
		gtHellishBomb: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 90, EXPLAutoSound);
		end;
	DeleteGear(Gear);
	exit
	end;

CalcRotationDirAngle(Gear);

if Gear^.Kind = gtHellishBomb then
	begin
	if Gear^.Timer = 3000 then PlaySound(sndHellish, false);

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

if (Gear^.State and (gstCollision or gstMoving)) = (gstCollision or gstMoving) then
	if (hwAbs(Gear^.dX) > _0_1) or
	   (hwAbs(Gear^.dY) > _0_1) then
		PlaySound(sndGrenadeImpact, false)
end;

procedure doStepWatermelon(Gear: PGear);
begin
AllInactive:= false;
PlaySound(sndMelon, 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 then
	CalcRotationDirAngle(Gear)
else
	if (GameTicks and $1F) = 0 then
		AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)
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
	AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepHealthTagWork(Gear: PGear);
begin
if Gear^.Kind = gtHealthTag then
   AllInactive:= false;
dec(Gear^.Timer);
Gear^.Y:= Gear^.Y + Gear^.dY;
if Gear^.Timer = 0 then
	begin
	if Gear^.Kind = gtHealthTag then
		PHedgehog(Gear^.Hedgehog)^.Gear^.Active:= true; // to let current hh die
	DeleteGear(Gear)
	end
end;

procedure doStepHealthTagWorkUnderWater(Gear: PGear);
begin
AllInactive:= false;
Gear^.Y:= Gear^.Y - _0_08;
if hwRound(Gear^.Y) < cWaterLine + 10 then
   DeleteGear(Gear)
end;

procedure doStepHealthTag(Gear: PGear);
var s: shortstring;
    font: THWFont;
begin
if Gear^.Kind = gtHealthTag then
   begin
   AllInactive:= false;
   font:= fnt16;
   Gear^.dY:= -_0_08
   end else
   begin
   font:= fntSmall;
   Gear^.dY:= -_0_02
   end;

str(Gear^.State, s);
Gear^.Tex:= RenderStringTex(s, PHedgehog(Gear^.Hedgehog)^.Team^.Clan^.Color, font);
if hwRound(Gear^.Y) < cWaterLine then Gear^.doStep:= @doStepHealthTagWork
                                 else Gear^.doStep:= @doStepHealthTagWorkUnderWater;
Gear^.Y:= Gear^.Y - int2hwFloat(Gear^.Tex^.h)
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(sndGraveImpact, false)
      end;
Gear^.Y:= Gear^.Y + Gear^.dY;
CheckGearDrowning(Gear);
Gear^.dY:= Gear^.dY + cGravity
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepUFOWork(Gear: PGear);
var t: hwFloat;
    y: LongInt;
begin
AllInactive:= false;
t:= Distance(Gear^.dX, Gear^.dY);
Gear^.dX:= Gear^.Elasticity * (Gear^.dX + _0_000004 * (TargetPoint.X - hwRound(Gear^.X)));
Gear^.dY:= Gear^.Elasticity * (Gear^.dY + _0_000004 * (TargetPoint.Y - hwRound(Gear^.Y)));
t:= t / Distance(Gear^.dX, Gear^.dY);
Gear^.dX:= Gear^.dX * t;
Gear^.dY:= Gear^.dY * t;
Gear^.X:= Gear^.X + Gear^.dX;
Gear^.Y:= Gear^.Y + Gear^.dY;

if (GameTicks and $3F) = 0 then
   begin
   y:= hwRound(Gear^.Y);
   if y + Gear^.Radius < cWaterLine then
      AddGear(hwRound(Gear^.X), y, gtSmokeTrace, 0, _0, _0, 0);
   end;

CheckCollision(Gear);
dec(Gear^.Timer);
if ((Gear^.State and gstCollision) <> 0) or (Gear^.Timer = 0) then
   begin
   StopSound(sndUFO);
   doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
   DeleteGear(Gear);
   end;
end;

procedure doStepUFO(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
   PlaySound(sndUFO, true);
   Gear^.Timer:= 5000;
   Gear^.doStep:= @doStepUFOWork
   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;
begin
AllInactive:= false;

if ((Gear^.State and gstAnimation) = 0) then
	begin
	dec(Gear^.Timer);
	if Gear^.Timer = 0 then
		begin
		PlaySound(sndShotgunFire, false);
		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;
dec(i)
until i = 0;
if (Gear^.X < _0) or (Gear^.Y < _0) or (Gear^.X > _2048) or (Gear^.Y > _1024) then
	Gear^.doStep:= @doStepShotIdle
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepDEagleShotWork(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 $FFFFFC00) = 0) and ((x and $FFFFF800) = 0)
     and (Land[y, x] <> 0) then inc(Gear^.Damage);
  if Gear^.Damage > 5 then AmmoShove(Gear, 7, 20);
  dec(i)
until (i = 0) or (Gear^.Damage > Gear^.Health);
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^.Health <= 0) or (Gear^.X < _0) or (Gear^.Y < _0) or (Gear^.X > _2048) or (Gear^.Y > _1024) then
	Gear^.doStep:= @doStepShotIdle
end;

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

////////////////////////////////////////////////////////////////////////////////
procedure doStepActionTimer(Gear: PGear);
begin
dec(Gear^.Timer);
case Gear^.Kind of
    gtATStartGame: begin
                   AllInactive:= false;
                   if Gear^.Timer = 0 then
                      AddCaption(trmsg[sidStartFight], $FFFFFF, capgrpGameState);
                   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 = 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 gm_Destroy) <> 0)or((HHGear^.State and gstHHDriven) = 0) then
	begin
	StopSound(sndPickhammer);
	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
	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;
	Gear^.X:= Gear^.X + Gear^.dX;
	Gear^.Y:= Gear^.Y + _1_9;
	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 Gear^.Y > _1024 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 gm_Attack) <> 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 gm_Left) <> 0) then Gear^.dX:= - _0_3 else
   if ((Gear^.Message and gm_Right) <> 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);

PlaySound(sndPickhammer, true);
doStepPickHammerWork(Gear);
Gear^.doStep:= @doStepPickHammerWork
end;

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

procedure doStepBlowTorchWork(Gear: PGear);
var HHGear: PGear;
    b: boolean;
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 Gear^.Timer mod cHHStepTicks = 0 then
   begin
   b:= true;
   if Gear^.dX.isNegative then HHGear^.Message:= (HHGear^.Message or gm_Left) and not gm_Right
                          else HHGear^.Message:= (HHGear^.Message or gm_Right) and not gm_Left;

   HHGear^.State:= HHGear^.State and not gstAttacking;
   HedgehogStep(HHGear);
   HHGear^.State:= HHGear^.State or gstAttacking;

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

   if (HHGear^.State and gstMoving) <> 0 then Gear^.Timer:= 0
   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 + 6);

if (Gear^.Timer = 0) or ((HHGear^.Message and gm_Attack) <> 0) then
   begin
   HHGear^.Message:= 0;
   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;
Gear^.doStep:= @doStepBlowTorchWork
end;

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

procedure doStepRopeWork(Gear: PGear);
const flCheck: boolean = false;
var HHGear: PGear;
    len, cs, cc, tx, ty, nx, ny: hwFloat;
    lx, ly: LongInt;

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

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

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

Gear^.dX:= HHGear^.X - Gear^.X;
Gear^.dY:= HHGear^.Y - Gear^.Y;

if (Gear^.Message and gm_Left  <> 0) then HHGear^.dX:= HHGear^.dX - _0_0002 else
if (Gear^.Message and gm_Right <> 0) then HHGear^.dX:= HHGear^.dX + _0_0002;

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

cs:= Gear^.dY + HHGear^.dY;
cc:= Gear^.dX + HHGear^.dX;
len:= _1 / Distance(cc, cs);
cc:= cc * len; // rope vector plus hedgehog direction vector normalized
cs:= cs * len;

nx:= hwAbs(cs) * hwSign(HHGear^.dX) * 7; // hedgehog direction normalized with length 7
ny:= hwAbs(cc) * hwSign(HHGear^.dY) * 7;

flCheck:= not flCheck;
if flCheck then  // check whether rope needs dividing
   begin
   len:= Gear^.Elasticity - _20;
   while len > _5 do
         begin
         tx:= cc*len;
         ty:= cs*len;
         lx:= hwRound(Gear^.X + tx + nx);
         ly:= hwRound(Gear^.Y + ty + ny);
         if ((ly and $FFFFFC00) = 0) and ((lx and $FFFFF800) = 0) and (Land[ly, lx] <> 0) then
           begin
           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:= (cc * HHGear^.dY) > (cs * HHGear^.dX);
                dLen:= len
                end;
           Gear^.X:= Gear^.X + tx;
           Gear^.Y:= Gear^.Y + ty;
           inc(RopePoints.Count);
           TryDo(RopePoints.Count <= MAXROPEPOINTS, 'Rope points overflow', true);
           Gear^.Elasticity:= Gear^.Elasticity - len;
           Gear^.Friction:= Gear^.Friction - len;
           break
           end;
         len:= len - _0_2
         end;
   end else
   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;
      if RopePoints.ar[Pred(RopePoints.Count)].b xor ((tx - Gear^.X) * (ty - HHGear^.Y) > (tx - HHGear^.X) * (ty - Gear^.Y)) 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
         end
      end;

Gear^.dX:= HHGear^.X - Gear^.X;
Gear^.dY:= HHGear^.Y - Gear^.Y;

cs:= Gear^.dY + HHGear^.dY;
cc:= Gear^.dX + HHGear^.dX;
len:= _1 / Distance(cc, cs);
cc:= cc * len;
cs:= cs * len;

HHGear^.dX:= HHGear^.X;
HHGear^.dY:= HHGear^.Y;

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

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

HHGear^.X:= Gear^.X + cc*Gear^.Elasticity;
HHGear^.Y:= Gear^.Y + cs*Gear^.Elasticity;

HHGear^.dX:= HHGear^.X - HHGear^.dX;
HHGear^.dY:= HHGear^.Y - HHGear^.dY;

if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
   HHGear^.dX:= -_0_6 * HHGear^.dX;
if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) then
   HHGear^.dY:= -_0_6 * HHGear^.dY;

len:= Distance(HHGear^.dX, HHGear^.dY);
if len > _0_8 then
   begin
   len:= _0_8 / len;
   HHGear^.dX:= HHGear^.dX * len;
   HHGear^.dY:= HHGear^.dY * len;
   end;

if (Gear^.Message and gm_Attack) <> 0 then
   if (Gear^.State and gsttmpFlag) <> 0 then DeleteMe 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;
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
   if TestCollisionYwithGear(HHGear, 1) then
      begin
      CheckHHDamage(HHGear);
      HHGear^.dY:= _0;
      HHGear^.State:= HHGear^.State and not (gstMoving or gstHHJumping);
      end else
      begin
      if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX);
      HHGear^.X:= HHGear^.X + HHGear^.dX;
      HHGear^.Y:= HHGear^.Y + HHGear^.dY;
      Gear^.X:= Gear^.X + HHGear^.dX;
      Gear^.Y:= Gear^.Y + HHGear^.dY;
      HHGear^.dY:= HHGear^.dY + cGravity;
      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;
                with HHGear^ do State:= State and not gstAttacking;
                tt:= _0
                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
	begin
	Gear^.doStep:= @doStepRopeWork;
	with HHGear^ do State:= State and not (gstAttacking or gstHHHJump);

	OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^);
	ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^);
	
	if Gear^.Elasticity < _10 then
		Gear^.Elasticity:= _10000;
	end;

if (Gear^.Elasticity > Gear^.Friction) or ((Gear^.Message and gm_Attack) = 0) then
   begin
   with PHedgehog(Gear^.Hedgehog)^.Gear^ do
        begin
        State:= State and not gstAttacking;
        Message:= Message and not gm_Attack
        end;
   DeleteGear(Gear)
   end
end;

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

////////////////////////////////////////////////////////////////////////////////
procedure doStepSmokeTrace(Gear: PGear);
begin
inc(Gear^.Timer);
if Gear^.Timer > 64 then
	begin
	Gear^.Timer:= 0;
	dec(Gear^.State)
	end;
Gear^.dX:= Gear^.dX + cWindSpeed;
Gear^.X:= Gear^.X + Gear^.dX;
if Gear^.State = 0 then DeleteGear(Gear)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepExplosionWork(Gear: PGear);
begin
inc(Gear^.Timer);
if Gear^.Timer > 75 then
	begin
	inc(Gear^.State);
	Gear^.Timer:= 0;
	if Gear^.State > 5 then DeleteGear(Gear)
	end;
end;

procedure doStepExplosion(Gear: PGear);
var i: LongWord;
begin
for i:= 0 to 31 do AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire);
for i:= 0 to 8 do AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplPart);
for i:= 0 to 8 do AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplPart2);
Gear^.doStep:= @doStepExplosionWork
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) 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, false);
		if Gear^.Timer = 0 then
			begin
			doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, 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 = 0 then
	begin
	doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, EXPLAutoSound);
	DeleteGear(Gear);
	exit
	end;
dec(Gear^.Timer);
end;

///////////////////////////////////////////////////////////////////////////////
procedure doStepCase(Gear: PGear);
var i, x, y: LongInt;
begin
if (Gear^.Message and gm_Destroy) > 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 (gm_LJump or gm_HJump);
	exit
	end;

if Gear^.Damage > 0 then
	begin
	x:= hwRound(Gear^.X);
	y:= hwRound(Gear^.Y);
	DeleteGear(Gear);
	if Gear^.Kind = gtCase then
		begin
		doMakeExplosion(x, y, 25, EXPLAutoSound);
		for i:= 0 to 63 do
			AddGear(x, y, gtFlame, 0, _0, _0, 0);
		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 (Gear^.dY.isNegative) and TestCollisionYwithGear(Gear, -1) then Gear^.dY:= _0 else
	if (not Gear^.dY.isNegative) and TestCollisionYwithGear(Gear, 1) then
		begin
		Gear^.dY:= - Gear^.dY * Gear^.Elasticity;
		if Gear^.dY > - _0_001 then Gear^.dY:= _0
			else if Gear^.dY < - _0_03 then PlaySound(sndGraveImpact, false);
		end;
	CheckGearDrowning(Gear);
	end;

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

////////////////////////////////////////////////////////////////////////////////
const cSorterWorkTime = 640;
var thexchar: array[0..cMaxTeams] of
			record
			dy, ny, dw: LongInt;
			team: PTeam;
			SortFactor: QWord;
			end;
    currsorter: PGear = nil;

procedure doStepTeamHealthSorterWork(Gear: PGear);
var i: LongInt;
begin
AllInactive:= false;
dec(Gear^.Timer);
if (Gear^.Timer and 15) = 0 then
	for i:= 0 to Pred(TeamsCount) do
		with thexchar[i] do
			begin
			{$WARNINGS OFF}
			team^.DrawHealthY:= ny + dy * Gear^.Timer div 640;
			team^.TeamHealthBarWidth:= team^.NewTeamHealthBarWidth + dw * Gear^.Timer div cSorterWorkTime;
			{$WARNINGS ON}
			end;

if (Gear^.Timer = 0) or (currsorter <> Gear) then
	begin
	if currsorter = Gear then currsorter:= nil;
	DeleteGear(Gear)
	end
end;

procedure doStepTeamHealthSorter(Gear: PGear);
var i: Longword;
	b: boolean;
	t: LongInt;
begin
AllInactive:= false;

for t:= 0 to Pred(TeamsCount) do
	with thexchar[t] do
		begin
		dy:= TeamsArray[t]^.DrawHealthY;
		dw:= TeamsArray[t]^.TeamHealthBarWidth - TeamsArray[t]^.NewTeamHealthBarWidth;
		team:= TeamsArray[t];
		SortFactor:= TeamsArray[t]^.Clan^.ClanHealth;
		SortFactor:= (SortFactor shl  3) + TeamsArray[t]^.Clan^.ClanIndex;
		SortFactor:= (SortFactor shl 30) + TeamsArray[t]^.TeamHealth;
		end;

if TeamsCount > 1 then
	repeat
	b:= true;
	for t:= 0 to TeamsCount - 2 do
		if (thexchar[t].SortFactor > thexchar[Succ(t)].SortFactor) then
			begin
			thexchar[cMaxTeams]:= thexchar[t];
			thexchar[t]:= thexchar[Succ(t)];
			thexchar[Succ(t)]:= thexchar[cMaxTeams];
			b:= false
			end
	until b;

t:= - 4;
for i:= 0 to Pred(TeamsCount) do
	with thexchar[i] do
		begin
		dec(t, team^.HealthTex^.h + 2);
		ny:= t;
		dy:= dy - ny
		end;

Gear^.Timer:= cSorterWorkTime;
Gear^.doStep:= @doStepTeamHealthSorterWork;
currsorter:= 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);
begin
AllInactive:= false;
if not TestCollisionYwithGear(Gear, 1) then
	begin
	if hwAbs(Gear^.dX - cWindSpeed) > _0_1 then
		Gear^.dX:= (Gear^.dX - cWindSpeed) * _0_5 + cWindSpeed;

	Gear^.dY:= Gear^.dY + cGravity;
	if Gear^.dY > _0_1 then Gear^.dY:= Gear^.dY * _0_995;
	
	Gear^.X:= Gear^.X + Gear^.dX;
	Gear^.Y:= Gear^.Y + Gear^.dY;
	
	if not (Gear^.Y < _1024) then
		begin
		DeleteGear(Gear);
		exit
		end
	end else begin
	if Gear^.Timer > 0 then dec(Gear^.Timer)
		else begin
		Gear^.Radius:= 5;
		AmmoShove(Gear, 3, 100);
		Gear^.Radius:= 1;
		doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 4, EXPLNoDamage);
		dec(Gear^.Health);
		Gear^.Timer:= 1250 - Gear^.Tag * 12
		end
	end;

//if (((GameTicks div 8) mod 64) = Gear^.Tag) then
//	AmmoFlameWork(Gear);

if Gear^.Health = 0 then
	DeleteGear(Gear)
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepFirePunchWork(Gear: PGear);
var HHGear: PGear;
begin
AllInactive:= false;
if ((Gear^.Message and gm_Destroy) <> 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;
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)), false)
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 gm_Attack) <> 0) then
	begin
	with HHGear^ do
		begin
		Message:= 0;
		SetLittle(dX);
		dY:= _0;
		State:= State or gstMoving;
		end;
	DeleteGear(Gear);
	exit
	end;

if not TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
	HHGear^.X:= HHGear^.X + cWindSpeed * 200;

if (Gear^.Message and gm_Left) <> 0 then HHGear^.X:= HHGear^.X - cMaxWindSpeed * 40
else if (Gear^.Message and gm_Right) <> 0 then HHGear^.X:= HHGear^.X + cMaxWindSpeed * 40;
if (Gear^.Message and gm_Up) <> 0 then HHGear^.Y:= HHGear^.Y - cGravity * 40
else if (Gear^.Message and gm_Down) <> 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);

OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^);
ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^);

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

Gear^.doStep:= @doStepParachuteWork;

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

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

if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then
	begin
	dec(Gear^.Health);
	case Gear^.State of
			0: FollowGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0);
			1: FollowGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine,    0, cBombsSpeed * Gear^.Tag, _0, 0);
			end;
	Gear^.dX:= Gear^.dX + int2hwFloat(30 * Gear^.Tag)
	end;

if (GameTicks and $3F) = 0 then
	AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0);

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

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

if Gear^.X.QWordValue = 0 then Gear^.Tag:=  1
                          else Gear^.Tag:= -1;
Gear^.X:= _1024 - _2048 * Gear^.Tag;
Gear^.Y:= -_300;
Gear^.dX:= int2hwFloat(TargetPoint.X - 5 * Gear^.Tag * 15);

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

Gear^.Health:= 6;
Gear^.doStep:= @doStepAirAttackWork;
PlaySound(sndIncoming, false)
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
	AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)
end;

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

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

HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;
if not TryPlaceOnLand(TargetPoint.X - SpritesData[sprAmGirder].Width div 2,
                      TargetPoint.Y - SpritesData[sprAmGirder].Height div 2,
                      sprAmGirder, Gear^.State, true) then
	begin
	HHGear^.Message:= HHGear^.Message and not gm_Attack;
	HHGear^.State:= HHGear^.State and not gstAttacking;
	HHGear^.State:= HHGear^.State or gstHHChooseTarget;
	DeleteGear(Gear);
	isCursorVisible:= true
	end
else begin
	DeleteGear(Gear);
	AfterAttack
	end;
TargetPoint.X:= NoPointX
end;

////////////////////////////////////////////////////////////////////////////////
procedure doStepTeleportAfter(Gear: PGear);
var HHGear: PGear;
begin
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 gm_Attack;
		HHGear^.State:= HHGear^.State and not gstAttacking;
		HHGear^.State:= HHGear^.State or gstHHChooseTarget;
		DeleteGear(Gear);
		isCursorVisible:= true
		end
	else begin
		DeleteCI(HHGear);
		SetAllHHToActive;
		Gear^.doStep:= @doStepTeleportAnim;
		Gear^.X:= HHGear^.X;
		Gear^.Y:= HHGear^.Y;
		HHGear^.X:= int2hwFloat(TargetPoint.X);
		HHGear^.Y:= int2hwFloat(TargetPoint.Y);
		HHGear^.State:= HHGear^.State or gstMoving
		end;
TargetPoint.X:= NoPointX
end;

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

if ((Gear^.Message and not gm_Switch) <> 0) or (TurnTimeLeft = 0) then
	begin
	HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;
	Msg:= Gear^.Message and not gm_Switch;
	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 gm_Switch) <> 0 then
	begin
	HHGear:= CurrentHedgehog^.Gear;
	HHGear^.Message:= HHGear^.Message and not gm_Switch;
	Gear^.Message:= Gear^.Message and not gm_Switch;
	State:= HHGear^.State;
	HHGear^.State:= 0;
	HHGear^.Active:= false;
	HHGear^.Z:= cHHZ;
	RemoveGearFromList(HHGear);
	InsertGearToList(HHGear);

	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 gm_Attack
	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
	AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)
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);

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, false);
	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
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
			gi^.State:= gi^.State or gstWinner;
		gi:= gi^.NextGear
		end;
	Gear^.doStep:= @doStepCakeExpl;
	PlaySound(sndCake, false)
	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
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 NextAngle
		end;

if (yy = 0) then
	if TestCollisionXwithGear(Gear, xx) then
		PrevAngle
	else begin
		Gear^.Tag:= 0;
		Gear^.X:= Gear^.X + int2hwFloat(xx);
		if not TestCollisionYwithGear(Gear, yyn) then NextAngle
		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);
if (Gear^.Health = 0) or ((Gear^.Message and gm_Attack) <> 0) then
	begin
	FollowGear:= Gear;
	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 gm_Attack);
DeleteCI(HHGear);

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 $FFFFFC00) = 0) and ((x and $FFFFF800) = 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)
	end;
	
if Gear^.Pos = 8 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 2047 do
		Land[cWaterLine, i]:= 0;
	SetAllToActive
	end;

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