hedgewars/uGears.pas
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
 *)

unit uGears;
interface
uses SDLh, uConsts, uFloat;
{$INCLUDE options.inc}
const AllInactive: boolean = false;
      PrvInactive: boolean = false;

type PGear = ^TGear;
	TGearStepProcedure = procedure (Gear: PGear);
	TGear = record
			NextGear, PrevGear: PGear;
			Active: Boolean;
			Ammo : PAmmo;
			State : Longword;
			X : hwFloat;
			Y : hwFloat;
			dX: hwFloat;
			dY: hwFloat;
			Kind: TGearType;
			Pos: Longword;
			doStep: TGearStepProcedure;
			Radius: LongInt;
			Angle, Power : Longword;
			DirAngle: real;
			Timer : LongWord;
			Elasticity: hwFloat;
			Friction  : hwFloat;
			Message, MsgParam : Longword;
			Hedgehog: pointer;
			Health, Damage: LongInt;
			CollisionIndex: LongInt;
			Tag: LongInt;
			Tex: PTexture;
			Z: Longword;
			IntersectGear: PGear;
			TriggerId: Longword;
			end;

function  AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
procedure ProcessGears;
procedure SetAllToActive;
procedure SetAllHHToActive;
procedure DrawGears;
procedure FreeGearsList;
procedure AddMiscGears;
procedure AssignHHCoords;
procedure InsertGearToList(Gear: PGear);
procedure RemoveGearFromList(Gear: PGear);

var CurAmmoGear: PGear = nil;
    GearsList: PGear = nil;
    KilledHHs: Longword = 0;

implementation
uses uWorld, uMisc, uStore, uConsole, uSound, uTeams, uRandom, uCollisions,
	uLand, uIO, uLandGraphics, uAIMisc, uLocale, uAI, uAmmos, uTriggers, GL,
	uStats, uVisualGears;

const MAXROPEPOINTS = 384;
var RopePoints: record
                Count: Longword;
                HookAngle: GLfloat;
                ar: array[0..MAXROPEPOINTS] of record
                                  X, Y: hwFloat;
                                  dLen: hwFloat;
                                  b: boolean;
                                  end;
                 end;

procedure DeleteGear(Gear: PGear); forward;
procedure doMakeExplosion(X, Y, Radius: LongInt; Mask: LongWord); forward;
procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward;
procedure AmmoFlameWork(Ammo: PGear); forward;
function  CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear; forward;
procedure SpawnBoxOfSmth; forward;
procedure AfterAttack; forward;
procedure FindPlace(Gear: PGear; withFall: boolean; Left, Right: LongInt); forward;
procedure HedgehogStep(Gear: PGear); forward;
procedure HedgehogChAngle(Gear: PGear); forward;
procedure ShotgunShot(Gear: PGear); forward;
procedure AddDamageTag(X, Y, Damage: LongWord; Gear: PGear); forward;

{$INCLUDE GSHandlers.inc}
{$INCLUDE HHHandlers.inc}

const doStepHandlers: array[TGearType] of TGearStepProcedure = (
			@doStepBomb,
			@doStepHedgehog,
			@doStepGrenade,
			@doStepHealthTag,
			@doStepGrave,
			@doStepUFO,
			@doStepShotgunShot,
			@doStepPickHammer,
			@doStepRope,
			@doStepSmokeTrace,
			@doStepExplosion,
			@doStepMine,
			@doStepCase,
			@doStepDEagleShot,
			@doStepDynamite,
			@doStepTeamHealthSorter,
			@doStepBomb,
			@doStepCluster,
			@doStepShover,
			@doStepFlame,
			@doStepFirePunch,
			@doStepActionTimer,
			@doStepActionTimer,
			@doStepActionTimer,
			@doStepParachute,
			@doStepAirAttack,
			@doStepAirBomb,
			@doStepBlowTorch,
			@doStepGirder,
			@doStepTeleport,
			@doStepHealthTag,
			@doStepSwitcher,
			@doStepCase,
			@doStepMortar,
			@doStepWhip,
			@doStepKamikaze,
			@doStepCake,
			@doStepSeduction,
			@doStepWatermelon,
			@doStepCluster,
			@doStepBomb,
			@doStepSmokeTrace,
			@doStepWaterUp
			);

procedure InsertGearToList(Gear: PGear);
var tmp, ptmp: PGear;
begin
if GearsList = nil then
   GearsList:= Gear
   else begin
   tmp:= GearsList;
   ptmp:= GearsList;
   while (tmp <> nil) and (tmp^.Z <= Gear^.Z) do
          begin
          ptmp:= tmp;
          tmp:= tmp^.NextGear
          end;

   if ptmp <> nil then
      begin
      Gear^.NextGear:= ptmp^.NextGear;
      Gear^.PrevGear:= ptmp;
      if ptmp^.NextGear <> nil then ptmp^.NextGear^.PrevGear:= Gear;
      ptmp^.NextGear:= Gear
      end
   else GearsList:= Gear
   end
end;

procedure RemoveGearFromList(Gear: PGear);
begin
if Gear^.NextGear <> nil then Gear^.NextGear^.PrevGear:= Gear^.PrevGear;
if Gear^.PrevGear <> nil then Gear^.PrevGear^.NextGear:= Gear^.NextGear
   else GearsList:= Gear^.NextGear
end;

function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
const Counter: Longword = 0;
var Result: PGear;
begin
inc(Counter);
{$IFDEF DEBUGFILE}AddFileLog('AddGear: ('+inttostr(x)+','+inttostr(y)+'), d('+floattostr(dX)+','+floattostr(dY)+')');{$ENDIF}
New(Result);
{$IFDEF DEBUGFILE}AddFileLog('AddGear: type = ' + inttostr(ord(Kind)));{$ENDIF}
FillChar(Result^, sizeof(TGear), 0);
Result^.X:= int2hwFloat(X);
Result^.Y:= int2hwFloat(Y);
Result^.Kind := Kind;
Result^.State:= State;
Result^.Active:= true;
Result^.dX:= dX;
Result^.dY:= dY;
Result^.doStep:= doStepHandlers[Kind];
Result^.CollisionIndex:= -1;
Result^.Timer:= Timer;
Result^.Z:= cUsualZ;

if CurrentTeam <> nil then
   begin
   Result^.Hedgehog:= CurrentHedgehog;
   Result^.IntersectGear:= CurrentHedgehog^.Gear
   end;

case Kind of
   gtAmmo_Bomb,
 gtClusterBomb: begin
                Result^.Radius:= 4;
                Result^.Elasticity:= _0_6;
                Result^.Friction:= _0_96;
                end;
  gtWatermelon: begin
                Result^.Radius:= 4;
                Result^.Elasticity:= _0_8;
                Result^.Friction:= _0_995;
                end;
    gtHedgehog: begin
                Result^.Radius:= cHHRadius;
                Result^.Elasticity:= _0_35;
                Result^.Friction:= _0_999;
                Result^.Angle:= cMaxAngle div 2;
                Result^.Z:= cHHZ;
                end;
gtAmmo_Grenade: begin
                Result^.Radius:= 4;
                end;
   gtHealthTag: begin
                Result^.Timer:= 1500;
                Result^.Z:= 2001;
                end;
       gtGrave: begin
                Result^.Radius:= 10;
                Result^.Elasticity:= _0_6;
                end;
         gtUFO: begin
                Result^.Radius:= 5;
                Result^.Timer:= 500;
                Result^.Elasticity:= _0_9
                end;
 gtShotgunShot: begin
                Result^.Timer:= 900;
                Result^.Radius:= 2
                end;
  gtPickHammer: begin
                Result^.Radius:= 10;
                Result^.Timer:= 4000
                end;
  gtSmokeTrace,
   gtEvilTrace: begin
                Result^.X:= Result^.X - _16;
                Result^.Y:= Result^.Y - _16;
                Result^.State:= 8;
                Result^.Z:= cSmokeZ
                end;
        gtRope: begin
                Result^.Radius:= 3;
                Result^.Friction:= _450;
                RopePoints.Count:= 0;
                end;
   gtExplosion: begin
                Result^.X:= Result^.X;
                Result^.Y:= Result^.Y;
                end;
        gtMine: begin
                Result^.State:= Result^.State or gstMoving;
                Result^.Radius:= 2;
                Result^.Elasticity:= _0_55;
                Result^.Friction:= _0_995;
                Result^.Timer:= 3000;
                end;
        gtCase: begin
                Result^.Radius:= 16;
                Result^.Elasticity:= _0_3
                end;
  gtDEagleShot: begin
                Result^.Radius:= 1;
                Result^.Health:= 50
                end;
    gtDynamite: begin
                Result^.Radius:= 3;
                Result^.Elasticity:= _0_55;
                Result^.Friction:= _0_03;
                Result^.Timer:= 5000;
                end;
     gtCluster: Result^.Radius:= 2;
      gtShover: Result^.Radius:= 20;
       gtFlame: begin
                Result^.Tag:= Counter mod 64;
                Result^.Radius:= 1;
                Result^.Health:= 2;
                Result^.dY:= (getrandom - _0_8) * _0_03;
                Result^.dX:= (getrandom - _0_5) * _0_4
                end;
   gtFirePunch: begin
                Result^.Radius:= 15;
                Result^.Tag:= Y
                end;
     gtAirBomb: begin
                Result^.Radius:= 5;
                end;
   gtBlowTorch: begin
                Result^.Radius:= cHHRadius + cBlowTorchC;
                Result^.Timer:= 7500;
                end;
 gtSmallDamage: begin
                Result^.Timer:= 1100;
                Result^.Z:= 2000;
                end;
    gtSwitcher: begin
                Result^.Z:= cCurrHHZ
                end;
      gtTarget: begin
                Result^.Radius:= 16;
                Result^.Elasticity:= _0_3
                end;
      gtMortar: begin
                Result^.Radius:= 4;
                Result^.Elasticity:= _0_2;
                Result^.Friction:= _0_08
                end;
        gtWhip: Result^.Radius:= 20;
    gtKamikaze: begin
                Result^.Health:= 2048;
                Result^.Radius:= 20
                end;
        gtCake: begin
                Result^.Health:= 2048;
                Result^.Radius:= 7;
                Result^.Z:= cOnHHZ;
                if hwSign(dX) > 0 then Result^.Angle:= 1 else Result^.Angle:= 3
                end;
 gtHellishBomb: begin
                Result^.Radius:= 4;
                Result^.Elasticity:= _0_5;
                Result^.Friction:= _0_96;
                end;
     end;
InsertGearToList(Result);
AddGear:= Result
end;

procedure DeleteGear(Gear: PGear);
var team: PTeam;
    t: Longword;
begin
DeleteCI(Gear);

if Gear^.Tex <> nil then
   begin
   FreeTexture(Gear^.Tex);
   Gear^.Tex:= nil
   end;

if Gear^.Kind = gtHedgehog then
   if CurAmmoGear <> nil then
      begin
      Gear^.Message:= gm_Destroy;
      CurAmmoGear^.Message:= gm_Destroy;
      exit
      end else
      begin
      if not (hwRound(Gear^.Y) < cWaterLine) then
         begin
         t:= max(Gear^.Damage, Gear^.Health);
         Gear^.Damage:= t;
         AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtHealthTag, t, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
         uStats.HedgehogDamaged(Gear)
         end;
      team:= PHedgehog(Gear^.Hedgehog)^.Team;
      if CurrentHedgehog^.Gear = Gear then
         FreeActionsList; // to avoid ThinkThread on drawned gear
      PHedgehog(Gear^.Hedgehog)^.Gear:= nil;
      inc(KilledHHs);
      RecountTeamHealth(team);
      end;
{$IFDEF DEBUGFILE}AddFileLog('DeleteGear');{$ENDIF}
if Gear^.TriggerId <> 0 then TickTrigger(Gear^.TriggerId);
if CurAmmoGear = Gear then CurAmmoGear:= nil;
if FollowGear = Gear then FollowGear:= nil;
RemoveGearFromList(Gear);
Dispose(Gear)
end;

function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs
var Gear: PGear;
begin
CheckNoDamage:= true;
Gear:= GearsList;
while Gear <> nil do
	begin
	if Gear^.Kind = gtHedgehog then
		if Gear^.Damage <> 0 then
		begin
		CheckNoDamage:= false;
		uStats.HedgehogDamaged(Gear);

		if Gear^.Health < Gear^.Damage then
			Gear^.Health:= 0
		else
			dec(Gear^.Health, Gear^.Damage);

		AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) - cHHRadius - 12,
				gtHealthTag, Gear^.Damage, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
		RenderHealth(PHedgehog(Gear^.Hedgehog)^);
		RecountTeamHealth(PHedgehog(Gear^.Hedgehog)^.Team);

		Gear^.Damage:= 0
		end;
	Gear:= Gear^.NextGear
	end;
end;

procedure HealthMachine;
var Gear: PGear;
begin
Gear:= GearsList;

while Gear <> nil do
	begin
	if Gear^.Kind = gtHedgehog then
		Gear^.Damage:= min(cHealthDecrease, Gear^.Health - 1);

	Gear:= Gear^.NextGear
	end;
end;

procedure AddDamageTag(X, Y, Damage: LongWord; Gear: PGear);
begin
if cAltDamage then
   AddGear(X, Y, gtSmallDamage, Damage, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
end;

procedure ProcessGears;
const delay: LongWord = 0;
      step: (stDelay, stChDmg, stTurnReact,
             stAfterDelay, stChWin, stWater, stChWin2, stHealth,
             stSpawn, stNTurn) = stDelay;

var Gear, t: PGear;
begin
PrvInactive:= AllInactive;
AllInactive:= true;
t:= GearsList;
while t <> nil do
      begin
      Gear:= t;
      t:= Gear^.NextGear;
      if Gear^.Active then Gear^.doStep(Gear);
      end;

if AllInactive then
   case step of
        stDelay: begin
                 if delay = 0 then
                    delay:= cInactDelay
                 else
                    dec(delay);

                 if delay = 0 then
                    inc(step)
                 end;
        stChDmg: if CheckNoDamage then inc(step) else step:= stDelay;
    stTurnReact: begin
                 if (not bBetweenTurns) and (not isInMultiShoot) then
                    begin
                    uStats.TurnReaction;
                    inc(step)
                    end else
                    inc(step, 2);
                 end;
   stAfterDelay: begin
                 if delay = 0 then
                    delay:= cInactDelay
                 else
                    dec(delay);

                 if delay = 0 then
                    inc(step)
                 end;
        stChWin: begin
                 CheckForWin;
                 inc(step)
                 end;
        stWater: begin
                 if TotalRounds = 17 then bWaterRising:= true;

                 if bWaterRising then
                    AddGear(0, 0, gtWaterUp, 0, _0, _0, 0);

                 inc(step)
                 end;
       stChWin2: begin
                 CheckForWin;
                 inc(step)
                 end;
       stHealth: begin
                 if (TotalRounds = 15) and (cHealthDecrease = 0) then
                    begin
                    cHealthDecrease:= 5;
                    AddCaption(trmsg[sidSuddenDeath], $FFFFFF, capgrpGameState)
                    end;

                 if (cHealthDecrease = 0)
                   or bBetweenTurns
                   or isInMultiShoot
                   or (TotalRounds = 0) then inc(step)
                    else begin
                    bBetweenTurns:= true;
                    HealthMachine;
                    step:= stChDmg
                    end
                 end;
        stSpawn: begin
                 if not isInMultiShoot then SpawnBoxOfSmth;
                 inc(step)
                 end;
        stNTurn: begin
                 if isInMultiShoot then isInMultiShoot:= false
                    else begin
                    ParseCommand('/nextturn', true);
                    SwitchHedgehog;

                    inc(step);

                    AfterSwitchHedgehog;
                    bBetweenTurns:= false
                    end;
                 step:= Low(step)
                 end;
        end;

if TurnTimeLeft > 0 then
		if CurrentHedgehog^.Gear <> nil then
			if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0)
				and not isInMultiShoot then
				begin
				if (TurnTimeLeft = 5000)
					and (CurrentHedgehog^.Gear <> nil)
					and ((CurrentHedgehog^.Gear^.State and gstAttacked) = 0) then PlaySound(sndHurry, false);
				dec(TurnTimeLeft)
				end;

if (not CurrentTeam^.ExtDriven) and
	((GameTicks and $FFFF) = $FFFF) then
	begin
	SendIPCTimeInc;
	inc(hiTicks) // we do not recieve a message for this
	end;

inc(GameTicks)
end;

procedure SetAllToActive;
var t: PGear;
begin
AllInactive:= false;
t:= GearsList;
while t <> nil do
      begin
      t^.Active:= true;
      t:= t^.NextGear
      end
end;

procedure SetAllHHToActive;
var t: PGear;
begin
AllInactive:= false;
t:= GearsList;
while t <> nil do
      begin
      if t^.Kind = gtHedgehog then t^.Active:= true;
      t:= t^.NextGear
      end
end;

procedure DrawHH(Gear: PGear);
var t: LongInt;
	amt: TAmmoType;
	hx, hy, m: LongInt;
	aAngle, dAngle: real;
	defaultPos, HatVisible: boolean;
begin
if (Gear^.State and gstHHDeath) <> 0 then
	begin
	DrawSprite(sprHHDeath, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 26 + WorldDy, Gear^.Pos);
	exit
	end;

defaultPos:= true;
HatVisible:= false;

if (Gear^.State and gstDrowning) <> 0 then
	begin
	DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
			hwSign(Gear^.dX),
			1,
			7,
			0);
	defaultPos:= false
	end else

if (Gear^.State and gstWinner) <> 0 then
	begin
	DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
			hwSign(Gear^.dX),
			2,
			0,
			0);
	defaultPos:= false
	end else

if (Gear^.State and gstHHDriven) <> 0 then
	begin
	hx:= hwRound(Gear^.X) + 1 + 8 * hwSign(Gear^.dX) + WorldDx;
	hy:= hwRound(Gear^.Y) - 2 + WorldDy;
	aangle:= Gear^.Angle * 180 / cMaxAngle - 90;

	if CurAmmoGear <> nil then
	begin
		case CurAmmoGear^.Kind of
			gtShotgunShot: begin
					if (CurAmmoGear^.State and gstAnimation <> 0) then
						DrawRotated(sprShotgun, hx, hy, hwSign(Gear^.dX), aangle)
					else
						DrawRotated(sprHandShotgun, hx, hy, hwSign(Gear^.dX), aangle);
					HatVisible:= true
				end;
			gtDEagleShot: DrawRotated(sprDEagle, hx, hy, hwSign(Gear^.dX), aangle);
			gtRope: begin
				if Gear^.X < CurAmmoGear^.X then
					begin
					dAngle:= 0;
					m:= 1
					end else
					begin
					dAngle:= 180;
					m:= -1
					end;
				DrawHedgehog(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,
						m,
						1,
						0,
						DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + dAngle);
				defaultPos:= false
				end;
			gtBlowTorch: begin
				DrawRotated(sprBlowTorch, hx, hy, hwSign(Gear^.dX), aangle);
				DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
						hwSign(Gear^.dX),
						3,
						PHedgehog(Gear^.Hedgehog)^.visStepPos div 2,
						0);
				defaultPos:= false
				end;
			gtShover: DrawRotated(sprHandBaseball, hx, hy, hwSign(Gear^.dX), aangle + 180);
			gtFirePunch: begin
				DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
						hwSign(Gear^.dX),
						1,
						4,
						0);
				defaultPos:= false
				end;
			gtPickHammer,
			gtTeleport: defaultPos:= false;
			gtWhip: begin
				DrawRotatedF(sprWhip,
						hwRound(Gear^.X) + 1 + WorldDx,
						hwRound(Gear^.Y) - 3 + WorldDy,
						1,
						hwSign(Gear^.dX),
						0);
				defaultPos:= false
				end;
			gtKamikaze: begin
				if CurAmmoGear^.Pos = 0 then
					DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
							hwSign(Gear^.dX),
							1,
							6,
							0)
				else
					DrawRotatedF(sprKamikaze,
							hwRound(Gear^.X) + WorldDx,
							hwRound(Gear^.Y) + WorldDy,
							CurAmmoGear^.Pos - 1,
							1,
							DxDy2Angle(Gear^.dY, Gear^.dX));

				defaultPos:= false
				end;
			gtSeduction: begin
				if CurAmmoGear^.Pos >= 6 then
					DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
							hwSign(Gear^.dX),
							2,
							2,
							0)
				else
					begin
					DrawRotatedF(sprDress,
							hwRound(Gear^.X) + WorldDx,
							hwRound(Gear^.Y) + WorldDy,
							CurAmmoGear^.Pos,
							hwSign(Gear^.dX),
							0);
					DrawSprite(sprCensored, hwRound(Gear^.X) - 32 + WorldDx, hwRound(Gear^.Y) - 20 + WorldDy, 0)
					end;
				defaultPos:= false
				end;
		end;

		case CurAmmoGear^.Kind of
			gtShotgunShot,
			gtDEagleShot,
			gtShover: begin
				DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
						hwSign(Gear^.dX),
						0,
						4,
						0);
				defaultPos:= false
			end
		end
	end else

	if ((Gear^.State and gstHHJumping) <> 0) then
	begin
	if ((Gear^.State and gstHHHJump) <> 0) then
		DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
			- hwSign(Gear^.dX),
			1,
			1,
			0)
		else
		DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
			hwSign(Gear^.dX),
			1,
			1,
			0);
	defaultPos:= false
	end else

	if (Gear^.Message and (gm_Left or gm_Right) <> 0) then
		begin
		DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
			hwSign(Gear^.dX),
			0,
			PHedgehog(Gear^.Hedgehog)^.visStepPos div 2,
			0);
		defaultPos:= false;
		HatVisible:= true
		end
	else

	if ((Gear^.State and gstAnimation) <> 0) then
		begin
		DrawRotatedF(Wavez[TWave(Gear^.Tag)].Sprite,
				hwRound(Gear^.X) + 1 + WorldDx,
				hwRound(Gear^.Y) - 3 + WorldDy,
				Gear^.Pos,
				hwSign(Gear^.dX),
				0.0);
		defaultPos:= false
		end
	else
	if ((Gear^.State and gstAttacked) = 0) then
	begin
		amt:= CurrentHedgehog^.Ammo^[CurrentHedgehog^.CurSlot, CurrentHedgehog^.CurAmmo].AmmoType;
		case amt of
			amBazooka,
			amMortar: DrawRotated(sprHandBazooka, hx, hy, hwSign(Gear^.dX), aangle);
			amRope: DrawRotated(sprHandRope, hx, hy, hwSign(Gear^.dX), aangle);
			amShotgun: DrawRotated(sprHandShotgun, hx, hy, hwSign(Gear^.dX), aangle);
			amDEagle: DrawRotated(sprHandDEagle, hx, hy, hwSign(Gear^.dX), aangle);
			amBlowTorch: DrawRotated(sprHandBlowTorch, hx, hy, hwSign(Gear^.dX), aangle);
		end;

		case amt of
			amAirAttack,
			amMineStrike: DrawRotated(sprHandAirAttack, hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) + WorldDy, hwSign(Gear^.dX), 0);
			amPickHammer: DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
						hwSign(Gear^.dX),
						1,
						2,
						0);
			amBlowTorch: DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
						hwSign(Gear^.dX),
						1,
						3,
						0);
			amTeleport: DrawRotatedF(sprTeleport, hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy, 0, hwSign(Gear^.dX), 0);
			amKamikaze: DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
						hwSign(Gear^.dX),
						1,
						5,
						0);
			amWhip: DrawRotatedF(sprWhip,
						hwRound(Gear^.X) + 1 + WorldDx,
						hwRound(Gear^.Y) - 3 + WorldDy,
						0,
						hwSign(Gear^.dX),
						0);
		else
			DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
				hwSign(Gear^.dX),
				0,
				4,
				0);
			
			HatVisible:= true;
			with PHedgehog(Gear^.Hedgehog)^ do
				if (HatTex <> nil)
				and (HatVisibility > 0) then
					DrawTextureF(HatTex,
						HatVisibility,
						hwRound(Gear^.X) + 1 + WorldDx,
						hwRound(Gear^.Y) - 8 + WorldDy,
						0,
						hwSign(Gear^.dX),
						32);
		end;

		case amt of
			amBaseballBat: DrawRotated(sprHandBaseball,
					hwRound(Gear^.X) + 1 - 4 * hwSign(Gear^.dX) + WorldDx,
					hwRound(Gear^.Y) + 6 + WorldDy, hwSign(Gear^.dX), aangle);
		end;

		defaultPos:= false
	end
end else // not gstHHDriven
	begin
	if (Gear^.Damage > 0)
	and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then
		begin
		DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
			hwSign(Gear^.dX),
			2,
			1,
			Gear^.DirAngle);
		defaultPos:= false
		end else

	if ((Gear^.State and gstHHJumping) <> 0) then
		begin
		if ((Gear^.State and gstHHHJump) <> 0) then
			DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
				- hwSign(Gear^.dX),
				1,
				1,
				0)
			else
			DrawHedgehog(hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy,
				hwSign(Gear^.dX),
				1,
				1,
				0);
			defaultPos:= false
		end;
	end;

with PHedgehog(Gear^.Hedgehog)^ do
	begin
	if defaultPos then
		begin
		DrawRotatedF(sprHHIdle,
			hwRound(Gear^.X) + 1 + WorldDx,
			hwRound(Gear^.Y) - 3 + WorldDy,
			(RealTicks div 128 + Gear^.Pos) mod 19,
			hwSign(Gear^.dX),
			0);
		HatVisible:= true;
		end;

	if HatVisible then
		if HatVisibility < 1.0 then
			HatVisibility:= HatVisibility + 0.2
		else
	else
		if HatVisibility > 0.0 then
			HatVisibility:= HatVisibility - 0.2;
	
	if (HatTex <> nil)
	and (HatVisibility > 0) then
		if DefaultPos then
			DrawTextureF(HatTex,
				HatVisibility,
				hwRound(Gear^.X) + 1 + WorldDx,
				hwRound(Gear^.Y) - 8 + WorldDy,
				(RealTicks div 128 + Gear^.Pos) mod 19,
				hwSign(Gear^.dX),
				32)
		else
			DrawTextureF(HatTex,
				HatVisibility,
				hwRound(Gear^.X) + 1 + WorldDx,
				hwRound(Gear^.Y) - 8 + WorldDy,
				0,
				hwSign(Gear^.dX),
				32);
	end;


with PHedgehog(Gear^.Hedgehog)^ do
	begin
	if ((Gear^.State and not gstWinner) = 0)
		or (bShowFinger and ((Gear^.State and gstHHDriven) <> 0)) then
	begin
	t:= hwRound(Gear^.Y) - cHHRadius - 12 + WorldDy;
	if (cTagsMask and 1) <> 0 then
		begin
		dec(t, HealthTagTex^.h + 2);
		DrawCentered(hwRound(Gear^.X) + WorldDx, t, HealthTagTex)
		end;
	if (cTagsMask and 2) <> 0 then
		begin
		dec(t, NameTagTex^.h + 2);
		DrawCentered(hwRound(Gear^.X) + WorldDx, t, NameTagTex)
		end;
	if (cTagsMask and 4) <> 0 then
		begin
		dec(t, Team^.NameTagTex^.h + 2);
		DrawCentered(hwRound(Gear^.X) + WorldDx, t, Team^.NameTagTex)
		end
	end;
	if (Gear^.State and gstHHDriven) <> 0 then // Current hedgehog
		begin
		if bShowFinger and ((Gear^.State and gstHHDriven) <> 0) then
			DrawSprite(sprFinger, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 64 + WorldDy,
						GameTicks div 32 mod 16);

		if (Gear^.State and gstDrowning) = 0 then
			if (Gear^.State and gstHHThinking) <> 0 then
				DrawSprite(sprQuestion, hwRound(Gear^.X) - 10 + WorldDx, hwRound(Gear^.Y) - cHHRadius - 34 + WorldDy, 0)
				else
				if ShowCrosshair and ((Gear^.State and (gstAttacked or gstAnimation)) = 0) then
					begin
					if ((Gear^.State and gstHHHJump) <> 0) then m:= -1 else m:= 1;
					DrawRotatedTex(Team^.CrosshairTex,
							12, 12,
							Round(hwRound(Gear^.X) + hwSign(Gear^.dX) * m * Sin(Gear^.Angle*pi/cMaxAngle) * 80) + WorldDx,
							Round(hwRound(Gear^.Y) - Cos(Gear^.Angle*pi/cMaxAngle) * 80) + WorldDy, 0,
							hwSign(Gear^.dX) * (Gear^.Angle * 180.0) / cMaxAngle)
					end
			end
	end
end;

procedure DrawGears;
var Gear, HHGear: PGear;
    i: Longword;
    roplen: LongInt;

    procedure DrawRopeLine(X1, Y1, X2, Y2: LongInt);
    var  eX, eY, dX, dY: LongInt;
         i, sX, sY, x, y, d: LongInt;
         b: boolean;
    begin
    if (X1 = X2) and (Y1 = Y2) then
       begin
       OutError('WARNING: zero length rope line!', false);
       exit
       end;
    eX:= 0;
    eY:= 0;
    dX:= X2 - X1;
    dY:= Y2 - Y1;

    if (dX > 0) then sX:= 1
    else
      if (dX < 0) then
         begin
         sX:= -1;
         dX:= -dX
         end else sX:= dX;

    if (dY > 0) then sY:= 1
       else
    if (dY < 0) then
       begin
       sY:= -1;
       dY:= -dY
       end else sY:= dY;

    if (dX > dY) then d:= dX
                 else d:= dY;

    x:= X1;
    y:= Y1;

    for i:= 0 to d do
        begin
        inc(eX, dX);
        inc(eY, dY);
        b:= false;
        if (eX > d) then
           begin
           dec(eX, d);
           inc(x, sX);
           b:= true
           end;
        if (eY > d) then
           begin
           dec(eY, d);
           inc(y, sY);
           b:= true
           end;
        if b then
           begin
           inc(roplen);
           if (roplen mod 4) = 0 then DrawSprite(sprRopeNode, x - 2, y - 2, 0)
           end
       end
    end;

begin
Gear:= GearsList;
while Gear<>nil do
      begin
      case Gear^.Kind of
       gtAmmo_Bomb: DrawRotated(sprBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
        gtHedgehog: DrawHH(Gear);
    gtAmmo_Grenade: DrawRotated(sprGrenade, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
       gtHealthTag,
     gtSmallDamage: if Gear^.Tex <> nil then DrawCentered(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.Tex);
           gtGrave: DrawSurfSprite(hwRound(Gear^.X) + WorldDx - 16, hwRound(Gear^.Y) + WorldDy - 16, 32, (GameTicks shr 7) and 7, PHedgehog(Gear^.Hedgehog)^.Team^.GraveTex);
             gtUFO: DrawSprite(sprUFO, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 16 + WorldDy, (GameTicks shr 7) mod 4);
      gtPickHammer: DrawSprite(sprPHammer, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 50 + LongInt(((GameTicks shr 5) and 1) * 2) + WorldDy, 0);
            gtRope: begin
                    roplen:= 0;
                    if RopePoints.Count > 0 then
                       begin
                       i:= 0;
                       while i < Pred(RopePoints.Count) do
                             begin
                             DrawRopeLine(hwRound(RopePoints.ar[i].X) + WorldDx, hwRound(RopePoints.ar[i].Y) + WorldDy,
                                          hwRound(RopePoints.ar[Succ(i)].X) + WorldDx, hwRound(RopePoints.ar[Succ(i)].Y) + WorldDy);
                             inc(i)
                             end;
                       DrawRopeLine(hwRound(RopePoints.ar[i].X) + WorldDx, hwRound(RopePoints.ar[i].Y) + WorldDy,
                                    hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy);
                       DrawRopeLine(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,
                                    hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.X) + WorldDx, hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.Y) + WorldDy);
                       DrawRotated(sprRopeHook, hwRound(RopePoints.ar[0].X) + WorldDx, hwRound(RopePoints.ar[0].Y) + WorldDy, 1, RopePoints.HookAngle)
                       end else
                       begin
                       DrawRopeLine(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,
                                    hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.X) + WorldDx, hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.Y) + WorldDy);
                       DrawRotated(sprRopeHook, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
                       end;
                    end;
      gtSmokeTrace: if Gear^.State < 8 then DrawSprite(sprSmokeTrace, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.State);
       gtExplosion: DrawSprite(sprExplosion50, hwRound(Gear^.X) - 32 + WorldDx, hwRound(Gear^.Y) - 32 + WorldDy, Gear^.State);
            gtMine: if ((Gear^.State and gstAttacking) = 0)or((Gear^.Timer and $3FF) < 420)
                       then DrawRotated(sprMineOff, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle)
                       else DrawRotated(sprMineOn, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
            gtCase: case Gear^.Pos of
                         posCaseAmmo  : DrawSprite(sprCase, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 16 + WorldDy, 0);
                         posCaseHealth: begin
                                        i:= (GameTicks shr 6) mod 64;
                                        if i > 12 then i:= 0;
                                        DrawSprite(sprFAid, hwRound(Gear^.X) - 24 + WorldDx, hwRound(Gear^.Y) - 24 + WorldDy, i);
                                        end;
                         end;
        gtDynamite: DrawSprite2(sprDynamite, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 25 + WorldDy, Gear^.Tag and 1, Gear^.Tag shr 1);
     gtClusterBomb: DrawRotated(sprClusterBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
         gtCluster: DrawSprite(sprClusterParticle, hwRound(Gear^.X) - 8 + WorldDx, hwRound(Gear^.Y) - 8 + WorldDy, 0);
           gtFlame: DrawSprite(sprFlame, hwRound(Gear^.X) - 8 + WorldDx, hwRound(Gear^.Y) - 8 + WorldDy,(GameTicks div 128 + Gear^.Angle) mod 8);
       gtParachute: DrawSprite(sprParachute, hwRound(Gear^.X) - 24 + WorldDx, hwRound(Gear^.Y) - 48 + WorldDy, 0);
       gtAirAttack: if Gear^.Tag > 0 then DrawSprite(sprAirplane, hwRound(Gear^.X) - 60 + WorldDx, hwRound(Gear^.Y) - 25 + WorldDy, 0)
                                     else DrawSprite(sprAirplane, hwRound(Gear^.X) - 60 + WorldDx, hwRound(Gear^.Y) - 25 + WorldDy, 1);
         gtAirBomb: DrawRotated(sprAirBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
        gtTeleport: begin
                    HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;
                    DrawRotatedF(sprTeleport, hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy, Gear^.Pos, hwSign(HHGear^.dX), 0);
                    DrawRotatedF(sprTeleport, hwRound(HHGear^.X) + 1 + WorldDx, hwRound(HHGear^.Y) - 3 + WorldDy, 11 - Gear^.Pos, hwSign(HHGear^.dX), 0);
                    end;
        gtSwitcher: DrawSprite(sprSwitch, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 56 + WorldDy, (GameTicks shr 6) mod 12);
          gtTarget: DrawSprite(sprTarget, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 16 + WorldDy, 0);
          gtMortar: DrawRotated(sprMortar, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
          gtCake: if Gear^.Pos = 6 then
                     DrawRotatedf(sprCakeWalk, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, (GameTicks div 40) mod 6, hwSign(Gear^.dX), Gear^.DirAngle + hwSign(Gear^.dX) * 90)
                  else
                     DrawRotatedf(sprCakeDown, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 5 - Gear^.Pos, hwSign(Gear^.dX), 0);
       gtSeduction: if Gear^.Pos >= 6 then DrawSprite(sprSeduction, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 16 + WorldDy, 0);
      gtWatermelon: DrawRotatedf(sprWatermelon, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, 0, Gear^.DirAngle);
      gtMelonPiece: DrawRotatedf(sprWatermelon, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 1, 0, Gear^.DirAngle);
     gtHellishBomb: DrawRotated(sprHellishBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
      gtEvilTrace: if Gear^.State < 8 then DrawSprite(sprEvilTrace, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.State);
         end;
      Gear:= Gear^.NextGear
      end;
end;

procedure FreeGearsList;
var t, tt: PGear;
begin
tt:= GearsList;
GearsList:= nil;
while tt<>nil do
      begin
      t:= tt;
      tt:= tt^.NextGear;
      Dispose(t)
      end;
end;

procedure AddMiscGears;
var i: LongInt;
begin
AddGear(0, 0, gtATStartGame, 0, _0, _0, 2000);
if (GameFlags and gfForts) = 0 then
   for i:= 0 to Pred(cLandAdditions) do
       FindPlace(AddGear(0, 0, gtMine, 0, _0, _0, 0), false, 0, 2048);
end;

procedure doMakeExplosion(X, Y, Radius: LongInt; Mask: LongWord);
var Gear: PGear;
    dmg, dmgRadius: LongInt;
begin
TargetPoint.X:= NoPointX;
{$IFDEF DEBUGFILE}if Radius > 3 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')');{$ENDIF}
if (Radius > 10) then AddGear(X, Y, gtExplosion, 0, _0, _0, 0);
if (Mask and EXPLAutoSound) <> 0 then PlaySound(sndExplosion, false);
if (Mask and EXPLAllDamageInRadius)=0 then dmgRadius:= Radius shl 1
                                      else dmgRadius:= Radius;
Gear:= GearsList;
while Gear <> nil do
	begin
	dmg:= dmgRadius  + cHHRadius div 2 - hwRound(Distance(Gear^.X - int2hwFloat(X), Gear^.Y - int2hwFloat(Y)));
	if (dmg > 1) and
		((Gear^.State and gstNoDamage) = 0) then
		begin
		dmg:= min(dmg div 2, Radius);
		case Gear^.Kind of
			gtHedgehog,
				gtMine,
				gtCase,
				gtTarget,
				gtFlame: begin
						{$IFDEF DEBUGFILE}AddFileLog('Damage: ' + inttostr(dmg));{$ENDIF}
						if (Mask and EXPLNoDamage) = 0 then
							begin
							inc(Gear^.Damage, dmg);
							if Gear^.Kind = gtHedgehog then
								AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), dmg, Gear)
							end;
						if ((Mask and EXPLDoNotTouchHH) = 0) or (Gear^.Kind <> gtHedgehog) then
							begin
							DeleteCI(Gear);
							Gear^.dX:= Gear^.dX + SignAs(_0_005 * dmg + cHHKick, Gear^.X - int2hwFloat(X));
							Gear^.dY:= Gear^.dY + SignAs(_0_005 * dmg + cHHKick, Gear^.Y - int2hwFloat(Y));
							Gear^.State:= (Gear^.State or gstMoving) and (not gstWinner);
							Gear^.Active:= true;
							FollowGear:= Gear
							end;
						end;
				gtGrave: begin
						Gear^.dY:= - _0_004 * dmg;
						Gear^.Active:= true;
						end;
			end;
		end;
	Gear:= Gear^.NextGear
	end;

if (Mask and EXPLDontDraw) = 0 then
	if (GameFlags and gfSolidLand) = 0 then DrawExplosion(X, Y, Radius);

uAIMisc.AwareOfExplosion(0, 0, 0)
end;

procedure ShotgunShot(Gear: PGear);
var t: PGear;
    dmg: LongInt;
begin
Gear^.Radius:= cShotgunRadius;
t:= GearsList;
while t <> nil do
	begin
	dmg:= min(Gear^.Radius + t^.Radius - hwRound(Distance(Gear^.X - t^.X, Gear^.Y - t^.Y)), 25);
	if dmg > 0 then
	case t^.Kind of
		gtHedgehog,
			gtMine,
			gtCase,
			gtTarget: begin
					inc(t^.Damage, dmg);

					if t^.Kind = gtHedgehog then
							AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), dmg, t);

					DeleteCI(t);
					t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
					t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
					t^.State:= t^.State or gstMoving;
					t^.Active:= true;
					FollowGear:= t
					end;
			gtGrave: begin
					t^.dY:= - _0_1;
					t^.Active:= true
					end;
		end;
	t:= t^.NextGear
	end;
if (GameFlags and gfSolidLand) = 0 then DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius)
end;

procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
var t: PGearArray;
    i: LongInt;
begin
t:= CheckGearsCollision(Ammo);
i:= t^.Count;
while i > 0 do
	begin
	dec(i);
	if (t^.ar[i]^.State and gstNoDamage) = 0 then
		case t^.ar[i]^.Kind of
			gtHedgehog,
			gtMine,
			gtTarget,
			gtCase: begin
					inc(t^.ar[i]^.Damage, Damage);

					if (t^.ar[i]^.Kind = gtHedgehog) and (Damage > 0) then
						AddDamageTag(hwRound(t^.ar[i]^.X), hwRound(t^.ar[i]^.Y), Damage, t^.ar[i]);

					DeleteCI(t^.ar[i]);
					t^.ar[i]^.dX:= Ammo^.dX * Power * _0_01;
					t^.ar[i]^.dY:= Ammo^.dY * Power * _0_01;
					t^.ar[i]^.Active:= true;
					t^.ar[i]^.State:= t^.ar[i]^.State or gstMoving;

					if TestCollisionXwithGear(t^.ar[i], hwSign(t^.ar[i]^.dX)) then
						begin
						if not (TestCollisionXwithXYShift(t^.ar[i], _0, -3, hwSign(t^.ar[i]^.dX))
							or TestCollisionYwithGear(t^.ar[i], -1)) then t^.ar[i]^.Y:= t^.ar[i]^.Y - _1;
						if not (TestCollisionXwithXYShift(t^.ar[i], _0, -2, hwSign(t^.ar[i]^.dX))
							or TestCollisionYwithGear(t^.ar[i], -1)) then t^.ar[i]^.Y:= t^.ar[i]^.Y - _1;
						if not (TestCollisionXwithXYShift(t^.ar[i], _0, -1, hwSign(t^.ar[i]^.dX))
							or TestCollisionYwithGear(t^.ar[i], -1)) then t^.ar[i]^.Y:= t^.ar[i]^.Y - _1;
						end;
					
					FollowGear:= t^.ar[i]
					end;
		end
	end;
SetAllToActive
end;

procedure AssignHHCoords;
var i, t, p, j: LongInt;
    ar: array[0..Pred(cMaxHHs)] of PGear;
    Count: Longword;
begin
if (GameFlags and gfForts) <> 0 then
	begin
	t:= 0;
	for p:= 0 to 1 do
		begin
		with ClansArray[p]^ do
			for j:= 0 to Pred(TeamsNumber) do
				with Teams[j]^ do
					for i:= 0 to cMaxHHIndex do
						with Hedgehogs[i] do
							if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
								begin
								FindPlace(Gear, false, t, t + 1024);
								Gear^.Pos:= GetRandom(19);
								Gear^.dX.isNegative:= p = 1;
								end;
		inc(t, 1024)
		end
	end else // mix hedgehogs
	begin
	Count:= 0;
	for p:= 0 to Pred(TeamsCount) do
		with TeamsArray[p]^ do
		begin
		for i:= 0 to cMaxHHIndex do
			with Hedgehogs[i] do
				if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
					begin
					ar[Count]:= Gear;
					inc(Count)
					end;
		end;

	while (Count > 0) do
		begin
		i:= GetRandom(Count);
		FindPlace(ar[i], false, 0, 2048);
		ar[i]^.dX.isNegative:= ar[i]^.X > _1024;
		ar[i]^.Pos:= GetRandom(19);
		ar[i]:= ar[Count - 1];
		dec(Count)
		end
	end
end;

function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
var t: PGear;
begin
t:= GearsList;
rX:= sqr(rX);
rY:= sqr(rY);
while t <> nil do
      begin
      if (t <> Gear) and (t^.Kind = Kind) then
         if not((hwSqr(Gear^.X - t^.X) / rX + hwSqr(Gear^.Y - t^.Y) / rY) > _1) then
            exit(t);
      t:= t^.NextGear
      end;
CheckGearNear:= nil
end;

procedure AmmoFlameWork(Ammo: PGear);
var t: PGear;
begin
t:= GearsList;
while t <> nil do
	begin
	if (t^.Kind = gtHedgehog) and (t^.Y < Ammo^.Y) then
		if not (hwSqr(Ammo^.X - t^.X) + hwSqr(Ammo^.Y - t^.Y - int2hwFloat(cHHRadius)) * 2 > _2) then
			begin
			inc(t^.Damage, 5);
			t^.dX:= t^.dX + (t^.X - Ammo^.X) * _0_02;
			t^.dY:= - _0_25;
			t^.Active:= true;
			DeleteCI(t);
			FollowGear:= t
			end;
	t:= t^.NextGear
	end;
end;

function CheckGearsNear(mX, mY: LongInt; Kind: TGearsType; rX, rY: LongInt): PGear;
var t: PGear;
begin
t:= GearsList;
rX:= sqr(rX);
rY:= sqr(rY);
while t <> nil do
      begin
      if t^.Kind in Kind then
         if not (hwSqr(int2hwFloat(mX) - t^.X) / rX + hwSqr(int2hwFloat(mY) - t^.Y) / rY > _1) then
            exit(t);
      t:= t^.NextGear
      end;
CheckGearsNear:= nil
end;

function CountGears(Kind: TGearType): Longword;
var t: PGear;
    Result: Longword;
begin
Result:= 0;
t:= GearsList;
while t <> nil do
      begin
      if t^.Kind = Kind then inc(Result);
      t:= t^.NextGear
      end;
CountGears:= Result
end;

procedure SpawnBoxOfSmth;
var t: LongInt;
    i: TAmmoType;
begin
if (cCaseFactor = 0) or
   (CountGears(gtCase) >= 5) or
   (getrandom(cCaseFactor) <> 0) then exit;

FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
case getrandom(2) of
     0: begin
        FollowGear^.Health:= 25;
        FollowGear^.Pos:= posCaseHealth
        end;
     1: begin
        t:= 0;
        for i:= Low(TAmmoType) to High(TAmmoType) do
            inc(t, Ammoz[i].Probability);
        t:= GetRandom(t);
        i:= Low(TAmmoType);
        dec(t, Ammoz[i].Probability);
        while t >= 0 do
          begin
          inc(i);
          dec(t, Ammoz[i].Probability)
          end;
        PlaySound(sndReinforce, false);
        FollowGear^.Pos:= posCaseAmmo;
        FollowGear^.State:= Longword(i)
        end;
     end;
FindPlace(FollowGear, true, 0, 2048)
end;

procedure FindPlace(Gear: PGear; withFall: boolean; Left, Right: LongInt);

    function CountNonZeroz(x, y, r: LongInt): LongInt;
    var i: LongInt;
        Result: LongInt;
    begin
    Result:= 0;
    if (y and $FFFFFC00) = 0 then
      for i:= max(x - r, 0) to min(x + r, 2043) do
        if Land[y, i] <> 0 then inc(Result);
    CountNonZeroz:= Result
    end;

var x: LongInt;
    y, sy: LongInt;
    ar: array[0..511] of TPoint;
    ar2: array[0..1023] of TPoint;
    cnt, cnt2: Longword;
    delta: LongInt;
begin
delta:= 250;
cnt2:= 0;
repeat
  x:= Left + LongInt(GetRandom(Delta));
  repeat
     inc(x, Delta);
     cnt:= 0;
     y:= -Gear^.Radius * 2;
     while y < 1023 do
        begin
        repeat
         inc(y, 2);
        until (y > 1023) or (CountNonZeroz(x, y, Gear^.Radius - 1) = 0);
        sy:= y;
        repeat
          inc(y);
        until (y > 1023) or (CountNonZeroz(x, y, Gear^.Radius - 1) <> 0);
        if (y - sy > Gear^.Radius * 2)
        and (y < 1023)
        and (CheckGearsNear(x, y - Gear^.Radius, [gtHedgehog, gtMine, gtCase], 110, 110) = nil) then
           begin
           ar[cnt].X:= x;
           if withFall then ar[cnt].Y:= sy + Gear^.Radius
                       else ar[cnt].Y:= y - Gear^.Radius;
           inc(cnt)
           end;
        inc(y, 45)
        end;
     if cnt > 0 then
        with ar[GetRandom(cnt)] do
          begin
          ar2[cnt2].x:= x;
          ar2[cnt2].y:= y;
          inc(cnt2)
          end
  until (x + Delta > Right);
dec(Delta, 60)
until (cnt2 > 0) or (Delta < 70);
if cnt2 > 0 then
   with ar2[GetRandom(cnt2)] do
      begin
      Gear^.X:= int2hwFloat(x);
      Gear^.Y:= int2hwFloat(y);
      {$IFDEF DEBUGFILE}
      AddFileLog('Assigned Gear coordinates (' + inttostr(x) + ',' + inttostr(y) + ')');
      {$ENDIF}
      end
   else
   begin
   OutError('Can''t find place for Gear', false);
   DeleteGear(Gear)
   end
end;

initialization

finalization
FreeGearsList;

end.