
enum eMinigunAnims
{
	anim_Minigun_Fire1 = anim_weaponAttack1,
	anim_Minigun_Fire2 = anim_weaponAttack2
}

// there's a fair bit of commented code from when first shot was fired after shot period,
// rather than immediately when beginning to fire from idle
final class TurokMinigun : TurokWeapon
{
	int Period1 = 8, Period2 = 5;
	int tracer = 0, shotTick, altCount;
	float recoilPitch, recoilRoll;
//	kVec3 oldShellLoc;
	bool bFiring = false, bWindDown = false;

	TurokMinigun( kWeapon@ a )
	{
		super( a );
		BobDamping = 0.975f;
	}

	void AnimProperties( int&out first, int&out last, float&out rate, bool&out bLoop )
	{
		rate = 60; bLoop = false;
		switch ( PlayingID() )
		{
			case anim_weaponSwapIn:  first =   0; last =  33; break;
			case anim_weaponSwapOut: first = 318; last = 337; break;
			case anim_Minigun_Fire2:                          rate *= 60.0f/45;
			case anim_Minigun_Fire1: first =  34; last =  60; bLoop = true; break;
			default: /* idle */
			      if ( bWindDown ) { first =  61; last = 119; }
			                  else { first = 119; last = 318; bLoop = true; } break;
		}
	}

	void AnimEnd()
	{
		switch ( PlayingID() )
		{
			// if idle "ended," that means we were winding down, and should reset IdleTick for actual idle anim to start
			case anim_weaponWalk: case anim_weaponRun: case anim_weaponIdle:
				bWindDown = false;
				IdleTick = self.GameTicks();
				return; // don't end anim
		}
		TurokWeapon::AnimEnd();
	}

	void OnBeginFire()
	{
		PlayAnim( anim_Minigun_Fire1 );
		StopWeaponSound(); // stop wind-down sound
		self.PlaySound( "UT/sounds/Minigun/Fire1.ksnd" );
//		shotTick = self.GameTicks();
		altCount = 0;
		bFiring = true;
	}

	void OnTick()
	{
		TurokWeapon::OnTick();
		AmmoLED();
		Recoil();
		if ( self.Owner().Locked() )
		{
			self.StopLoopingSounds(); // stop fire sound
			return;
		}
		switch ( PlayingID() )
		{
			// stop firing when out of ammo, or fire is released
			case anim_Minigun_Fire1: case anim_Minigun_Fire2:
//				if ( !HasAmmo() || (!bFire && altCount > 0) )
				if ( !HasAmmo() && TimeToFire() )
					WindDown( true );
				break;
			// check if a forced swap-out (climbing, swimming) interrupted firing
			case anim_weaponSwapOut:
			// jumping off a wall at just the wrong time can prevent forced swap-out from playing
			// so cancel everything during swap-in, too
			case anim_weaponSwapIn:
				if ( bFiring ) WindDown( false );
				// make sure this is cleared in case we're put down during wind-down
				bWindDown = false;
		}
	}

	void Recoil()
	{
		float f = self.GameTicks() - shotTick;
		if ( f > Period2 ) return;
		f = 1 - f / Period2;
		f = Math::Sin( Math::pi * f ) * f;
		OwnerP().RecoilPitch() = f * recoilPitch;
		OwnerP().Roll()        = f * recoilRoll;
	}

	void WindDown( bool bPlayAnim )
	{
		self.StopLoopingSounds(); // stop fire sound
		PlayWeaponSound( "UT/sounds/Minigun/WindDown.ksnd" );
		bWindDown = bPlayAnim;
		if ( bFiring ) PlayAnim( anim_weaponIdle );
		bFiring = false;
	}

	bool TimeToFire()
	{
		int period;
		switch ( PlayingID() )
		{
			case anim_Minigun_Fire1:  period = Period1;  break;
			case anim_Minigun_Fire2:  period = Period2;  break;
		}
		return self.GameTicks() - shotTick >= period;
	}

	void OnFire()
	{
/*		int period;
		float spread;
		switch ( PlayingID() )
		{
			case anim_Minigun_Fire1:
				self.PlaySound( "UT/sounds/Minigun/Fire1.ksnd" );
				period = 8;  spread = MinigunSpread1;  break;
			case anim_Minigun_Fire2:
				self.PlaySound( "UT/sounds/Minigun/Fire2.ksnd" );
				period = 5;  spread = MinigunSpread2;  break;
			default: // winding down
				if ( HasAmmo() && bFire )
					OnBeginFire();
				return;
		}
		if ( self.GameTicks() - shotTick < period )
			return;
*/		if ( !HasAmmo() || !TimeToFire() )
			return;
		if ( !bFire && altCount > 0 )
		{
			WindDown( true );
			return;
		}
		shotTick = self.GameTicks();

//		ShootBullet( spread );
		switch ( PlayingID() )
		{
			case anim_Minigun_Fire1:
				self.PlaySound( "UT/sounds/Minigun/Fire1.ksnd" );
				ShootBullet( MinigunSpread1 );
				break;
			case anim_Minigun_Fire2:
				self.PlaySound( "UT/sounds/Minigun/Fire2.ksnd" );
				// mid, low, mid, high (tracer)
/*				switch ( tracer % 4 )
				{
					case 1:   ShootBullet( MinigunSpread2 * 0.5f  );  break;
					case 0:
					case 2:   ShootBullet( MinigunSpread2 * 0.75f );  break;
					default:  ShootBullet( MinigunSpread2         );  break;
				}
*/				if ( tracer % 2 == 0 )
					ShootBullet( MinigunSpread2 * 0.75f );
				else
					ShootBullet( MinigunSpread2 );
				break;
		}

		switch ( PlayingID() )
		{
		case anim_Minigun_Fire1:
			// speed up after 4 slow shots
			if ( bFire2 )
			{
				if ( ++altCount >= 4 )
				{
					PlayAnim( anim_Minigun_Fire2 );
					self.StopLoopingSounds(); // stop slow fire sound
					self.PlaySound( "UT/sounds/Minigun/Fire2.ksnd" );
					// prevent new fire sound from playing just a "blip" if player releases alt fire too fast
					altCount = 0;
				}
			}
			// count up to 2 shots while firing, so pressing alt fire later can speed up sooner than if starting from still
			else if ( altCount < 2 )
				++altCount;
			break;
		case anim_Minigun_Fire2:
			// slow back down if alt fire is released, but primary fire is still pressed
			if ( !bFire2 && bFire1 )
			{
				PlayAnim( anim_Minigun_Fire1 );
				self.StopLoopingSounds(); // stop fast fire sound
				self.PlaySound( "UT/sounds/Minigun/Fire1.ksnd" );
			}
			altCount = 2;
			break;
		}
	}

	void ShootBullet( float spread )
	{
		kStr fx = "UT/fx/Minigun/Flash";
		self.FireProjectile( fx + (1 + Math::RandMax(9)) +".kfx", 0, 0, 0 );

//		kVec3 offset = kVec3( 13.5f, 27.648f, -8.5f ) + self.RenderModel().Offset() * 0.2f;
		kVec3 offset = kVec3( 13, 27.648f, -11.5f ) + self.RenderModel().Offset() * 0.3f;
		self.FireProjectile( "UT/fx/Minigun/Shell.kfx", offset.x, offset.y, offset.z, true );
//		SpawnShell();

		offset.Set( 8, 25.6f, -7 );
		kVec3 dir = kVec3(0,1,0).Randomize( spread ) * OwnerP().Rotation();
		kQuat q = ToQuat( dir );
		fx = "UT/fx/Minigun/Bullet";
		if ( UDamage() ) fx += "_Amp";
		SpawnProj( fx + ".kfx", offset, q );
		if ( ++tracer % 4 == 0 )
			SpawnProj( "UT/fx/Tracer.kfx", offset, q );

		UseAmmo( 1 );

		self.RunFxEvent( "MinigunFire" );
		OwnerP().LoudNoiseAlert();
		recoilPitch = -0.015f * (1 + Math::RandFloat()) / 2;
		recoilRoll  = -0.015f * (1 + Math::RandFloat()) / 2 * (tracer % 2);
	}

/*	from Turok+, but doesn't seem to work right
	void SpawnShell()
	{
		kVec3 eye = EyePos();
		kQuat rot = OwnerP().Rotation();
		kVec3 offset = kVec3( 13, 27.648f, -9 ) + self.RenderModel().Offset() * 0.2f;
		kVec3 v = offset;

		if ( altCount > 0 )
		{
			// get old shell location relative to current eye pos and rotation
			oldShellLoc -= eye;
			kQuat q = rot;
			q.w *= -1;
			oldShellLoc = oldShellLoc * q;
			// pick spawn point randomly between ejection port and old shell loc
			float f = Math::RandFloat();
			v.Lerp( oldShellLoc, f );
		}

		self.FireProjectile( "UT/fx/Minigun/Shell.kfx", v.x, v.y, v.z, true );

		// extrapolate shell location after shot period
		float dt = 5.0f/60;//ShotPeriod * GameSpeed();
		offset += kVec3( 1, 1, 0.5f ).Normalize() * 276 * dt;
		oldShellLoc = eye + offset*rot;
		oldShellLoc.z -= 0.5f * 768 * dt*dt;
	}*/
}
