#include "PlayableCharacter.h"

#include "Mix/HID.h"
#include "Mix/Scene.h"

namespace Utility{

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::定数
////////////////////////////////////////////////////////////////////////////////////////////////////

const Float32 PlayableCharacter::TRANSITION_RATIO_MIN = MIX_FLOAT_EPSILON;
const Float32 PlayableCharacter::TRANSITION_RATIO_MAX = 1.0f - MIX_FLOAT_EPSILON;

const Float32 PlayableCharacter::LF_STAND_TO_WALK = 0.2f;
const Float32 PlayableCharacter::LF_WALK_TO_STAND = 0.1f;
const Float32 PlayableCharacter::LF_WALK_TO_RUN = 0.99f;
const Float32 PlayableCharacter::LF_RUN_TO_WALK = 0.9f;

const Float32 PlayableCharacter::AERIAL_FRICTION = 0.0f;
const Float32 PlayableCharacter::GROUND_FRICTION = 0.3f;

const Float32 PlayableCharacter::GRAVITY = 0.88f;
const Float32 PlayableCharacter::WALK_TO_JUMP_POWER = 0.03f;
const Float32 PlayableCharacter::RUN_TO_JUMP_POWER = 0.08f;
const Float32 PlayableCharacter::JUMP_XZ_ATTEN = 0.97f;
const Float32 PlayableCharacter::JUMP_TO_FALLING_FRAME = 22.0f;
const Float32 PlayableCharacter::JUMP_TO_FALLING_XZ_MAG = 4.0f;
const Float32 PlayableCharacter::JUMP_TO_FALLING_Y = 0.4f;
const Float32 PlayableCharacter::LANDING_ATTEN = 0.9f;

const Float32 PlayableCharacter::GROUND_DAMAGE_VELOCITY = 0.1f;
const Float32 PlayableCharacter::AERIAL_DAMAGE_VELOCITY = 0.2f;

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::StandTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::StandTask::StandTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td[4];
	Utility::Motion::StateMachine::STATE_DESC sd;

	td[0].state = PlayableCharacter::STATE_BODY_WALK;
	td[0].bForce = False;
	td[0].timeLength = 0.2f;

	td[1].state = PlayableCharacter::STATE_BODY_JUMP;
	td[1].bForce = False;
	td[1].timeLength = 0.1f;

	td[2].state = PlayableCharacter::STATE_BODY_BACKSTEP;
	td[2].bForce = False;
	td[2].timeLength = 0.2f;

	td[3].state = PlayableCharacter::STATE_BODY_DAMAGE_GROUND;
	td[3].bForce = True;
	td[3].timeLength = 0.1f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = L"Stand";
	sd.loopCount = Mix::Scene::MOTION_INFINITE_LOOP;
	sd.transitions = td;
	sd.transitionCount = 4;
	sd.bInherit = True;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_STAND, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::StandTask::~StandTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::StandTask::OnUpdate( Float32 dt )
{
	Mix::Vector2 leftStick = m_pGamepad->GetStickState( Mix::HID::GAMEPAD_LEFT );

	if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_JUMP ) & Mix::HID::PRESSED )
	{
		if( m_pState->GetMotionStatePtr()->GetTransitionRatio() >= PlayableCharacter::TRANSITION_RATIO_MAX )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_JUMP );
		}
	}
	else if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_BACKSTEP ) & Mix::HID::PRESSED )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_BACKSTEP );
	}
	else
	{
		if( leftStick.GetLength() >= PlayableCharacter::LF_STAND_TO_WALK )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_WALK );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::WalkTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::WalkTask::WalkTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td[4];
	Utility::Motion::StateMachine::STATE_DESC sd;

	td[0].state = PlayableCharacter::STATE_BODY_STAND;
	td[0].bForce = False;
	td[0].timeLength = 0.3f;

	td[1].state = PlayableCharacter::STATE_BODY_RUN;
	td[1].bForce = False;
	td[1].timeLength = 0.2f;

	td[2].state = PlayableCharacter::STATE_BODY_JUMP;
	td[2].bForce = False;
	td[2].timeLength = 0.1f;

	td[3].state = PlayableCharacter::STATE_BODY_DAMAGE_GROUND;
	td[3].bForce = True;
	td[3].timeLength = 0.1f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = L"Walk";
	sd.loopCount = Mix::Scene::MOTION_INFINITE_LOOP;
	sd.transitions = td;
	sd.transitionCount = 4;
	sd.bInherit = True;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_WALK, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::WalkTask::~WalkTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::WalkTask::OnUpdate( Float32 dt )
{
	Mix::Vector2 leftStick = m_pGamepad->GetStickState( Mix::HID::GAMEPAD_LEFT );
	Float32 len = leftStick.GetLength();

	if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_JUMP ) & Mix::HID::PRESSED )
	{
		if( m_pState->GetMotionStatePtr()->GetTransitionRatio() >= PlayableCharacter::TRANSITION_RATIO_MAX )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_JUMP );
		}
	}
	else
	{
		if( len >= PlayableCharacter::LF_WALK_TO_RUN )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_RUN );
		}
		else if( len <= PlayableCharacter::LF_WALK_TO_STAND )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_STAND );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::RunTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::RunTask::RunTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td[3];
	Utility::Motion::StateMachine::STATE_DESC sd;

	td[0].state = PlayableCharacter::STATE_BODY_WALK;
	td[0].bForce = False;
	td[0].timeLength = 0.3f;

	td[1].state = PlayableCharacter::STATE_BODY_JUMP;
	td[1].bForce = False;
	td[1].timeLength = 0.1f;

	td[2].state = PlayableCharacter::STATE_BODY_DAMAGE_GROUND;
	td[2].bForce = True;
	td[2].timeLength = 0.1f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = L"Run";
	sd.loopCount = Mix::Scene::MOTION_INFINITE_LOOP;
	sd.transitions = td;
	sd.transitionCount = 3;
	sd.bInherit = True;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_RUN, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::RunTask::~RunTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::RunTask::OnUpdate( Float32 dt )
{
	if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_JUMP ) & Mix::HID::PRESSED )
	{
		if( m_pState->GetMotionStatePtr()->GetTransitionRatio() >= PlayableCharacter::TRANSITION_RATIO_MAX )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_JUMP );
		}
	}
	else
	{
		Mix::Vector2 leftStick = m_pGamepad->GetStickState( Mix::HID::GAMEPAD_LEFT );
		Float32 len = leftStick.GetLength();

		if( len <= PlayableCharacter::LF_RUN_TO_WALK )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_WALK );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::BackstepTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::BackstepTask::BackstepTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel ) : StateTask( pSubject, pChannel )
{
	Utility::Motion::StateMachine::TRANSITION_DESC td;
	Utility::Motion::StateMachine::STATE_DESC sd;

	td.state = PlayableCharacter::STATE_BODY_STAND;
	td.bForce = False;
	td.timeLength = 0.2f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = L"BackStep";
	sd.loopCount = 0;
	sd.transitions = &td;
	sd.transitionCount = 1;
	sd.bInherit = True;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_BACKSTEP, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::BackstepTask::~BackstepTask( void )
{
}

void PlayableCharacter::BackstepTask::OnUpdate( Float32 dt )
{
	if( m_pState->GetMotionStatePtr()->GetCondition() == 0 )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_STAND );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::JumpTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::JumpTask::JumpTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel ) : StateTask( pSubject, pChannel )
{
	Utility::Motion::StateMachine::TRANSITION_DESC td[2];
	Utility::Motion::StateMachine::STATE_DESC sd;

	td[0].state = PlayableCharacter::STATE_BODY_FALLING;
	td[0].bForce = False;
	td[0].timeLength = 0.5f;

	td[1].state = PlayableCharacter::STATE_BODY_DAMAGE_GROUND;
	td[1].bForce = True;
	td[1].timeLength = 0.1f;

	sd.channelMask = 0x00000001;
	sd.pMotionName = L"Jump";
	sd.loopCount = 0;
	sd.transitions = td;
	sd.transitionCount = 2;
	sd.bInherit = True;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_JUMP, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::JumpTask::~JumpTask( void )
{
}

void PlayableCharacter::JumpTask::OnBegin( void )
{
	m_InitalLinearVelocity = m_pSubject->BakeIntoLinearVelocity();
}

void PlayableCharacter::JumpTask::OnUpdate( Float32 dt )
{
	if( m_pState->GetMotionStatePtr()->GetFrame() >= PlayableCharacter::JUMP_TO_FALLING_FRAME )
	{
		m_pSubject->ApplyLinearVelocity( m_InitalLinearVelocity );
		m_pSubject->Jump();

		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_FALLING );
	}
	else
	{
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::FallingTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::FallingTask::FallingTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel ) : StateTask( pSubject, pChannel )
{
	Utility::Motion::StateMachine::TRANSITION_DESC td[2];
	Utility::Motion::StateMachine::STATE_DESC sd;
	Utility::Motion::StateMachine::State* pState;

	td[0].state = PlayableCharacter::STATE_BODY_LANDING;
	td[0].bForce = True;
	td[0].timeLength = 0.1f;

	td[1].state = PlayableCharacter::STATE_BODY_DAMAGE_AIR;
	td[1].bForce = True;
	td[1].timeLength = 0.1f;

	sd.channelMask = 0x00000001;
	sd.pMotionName = L"Falling";
	sd.loopCount = Mix::Scene::MOTION_INFINITE_LOOP;
	sd.transitions = td;
	sd.transitionCount = 2;
	sd.bInherit = True;

	pState = pChannel->AddState( PlayableCharacter::STATE_BODY_FALLING, sd );
	pState->SetTaskPtr( this );
}

PlayableCharacter::FallingTask::~FallingTask( void )
{
}

void PlayableCharacter::FallingTask::OnUpdate( Float32 dt )
{
	if( m_pSubject->OnGround() == True )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_LANDING );
	}
	else
	{
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::LandingTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::LandingTask::LandingTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td[2];
	Utility::Motion::StateMachine::STATE_DESC sd;

	td[0].state = PlayableCharacter::STATE_BODY_STAND;
	td[0].bForce = False;
	td[0].timeLength = 0.3f;

	td[1].state = PlayableCharacter::STATE_BODY_RUN;
	td[1].bForce = False;
	td[1].timeLength = 1.0f;

	sd.channelMask = 0x00000001;
	sd.pMotionName = L"Landing";
	sd.loopCount = 0;
	sd.transitions = td;
	sd.transitionCount = 2;
	sd.bInherit = True;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_LANDING, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::LandingTask::~LandingTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::LandingTask::OnUpdate( Float32 dt )
{
	if( m_pState->GetMotionStatePtr()->GetCondition() == 0 )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_STAND );
	}
	else
	{
		Mix::Vector2 leftStick = m_pGamepad->GetStickState( Mix::HID::GAMEPAD_LEFT );

		if( ( m_pState->GetMotionStatePtr()->GetTransitionRatio() >= PlayableCharacter::TRANSITION_RATIO_MAX ) &&
			( leftStick.GetLength() > PlayableCharacter::LF_RUN_TO_WALK ) )
		{
			m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_RUN );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::DamageGroundTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::DamageGroundTask::DamageGroundTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel ) : StateTask( pSubject, pChannel )
{
	Utility::Motion::StateMachine::TRANSITION_DESC td;
	Utility::Motion::StateMachine::STATE_DESC sd;

	td.state = PlayableCharacter::STATE_BODY_STAND;
	td.bForce = False;
	td.timeLength = 0.5f;

	sd.channelMask = 0x00000001;
	sd.pMotionName = L"Damage_Ground";
	sd.loopCount = 0;
	sd.transitions = &td;
	sd.transitionCount = 1;
	sd.bInherit = False;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_DAMAGE_GROUND, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::DamageGroundTask::~DamageGroundTask( void )
{
}

void PlayableCharacter::DamageGroundTask::OnBegin( void )
{
	m_pSubject->ApplyLocalLinearVelocity( Mix::Vector3( 0.0f, 0.0f, GROUND_DAMAGE_VELOCITY ) );
}

void PlayableCharacter::DamageGroundTask::OnUpdate( Float32 dt )
{
	if( m_pState->GetMotionStatePtr()->GetCondition() == 0 )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_STAND );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::DamageAirTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::DamageAirTask::DamageAirTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel ) : StateTask( pSubject, pChannel )
{
	Utility::Motion::StateMachine::TRANSITION_DESC td[2];
	Utility::Motion::StateMachine::STATE_DESC sd;

	td[0].state = PlayableCharacter::STATE_BODY_FALLING;
	td[0].bForce = False;
	td[0].timeLength = 1.0f;
	td[1].state = PlayableCharacter::STATE_BODY_LANDING;
	td[1].bForce = True;
	td[1].timeLength = 0.1f;

	sd.channelMask = 0x00000001;
	sd.pMotionName = L"Damage_Air";
	sd.loopCount = 0;
	sd.transitions = &( td[0] );
	sd.transitionCount = 2;
	sd.bInherit = False;

	m_pState = pChannel->AddState( PlayableCharacter::STATE_BODY_DAMAGE_AIR, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::DamageAirTask::~DamageAirTask( void )
{
}

void PlayableCharacter::DamageAirTask::OnBegin( void )
{
	m_pSubject->ApplyLocalLinearVelocity( Mix::Vector3( 0.0f, 0.0f, AERIAL_DAMAGE_VELOCITY ) );
}

void PlayableCharacter::DamageAirTask::OnUpdate( Float32 dt )
{
	if( m_pState->GetMotionStatePtr()->GetTransitionRatio() >= PlayableCharacter::TRANSITION_RATIO_MAX )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_FALLING );
	}
	else if( m_pSubject->OnGround() == True )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_BODY_LANDING );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::UpperStandTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::UpperStandTask::UpperStandTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td;
	Utility::Motion::StateMachine::STATE_DESC sd;
	Utility::Motion::StateMachine::State* pState;

	td.state = PlayableCharacter::STATE_UPPER_READY;
	td.bForce = True;
	td.timeLength = 0.2f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = NULL;
	sd.loopCount = 0;
	sd.transitions = &td;
	sd.transitionCount = 1;
	sd.bInherit = True;

	pState = pChannel->AddState( PlayableCharacter::STATE_UPPER_STAND, sd );
	pState->SetTaskPtr( this );
}

PlayableCharacter::UpperStandTask::~UpperStandTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::UpperStandTask::OnUpdate( Float32 dt )
{
	if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_READY ) & Mix::HID::PRESSED )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_UPPER_READY );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::UpperReadyTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::UpperReadyTask::UpperReadyTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td[2];
	Utility::Motion::StateMachine::STATE_DESC sd;
	Utility::Motion::StateMachine::State* pState;

	td[0].state = PlayableCharacter::STATE_UPPER_STAND;
	td[0].bForce = True;
	td[0].timeLength = 0.2f;

	td[1].state = PlayableCharacter::STATE_UPPER_GUARD;
	td[1].bForce = True;
	td[1].timeLength = 0.2f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = L"Ready";
	sd.loopCount = 0;
	sd.transitions = td;
	sd.transitionCount = 2;
	sd.bInherit = True;

	pState = pChannel->AddState( PlayableCharacter::STATE_UPPER_READY, sd );
	pState->SetTaskPtr( this );
}

PlayableCharacter::UpperReadyTask::~UpperReadyTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::UpperReadyTask::OnUpdate( Float32 dt )
{
	if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_READY ) & Mix::HID::PRESSED )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_UPPER_STAND );
	}
	else if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_GUARD ) & Mix::HID::PRESSED )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_UPPER_GUARD );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::UpperGuardTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::UpperGuardTask::UpperGuardTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td;
	Utility::Motion::StateMachine::STATE_DESC sd;
	Utility::Motion::StateMachine::State* pState;

	td.state = PlayableCharacter::STATE_UPPER_READY;
	td.bForce = True;
	td.timeLength = 0.2f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = L"Guard";
	sd.loopCount = 0;
	sd.transitions = &td;
	sd.transitionCount = 1;
	sd.bInherit = True;

	pState = pChannel->AddState( PlayableCharacter::STATE_UPPER_GUARD, sd );
	pState->SetTaskPtr( this );
}

PlayableCharacter::UpperGuardTask::~UpperGuardTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::UpperGuardTask::OnUpdate( Float32 dt )
{
	if( m_pGamepad->GetButtonState( PlayableCharacter::BUTTON_GUARD ) == 0 )
	{
		m_pChannel->ChangeState( PlayableCharacter::STATE_UPPER_READY );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::ArmStandTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::ArmStandTask::ArmStandTask(	PlayableCharacter* pSubject,
												Utility::Motion::StateMachine::Channel* pChannel,
												Utility::Motion::StateMachine::Channel* pUpperChannel,
												UInt32 standStateID,
												UInt32 punchStateID,
												UInt32 punchButtonID,
												Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad ),
m_pUpperChannel( pUpperChannel ),
m_PunchButtonID( punchButtonID ),
m_PunchStateID( punchStateID )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td;
	Utility::Motion::StateMachine::STATE_DESC sd;
	Utility::Motion::StateMachine::State* pState;

	td.state = punchStateID;
	td.bForce = True;
	td.timeLength = 0.06f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = NULL;
	sd.loopCount = 0;
	sd.transitions = &td;
	sd.transitionCount = 1;
	sd.bInherit = True;

	pState = pChannel->AddState( standStateID, sd );
	pState->SetTaskPtr( this );
}

PlayableCharacter::ArmStandTask::~ArmStandTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::ArmStandTask::OnUpdate( Float32 dt )
{
	if( ( m_pUpperChannel->GetActiveStateID() != PlayableCharacter::STATE_UPPER_STAND ) &&
		( m_pGamepad->GetButtonState( m_PunchButtonID ) & Mix::HID::PRESSED ) )
	{
		m_pChannel->ChangeState( m_PunchStateID );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter::ArmPunchTask
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::ArmPunchTask::ArmPunchTask(	PlayableCharacter* pSubject,
												Utility::Motion::StateMachine::Channel* pChannel,
												const wchar_t* pMotionName,
												UInt32 standStateID,
												UInt32 punchStateID,
												UInt32 punchButtonID,
												Mix::HID::IGamepad* pGamepad ) : StateTask( pSubject, pChannel ),
m_pGamepad( pGamepad ),
m_PunchButtonID( punchButtonID ),
m_StandStateID( standStateID )
{
	MIX_ADD_REF( m_pGamepad );

	Utility::Motion::StateMachine::TRANSITION_DESC td;
	Utility::Motion::StateMachine::STATE_DESC sd;

	td.state = standStateID;
	td.bForce = True;
	td.timeLength = 0.06f;

	sd.channelMask = 0xFFFFFFFF;
	sd.pMotionName = pMotionName;
	sd.loopCount = 0;
	sd.transitions = &td;
	sd.transitionCount = 1;
	sd.bInherit = True;

	m_pState = pChannel->AddState( punchStateID, sd );
	m_pState->SetTaskPtr( this );
}

PlayableCharacter::ArmPunchTask::~ArmPunchTask( void )
{
	MIX_RELEASE( m_pGamepad );
}

void PlayableCharacter::ArmPunchTask::OnUpdate( Float32 dt )
{
	if( ( m_pGamepad->GetButtonState( m_PunchButtonID ) == 0 ) ||
		( m_pState->GetMotionStatePtr()->GetTransitionRatio() >= PlayableCharacter::TRANSITION_RATIO_MAX ) )
	{
		m_pChannel->ChangeState( m_StandStateID );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PlayableCharacter
////////////////////////////////////////////////////////////////////////////////////////////////////

PlayableCharacter::PlayableCharacter(	Mix::Scene::IUniversalCamera* pCamera,
										Mix::HID::IGamepad* pGamepad,
										Mix::Scene::IActorModel* pModel ) :
m_pGamepad( NULL ),
m_pCamera( NULL ),
m_pModel( NULL ),
m_pMotionMixer( NULL ),
m_pKChar( NULL ),
m_pStateMachine( new Utility::Motion::StateMachine() )
{
	MIX_ASSERT( pGamepad != NULL );
	MIX_ASSERT( pCamera != NULL );
	MIX_ASSERT( pModel != NULL );

	MIX_ADD_REF( pGamepad );
	m_pGamepad = pGamepad;

	MIX_ADD_REF( pCamera );
	m_pCamera = pCamera;

	MIX_ADD_REF( pModel );
	m_pModel = pModel;

	m_pModel->GetMotionMixer( &m_pMotionMixer );
	MIX_ASSERT( m_pMotionMixer != NULL );

	m_pModel->GetKinematicCharacter( &m_pKChar );
	MIX_ASSERT( m_pKChar != NULL );

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

	Mix::Scene::IActorDynamicsDirector* pDynamicsDirector = NULL;

	m_pModel->GetDynamicsDirector( &pDynamicsDirector );

	pDynamicsDirector->SetColliderMode( Mix::Scene::DC_KINEMATIC );
	pDynamicsDirector->SetCharacterMode( Mix::Scene::DKC_CAST );

	MIX_RELEASE( pDynamicsDirector );

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

	m_pKChar->SetGravity( 20.0f );
	m_pKChar->SetInitalJumpSpeed( 12.0f );

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

	Mix::Scene::IMotionController* pMotionController = NULL;
	Utility::Motion::StateMachine::Channel* pChannel = NULL;

	m_StateTaskList.reserve( PlayableCharacter::STATE_ID_MAX );

	if( m_pMotionMixer->GetControllerByName( L"Body", &pMotionController ) == True )
	{
		pChannel = m_pStateMachine->AddChannel( PlayableCharacter::CHANNEL_BODY, pMotionController );

		m_StateTaskList.push_back( new PlayableCharacter::StandTask( this, pChannel, pGamepad ) );
		m_StateTaskList.push_back( new PlayableCharacter::WalkTask( this, pChannel, pGamepad ) );
		m_StateTaskList.push_back( new PlayableCharacter::RunTask( this, pChannel, pGamepad ) );
		m_StateTaskList.push_back( new PlayableCharacter::BackstepTask( this, pChannel ) );
		m_StateTaskList.push_back( new PlayableCharacter::JumpTask( this, pChannel ) );
		m_StateTaskList.push_back( new PlayableCharacter::FallingTask( this, pChannel ) );
		m_StateTaskList.push_back( new PlayableCharacter::LandingTask( this, pChannel, pGamepad ) );
		m_StateTaskList.push_back( new PlayableCharacter::DamageGroundTask( this, pChannel ) );
		m_StateTaskList.push_back( new PlayableCharacter::DamageAirTask( this, pChannel ) );

		pChannel->ChangeState( PlayableCharacter::STATE_BODY_STAND );

		MIX_RELEASE( pMotionController );
	}

	if( m_pMotionMixer->GetControllerByName( L"Upper", &pMotionController ) == True )
	{
		pChannel = m_pStateMachine->AddChannel( PlayableCharacter::CHANNEL_UPPER, pMotionController );

		m_StateTaskList.push_back( new PlayableCharacter::UpperStandTask( this, pChannel, pGamepad ) );
		m_StateTaskList.push_back( new PlayableCharacter::UpperReadyTask( this, pChannel, pGamepad ) );
		m_StateTaskList.push_back( new PlayableCharacter::UpperGuardTask( this, pChannel, pGamepad ) );

		pChannel->ChangeState( PlayableCharacter::STATE_UPPER_STAND );

		MIX_RELEASE( pMotionController );
	}

	if( m_pMotionMixer->GetControllerByName( L"Arm_L", &pMotionController ) == True )
	{
		pChannel = m_pStateMachine->AddChannel( PlayableCharacter::CHANNEL_ARML, pMotionController );

		m_StateTaskList.push_back( new PlayableCharacter::ArmStandTask(
			this,
			pChannel,
			m_pStateMachine->GetChannel( PlayableCharacter::CHANNEL_UPPER ),
			PlayableCharacter::STATE_ARML_STAND,
			PlayableCharacter::STATE_ARML_PUNCH,
			PlayableCharacter::BUTTON_PUNCH_L,
			pGamepad ) );

		m_StateTaskList.push_back( new PlayableCharacter::ArmPunchTask(
			this,
			pChannel,
			L"Punch_L",
			PlayableCharacter::STATE_ARML_STAND,
			PlayableCharacter::STATE_ARML_PUNCH,
			PlayableCharacter::BUTTON_PUNCH_L,
			pGamepad ) );

		pChannel->ChangeState( PlayableCharacter::STATE_ARML_STAND );

		MIX_RELEASE( pMotionController );
	}

	if( m_pMotionMixer->GetControllerByName( L"Arm_R", &pMotionController ) == True )
	{
		pChannel = m_pStateMachine->AddChannel( PlayableCharacter::CHANNEL_ARMR, pMotionController );

		m_StateTaskList.push_back( new PlayableCharacter::ArmStandTask(
			this,
			pChannel,
			m_pStateMachine->GetChannel( PlayableCharacter::CHANNEL_UPPER ),
			PlayableCharacter::STATE_ARMR_STAND,
			PlayableCharacter::STATE_ARMR_PUNCH,
			PlayableCharacter::BUTTON_PUNCH_R,
			pGamepad ) );

		m_StateTaskList.push_back( new PlayableCharacter::ArmPunchTask(
			this,
			pChannel,
			L"Punch_R",
			PlayableCharacter::STATE_ARMR_STAND,
			PlayableCharacter::STATE_ARMR_PUNCH,
			PlayableCharacter::BUTTON_PUNCH_R,
			pGamepad ) );

		pChannel->ChangeState( PlayableCharacter::STATE_ARMR_STAND );

		MIX_RELEASE( pMotionController );
	}
}

PlayableCharacter::~PlayableCharacter( void )
{
	Dispose();
}

void PlayableCharacter::Dispose( void )
{
	for( PlayableCharacter::StateTaskList::iterator it = m_StateTaskList.begin(); it != m_StateTaskList.end(); ++it )
	{
		MIX_DELETE( ( *it ) );
	}

	MIX_DELETE( m_pStateMachine );

	MIX_RELEASE( m_pKChar );
	MIX_RELEASE( m_pMotionMixer );
	MIX_RELEASE( m_pModel );
	MIX_RELEASE( m_pCamera );
	MIX_RELEASE( m_pGamepad );
}

const Mix::Vector3& PlayableCharacter::BakeIntoLinearVelocity( void )
{
	const Mix::Vector3& linearVelocity = m_pModel->GetLinearVelocity();

	m_pKChar->SetLinearVelocity( linearVelocity );

	return linearVelocity;
}

void PlayableCharacter::ApplyLinearVelocity( const Mix::Vector3& vel )
{
	m_pKChar->SetLinearVelocity( vel );
}

void PlayableCharacter::ApplyLocalLinearVelocity( const Mix::Vector3& vel )
{
	m_pKChar->SetLinearVelocity( Mix::Matrix4x4( m_pKChar->GetWorldRotation() ) * vel );
}

Boolean PlayableCharacter::OnGround( void ) const
{
	return m_pKChar->OnGround();
}

void PlayableCharacter::Jump( void )
{
	m_pKChar->Jump();
}

void PlayableCharacter::Action( PlayableCharacter::ACTION_TYPE type, const void* pData )
{
	if( type == PlayableCharacter::ACTION_DAMAGE )
	{
		Utility::Motion::StateMachine::Channel* pChannel = m_pStateMachine->GetChannel( PlayableCharacter::CHANNEL_BODY );
		if( pChannel != NULL )
		{
			const Utility::Motion::StateMachine::State* pState = pChannel->GetActiveStatePtr();
			if( pState != NULL )
			{
				switch( pState->GetID() )
				{
				case PlayableCharacter::STATE_BODY_STAND:
				case PlayableCharacter::STATE_BODY_WALK:
				case PlayableCharacter::STATE_BODY_RUN:
				case PlayableCharacter::STATE_BODY_JUMP:
					pChannel->ChangeState( PlayableCharacter::STATE_BODY_DAMAGE_GROUND );
					break;

				case PlayableCharacter::STATE_BODY_FALLING:
					pChannel->ChangeState( PlayableCharacter::STATE_BODY_DAMAGE_AIR );
					break;
				}
			}
		}
	}
}

const Mix::Matrix4x4& PlayableCharacter::GetWorldMatrix( void ) const
{
	return m_pModel->GetWorldMatrix();
}

void PlayableCharacter::OnUpdate( Float32 dt )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// キネマティックキャラクター
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Vector2 leftStick = m_pGamepad->GetStickState( Mix::HID::GAMEPAD_LEFT );

	/*
		回転
	*/

	if( leftStick.GetLength() >= 0.2f )
	{
		const Mix::Quaternion& worldRot = m_pKChar->GetWorldRotation();
		const Mix::Vector3& view = m_pCamera->GetViewForward();
		Mix::Vector2 dir = leftStick.ToNormalize();

		Mix::Vector3 vy( 0.0f, 1.0f, 0.0f );
		Mix::Vector3 vz = Mix::Vector3( -view.x, 0.0f, -view.z ).ToNormalize();
		Mix::Vector3 vx = Mix::Vector3::Cross( vy, vz );

		Mix::Vector3 dy( 0.0f, 1.0f, 0.0f );
		Mix::Vector3 dz( dir.x, 0.0f, -dir.y );
		Mix::Vector3 dx = Mix::Vector3::Cross( dy, dz );

		Mix::Matrix4x4 viewMat;
		Mix::Matrix4x4 dirMat;
		Mix::Matrix4x4 rotMat;
		Mix::Quaternion nextRot;
		Mix::Quaternion diffRot;

		viewMat.SetRow( 0, Mix::Vector4( vx.x, vx.y, vx.z, 0.0f ) );
		viewMat.SetRow( 1, Mix::Vector4( vy.x, vy.y, vy.z, 0.0f ) );
		viewMat.SetRow( 2, Mix::Vector4( vz.x, vz.y, vz.z, 0.0f ) );

		dirMat.SetRow( 0, Mix::Vector4( dx.x, dx.y, dx.z, 0.0f ) );
		dirMat.SetRow( 1, Mix::Vector4( dy.x, dy.y, dy.z, 0.0f ) );
		dirMat.SetRow( 2, Mix::Vector4( dz.x, dz.y, dz.z, 0.0f ) );

		rotMat = dirMat * viewMat;
		nextRot = Mix::Quaternion::Slerp( worldRot, rotMat.GetRotation(), 0.1f );
		diffRot = Mix::Quaternion::Difference( worldRot, nextRot );

		m_pKChar->SetAngularVelocity( diffRot );
	}
	else
	{
		m_pKChar->SetAngularVelocity( Mix::Quaternion::Identity() );
	}

	/*
		移動
	*/

	Mix::Vector3 linearVelocity = m_pKChar->GetLinearVelocity();
	Mix::Vector3 moveDir = linearVelocity;
	Float32 moveSpeed = moveDir.Normalize();

	moveSpeed -= ( ( m_pKChar->OnGround() == True )? GROUND_FRICTION : AERIAL_FRICTION ) * dt;
	linearVelocity = ( moveSpeed > MIX_FLOAT_EPSILON )? ( moveDir * moveSpeed ) : Mix::Vector3::Zero();

	m_pKChar->SetLinearVelocity( linearVelocity );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ステートマシン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pStateMachine->Update( dt );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// モデル
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pModel->Update( dt );
}

Boolean PlayableCharacter::OnRefresh( void )
{
	m_pModel->Refresh();

	return True;
}

}
