#include "../idlib/precompiled.h"
#pragma hdrstop

#include "Game_local.h"

CLASS_STATES_DECLARATION ( idPlayer )

	// Wait States
	STATE ( "Wait_Alive",					idPlayer::State_Wait_Alive )
	STATE ( "Wait_ReloadAnim",				idPlayer::State_Wait_ReloadAnim )

	// Torso States
	STATE ( "Torso_Idle",					idPlayer::State_Torso_Idle )
	STATE ( "Torso_IdleThink",				idPlayer::State_Torso_IdleThink )
	STATE ( "Torso_Teleport",				idPlayer::State_Torso_Teleport )
	STATE ( "Torso_RaiseWeapon",			idPlayer::State_Torso_RaiseWeapon )
	STATE ( "Torso_LowerWeapon",			idPlayer::State_Torso_LowerWeapon )
	STATE ( "Torso_Fire",					idPlayer::State_Torso_Fire )
	STATE ( "Torso_Fire_Windup",			idPlayer::State_Torso_Fire_Windup )
	STATE ( "Torso_Reload",					idPlayer::State_Torso_Reload )
	STATE ( "Torso_Pain",					idPlayer::State_Torso_Pain )
	STATE ( "Torso_Dead",					idPlayer::State_Torso_Dead )
	STATE ( "Torso_Emote",					idPlayer::State_Torso_Emote )

	// Leg States
	STATE ( "Legs_Idle",					idPlayer::State_Legs_Idle )
	STATE ( "Legs_Crouch",					idPlayer::State_Legs_Crouch )
	STATE ( "Legs_Uncrouch",				idPlayer::State_Legs_Uncrouch )
	STATE ( "Legs_Run_Forward",				idPlayer::State_Legs_Run_Forward )
	STATE ( "Legs_Run_Backward",			idPlayer::State_Legs_Run_Backward )
	STATE ( "Legs_Run_Left",				idPlayer::State_Legs_Run_Left )
	STATE ( "Legs_Run_Right",				idPlayer::State_Legs_Run_Right )
	STATE ( "Legs_Walk_Forward",			idPlayer::State_Legs_Walk_Forward )
	STATE ( "Legs_Walk_Backward",			idPlayer::State_Legs_Walk_Backward )
	STATE ( "Legs_Walk_Left",				idPlayer::State_Legs_Walk_Left )
	STATE ( "Legs_Walk_Right",				idPlayer::State_Legs_Walk_Right )
	STATE ( "Legs_Crouch_Idle",				idPlayer::State_Legs_Crouch_Idle )
	STATE ( "Legs_Crouch_Forward",			idPlayer::State_Legs_Crouch_Forward )
	STATE ( "Legs_Crouch_Backward",			idPlayer::State_Legs_Crouch_Backward )
	STATE ( "Legs_Fall",					idPlayer::State_Legs_Fall )
	STATE ( "Legs_Jump",					idPlayer::State_Legs_Jump )
	STATE ( "Legs_Fall",					idPlayer::State_Legs_Fall )
	STATE ( "Legs_Land",					idPlayer::State_Legs_Land )
	STATE ( "Legs_Dead",					idPlayer::State_Legs_Dead )

END_CLASS_STATES

/*
================
idPlayer::State_Torso_Idle
================
*/
stateResult_t idPlayer::State_Torso_Idle ( const stateParms_t& parms ) {
	if ( SRESULT_WAIT == State_Torso_IdleThink ( parms ) ) {
		PlayCycle ( ANIMCHANNEL_TORSO, "idle", parms.blendFrames  );
		PostAnimState ( ANIMCHANNEL_TORSO, "Torso_IdleThink", parms.blendFrames );
	}
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Torso_IdleThink
================
*/
stateResult_t idPlayer::State_Torso_IdleThink ( const stateParms_t& parms ) {
	if ( pfl.teleport ) {
		PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Teleport", 0 );
		return SRESULT_DONE;
	}
	if ( pfl.weaponFired ) {
		PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Fire", 0 );
		return SRESULT_DONE;
	}
	
	if( pfl.attackHeld && weapon && weapon->wfl.hasWindupAnim)	{
		PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Fire_Windup", 0 );
		return SRESULT_DONE;
	}
	
	if ( pfl.attackHeld && HasAnim ( ANIMCHANNEL_TORSO, "startfire" ) ) {
   		PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Fire_StartFire", 2 );
   		return SRESULT_DONE;
   	}
   	if ( pfl.pain ) {
  		PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Pain", 0 );
  		return SRESULT_DONE;
   	}
	if ( emote != PE_NONE ) {
		PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Emote", 5 );
		return SRESULT_DONE;
	}

   	
   	return SRESULT_WAIT;
}

/*
================
idPlayer::State_Torso_Teleport
================
*/
stateResult_t idPlayer::State_Torso_Teleport ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:		
			pfl.teleport = false;
			PlayAnim ( ANIMCHANNEL_TORSO, "teleport", parms.blendFrames );
			return SRESULT_STAGE ( STAGE_WAIT );
		
		case STAGE_WAIT:
			if ( AnimDone ( ANIMCHANNEL_TORSO, 4 ) ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Torso_RaiseWeapon
================
*/
stateResult_t idPlayer::State_Torso_RaiseWeapon ( const stateParms_t& parms ) {
	PlayAnim( ANIMCHANNEL_TORSO, "raise", parms.blendFrames );
	PostAnimState ( ANIMCHANNEL_TORSO, "Wait_TorsoAnim", 3 );
	PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 3 );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Torso_LowerWeapon
================
*/
stateResult_t idPlayer::State_Torso_LowerWeapon ( const stateParms_t& parms ) {	
	PlayAnim( ANIMCHANNEL_TORSO, "lower", parms.blendFrames );
	PostAnimState ( ANIMCHANNEL_TORSO, "Wait_TorsoAnim", 3 );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Torso_Fire
================
*/
stateResult_t idPlayer::State_Torso_Fire ( const stateParms_t& parms ) {
	enum {
		TORSO_FIRE_INIT,
		TORSO_FIRE_WAIT,
		TORSO_FIRE_AIM,
		TORSO_FIRE_AIMWAIT
	};

	switch ( parms.stage ) {
		// Start the firing sequence
		case TORSO_FIRE_INIT:
 			PlayAnim ( ANIMCHANNEL_TORSO, "fire", parms.blendFrames );
			pfl.weaponFired = false;
			return SRESULT_STAGE(TORSO_FIRE_WAIT);
		
		// Wait for the firing animation to be finished
		case TORSO_FIRE_WAIT:
			if ( pfl.weaponFired ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Fire", parms.blendFrames );
				return SRESULT_DONE;
			}
			if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {	
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", parms.blendFrames );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
			
		// Keep the gun aimed but dont shoot
		case TORSO_FIRE_AIM:
			PlayAnim ( ANIMCHANNEL_TORSO, "aim", 3 );
			return SRESULT_STAGE(TORSO_FIRE_AIMWAIT);
			
		// Keep the gun aimed as long as the attack button is held and nothing is firing
		case TORSO_FIRE_AIMWAIT:
			if ( pfl.weaponFired ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Fire", 3 );
				return SRESULT_DONE;
			} else if ( !pfl.attackHeld ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", parms.blendFrames );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_DONE;
}		

/*
================
idPlayer::State_Torso_Fire_Windup
================
*/
stateResult_t idPlayer::State_Torso_Fire_Windup ( const stateParms_t& parms ) {
	enum {
		TORSO_FIRE_INIT,
		TORSO_WINDUP_WAIT,
		TORSO_FIRE_LOOP,
		TORSO_FIRE_WAIT,
		TORSO_WINDDOWN_START,
		TORSO_WINDDOWN_WAIT,
		TORSO_FIRE_AIM,
		TORSO_FIRE_AIMWAIT
	};

	switch ( parms.stage ) {
		// Start the firing sequence
		case TORSO_FIRE_INIT:
			//jshepard: HACK for now we're blending here, but we need to support charge up anims here
 			PlayAnim ( ANIMCHANNEL_TORSO, "fire", 4 );
			pfl.weaponFired = false;
			return SRESULT_STAGE(TORSO_WINDUP_WAIT);

		// wait for the windup anim to end, or the attackHeld to be false.
		case TORSO_WINDUP_WAIT:
			if( !pfl.attackHeld )	{
				return SRESULT_STAGE( TORSO_WINDDOWN_START );
			}
			if( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames )) {
				return SRESULT_STAGE( TORSO_FIRE_LOOP );								
			}
			return SRESULT_WAIT;
		
		// play the firing loop
		case TORSO_FIRE_LOOP:
			if( !pfl.attackHeld )	{
				return SRESULT_STAGE( TORSO_WINDDOWN_START );
			}
			PlayAnim ( ANIMCHANNEL_TORSO, "fire", parms.blendFrames );
			return SRESULT_STAGE( TORSO_FIRE_WAIT );
			
		// loop the fire anim
		case TORSO_FIRE_WAIT:
			if( !pfl.attackHeld )	{
				return SRESULT_STAGE( TORSO_WINDDOWN_START );
			}
			//loop the attack anim
			if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {	
				PlayAnim ( ANIMCHANNEL_TORSO, "fire", parms.blendFrames );
				return SRESULT_STAGE( TORSO_FIRE_WAIT );
			}
			return SRESULT_WAIT;

		//wind down
		case TORSO_WINDDOWN_START:
			//jshepard: HACK just blend back into idle for now, we could support winddown anims here.
			PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
			pfl.weaponFired = false;
			return SRESULT_DONE;
		
	}
	return SRESULT_DONE;
}		
/*
================
idPlayer::State_Torso_Reload
================
*/
stateResult_t idPlayer::State_Torso_Reload ( const stateParms_t& parms ) {
	enum {
		TORSO_RELOAD_START,
		TORSO_RELOAD_STARTWAIT,
		TORSO_RELOAD_LOOP,
		TORSO_RELOAD_LOOPWAIT,
		TORSO_RELOAD_WAIT,
		TORSO_RELOAD_END
	};
	switch ( parms.stage ) {
		// Start the reload by either playing the reload animation or the reload_start animation
		case TORSO_RELOAD_START:
			if ( HasAnim ( ANIMCHANNEL_TORSO, "reload_start" ) ) {
				PlayAnim ( ANIMCHANNEL_TORSO, "reload_start", parms.blendFrames );
				return SRESULT_STAGE(TORSO_RELOAD_STARTWAIT);
			}
			
			PlayAnim( ANIMCHANNEL_TORSO, "reload", parms.blendFrames );
			return SRESULT_STAGE(TORSO_RELOAD_WAIT);
			
		// Wait for the reload_start animation to finish and transition to reload_loop
		case TORSO_RELOAD_STARTWAIT:
			if ( AnimDone ( ANIMCHANNEL_TORSO, 0 ) ) {
				return SRESULT_STAGE(TORSO_RELOAD_LOOP);
			} else if ( pfl.weaponFired ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 3 );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
			
		// Play a single reload from the reload loop
		case TORSO_RELOAD_LOOP:
			if ( !pfl.reload ) {
				return SRESULT_STAGE(TORSO_RELOAD_END);
			}
			PlayAnim ( ANIMCHANNEL_TORSO, "reload_loop", 0 );
			return SRESULT_STAGE(TORSO_RELOAD_LOOPWAIT);

		// Wait for the looping reload to finish and either start a new one or end the reload
		case TORSO_RELOAD_LOOPWAIT:
			if ( AnimDone ( ANIMCHANNEL_TORSO, 0 ) ) {
				return SRESULT_STAGE(TORSO_RELOAD_LOOP);
			} else if ( pfl.weaponFired ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 3 );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;

		// End the reload
		case TORSO_RELOAD_END:
			if ( pfl.weaponFired ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 3 );
				return SRESULT_DONE;
			}

			PlayAnim( ANIMCHANNEL_TORSO, "reload_end", 3 );
			return SRESULT_STAGE(TORSO_RELOAD_WAIT);
		
		// Wait for reload to finish (called by both reload_end and reload)
		case TORSO_RELOAD_WAIT:
			if ( pfl.weaponFired || AnimDone ( ANIMCHANNEL_TORSO, 3 ) ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 3 );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Torso_Pain
================
*/
stateResult_t idPlayer::State_Torso_Pain ( const stateParms_t& parms ) {
	PlayAnim ( ANIMCHANNEL_TORSO, painAnim.Length()?painAnim:"pain", parms.blendFrames );
	PostAnimState ( ANIMCHANNEL_TORSO, "Wait_TorsoAnim", 4  );
	PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Torso_Dead
================
*/
stateResult_t idPlayer::State_Torso_Dead ( const stateParms_t& parms ) {
	PostAnimState ( ANIMCHANNEL_TORSO, "Wait_Alive", 0 );
	PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Torso_Emote
================
*/
stateResult_t idPlayer::State_Torso_Emote ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			if( emote == PE_GRAB_A ) {
				PlayAnim ( ANIMCHANNEL_TORSO, "grab_a", parms.blendFrames );
			} else if( emote == PE_GRAB_B ) {
				PlayAnim ( ANIMCHANNEL_TORSO, "grab_b", parms.blendFrames );
			} else if( emote == PE_SALUTE ) {
				PlayAnim ( ANIMCHANNEL_TORSO, "salute", parms.blendFrames );
			} else if( emote == PE_CHEER ) {
				PlayAnim ( ANIMCHANNEL_TORSO, "cheer", parms.blendFrames );
			} else if( emote == PE_TAUNT ) {
				PlayAnim ( ANIMCHANNEL_TORSO, "taunt", parms.blendFrames );
			}

			emote = PE_NONE;
			
			return SRESULT_STAGE ( STAGE_WAIT );

		case STAGE_WAIT:
			if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
				PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", parms.blendFrames );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}

/*
===============================================================================

	AI Leg States

===============================================================================
*/

/*
================
idPlayer::IsLegsIdle
================
*/
bool idPlayer::IsLegsIdle( bool crouching ) const {
	return ( ( pfl.crouch == crouching ) && pfl.onGround && ( pfl.forward == pfl.backward ) && ( pfl.strafeLeft == pfl.strafeRight ) );
}

/*
================
idPlayer::State_Legs_Idle
================
*/
stateResult_t idPlayer::State_Legs_Idle( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			if ( pfl.crouch ) {
				PostAnimState( ANIMCHANNEL_LEGS, "Legs_Crouch", parms.blendFrames );
				return SRESULT_DONE;
			}
			IdleAnim( ANIMCHANNEL_LEGS, "idle", parms.blendFrames );
			return SRESULT_STAGE( STAGE_WAIT );
		
		case STAGE_WAIT:
			// #34 - have a delay while idle before going to something else if a ground flag change happens
			// this makes the legs animation stay in idle state when the ground flag value flip-flops fast
			if ( prevOnGround != pfl.onGround ) {
				// when explicitely jumping, don't impose any delay
				bool explicitJump = ( !pfl.onGround && usercmd.upmove > 0 );
				if ( !explicitJump ) {
					ignoreOnGroundUntilFrame = gameLocal.framenum + 5;
				}
				prevOnGround = pfl.onGround;
			}
			if ( gameLocal.framenum <= ignoreOnGroundUntilFrame ) {
				return SRESULT_WAIT;
			}

			// If now crouching go back to idle so we can transition to crouch 
			if ( pfl.crouch ) {
 				PostAnimState( ANIMCHANNEL_LEGS, "Legs_Crouch", 4 );
				return SRESULT_DONE;
			} else if ( pfl.jump ) {
				PostAnimState( ANIMCHANNEL_LEGS, "Legs_Jump", 4 );
				return SRESULT_DONE;
			} else if ( !pfl.onGround ) {
				PostAnimState( ANIMCHANNEL_LEGS, "Legs_Fall", 4 );
				return SRESULT_DONE;
			} else if ( pfl.forward && !pfl.backward ) {
				if( usercmd.buttons & BUTTON_RUN ) {
 					PlayCycle( ANIMCHANNEL_LEGS, "run_forward", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Forward", parms.blendFrames );
				} else {
					PlayCycle( ANIMCHANNEL_LEGS, "walk_forward", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Forward", parms.blendFrames );
				}
				
				return SRESULT_DONE;
			} else if ( pfl.backward && !pfl.forward ) {
				if ( usercmd.buttons & BUTTON_RUN ) {
					PlayCycle( ANIMCHANNEL_LEGS, "run_backwards", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Backward", parms.blendFrames );
				} else {
					PlayCycle( ANIMCHANNEL_LEGS, "walk_backwards", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Backward", parms.blendFrames );
				}
			
				return SRESULT_DONE;
			} else if ( pfl.strafeLeft && !pfl.strafeRight ) {
				if ( usercmd.buttons & BUTTON_RUN ) {
					PlayCycle( ANIMCHANNEL_LEGS, "run_strafe_left", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Left", parms.blendFrames );
				} else {
					PlayCycle( ANIMCHANNEL_LEGS, "walk_left", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Left", parms.blendFrames );
				}
				
				return SRESULT_DONE;
			} else if ( pfl.strafeRight && !pfl.strafeLeft ) {
				if ( usercmd.buttons & BUTTON_RUN ) {
					PlayCycle( ANIMCHANNEL_LEGS, "run_strafe_right", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Right", parms.blendFrames );
				} else {
					PlayCycle( ANIMCHANNEL_LEGS, "walk_right", parms.blendFrames );
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Right", parms.blendFrames );
				}

				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
		
	}
	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Legs_Crouch_Idle
================
*/
stateResult_t idPlayer::State_Legs_Crouch_Idle( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:			
			if ( !pfl.crouch ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Uncrouch", parms.blendFrames );
				return SRESULT_DONE;
			}
			PlayCycle ( ANIMCHANNEL_LEGS, "crouch", parms.blendFrames );
			return SRESULT_STAGE ( STAGE_WAIT );
		
		case STAGE_WAIT:
			if ( !pfl.crouch || pfl.jump ) {
 				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Uncrouch", 4 );
				return SRESULT_DONE;
			} else if ( (pfl.forward && !pfl.backward) || (pfl.strafeLeft != pfl.strafeRight) ) {				
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Crouch_Forward", parms.blendFrames );
				return SRESULT_DONE;
			} else if ( pfl.backward && !pfl.forward ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Crouch_Backward", parms.blendFrames );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Legs_Crouch
================
*/
stateResult_t idPlayer::State_Legs_Crouch ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			PlayAnim ( ANIMCHANNEL_LEGS, "crouch_down", parms.blendFrames );
			return SRESULT_STAGE ( STAGE_WAIT );
		
		case STAGE_WAIT:
			if ( !IsLegsIdle ( true ) || AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Crouch_Idle", parms.blendFrames );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Legs_Uncrouch
================
*/
stateResult_t idPlayer::State_Legs_Uncrouch ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			PlayAnim ( ANIMCHANNEL_LEGS, "crouch_up", parms.blendFrames );
			return SRESULT_STAGE ( STAGE_WAIT );
		
		case STAGE_WAIT:
			if ( !IsLegsIdle ( false ) || AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Legs_Run_Forward
================
*/
stateResult_t idPlayer::State_Legs_Run_Forward ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && !pfl.backward && pfl.forward ) {
		if( usercmd.buttons & BUTTON_RUN ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "walk_forward", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Forward", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Run_Backward
================
*/
stateResult_t idPlayer::State_Legs_Run_Backward ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && !pfl.forward && pfl.backward ) {
		if( usercmd.buttons & BUTTON_RUN ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "walk_backwards", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Backward", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Run_Left
================
*/
stateResult_t idPlayer::State_Legs_Run_Left ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && (pfl.forward == pfl.backward) && pfl.strafeLeft && !pfl.strafeRight ) {
		if( usercmd.buttons & BUTTON_RUN ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "walk_left", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Left", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Run_Right
================
*/
stateResult_t idPlayer::State_Legs_Run_Right ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && (pfl.forward == pfl.backward) && pfl.strafeRight && !pfl.strafeLeft ) {
		if( usercmd.buttons & BUTTON_RUN ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "walk_right", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Walk_Right", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Walk_Forward
================
*/
stateResult_t idPlayer::State_Legs_Walk_Forward ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && !pfl.backward && pfl.forward ) {
		if( !(usercmd.buttons & BUTTON_RUN) ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "run_forward", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Forward", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Walk_Backward
================
*/
stateResult_t idPlayer::State_Legs_Walk_Backward ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && !pfl.forward && pfl.backward ) {
		if( !(usercmd.buttons & BUTTON_RUN) ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "run_backwards", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Backward", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Walk_Left
================
*/
stateResult_t idPlayer::State_Legs_Walk_Left ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && (pfl.forward == pfl.backward) && pfl.strafeLeft && !pfl.strafeRight ) {
		if( !(usercmd.buttons & BUTTON_RUN) ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "run_strafe_left", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Left", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Walk_Right
================
*/
stateResult_t idPlayer::State_Legs_Walk_Right ( const stateParms_t& parms ) {
	if ( !pfl.jump && pfl.onGround && !pfl.crouch && (pfl.forward == pfl.backward) && pfl.strafeRight && !pfl.strafeLeft ) {
		if( !(usercmd.buttons & BUTTON_RUN) ) {
			return SRESULT_WAIT;
		} else {
			PlayCycle( ANIMCHANNEL_LEGS, "run_strafe_right", parms.blendFrames );
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Run_Right", parms.blendFrames );
			return SRESULT_DONE;
		}
	}
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Crouch_Forward
================
*/
stateResult_t idPlayer::State_Legs_Crouch_Forward ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			PlayCycle( ANIMCHANNEL_LEGS, "crouch_walk", parms.blendFrames );
			return SRESULT_STAGE ( STAGE_WAIT );
		
		case STAGE_WAIT:
			if ( !pfl.jump && pfl.onGround && pfl.crouch && ((!pfl.backward && pfl.forward) || (pfl.strafeLeft != pfl.strafeRight)) ) {
				return SRESULT_WAIT;
			}
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Crouch_Idle", 2 );
			return SRESULT_DONE;
	}
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Crouch_Backward
================
*/
stateResult_t idPlayer::State_Legs_Crouch_Backward ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			PlayCycle( ANIMCHANNEL_LEGS, "crouch_walk_backward", parms.blendFrames );
			return SRESULT_STAGE ( STAGE_WAIT );
		
		case STAGE_WAIT:
			if ( !pfl.jump && pfl.onGround && pfl.crouch && !pfl.forward && pfl.backward ) {
				return SRESULT_WAIT;
			}
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Crouch_Idle", parms.blendFrames );
			return SRESULT_DONE;
	}
	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Legs_Jump
================
*/
stateResult_t idPlayer::State_Legs_Jump ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			// prevent infinite recursion
			pfl.jump = false;
			if ( pfl.run ) {
				PlayAnim ( ANIMCHANNEL_LEGS, "run_jump", parms.blendFrames );
			} else {
				PlayAnim ( ANIMCHANNEL_LEGS, "jump", parms.blendFrames );
			}
			return SRESULT_STAGE ( STAGE_WAIT );
		
		case STAGE_WAIT:
			if ( pfl.onGround ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Land", 4 );
				return SRESULT_DONE;
			}
			if ( AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
				if ( pfl.crouch ) {
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Crouch", 4 );
				} else {
					PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Fall", 4 );
				}
				return SRESULT_DONE;
			}			
			return SRESULT_WAIT;
	}
 	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Legs_Fall
================
*/
stateResult_t idPlayer::State_Legs_Fall ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			if ( pfl.onGround ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Land", 2 );
				return SRESULT_DONE;
			}
			PlayCycle ( ANIMCHANNEL_LEGS, "fall", parms.blendFrames );
			return SRESULT_STAGE ( STAGE_WAIT );			
		case STAGE_WAIT:
			if ( pfl.onGround ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Land", 2 );
				return SRESULT_DONE;
			}
			if ( pfl.crouch ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Crouch", 2 );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Legs_Land
================
*/
stateResult_t idPlayer::State_Legs_Land ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_WAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			if ( IsLegsIdle ( false ) && ( pfl.hardLanding || pfl.softLanding ) ) {					
				if ( pfl.hardLanding ) {
					PlayAnim ( ANIMCHANNEL_LEGS, "hard_land", parms.blendFrames );
				} else {
					PlayAnim ( ANIMCHANNEL_LEGS, "soft_land", parms.blendFrames );
				}
				return SRESULT_STAGE ( STAGE_WAIT );
			}
			PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
			return SRESULT_DONE;
			
		case STAGE_WAIT:
			if ( !IsLegsIdle ( false ) || AnimDone ( ANIMCHANNEL_LEGS, parms.blendFrames ) ) {
				PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", parms.blendFrames );
				return SRESULT_DONE;
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}

/*
================
idPlayer::State_Legs_Dead
================
*/
stateResult_t idPlayer::State_Legs_Dead ( const stateParms_t& parms ) {
	PostAnimState ( ANIMCHANNEL_LEGS, "Wait_Alive", 0 );
	PostAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Wait_Alive

Waits until the player is alive again.
================
*/
stateResult_t idPlayer::State_Wait_Alive ( const stateParms_t& parms ) {
	if ( pfl.dead ) {
		return SRESULT_WAIT;
	}
	return SRESULT_DONE;
}

/*
================
idPlayer::State_Wait_ReloadAnim
================
*/
stateResult_t idPlayer::State_Wait_ReloadAnim ( const stateParms_t& parms ) {
	// The gun firing can cancel any of the relod animations
	if ( pfl.weaponFired ) {		
		SetAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", parms.blendFrames );
		return SRESULT_DONE;
	}
	
	// wait for the animation to finish
	if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
		return SRESULT_DONE;
	}
	
	return SRESULT_WAIT;
}
