#include "stdafx.h"
#include "hero.h"

#include "PlayerSceneNode.h"
#include "TransMonSceneNode.h"
#include "ObjectNameCard.h"

#include "SoundSystem.h"
#include "InputSystem.h"

#include "SceneManager.h"
#include "Application.h"

#include "ObjectManager.h"
#include "GameResourceManager.h"
#include "WorldManager.h"
#include "SkillExecuter.h"
#include "SkillManager.h"
#include "SkillScript.h"
#include "StageScript.h"
#include "MonsterScript.h"
#include "GatheringScript.h"

#include "CameraManager.h"
#include "LookAtCamera.h"
#include "CommonNetworkMsgStruct.h"
#include "Protocol.h"
#include "Pathfinder.h"
#include "RangeCheck.h"

//#include "Skill_Common.h"
#include "Item_Common.h"
#include "SoundSystem.h"
#include "AppTimer.h"
#include "Duel_Common.h"
#include "Gathering_Common.h"

#include "CastingBar.h"
#include "HeroInfoWindow.h"
#include "HeroStateWindow.h"
#include "TargetWindow.h"
#include "SmallMiniMapWindow.h"
#include "MiniMapWindow.h"

#include "skillwindow.h"
#include "BuffWindow.h"

#include "PVPManager.h"
#include "DuelManager.h"
#include "DuelWindow.h"

#include "GameUIManager.h"
#include "PartyManager.h"
#include "DamagePrintManager.h"
#include "DramaturgyManager.h"
#include "QuestManager.h"
#include "TitleManager.h"
#include "ItemMixManager.h"

#include "Npc.h"
#include "Monster.h"
#include "MonsterSceneNode.h"
#include "VehicleSceneNode.h"
#include "ChatManager.h"

#include "Tarot_Common.h"
#include "ItemManager.h"
#include "QuickManager.h"

#include "uicursor.h"
//
#include "TrailGeometry.h"
#include "UIMsgboxEvent.h"

#include "DuelMsgWindow.h"
#include "EffectSceneNode.h"
#ifdef _GMTOOL
/// 치트
#include "CheatManager.h"
#endif

cPlayerSceneNodeParam  gTempParam;
cMonsterSceneNodeParam gTempParamMon;

cHero::cHero( unsigned char type )
: cPlayer(type)
{
	mpCamera = NULL;
	mDefaultCameraOffset = 0.0f;
	mCameraOffset = 0.0f;

	/// 길찾기
	mPathFinder = NULL;
	mPathIndex = 0;
	mPathCount = 0;

	mIsCalcStatus = false;
	mIsUdateHeroInfo = false;

	/// default로 기본공격
	mWaitSkillIdx = 0;
	mWaitWeaponSwitch = false;

	/// 공격중 이동 대기 (Attack상태에서만 유효하다)
	mWaitMove = false;

	mAfterAction.mActionType = eAfterAction_None;

	InitStatusInfoType();

	mMageJobType = false;

	mMoney = 0;
	mDeposit = 0;
	mArmorSets  = ARMOR_SETS_NONE;

	mNpcIdx = 0;

	mPeaceToggleTime = 0;
	mPeaceToggleFlag = true;

	mSwitchWeaponTime = 0;
	mSwitchWeaponFlag = false;

	mMoveTargetEffect = 0;
	mMoveTargetEffectIdx = (unsigned long)-1;

	mJobUsedSkillPoint.Resize( 5 );

	mDamageAfterAnim = ANITYPE_IDLE;
}

cHero::~cHero()
{
	mMoveTargetEffect = 0;
	mMoveTargetEffectIdx = (unsigned long)-1;

	ClearTabTargetList();

	mJobUsedSkillPoint.Clear();
}

bool cHero::Create( sPlayerInfo* pBaseInfo, sPlayerWearInfo* pWearInfo, sPlayerWeaponInfo* pWeaponInfo, 
				   sPlayerExrInfo* pExrInfo, NiPoint3 sPos, NiMatrix3 sRot, float fScale )
{
	::memcpy( &mPlayerInfo, pBaseInfo, sizeof(sPlayerInfo) );
	::memcpy( &mPlayerExrInfo, pExrInfo, sizeof(sPlayerExrInfo) );
	::memcpy( &mPlayerWearInfo, pWearInfo, sizeof(sPlayerWearInfo) );
	::memcpy( &mPlayerWeaponInfo, pWeaponInfo, sizeof(sPlayerWeaponInfo) );

	gTempParam.mpObject = gTempParamMon.mpObject = this;
	gTempParam.mTranslate = gTempParamMon.mTranslate = sPos;
	gTempParam.mRotate = gTempParamMon.mRotate = sRot;
	gTempParam.mScale = gTempParamMon.mScale = fScale;

	mCantMove = false;
	mCantSkill = false;

	///
	mDelay = 0;
	mAngle = 0.0f;
	mAccumAngle = 0.0f;
	mAxis = NiPoint3::UNIT_Z;

	mMoveTargetEffect = 0;
	mMoveTargetEffectIdx = (unsigned long)-1;

	mSitDownStartTime = 0;
	mSitDownStatusCalc = false;

//	mMoveTargetList.Clear();

	return true;
}

void cHero::SetHeroInfoNet( MSGROOT* pInfo )
{
	MSG_HEROINFO* pHInfo = (MSG_HEROINFO*)pInfo;

	memcpy( HERO->GetHeroInfo(), &pHInfo->mHeroInfo, sizeof(sHeroInfo) );
	memcpy( HERO->GetHeroExrInfo(), &pHInfo->mExrInfo, sizeof(sPlayerExrInfo) );

	if( pHInfo->mExrInfo.mChgMonsterIdx == 0 )
	{
		const char* strFile = GAMERESOURCEMAN->GetModelFileName( (mPlayerInfo.Race * eGENDER_MAX) + mPlayerInfo.Gender );
		gTempParam.mPathName = strFile;
	}
	else
	{
		/// 외형 몬스터화
		sMonsterScript* pMonsterInfo;
		pMonsterInfo = MONSTERSCRIPT->GetMonsterListInfo( mPlayerExrInfo.mChgMonsterIdx );
		if( pMonsterInfo == NULL )
		{
			assert(0);
			return;
		}

		float scale = 1.0f;
		if( pMonsterInfo->mMonsterScale == 0.0f )
			scale = pMonsterInfo->mMonsterScale;

		const char* strFile = GAMERESOURCEMAN->GetModelFileName( pMonsterInfo->mKfmIdx );

		cString path;
		path.Format( "./Data/Monster/%s", strFile );

		gTempParamMon.mPathName = path;
		gTempParamMon.mScale = scale;
	}
}


bool cHero::GameIn( unsigned long channelNum )
{
	if( mPlayerExrInfo.mChgMonsterIdx == 0 )
	{
		if( WORLDMAN->CalcHeight( &gTempParam.mTranslate.z, gTempParam.mTranslate.x, gTempParam.mTranslate.y ) == false )
			assert(NULL);

		gTempParam.mWoldAccumTime = THEAPP->GetWorldAccumTime();

		mpObjectSceneNode = CreateSceneNode( &gTempParam );
		if( mpObjectSceneNode == 0 )
		{
			assert(0);
			return false;
		}

		/// head 정보 셋팅
		ChangeBaseHair( mPlayerInfo.HeadInfo[eHEAD_HAIR] );
		SetBaseFace();

		if( mPlayerInfo.HeadInfo[eHEAD_FACE_STATIC] != 0 )
			ChangeFaceTexture( mPlayerInfo.HeadInfo[eHEAD_FACE_STATIC], true );
		else
			ChangeFaceTexture( mPlayerInfo.HeadInfo[eHEAD_FACE], true );


//		if( PVPMAN->IsEnableBattle() == true )
//		{
//			/// 팀정보에 따른 데이터 변경
//		}
//		else
		{
			/// 착용 정보 셋팅
			for( unsigned int ui=0; ui<eWEAR_MAX; ui++ )
			{
				if( mPlayerWearInfo.WearIdx[ui] == 0 )
				{
					if( ui != eWEAR_HAT ) 
					{
						/// 누드 복장을 적용해야 하는 경우
						ChangeBaseWear( ePART_BODY1 + ui );
					}
				}
				else
				{
					/// 장비를 착용
					EquipWearItem( mPlayerWearInfo.WearIdx[ui] );
				}
			}
		}

		/// 착용 아이템 설정
		/// 초기화 구문이므로 직접 제어한다..
		EquipWeaponItem( &mPlayerWeaponInfo );
	}
	else
	{
		if( WORLDMAN->CalcHeight( &gTempParamMon.mTranslate.z, gTempParamMon.mTranslate.x, gTempParamMon.mTranslate.y ) == false )
			assert(NULL);

		gTempParamMon.mWoldAccumTime = THEAPP->GetWorldAccumTime();

		mpObjectSceneNode = (cDynamicSceneNode*)SCENEMAN->CreateTransMonster( gTempParamMon, true );
		if( mpObjectSceneNode == 0 )
		{
			assert(0);
			return false;
		}

		sMonsterScript* pMonsterInfo;
		pMonsterInfo = MONSTERSCRIPT->GetMonsterListInfo( mPlayerExrInfo.mChgMonsterIdx );
		if( pMonsterInfo == NULL )
		{
			assert(0);
			return false;
		}

		float monsterSize = 1.0f;
		if( pMonsterInfo->mMonsterFixSize != 0.0f )
			monsterSize = pMonsterInfo->mMonsterFixSize;
	}

	SetFixedObjectSizeScale( mPlayerExrInfo.mFixedObjectSizePer );

	ShowNameGauge( true );

	mChannelNum = channelNum;

	SetState( eOBJECT_STATE_IDLE );
	mPlayerExrInfo.mStateIdle = eIDLE_NORMAL;
	mPlayerExrInfo.mStateStop = eSTOP_NONE;

	/// Hero 가지고 다니는 카메라 설정.. 
	CAMERAMAN->ChangeCamera( eCamera_Hero );
	cLookAtCamera* pCamera = (cLookAtCamera*)CAMERAMAN->GetCurrent();

	NiPoint3 sCenter = mpObjectSceneNode->GetObjectCenter();
	NiPoint3 sFoot = mpObjectSceneNode->GetObjectFoot();
	mDefaultCameraOffset = sCenter.z - sFoot.z +22.0f;
	mCameraOffset = mDefaultCameraOffset;

	pCamera->SetLookAt( NiPoint3( sFoot.x, sFoot.y, sFoot.z + mCameraOffset ) );

	NiPoint3 sHead = mpObjectSceneNode->GetObjectHead();
	mStatureValue = (sHead.z - sFoot.z) / mpObjectSceneNode->GetWorldScale();

	///
	mDesiredDir = mpObjectSceneNode->GetRotate()*mDesiredDir;
	mDesiredDir.Unitize();

	pCamera->ViewHeroFront( -mDesiredDir );

	/// 길찾기 셋팅
	SetPathFinder( WORLDMAN->GetPathFinder() );

	/// 스탯 계산..
	STATUSCALC->CalcPlayerInit();

	mpObjectSceneNode->UpdateNameCardTextValue();
//	mpObjectSceneNode->UpdateNameCardGaugeValue();

	/// 스킬창 셋팅
	cSkillWindow* skillWindow = GAMEUI->GetSkillWindow();
	if( skillWindow )
	{
		tArray<unsigned long>* parray = GAMERESOURCEMAN->GetParentJob( mPlayerInfo.Job );
		if( parray )
		{
			assert( parray->IsEmpty() == false );

			if( parray->IsEmpty() == false )
			{
				for( unsigned int i=0;i<parray->GetSize();++i)
					skillWindow->AddJobTab( (*parray)[i] );
			}
		}
		
		skillWindow->AddJobTab( mPlayerInfo.Job );
		skillWindow->SetCurrent( 0 );
	}

	/// 스킬 정보 세팅
	SKILLMAN->UpdateAllSkill();

	mMageJobType = GAMERESOURCEMAN->IsCorrelateMageJob( mPlayerInfo.Job );

	mIsCalcStatus = true;
	mIsUdateHeroInfo = true;
	GAMEUI->UpdateSkillPoint();

	/// 
	ITEMMAN->CheckEquipAbility();

	/// 착용 방어구셋 갱신
	ResetArmorSets();

	/// hero의 정보 초기화
	GAMEUI->InitHeroInfo();

	/// 전체 아이템 무게 계산
	ITEMMAN->SetMaxWeightGauge( (float)GetMaxWeight() );

	/// 전체 아이템 쿨타임 세팅 
	ITEMMAN->SetAllItemCool();

	/// 호칭 정보 세팅
	TITLEMAN->Open();

	/// Vehicle Check
	if( mPlayerExrInfo.mVehicleIdx != 0 )
	{
		if( mPlayerExrInfo.mChgMonsterIdx == 0 )
		{
			RideVehicle( mPlayerExrInfo.mVehicleIdx );
		}
		else
		{
			assert(0);
		}
	}

	/// 전장 세력 이펙트
	PVPMAN->LinkPvpForceEffect( this, mPlayerExrInfo.mForceType );

	SetLinkdEffect( eLINK_FOOT, "./Data/Effect/Eff_PC_LogIn_01.nif", 0 );

	return true;
}

void cHero::ReCreate( char race, char gender )
{
	if( mpObjectSceneNode->GetTargetAnimation() != 0 )
		return;

	cPlayer::ReCreate( race, gender );

	cLookAtCamera* pCamera = (cLookAtCamera*)CAMERAMAN->GetCurrent();

	NiPoint3 sCenter = mpObjectSceneNode->GetObjectCenter();
	NiPoint3 sFoot = mpObjectSceneNode->GetObjectFoot();
	mDefaultCameraOffset = sCenter.z - sFoot.z +22.0f;
	mCameraOffset = mDefaultCameraOffset;

	pCamera->SetLookAt( NiPoint3( sFoot.x, sFoot.y, sFoot.z + mCameraOffset ) );
}

void cHero::SetState( unsigned int state, bool bShowAni )
{
	mIdleTime = 0;

	/// 죽은 경우 무조건 다음 동작으로 대기가 와야 한다.
	if( mPlayerExrInfo.mState == eOBJECT_STATE_DIE )
	{
		if( state != eOBJECT_STATE_IDLE )
		{
			assert(0);
			return;
		}

		SetReadyUseSkill( true );

		mAfterAction.mActionType = eAfterAction_None;
		mWaitMove = false;
		mWaitSkillIdx = 0;
	}

	if( mPlayerExrInfo.mState == ePLAYER_STATE_GATHERING && state != ePLAYER_STATE_GATHERING )
	{
		if( mGatheringToolObj != UINT_MAX )
		{
			mpObjectSceneNode->UnLinkObject( mGatheringToolObj );
			mGatheringToolObj = UINT_MAX;
		}
	}

	if( mPlayerExrInfo.mState == eOBJECT_STATE_MOVE )
	{
		if( state != eOBJECT_STATE_MOVE )
		{
			if( mMoveTargetEffectIdx != (unsigned long)-1 )
			{
				cEffectSceneNode* node = SCENEMAN->GetEffectSceneNode( mMoveTargetEffectIdx );
				if( node )
				{
					assert( node == mMoveTargetEffect );
					node->Remove();
				}
				mMoveTargetEffect = 0;
				mMoveTargetEffectIdx = (unsigned long)-1;
			}
		}
	}

	/// 081209 PKH 일어 서기 - 스텟 재계산
	if( mPlayerExrInfo.mState == ePLAYER_STATE_SITDOWN && state != ePLAYER_STATE_SITDOWN )
	{		
		if( IsSitDownCalc() == true )
			CalcStatus();
		mSitDownStartTime = 0;
		mSitDownStatusCalc = false;

	}

	if( state == ePLAYER_STATE_ITEMPICK || state == ePLAYER_STATE_GATHERING )
	{
		CURSOR->SetCursor( eCURSOR_DEFAULT );
	}

	if( state != ePLAYER_STATE_SITDOWN )
		mSitDownFlag = false;

	if( state != eOBJECT_STATE_STOP )
		mPlayerExrInfo.mStateStop = eSTOP_NONE;

	if( state == eOBJECT_STATE_IDLE )
	{
		SetReadyUseSkill( true );

		mAfterAction.mActionType = eAfterAction_None;
		mWaitMove = false;
		mWaitSkillIdx = 0;
	}
	else if( state == eOBJECT_STATE_ATTACK )
	{
		ShowWeapon( true );
		mPlayerExrInfo.mState = (eOBJECTSTATE)state;
		SetIdleFlag(eIDLE_NORMAL);

		return;
	}
	else if( state == eOBJECT_STATE_DIE )
	{
		/// npc랑 대화 중이었으면 대화를 초기화한다.
		mNpcIdx = 0;
		SetReadyUseSkill( true );

		/// 죽기전에 상태에 따른 UI 닫기
		GAMEUI->HideUIByDie();

		/// 사망시 보유 효과 제거..
		ClearDieBuff();
		mCantMove = false;
		mCantSkill = false;
		mIsStun = false;
		mIsSleep = false;
	}

	cPlayer::SetState( state, bShowAni );
}

void cHero::SetTargetObject( unsigned char type, unsigned long idx )
{
	if( mTarget.type == type && mTarget.index == idx )
		return;

	if( type == eOBJECTTYPE_NONE )
		ClearTabTargetList();

	/// 기존 타겟
	cBaseObject* obj = GetTargetObject();
	if( obj )
	{
		obj->SetOverFlag( false );
		OBJECTMAN->ResetOverObject();
	}

	cBaseObject::SetTargetObject( type, idx );

	/// 새 타겟
	cTargetWindow* targetWindow = GAMEUI->GetTargetWindow();
	obj = GetTargetObject();
	if( obj )
	{		
		obj->SetOverFlag( true );

		/// 타겟창 세팅
		if( targetWindow )
		{
			targetWindow->ClearBuff();

			/// 타겟의 버프 세팅
			typedef tPointerHashMap<unsigned long, void*> cTargetBuffMap;
			cTargetBuffMap* buffMap = obj->GetBufMap();
			if( buffMap )
			{
				if( buffMap->GetSize() > 0 )
				{
					cTargetBuffMap::cIterator i = buffMap->Begin();
					cTargetBuffMap::cIterator end = buffMap->End();
					for( ; i != end; ++i )
					{
						cInfluenceObject* p = (cInfluenceObject*)(i->mSecond);
						if( p )
						{
							/// 효과는 스킬인덱스가 효과인덱스로 쓰임.
							/// 버프/디버프 내부 구별
							targetWindow->AddBuff( p->GetManagedKey(), p->GetInfluenceIdx() );
						}
					}
				}
			}

			/// 타겟의 디버프 세팅
			typedef tPointerHashMap<unsigned long, void*> cTargetDebuffMap;
			cTargetDebuffMap* debuffMap = obj->GetDeBufMap();
			if( debuffMap )
			{
				if( debuffMap->GetSize() > 0 )
				{
					cTargetDebuffMap::cIterator i = debuffMap->Begin();
					cTargetDebuffMap::cIterator end = debuffMap->End();
					for( ; i != end; ++i )
					{
						cInfluenceObject* p = (cInfluenceObject*)(i->mSecond);
						if( p )
						{
							/// 효과는 스킬인덱스가 효과인덱스로 쓰임.
							/// 버프/디버프 내부 구별
							targetWindow->AddBuff( p->GetManagedKey(), p->GetInfluenceIdx() );
						}
					}
				}
			}
		}
	}
	else
	{
		if( targetWindow )
			targetWindow->Hide();
	}
	targetWindow->UpdateWindow();

	PARTYMAN->ChangeTarget( idx );
//	SKILLEXECUTER->ResetAttackFlag();
}

void cHero::HPChange( unsigned long objectHP, unsigned long objectMaxHP, bool die )
{
	if( die ) 
	{
		assert(objectHP==0);
		assert( GetState() != eOBJECT_STATE_DIE );

		if( GetState() != eOBJECT_STATE_DIE )
		{
			if( IsRideVehicle() == true )
				AlightVehicle();

			SetState( eOBJECT_STATE_DIE );
			SKILLMAN->CreateApplyObject( 0, this, 0, eAPPLYDRAMA_DIE1 );
		}

		/// 사망시 보유 효과 제거..
		ClearDieBuff();
		mCantMove = false;
		mCantSkill = false;
		mIsStun = false;
		mIsSleep = false;

		CalcStatus();
		
		mPlayerExrInfo.RestHP = 0;
		mPlayerExrInfo.RestMP = 0;
	}
	else
	{
		mPlayerExrInfo.RestHP = objectHP;
		mPlayerExrInfo.MaxHP = objectMaxHP;
	}

	mpObjectSceneNode->UpdateNameCardGaugeValue();

	/// 
	GAMEUI->UpdateHeroHPMP();
}

void cHero::HPChange( sTargetDamageHP* damage, cBaseObject* attacker )
{
	if( damage == 0 )
	{
		assert(0);
		return;
	}

	if( damage->mDie ) 
	{
		assert( damage->mObjectHP == 0 );
		assert( GetState() != eOBJECT_STATE_DIE );

		if( GetState() != eOBJECT_STATE_DIE )
		{
			if( IsRideVehicle() == true )
				AlightVehicle();

			if( attacker )
				SetDesiredDir( GetPos(), attacker->GetPos() );

			SetState( eOBJECT_STATE_DIE );
			if( damage->mDamageKind == eDAMAGE_CRITICAL )
			{
				ChangeAnimation( ANITYPE_DIE2 );
				SKILLMAN->CreateApplyObject( attacker, this, 0, eAPPLYDRAMA_DIE2 );
			}
			else
			{
				SKILLMAN->CreateApplyObject( attacker, this, 0, eAPPLYDRAMA_DIE1 );
			}
		}

		/// 사망시 보유 효과 제거..
		ClearDieBuff();
		mCantMove = false;
		mCantSkill = false;
		mIsStun = false;
		mIsSleep = false;


		mPlayerExrInfo.RestHP = 0;
		mPlayerExrInfo.RestMP = 0;

		CalcStatus();
	}
	else
	{
		mPlayerExrInfo.RestHP = damage->mObjectHP;
		mPlayerExrInfo.MaxHP = damage->mObjectMaxHP;
	}

	assert( GetHP() <= GetMaxHP() );
	mpObjectSceneNode->UpdateNameCardGaugeValue();

	/// 
	GAMEUI->UpdateHeroHPMP();
}

void cHero::MPChange( unsigned long objectMP, unsigned long objectMaxMP )
{
	if( GetState() == eOBJECT_STATE_DIE )
	{
		assert(objectMP == 0);
	}

	mPlayerExrInfo.RestMP = objectMP;
	mPlayerExrInfo.MaxMP = objectMaxMP;

	assert( GetMP() <= GetMaxMP() );
	mpObjectSceneNode->UpdateNameCardGaugeValue();

	///
	GAMEUI->UpdateHeroHPMP();
	QUICKMAN->CheckSkillIcon();
}

void cHero::AfterAttack( cBaseObject* pTarget )
{
	if( GatheringCancel() == false )
		return;

	/// 기본 공격 처리
	if( pTarget == 0 )
		return;

	/// 080508 PKH 정지 상태일때 일반공격 하면 에니메이션이 바껴서 체크
	if( GetState() == eOBJECT_STATE_STOP )
		return;

	unsigned long attackIdx = SKILLEXECUTER->GetChargeSkill();
	if( IsReadyUseSkill() == false )
	{
		SKILLEXECUTER->ChargeOff( attackIdx );
		return;
	}

	/// 공격 가능거리 여부 체크
	NiPoint3 targetPos = pTarget->GetPos();
//	targetPos.z = 0.0f;
	NiPoint3 heroPos = GetPos();
//	heroPos.z = 0.0f;

	/// 071127 PKH 
	float range = SKILLEXECUTER->GetChargeSkillRange();
	float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, range );
	cRangeCheck rangecheck( tempRange );
	if( rangecheck.IsRange( heroPos, targetPos ) )
	{
		/// 즉시 공격..
		if( StartAttack( attackIdx, pTarget ) == false )
		{
			/// 공격 상태 유지
			SetState( eOBJECT_STATE_ATTACK );
			SetReadyUseSkill( true );
		}
	}
	else
	{
		/// 후처리 등록한다.
		mAfterAction.mActionType = eAfterAction_Attack;
		mAfterAction.mTarget.index = pTarget->GetObjectID();
		mAfterAction.mTarget.type = pTarget->GetObjectType();
		mAfterAction.mDestPos = pTarget->GetPos();
		mAfterAction.mRange = range;
		mAfterAction.mExData1 = attackIdx;

		/// 타겟 위치로 이동 등록 ( 위치체크 및 상태 변경은 processAfterAction에 맞긴다. )
		if( ResetActionMovePath( pTarget->GetXPos(), pTarget->GetYPos(), range ) == false )
		{
			mAfterAction.mActionType = eAfterAction_None;
		}
	}

	SKILLEXECUTER->ChargeOff( attackIdx );
}

void cHero::AfterUseSkill( cBaseObject* pTarget )
{
	if( GatheringCancel() == false )
		return;

	unsigned long skillIdx = SKILLEXECUTER->GetChargeSkill();
	sPlayerSkillBaseInfo* pInfo = SKILLSCRIPT->GetPlayerSkillInfo( skillIdx );
	if( pInfo == 0 || pInfo->mBoundType == eBOUNDTYPE_FIELD )
	{
		assert(0);
		SKILLEXECUTER->ChargeOff( skillIdx );
		return;
	}

	if( pInfo->mBoundType == eBOUNDTYPE_SELF_NOTARGET )
	{
		/// 즉시 범위 스킬 시전
		if( IsReadyUseSkill() )
		{
			UseTargetSkill( skillIdx, 0 );
		}
		else
		{
			SKILLEXECUTER->ChargeOff( skillIdx );
			mWaitSkillIdx = skillIdx;
		}

		return;
	}

	float range = SKILLEXECUTER->GetChargeSkillRange();
	NiPoint3 targetPos = pTarget->GetPos();
	NiPoint3 heroPos = GetPos();

	float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, range );
	cRangeCheck rangecheck( tempRange );
	if( rangecheck.IsRange( heroPos, targetPos ) )
	{
		/// 시전
		if( IsReadyUseSkill() )
		{
			UseTargetSkill( skillIdx, pTarget );
			return;
		}
		else
		{
			mWaitSkillIdx = skillIdx;
		}
	}
	else
	{
		if( IsReadyUseSkill() )
		{
			/// 후처리 등록한다.
			mAfterAction.mActionType = eAfterAction_UseTargetSkill;
			mAfterAction.mTarget.index = pTarget->GetObjectID();
			mAfterAction.mTarget.type = pTarget->GetObjectType();
			mAfterAction.mDestPos = pTarget->GetPos();
			mAfterAction.mRange = range;
			mAfterAction.mExData1 = skillIdx;

			/// 타겟 위치로 이동 등록 ( 위치체크 및 상태 변경은 processAfterAction에 맞긴다. )
			
			if( ResetActionMovePath( pTarget->GetXPos(), pTarget->GetYPos(), range ) == false )
			{
				mAfterAction.mActionType = eAfterAction_None;
			}
		}
		else
		{
			mWaitSkillIdx = skillIdx;
		}
	}

	SKILLEXECUTER->ChargeOff( skillIdx );
}

void cHero::AfterUseSkill( NiPoint3 fieldPos )
{
	if( GatheringCancel() == false )
		return;

	unsigned long skillIdx = SKILLEXECUTER->GetChargeSkill();
	sPlayerSkillBaseInfo* pInfo = SKILLSCRIPT->GetPlayerSkillInfo( skillIdx );
	if( pInfo == 0 || pInfo->mBoundType != eBOUNDTYPE_FIELD )
	{
		assert(0);
		return;
	}

	float range = SKILLEXECUTER->GetChargeSkillRange();
	NiPoint3 heroPos = GetPos();

	cRangeCheck rangecheck( range );
	if( rangecheck.IsRange( heroPos, fieldPos ) )
	{
		/// 시전
		if( IsReadyUseSkill() )
		{
			UseFieldSkill( skillIdx, fieldPos );
			return;
		}
	}
/*
	else
	{
//		if( IsReadyUseSkill() )
//		{
			/// 후처리 등록한다.
			mAfterAction.mActionType = eAfterAction_UseFieldSkill;
			mAfterAction.mTarget.index = 0;
			mAfterAction.mTarget.type = eOBJECTTYPE_NONE;
			mAfterAction.mDestPos = fieldPos;
			mAfterAction.mRange = range;
			mAfterAction.mExData1 = skillIdx;

			/// 타겟 위치로 이동 등록 ( 위치체크 및 상태 변경은 processAfterAction에 맞긴다. )
			if( ResetActionMovePath( fieldPos.x, fieldPos.y, range ) == false )
				mAfterAction.mActionType = eAfterAction_None;
//		}
	}
*/
	SKILLEXECUTER->ChargeOff( skillIdx );
}

void cHero::AfterItemPick( unsigned long idx, float destX, float destY )
{
#ifdef _GMTOOL
	if( CHEATMAN->GetHideMode() )
		return;
#endif

	if( GatheringCancel() == false )
		return;

	/// float range = 20;	20...?
	float range = ITEM_VALID_DISTANCE;

	NiPoint3 targetPos = NiPoint3(destX, destY, 0.0f);

	NiPoint3 heroPos = GetPos();
	heroPos.z = 0.0f;

	cRangeCheck rangecheck( range );
	if( rangecheck.IsRange( heroPos, targetPos ) )
	{
		/// 루팅윈도우 닫기 
		GAMEUI->HideLootingWindow();

		if( ITEMMAN->IsLocked() == true )
			return;

		/// 상자 연속 오픈 시간 체크
		if( ITEMMAN->CheckOpenTime() == false )
			return;

		MSG_REQ_ITEM_GET_OPEN msg;
		msg.Category = NM_ITEM;
		msg.Protocol = NM_ITEM_GET_OPEN_REQ;
		msg.idx = idx;

		NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

		/// 락 걸기
		ITEMMAN->LockItemTry( ITEM_TRY_PICK );

		/// 바라봐야할 방향 설정
		NiPoint3 charDir = mDesiredDir;
		NiPoint3 viewDir = targetPos - heroPos;
		viewDir.z = 0.0f;
		if( viewDir != NiPoint3::ZERO )
		{
			if( viewDir.SqrLength() >= 0.001f )	
			{
				viewDir.Unitize();

				/// 회전 Matrix 산출
				NiMatrix3 matA, matB, matRot;
				matA.SetCol( 0, charDir );
				matA.SetCol( 1, -NiPoint3::UNIT_Z );
				matA.SetCol( 2, charDir.UnitCross( -NiPoint3::UNIT_Z ) );

				matB.SetCol( 0, viewDir );
				matB.SetCol( 1, -NiPoint3::UNIT_Z );
				matB.SetCol( 2, viewDir.UnitCross( -NiPoint3::UNIT_Z ) );

				matRot = matB * matA.Transpose();
				mpObjectSceneNode->SetRotate( mpObjectSceneNode->GetRotate()*matRot );
				mDesiredDir = viewDir;
			}
		}

		SetState( ePLAYER_STATE_ITEMPICK );
		return;
	}

	mAfterAction.mActionType = eAfterAction_ItemPick;
	mAfterAction.mExData1 = idx;
	mAfterAction.mDestPos = NiPoint3( destX, destY, 0.0f );
	mAfterAction.mRange = ITEM_VALID_DISTANCE;
	mAfterAction.mTarget.index = 0;
	mAfterAction.mTarget.type = eOBJECTTYPE_NONE;

	if( ResetActionMovePath( destX, destY, mAfterAction.mRange ) == false )
		mAfterAction.mActionType = eAfterAction_None;
}

void cHero::AfterGatheringPick( unsigned long idx, unsigned long classIdx, float destX, float destY )
{
#ifdef _GMTOOL
	if( CHEATMAN->GetHideMode() )
		return;
#endif

	float range = ITEM_VALID_DISTANCE;
	NiPoint3 targetPos = NiPoint3(destX, destY, 0.0f);
	NiPoint3 heroPos = GetPos();
	heroPos.z = 0.0f;

	cRangeCheck rangecheck( range );
	if( rangecheck.IsRange( heroPos, targetPos ) )
	{
		GatheringStart( idx, classIdx, targetPos );
		return;
	}

	mAfterAction.mActionType = eAfterAction_GatheringPick;
	mAfterAction.mExData1 = idx;
	mAfterAction.mExData2 = classIdx;
	mAfterAction.mDestPos = NiPoint3( destX, destY, 0.0f );
	mAfterAction.mRange = range;
	mAfterAction.mTarget.index = 0;
	mAfterAction.mTarget.type = eOBJECTTYPE_NONE;

	if( ResetActionMovePath( destX, destY, mAfterAction.mRange ) == false )
		mAfterAction.mActionType = eAfterAction_None;
}

void cHero::AfterNpcTalk( unsigned long npcIdx )
{
	AfterNpcTalk( OBJECTMAN->GetNpc(npcIdx) );
}

void cHero::AfterNpcTalk( cNpc* pNpc )
{
	if( GatheringCancel() == false )
		return;

	if( pNpc == 0 )
		return;

	if( DUELMAN->IsDuelEnable() )
		return;
	if( GetState() == eOBJECT_STATE_DIE )
		return;

	float range = NPC_VALID_DISTANCE;
	NiPoint3 targetPos = pNpc->GetPos();
	targetPos.z = 0.0f;

	NiPoint3 heroPos = GetPos();
	heroPos.z = 0.0f;

	float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pNpc, range );

	cRangeCheck rangecheck( tempRange );
	if( rangecheck.IsRange( heroPos, targetPos ) )
	{
		MSG_REQ_NPC_OPEN Msg;
		Msg.Category = NM_NPC;
		Msg.Protocol = NM_NPC_OPEN_REQ;
		Msg.mNpcIdx = pNpc->GetObjectID();
		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );

		SetState( eOBJECT_STATE_IDLE );

		return;
	}

	mAfterAction.mActionType = eAfterAction_NpcTalk;
	mAfterAction.mTarget.type = eOBJECTTYPE_NPC;
	mAfterAction.mTarget.index = pNpc->GetObjectID();
	mAfterAction.mDestPos = pNpc->GetPos();
	mAfterAction.mRange = NPC_VALID_DISTANCE;

	/// npc위치로 이동 등록 ( 위치체크 및 상태 변경은 processAfterAction에 맞긴다. )
	if( ResetActionMovePath( pNpc->GetXPos(), pNpc->GetYPos(), mAfterAction.mRange ) == false )
		mAfterAction.mActionType = eAfterAction_None;
}

///
void cHero::AfterUseTarotStore( cPlayer* pPlayer )
{
	if( GatheringCancel() == false )
		return;

	if( pPlayer == 0 )
		return;

	mAfterAction.mActionType = eAfterAction_UseTarotStore;
	mAfterAction.mTarget.type = eOBJECTTYPE_PLAYER;
	mAfterAction.mTarget.index = pPlayer->GetObjectID();
	mAfterAction.mDestPos = pPlayer->GetPos();
	mAfterAction.mRange = 100.0f;

	if( ResetActionMovePath( pPlayer->GetXPos(), pPlayer->GetYPos(), mAfterAction.mRange ) == false )
		mAfterAction.mActionType = eAfterAction_None;
}

void cHero::AfterUseStall( cPlayer* pPlayer )
{
	if( GatheringCancel() == false )
		return;

	if( pPlayer == 0 )
		return;

	mAfterAction.mActionType = eAfterAction_UseStall;
	mAfterAction.mTarget.type = eOBJECTTYPE_PLAYER;
	mAfterAction.mTarget.index = pPlayer->GetObjectID();
	mAfterAction.mDestPos = pPlayer->GetPos();
	mAfterAction.mRange = 100.0f;

	if( ResetActionMovePath( pPlayer->GetXPos(), pPlayer->GetYPos(), mAfterAction.mRange ) == false )
		mAfterAction.mActionType = eAfterAction_None;
}

void cHero::AfterMapChange( unsigned long scriptIdx )
{
	if( GatheringCancel() == false )
		return;

	sStageChangePos* pInfo = STAGESCRIPT->GetPosScriptInfo( scriptIdx );
	if( pInfo == 0 )
	{
		/// 맵이동 에러 메세지 출력
		return;
	}

	NiPoint3 portalPos;
	portalPos.x = pInfo->mPosX;
	portalPos.y = pInfo->mPosY; 
	portalPos.z = 0.0f; 

	float range = pInfo->mRange;

	NiPoint3 heroPos = GetPos();
	heroPos.z = 0.0f;

	cRangeCheck rangecheck( range );
	if( rangecheck.IsRange( heroPos, portalPos ) )
	{
		sStageChangePos* pInfo = STAGESCRIPT->GetPosScriptInfo( (unsigned long)scriptIdx );
		assert(pInfo);
		if( pInfo && pInfo->mTargetInfo )
		{
			TCHAR str[256] = {0,};
			_stprintf(str,GAMERESOURCEMAN->GetGameText( 60 ), GAMERESOURCEMAN->GetMapName(pInfo->mTargetInfo->mMapNumber) );

			GAMEUI->ShowGameUI();

			///
			UIMAN->CreateModal( eStage_Game, eMBEVENT_MAPCHANGE, str, GAMERESOURCEMAN->GetGameText( 59 ), eSKIN_YESNO, (void*)scriptIdx );
		}
		return;
	}

	mAfterAction.mActionType = eAfterAction_MapChange;
	mAfterAction.mExData1 = scriptIdx;
	mAfterAction.mDestPos = portalPos;
	mAfterAction.mRange = range;
	mAfterAction.mTarget.index = 0;
	mAfterAction.mTarget.type = eOBJECTTYPE_NONE;

	if( ResetActionMovePath( portalPos.x, portalPos.y, mAfterAction.mRange ) == false )
		mAfterAction.mActionType = eAfterAction_None;
}

void cHero::SetStatus1( ePLAYER_STATUS statusBase, float value )
{
	if( statusBase >= ePLAYER_STATUS_LEVEL )
	{
		assert(NULL);
		return;
	}

	switch( statusBase )
	{
	case ePLAYER_STATUS_STR:
		mStatus1.mStr = value; break;
	case ePLAYER_STATUS_DEX:
		mStatus1.mDex = value; break;
	case ePLAYER_STATUS_CON:
		mStatus1.mCon = value; break;
	case ePLAYER_STATUS_INT:
		mStatus1.mInt = value; break;
	case ePLAYER_STATUS_WIS:
		mStatus1.mWis = value; break;
	}
}

/// 070116 PKH 기본 스텟 추가 수치 등록
void cHero::SetStatus1Plus( ePLAYER_STATUS statusBasePlus, float value )
{
	if( statusBasePlus >= ePLAYER_STATUS_LEVEL )
	{
		assert(NULL);
		return;
	}

	switch( statusBasePlus )
	{
	case ePLAYER_STATUS_STR:
		mStatus1Plus.mStr = value; break;
	case ePLAYER_STATUS_DEX:
		mStatus1Plus.mDex = value; break;
	case ePLAYER_STATUS_CON:
		mStatus1Plus.mCon = value; break;
	case ePLAYER_STATUS_INT:
		mStatus1Plus.mInt = value; break;
	case ePLAYER_STATUS_WIS:
		mStatus1Plus.mWis = value; break;
	}
}

void cHero::SetStatus2( ePLAYER_STATUS_EXT extStatus, float value )
{
	if( extStatus >= ePLAYER_STATUS_EXT2_MAX )
	{
		assert(NULL);
		return;
	}

	switch( extStatus )
	{
	case ePLAYER_STATUS_EXT_ATK:
		break;	/// 최소 최대 값으로 재정의
	case ePLAYER_STATUS_EXT_RATK:
		break;	/// 최소 최대 값으로 재정의
	case ePLAYER_STATUS_EXT_DEF:
		mStatus2.mPhysicDefense = value; break;
	case ePLAYER_STATUS_EXT_MATK:
		break;	/// 최소 최대 값으로 재정의
	case ePLAYER_STATUS_EXT_MDEF:
		mStatus2.mMagicDefense = value; break;
	case ePLAYER_STATUS_EXT_CRI:
		mStatus2.mPhysicCritical = value; break;
	case ePLAYER_STATUS_EXT_MCRI:
		mStatus2.mMagicCritical = value; break;
	case ePLAYER_STATUS_EXT_MAXHP:
		{
			mStatus2.mMaxHP = value;

			if( mPlayerExrInfo.RestHP > (unsigned long)mStatus2.mMaxHP )
				mPlayerExrInfo.RestHP = (unsigned long)mStatus2.mMaxHP;
		}
		break;
	case ePLAYER_STATUS_EXT_MAXMP:
		{
			mStatus2.mMaxMP = value;

			if( mPlayerExrInfo.RestMP > (unsigned long)mStatus2.mMaxMP )
				mPlayerExrInfo.RestMP = (unsigned long)mStatus2.mMaxMP;
		}
		break;
	case ePLAYER_STATUS_EXT_RECOVHP:
		mStatus2.mRecovHP = value; break;
	case ePLAYER_STATUS_EXT_RECOVMP:
		mStatus2.mRecovMP = value; break;
	case ePLAYER_STATUS_EXT_HIT:
		mStatus2.mPhysicAttackRate = value; break;
	case ePLAYER_STATUS_EXT_MHIT:
		mStatus2.mMagicAttackRate = value; break;
	case ePLAYER_STATUS_EXT_AVOID:
		mStatus2.mAvoid = value; break;
	case ePLAYER_STATUS_EXT_WEIGHT:
		mStatus2.mMaxWeight = value; break;
	case ePLAYER_STATUS_EXT_ASPD:
		mStatus2.mAttackSpeed = (float)value; break;
	case ePLAYER_STATUS_EXT_COOLTIME:
		mStatus2.mCooltime = (float)value; break;
	case ePLAYER_STATUS_EXT_RANGE:
		mStatus2.mInRange = value; break;
	case ePLAYER_STATUS_EXT_RRANGE:
		mStatus2.mOutRange = value; break;
	case ePLAYER_STATUS_EXT_SPD:
		mStatus2.mMoveSpeed = value; break;

	case ePLAYER_STATUS_EXT_ATKMin:
		mStatus2.mPhysicMinAttack = value; break;
	case ePLAYER_STATUS_EXT_ATKMax:
		mStatus2.mPhysicMaxAttack = value; break;
	case ePLAYER_STATUS_EXT_RATKMin:
		mStatus2.mPhysicMinRangeAttack = value; break;
	case ePLAYER_STATUS_EXT_RATKMax:
		mStatus2.mPhysicMaxRangeAttack = value; break;
	case ePLAYER_STATUS_EXT_MATKMin:
		mStatus2.mMagicMinAttack = value; break;
	case ePLAYER_STATUS_EXT_MATKMax:
		mStatus2.mMagicMaxAttack = value; break;
	case ePLAYER_STATUS_EXT_HEALMin:
		mStatus2.mMinHeal = value; break;
	case ePLAYER_STATUS_EXT_HEALMax:
		mStatus2.mMaxHeal = value; break;
	}
}


void cHero::SetStatusEtc( ePLAYER_STATUS_EXT_ADD statusAdd, float value  )
{
	if( statusAdd >= ePLAYER_STATUS_EXT_ADD_MAX )
	{
		assert(NULL);
		return;
	}

	switch( statusAdd )
	{
	case ePLAYER_STATUS_EXT_ADD_EXPADDPER:
		mStatusEtc.mExpAddPer = value; break;
	case ePLAYER_STATUS_EXT_ADD_EXPADDPLUS:
		mStatusEtc.mExpAddPlus = value; break;
	case ePLAYER_STATUS_EXT_ADD_SXPADDPER:
		mStatusEtc.mSxpAddPer = value; break;
	case ePLAYER_STATUS_EXT_ADD_SXPADDPLUS:
		mStatusEtc.mSxpAddPlus = value; break;
	}
}


float cHero::CalcStatusSkillRange( float skillRange, eRANGETYPE mRangeType, bool itemPlus )
{

	if( mRangeType == 0 )
		return skillRange;

	/// 스킬 사거리
	float range = skillRange;

	/// 효과 사거리
	float statusRangePlus = 0;
	float statusRangePer = 0;

	if( mRangeType == eRANGETYPE_CLOSE )
	{
		statusRangePlus = STATUSCALC->GetStatus2Plus( ePLAYER_STATUS_EXT_RANGE );
		statusRangePer = STATUSCALC->GetStatus2Per( ePLAYER_STATUS_EXT_RANGE );
	}
	else if( mRangeType == eRANGETYPE_LONG )
	{
		statusRangePlus = STATUSCALC->GetStatus2Plus( ePLAYER_STATUS_EXT_RRANGE );
		statusRangePer = STATUSCALC->GetStatus2Per( ePLAYER_STATUS_EXT_RRANGE );
	}
	else
		assert(NULL);

	sEquipAbility* pBasicAbility = ITEMMAN->GetBasicAbil();

	/// 사거리 계산
	if( itemPlus == true )
	{
		range = ( range + pBasicAbility->mAttackRange ) * ( ( 100 + statusRangePer ) / 100 ) + statusRangePlus;
	}
	else
		range = range * ( ( 100 + statusRangePer ) / 100 ) + statusRangePlus;

	return range;
}


void cHero::InitStatusInfoType() 
{
	for( int i = ePLAYER_STATUS_STR ; ePLAYER_STATUS_LEVEL > i ; ++i )
	{
		mStatusBaseInfoType[i] = 0; 
	}
	for( int i = ePLAYER_STATUS_EXT_ATK ; ePLAYER_STATUS_EXT_MAX > i ; ++i )
	{
		mStatusExtInfoType[i] = 0; 
	}
}

void cHero::IdleUpdate( unsigned long deltaTime )
{
	mIdleTime += deltaTime;
	if( mIdleTime >= 18000 )
	{
		mIdleTime = 0;

		if( mPlayerExrInfo.mChgMonsterIdx == 0 )
		{
			/// idle2 animation
			if( mPlayerExrInfo.mStateIdle == eIDLE_PEACE )
				ChangeAnimation( ANITYPE_PEACE_IDLE2 );
			else
			{
				ChangeAnimation( ANITYPE_IDLE2 );
			}
		}
		else
		{
			ChangeAnimation_TransMon( M_ANITYPE_IDLE2 );
		}
	}
}

void cHero::MoveUpdate( unsigned long deltaTime )
{
	if( mPathCount == 0 )
		return;

	/// 현재 좌표를 획득
	NiPoint3 pos = mpObjectSceneNode->GetWorldTranslate();

	/// 이건 캐릭터별로 따로 설정되는 값이다.
	long speed = GetMoveSpeed();

	/// 보정을 위해서 목적지까지의 남은 거리를 계산한다.
	NiPoint2 dir1, dir2;
	dir1.x = mGotoX - pos.x;
	dir1.y = mGotoY - pos.y;
	dir1.Unitize();

	/// 속도 * 경과시간 만큼의 거리 계산
	pos.x += dir1.x * speed * (deltaTime * 0.001f);
	pos.y += dir1.y * speed * (deltaTime * 0.001f);
	pos.z = 0.0f;

	/// 보정을 위해서 현재계산된 위치로의 거리를 계산한다.
	dir2.x = mGotoX - pos.x;
	dir2.y = mGotoY - pos.y;
	dir2.Unitize();

	bool nextPath = false;
	if( dir2.Dot(dir1) <= 0.0f )
	{
		/// 이동중 deltaTime이 클경우(랙) 목적지를 넘어가는 경우 방지
		/// ※ if문을 빼고 목적지 끝마다 위치를 변경하면 정상플레이시 끊김발생
		if( deltaTime > 500 )
		{
			pos.x = mGotoX;
			pos.y = mGotoY;
		}

		if( mPathIndex < mPathCount-1 )
		{
			nextPath = true;
		}
		else
		{
			mPathCount = 0;
			SetState( eOBJECT_STATE_IDLE );
		}
	}

	if( WORLDMAN->CalcHeight( &pos.z, pos.x, pos.y ) )
	{
		SetPos( pos );
	}
	else
	{
		assert(0);
		return;
	}

	if( nextPath == true )
	{
		NiPoint2& nextPos = mPathArray[++mPathIndex];
		Move( nextPos.x, nextPos.y );
/*
		NiPoint2& nextPos = mPathArray[++mPathIndex];
		if( mPathFinder->IsPossible( NiPoint2(GetXPos(), GetYPos()), nextPos ) == false )
		{
			NiPoint2 start = NiPoint2( GetXPos(), GetYPos() );
			NiPoint2 goal = NiPoint2(mTargetPos.x, mTargetPos.y);

			mPathIndex = 0;
			if( mPathFinder->FindPath( mPathArray, &mPathCount, MAX_PATH_COUNT, start, goal ) == false )
			{
				assert(0);
				return;
			}

			Move( mPathArray[0].x, mPathArray[0].y );
		}
		else
		{
			Move( nextPos.x, nextPos.y );
		}
*/
	}

	/// 이동후 처리가 등록 되었다면 처리한다.
	/// 내부에서 거리비교에 따른 좌표 보정을 한다.
	ProcessAfterAction( pos );
}

void cHero::AttackUpdate( unsigned long deltaTime, unsigned long /*accumTime*/ )
{
	/// 스킬 동작 비완료시...
	if( IsReadyUseSkill() == false )
		return;

	/// 무기 교체 대기 - 교체
	if( mWaitWeaponSwitch )
	{
		if( mSwitchWeaponFlag == false )
		{
			if( ITEMMAN->TryToSwitchWeapon() )
				HERO->SetWeaponChangeFlag( true );
		}
		mWaitWeaponSwitch = false;

		if( mWaitMove == false )
			return;
	}

	/// 이동 대기인 경우
	if( mWaitMove )
	{
		if( mAfterAction.mActionType == eAfterAction_None )
			ResetMovePath( mWaitMovePos.x, mWaitMovePos.y );
		else
		{
			if( ResetActionMovePath( mWaitMovePos.x, mWaitMovePos.y, mAfterAction.mRange ) == false )
				mAfterAction.mActionType = eAfterAction_None;
		}

		mWaitMove = false;
		return;
	}


	/// test
	if( SKILLEXECUTER->IsSkillCharge() == true )
	{
		if( IsTransformMonster() == true )
			ChangeAnimation_TransMon( M_ANITYPE_IDLE1 );
		else
			ChangeAnimation( ANITYPE_IDLE );
		return;
	}

	/// 스킬 사용 대기
	/// 기본 공격 (착용 무기or직업군에 따라서... 자동 비자동으로 구분)
	if( IsTransformMonster() == true )
	{
		if( mWaitSkillIdx < eMONSTERATTACK_SKILL1 )
		{
			/// 자동공격에 어색하지 않도록 최소한의 딜레이를 주는게 좋을듯..
			if( SKILLEXECUTER->AutoAttack( GetTargetObject() ) == false ) 
				SetState( eOBJECT_STATE_IDLE );
		}
		else
		{
			/// 보유스킬 여부 및 cool time check
			if( SKILLMAN->IsUsedMonsterSkill( mWaitSkillIdx ) == false )
			{
				mWaitSkillIdx = 0;
				return;
			}

			SKILLEXECUTER->ExecuteSkill( mWaitSkillIdx );
			mWaitSkillIdx = 0;
		}
	}
	else
	{
		if( mWaitSkillIdx < NORMAL_ATTACK_SKILL_MAX )
		{
			/// 보유스킬 여부 및 cool time check
			if( SKILLMAN->IsUsedKeepSkill( NORMAL_ATTACK_SKILL ) == false )
				return;

			/// 자동공격에 어색하지 않도록 최소한의 딜레이를 주는게 좋을듯..
			if( SKILLEXECUTER->AutoAttack( GetTargetObject() ) == false ) 
				SetState( eOBJECT_STATE_IDLE );
		}
		else
		{
			/// 보유스킬 여부 및 cool time check
			if( SKILLMAN->IsUsedKeepSkill( mWaitSkillIdx ) == false )
			{
				mWaitSkillIdx = 0;
				return;
			}

			SKILLEXECUTER->ExecuteSkill( mWaitSkillIdx );
			mWaitSkillIdx = 0;
		}
	}
}


void cHero::Update( unsigned long deltaTime, unsigned long accumTime )
{
	if( mpObjectSceneNode  == NULL ) 
		return;

	if( mPlayerExrInfo.mChgMonsterIdx != 0 )
	{
		if( IsReadyUseSkill() )
		{
			if( mReadyRestoreMonster )
			{
				RestoreMonster();
				ChangeAnimation( ANITYPE_IDLE );
//				return;
			}
			else
				return Update_TransMon( deltaTime, accumTime );
		}
		else
			return Update_TransMon( deltaTime, accumTime );
	}

	ProcessDelayFlag( deltaTime );

	DesiredDirProcess( deltaTime );

	FaceProcess( deltaTime );

	/// 정보가 업데이트 될때마다 세팅
	if( mIsCalcStatus )
	{
		NowCalcStatus();
	}
	/// 스킬겨험치나 경험치가 업데이트될때마다 세팅
	else if( mIsUdateHeroInfo )
	{
		GAMEUI->UpdateExpSxp();

		mIsUdateHeroInfo = false;
	}


	/// 상태에 맞는 처리를 한다.
	switch( mPlayerExrInfo.mState )
	{
	case eOBJECT_STATE_IDLE:
		{
			IdleUpdate( deltaTime );
		}
		break;
	case eOBJECT_STATE_MOVE:
		{
			MoveUpdate( deltaTime );
		}
		break;
	case eOBJECT_STATE_ATTACK:
		{
			AttackUpdate( deltaTime, accumTime );
		}
		break;
	case eOBJECT_STATE_DIE:
		{
			DieUpdate();
		}
		break;
	case ePLAYER_STATE_SITDOWN:
		{
			SitDownUpdate();
		}
		break;
	}


	/// 애니메이션 연결에 대한 처리
	Interpret( accumTime );

	/// 카메라 처리
	cLookAtCamera* pCamera = (cLookAtCamera*)CAMERAMAN->GetCurrent();
	if( pCamera )
	{
		const NiPoint3& pos = mpObjectSceneNode->GetTranslate();
		pCamera->SetLookAt( pos.x, pos.y, pos.z + mCameraOffset );
	}

	///
	WORLDMAN->MoveSkyDome( pCamera->GetWorldTranslate() );
}

void cHero::ProcessAfterAction( NiPoint3& heroPos )
{
	if( mAfterAction.mActionType == eAfterAction_None )
		return;

	cBaseObject* pTarget = OBJECTMAN->GetObject( &mAfterAction.mTarget );
	NiPoint3 targetPos = mTargetPos;
	if( pTarget )
		targetPos = pTarget->GetPos();
	else
	{
		if( WORLDMAN->CalcHeight( &targetPos.z, targetPos.x, targetPos.y ) == false )
			assert(NULL);
	}

	NiPoint3 heroNotZPos = heroPos;
	heroNotZPos.z = 0.0f;
	NiPoint3 targetNotZPos = targetPos;
	targetNotZPos.z = 0.0f;

	/// 범위 검색이 기본..
	switch( mAfterAction.mActionType )
	{
	case eAfterAction_Attack:
		{	
			bool bAttack = false;
			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, mAfterAction.mRange );

			cRangeCheck rangecheck( tempRange );
			/// 상대방이 움직여서 내범위로 들어온 경우를 찾는다.
			NiPoint3 curPos = HERO->GetPos();
			if( rangecheck.IsRange( curPos, targetPos ) )
			{
				/// 이미 상대방이 내범위에 들어온 경우 멈춘다.
				heroPos.x = curPos.x;
				heroPos.y = curPos.y;

				bAttack = true;
			}
			else
			{
				/// 이동후 좌표로 범위 검사를 한다.
				if( rangecheck.IsRange( heroPos, targetPos ) )
				{
					NiPoint3 dir = heroPos - targetPos;
					if( dir.Unitize() != 0 )
					{
						/// heroPos 보정
						heroPos.x = targetPos.x + ((tempRange-1.0f)*dir.x);
						heroPos.y = targetPos.y + ((tempRange-1.0f)*dir.y);
					}

					bAttack = true;
				}
			}

			if( bAttack == true )
			{
				/// 공격 처리
				if( IsReadyUseSkill() )
				{
					/// 후처리 종료
					StopAfterAction();
					if( StartAttack( mAfterAction.mExData1, OBJECTMAN->GetObject( &mAfterAction.mTarget ) ) == false )
					{
						/// 공격 상태 유지
						SetState( eOBJECT_STATE_ATTACK );
						SetReadyUseSkill( true );
					}
				}
				else
				{
					/// 후처리 종료 , 공격 상태 유지
					mAfterAction.mActionType = eAfterAction_None;
					SetState( eOBJECT_STATE_ATTACK );
					SetReadyUseSkill( true );
				}
				return;
			}
		}
		break;
	case eAfterAction_UseTargetSkill:
		{
			bool bAttack = false;
			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, mAfterAction.mRange );

			cRangeCheck rangecheck( tempRange );

			/// 상대방이 움직여서 내범위로 들어온 경우를 찾는다.
			NiPoint3 curPos = HERO->GetPos();
			if( rangecheck.IsRange( curPos, targetPos ) )
			{
				/// 이미 상대방이 내범위에 들어온 경우 멈춘다.
				heroPos.x = curPos.x;
				heroPos.y = curPos.y;

				bAttack = true;
			}
			else
			{
				/// 이동후 좌표로 범위 검사를 한다.
				if( rangecheck.IsRange( heroPos, targetPos ) )
				{
					NiPoint3 dir = heroPos - targetPos;
					if( dir.Unitize() != 0 )
					{
						/// heroPos 보정
						heroPos.x = targetPos.x + ((tempRange-1.0f)*dir.x);
						heroPos.y = targetPos.y + ((tempRange-1.0f)*dir.y);
					}

					bAttack = true;
				}
			}

			if( bAttack == true )
			{
				/// 후처리 종료
				StopAfterAction();

				/// 공격 시작
				if( IsReadyUseSkill() )
				{
					UseTargetSkill( mAfterAction.mExData1, OBJECTMAN->GetObject(&mAfterAction.mTarget) );
				}

				mWaitSkillIdx = 0;
				return;
			}
		}
		break;
	case eAfterAction_UseFieldSkill:
		{
			bool bAttack = false;
			cRangeCheck rangecheck( mAfterAction.mRange );

			/// 상대방이 움직여서 내범위로 들어온 경우를 찾는다.
			NiPoint3 curPos = HERO->GetPos();
			if( rangecheck.IsRange( curPos, mAfterAction.mDestPos ) )
			{
				/// 이미 상대방이 내범위에 들어온 경우 멈춘다.
				heroPos.x = curPos.x;
				heroPos.y = curPos.y;

				bAttack = true;
			}
			else
			{
				/// 이동후 좌표로 범위 검사를 한다.
				if( rangecheck.IsRange( heroPos, mAfterAction.mDestPos ) )
				{
					NiPoint3 dir = heroPos - mAfterAction.mDestPos;
					if( dir.Unitize() != 0 )
					{
						/// heroPos 보정
						heroPos.x = mAfterAction.mDestPos.x + ((mAfterAction.mRange-1.0f)*dir.x);
						heroPos.y = mAfterAction.mDestPos.y + ((mAfterAction.mRange-1.0f)*dir.y);
					}

					bAttack = true;
				}
			}

			if( bAttack == true )
			{
				/// 후처리 종료
				StopAfterAction();

				/// 공격 시작
				UseFieldSkill( mAfterAction.mExData1, mAfterAction.mDestPos );

				mWaitSkillIdx = 0;
				return;
			}
		}
		break;
	case eAfterAction_ItemPick:
		{
#ifdef _GMTOOL
			if( CHEATMAN->GetHideMode() )
				return;
#endif

			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, 0, mAfterAction.mRange );

			cRangeCheck rangecheck( tempRange );

			if( rangecheck.IsRange( heroNotZPos, mAfterAction.mDestPos ) )
			{
				StopAfterAction();

				if( IsRideVehicle() == true )
				{
					MSGROOT msg;
					::memset( &msg, 0, sizeof(msg) );
					msg.Category = NM_PLAYER;
					msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
					NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

					AlightVehicle();
				}

				/// 루팅윈도우 닫기 
				GAMEUI->HideLootingWindow();

				if( ITEMMAN->IsLocked() == true )
					return;

				/// 상자 연속 오픈 시간 체크
				if( ITEMMAN->CheckOpenTime() == false )
					return;

				MSG_REQ_ITEM_GET_OPEN msg;
				msg.Category = NM_ITEM;
				msg.Protocol = NM_ITEM_GET_OPEN_REQ;
				msg.idx = mAfterAction.mExData1;

				NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

				SetState( ePLAYER_STATE_ITEMPICK );

				/// 락 걸기
				ITEMMAN->LockItemTry( ITEM_TRY_PICK );
			}

			return;
		}
		break;
	case eAfterAction_GatheringPick:
		{
#ifdef _GMTOOL
			if( CHEATMAN->GetHideMode() )
				return;
#endif
			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, 0, mAfterAction.mRange );

			cRangeCheck rangecheck( tempRange );
			if( rangecheck.IsRange( heroNotZPos, mAfterAction.mDestPos ) )
			{
				StopAfterAction();
				GatheringStart( mAfterAction.mExData1, mAfterAction.mExData2, mAfterAction.mDestPos );
			}
			return;
		}
		break;
	case eAfterAction_NpcTalk:
		{
			assert(mAfterAction.mTarget.type == eOBJECTTYPE_NPC);

			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, mAfterAction.mRange );

			cRangeCheck rangecheck( tempRange );

			if( rangecheck.IsRange( heroNotZPos, targetNotZPos ) )
			{
				StopAfterAction();

				MSG_REQ_NPC_OPEN Msg;
				Msg.Category = NM_NPC;
				Msg.Protocol = NM_NPC_OPEN_REQ;
				Msg.mNpcIdx = pTarget->GetObjectID();
				NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
			}

			return;
		}
		break;
	case eAfterAction_UseTarotStore:
		{
			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, mAfterAction.mRange );

			cRangeCheck rangecheck( tempRange );

			if( mAfterAction.mTarget.type != eOBJECTTYPE_PLAYER )
			{
				assert(0);
				StopAfterAction();
				return;
			}

			if( pTarget == 0 || pTarget->GetObjectType() != eOBJECTTYPE_PLAYER )
			{
				assert(0);
				StopAfterAction();
				return;
			}

			if( rangecheck.IsRange( heroNotZPos, targetNotZPos ) )
			{
				StopAfterAction();

				/// 타롯상점 정보 요청
				MSG_REQ_TAROT_SEEKER_OPEN msg;				// MSG_REQ_TAROT_SEEKER_JOIN msg;
				msg.Category = NM_TAROT;
				msg.Protocol = NM_TAROT_SEEKER_OPEN_REQ;	// NM_TAROT_SEEKER_JOIN_REQ
				msg.CharacterIdx = pTarget->GetObjectID();
				NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );
			}
			return;
		}
		break;
	case eAfterAction_UseStall:
		{
			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, mAfterAction.mRange );

			cRangeCheck ranceCheck( tempRange );

			if( mAfterAction.mTarget.type != eOBJECTTYPE_PLAYER )
			{
				assert(0);
				StopAfterAction();
				return;
			}

			if( pTarget == 0 || pTarget->GetObjectType() != eOBJECTTYPE_PLAYER )
			{
				assert(0);
				StopAfterAction();
				return;
			}

			if( ranceCheck.IsRange( heroNotZPos, targetNotZPos ) )
			{
				StopAfterAction();

				/// 노점상 열기 요청
				ITEMMAN->TryToUserSellJoin( mAfterAction.mTarget );
			}
		}
		break;
	case eAfterAction_MapChange:
		{
			float tempRange = OBJECTMANAGER->ObjectSizeRange( this, pTarget, mAfterAction.mRange );

			cRangeCheck rangecheck( tempRange );

			if( rangecheck.IsRange( heroNotZPos, mAfterAction.mDestPos ) )
			{
				StopAfterAction();

				sStageChangePos* pInfo = STAGESCRIPT->GetPosScriptInfo( (unsigned long)mAfterAction.mExData1 );
				assert(pInfo);
				if( pInfo && pInfo->mTargetInfo )
				{
					TCHAR str[256] = {0,};
					_stprintf(str,GAMERESOURCEMAN->GetGameText( 60 ), GAMERESOURCEMAN->GetMapName(pInfo->mTargetInfo->mMapNumber) );

					GAMEUI->ShowGameUI();

					///
					UIMAN->CreateModal( eStage_Game, eMBEVENT_MAPCHANGE, str, GAMERESOURCEMAN->GetGameText( 59 ), eSKIN_YESNO, (void*)mAfterAction.mExData1 );
				}
			}
			return;
		}
		break;
	default:
		return;
	}

	if( mAfterAction.mActionType == eAfterAction_MapChange )
		return;

	/// 타겟의 좌표가 변하는 경우 변화량에 의존한 타겟좌표 재설정
	if( pTarget )
	{
		if( mTargetPos != targetNotZPos )
		{
			float range = (heroNotZPos - mTargetPos).Length() * 0.10f;
			if( range < 100.0f )
				range = 100.0f;

			cRangeCheck r( range );
			if( r.IsRange( targetNotZPos, mTargetPos ) == false )
			{
				/// 제한 변화량 이상으로 타겟이 움직였다면..
				/// 객체의 고정 이동좌표를 바꾼다.
				if( ResetActionMovePath( targetNotZPos.x, targetNotZPos.y, mAfterAction.mRange ) == false )
				{
					assert(0);
				}
			}
		}
	}
}

bool cHero::Move( float x, float y )
{
	if( x < 0 || y < 0 )
	{
		assert(0);
		return false;
	}

	/// 이동.회전보간 - 도착지점 셋팅
	if( !cPlayer::Move( x, y ) )
	{
		return false;
	}

	/// 패킷날리기
	MSG_REQ_MOVE Msg;
	Msg.Category = NM_PLAYER;
	Msg.Protocol = NM_PLAYER_MOVE_REQ;
	Msg.startX = GetXPos();
	Msg.startY = GetYPos();
	Msg.destX = x;
	Msg.destY = y;
	NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );

	return true;
}

static unsigned int checkMove = 0;
bool cHero::ActionMove( float x, float y, float range )
{
	if(x < 0 || y < 0)
	{
		assert(0);
		return false;
	}

	/// 이동.회전보간 - 도착지점 셋팅
	if( !cPlayer::Move( x, y ) )
	{
		return false;
	}

	/// 패킷날리기
	MSG_REQ_ACTIONMOVE Msg;
	Msg.Category = NM_PLAYER;
	Msg.Protocol = NM_PLAYER_ACTIONMOVE_REQ;
	Msg.startX = GetXPos();
	Msg.startY = GetYPos();
	Msg.destX = x;
	Msg.destY = y;
	Msg.targetX = mTargetPos.x;
	Msg.targetY = mTargetPos.y;
	Msg.range = range;
	Msg.targetInfo = mAfterAction.mTarget;

	NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );

//	cStringT str;
//	str.Format( _T("(%d)actionMove %d %f [%f,%f]\n"), mWaitMove, checkMove++, range, x, y );
//	OutputDebugString( str.Cstr() );

	return true;
}

/// 새로운 path를 생성하고 첫번째 path로 이동..
bool cHero::ResetMovePath( float destX, float destY )
{
	if( IsReadyUseSkill() == false )
	{
		/// 스킬의 현재 상태가 캐스팅중이고 이동가능한지 체크한다.
		cSkillObject* p = SKILLEXECUTER->GetCurrentHeroSkillObject();
		if( p && p->GetDramaState() == eDRAMASTATE_CASTING )
		{
			cCastingBar* pWin = (cCastingBar*)UIMAN->GetContainer( eUIID_GAME_CASTINGBAR );
			if( pWin )
			{
				if( pWin->GetPercent() < 98.0f )
				{
					/// cast stop
					p->WaitCancel();
					pWin->Hide();
					HERO->SetState( eOBJECT_STATE_IDLE );

					/// 070712 PKH 스킬 요청 메세지 발송
					MSG_REQ_SKILL_CAST_CANCEL Msg;
					Msg.Category = NM_SKILL;
					Msg.Protocol = NM_SKILL_CAST_CANCEL_REQ;
					Msg.mAttacker.index = HERO->GetObjectID();
					Msg.mAttacker.type = HERO->GetObjectType();

					NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
				}
			}
			/// 캐스팅 취소 패킷을 쏜다..
			SKILLEXECUTER->SetCurrentHeroSkillObject( 0 );
		}
		else
		{
			mWaitMovePos.x = destX;
			mWaitMovePos.y = destY;
			mWaitMove = true;

			return false;
		}
	}

	if( GatheringCancel() == false )
		return false;

	if( ItemMixCancel() == false )
		return false;

	if( ItemEnhanceCancel() == false )
		return false;

	/// 이동 불가
	if( IsCantMove() == true )
		return false;

	/// 거리 제한
	NiPoint2 start( GetXPos(), GetYPos() );
	NiPoint2 goal( destX, destY );

	if( (start - goal).Length() > (float)MAX_PATH_DIST )
		return false;

	///
	mAfterAction.mActionType = eAfterAction_None;

	/// 길을 찾아보세~
	if( mPathFinder->FindPath( mPathArray, &mPathCount, MAX_PATH_COUNT, start, goal ) == false )
		return false;

	///
	mPathIndex = 0;
	mTargetPos.x = destX;
	mTargetPos.y = destY;
	mTargetPos.z = 0.0f;

	float z = 0.0f;
	if( WORLDMAN->CalcHeight( &z, mTargetPos.x, mTargetPos.y ) )
	{
		if( mMoveTargetEffectIdx != (unsigned long)-1 )
		{
			cEffectSceneNode* node = SCENEMAN->GetEffectSceneNode( mMoveTargetEffectIdx );
			if( node )
			{
				assert( node == mMoveTargetEffect );
				node->Remove();
			}
			mMoveTargetEffect = 0;
			mMoveTargetEffectIdx = (unsigned long)-1;
		}

		NiTransform trans;
		trans.MakeIdentity();
		trans.m_Translate = mTargetPos;
		trans.m_Translate.z = z;
		trans.m_fScale = 1.0f;

		mMoveTargetEffect = SCENEMAN->CreateSelfEffect( "./Data/Effect/Eff_PC_Target_03.nif", false, trans, true );
		if( mMoveTargetEffect )
			mMoveTargetEffectIdx = mMoveTargetEffect->GetIndexByManger();
	}
	else
	{
		assert(0);
	}


	/// 이동
	Move( mPathArray[0].x, mPathArray[0].y );
	return true;
}

bool cHero::ResetActionMovePath( float destX, float destY, float range )
{
	if( GatheringCancel() == false )
		return false;

	/// 이동 불가
	if( IsCantMove() == true )
		return false;

	if( range == 0.0f )
	{
		assert(0);
		return ResetMovePath( destX, destY );
	}

	if( IsReadyUseSkill() == false )
	{
		mWaitMovePos.x = destX;
		mWaitMovePos.y = destY;
		mWaitMove = true;

		return true;
	}

	/// 거리 제한
	NiPoint2 start( GetXPos(), GetYPos() );
	NiPoint2 goal( destX, destY );

	if( (start - goal).Length() > 30000.0f )
		return false;

	/// 길을 찾아보세~
	if( mPathFinder->FindPath( mPathArray, &mPathCount, MAX_PATH_COUNT, start, goal ) == false )
	{
		return false;
	}

	///
	mPathIndex = 0;
	mTargetPos.x = destX;
	mTargetPos.y = destY;
	mTargetPos.z = 0.0f;

	if( mMoveTargetEffectIdx != (unsigned long)-1 )
	{
		cEffectSceneNode* node = SCENEMAN->GetEffectSceneNode( mMoveTargetEffectIdx );
		if( node )
		{
			assert( node == mMoveTargetEffect );
			node->Remove();
		}
		mMoveTargetEffect = 0;
		mMoveTargetEffectIdx = (unsigned long)-1;
	}

	/// 이동
	ActionMove( mPathArray[0].x, mPathArray[0].y, range );
	return true;
	/*
	if( mPathFinder->ObstacleCheck( GetXPos(), GetYPos(), destX, destY ) )
	{
		/// 길찾기 시작
		mPathNodeSet.ResetPath( GetXPos(), GetYPos(), destX, destY );

		/// 갈수 있는지 검사.
		switch(mPathFinder->GetLastResult())
		{
		case PATH_OK:
			{
				NiPoint2 path = mPathNodeSet.GetNextPath();

				/// 최종 목적지 등록..
				mTargetPos.x = destX; mTargetPos.y = destY;
				mTargetPos.z = 0.0f;

				/// 길찾기의 다음 이동 좌표를 셋팅..
				destX = path.x; destY = path.y;

				/// 이동
				ActionMove( destX, destY, range );
			}
			break;
			/// 최종목적지가 막혀 있는 경우
		case PATH_BLOCK: 
			{
				return false;
			}
			/// 시작지점과 끝 지점이 같은 경우
		case PATH_ZERO:
			{
				float onetilesize = mPathFinder->GetOneTileSize();
				if( mPathFinder->IsObstacleIndex((unsigned int)(destX/onetilesize),(unsigned int)(destY/onetilesize)) )
				{
					return false;
				}
			}
			break;
		default: 
			{
				assert(0);
				return false;
			}
		}
	}
	else
	{
		/// 최종 목적지 등록..
		mTargetPos.x = destX; mTargetPos.y = destY;
		mTargetPos.z = 0.0f;

		/// 이동
		ActionMove( destX, destY, range );
	}
	return true;
	*/
}

void cHero::StopAfterAction()
{
	mAfterAction.mActionType = eAfterAction_None;
	SetState( eOBJECT_STATE_IDLE );

	mWaitSkillIdx = 0;
}

bool cHero::StartAttack( unsigned long attackIdx, cBaseObject* pTarget )
{
#ifdef _GMTOOL
	if( CHEATMAN->GetHideMode() )
		return false;
#endif

	if( IsRideVehicle() == true )
	{
		MSGROOT msg;
		::memset( &msg, 0, sizeof(msg) );
		msg.Category = NM_PLAYER;
		msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
		NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

		AlightVehicle();
	}

	/// 공격이 시작되는 시점( 자동 등록 제거 )
	AutoActionClear();

	if( GetState() == eOBJECT_STATE_DIE )
		return false;

	if( IsWeaponChanging() == true )
		return false;

	if( IsReadyUseSkill() == false )
		return false;

	if( pTarget == 0 )
	{
		assert(0);
		return false;
	}

	/// 바라봐야할 방향 설정
	NiPoint3 charDir = mDesiredDir;
	NiPoint3 viewDir = pTarget->GetPos() - GetPos();
	viewDir.z = 0.0f;
	if( viewDir != NiPoint3::ZERO )
	{
		if( viewDir.SqrLength() >= 0.001f )	
		{
			viewDir.Unitize();

			/// 회전 Matrix 산출
			NiMatrix3 matA, matB, matRot;
			matA.SetCol( 0, charDir );
			matA.SetCol( 1, -NiPoint3::UNIT_Z );
			matA.SetCol( 2, charDir.UnitCross( -NiPoint3::UNIT_Z ) );

			matB.SetCol( 0, viewDir );
			matB.SetCol( 1, -NiPoint3::UNIT_Z );
			matB.SetCol( 2, viewDir.UnitCross( -NiPoint3::UNIT_Z ) );

			matRot = matB * matA.Transpose();
			mpObjectSceneNode->SetRotate( mpObjectSceneNode->GetRotate()*matRot );
			mDesiredDir = viewDir;
		}
	}

	if( IsTransformMonster() )
		return StartAttack_Mon( attackIdx, pTarget );

	/// 쿨타임 체크
	if( SKILLMAN->IsUsedKeepSkill( NORMAL_ATTACK_SKILL ) == false )
	{
		return false;
	}

	/// 기본 스킬 생성..
	unsigned long key = SKILLMAN->CreateHeroSkillObject( attackIdx, this, pTarget, mPlayerExrInfo.mAttackSpeed );
	if( key )
	{
		/// 상태 설정
		SetState( eOBJECT_STATE_ATTACK );
		SetReadyUseSkill( false );

		/// 070712 PKH 스킬 요청 메세지 발송
		MSG_REQ_SKILL_USED Msg;
		Msg.Category = NM_SKILL;
		Msg.Protocol = NM_SKILL_USED_REQ;
		Msg.mTarget.index = pTarget->GetObjectID();
		Msg.mTarget.type = pTarget->GetObjectType();

		Msg.mSkillClassIndex = attackIdx;

		Msg.mManagedKey = key;
		Msg.mClientPosX = HERO->GetXPos();
		Msg.mClientPosY = HERO->GetYPos();

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}

	return true;
}

void cHero::UseTargetSkill( unsigned long skillIdx, cBaseObject* pTarget )
{
#ifdef _GMTOOL
	if( CHEATMAN->GetHideMode() )
		return;
#endif

	if( IsTransformMonster() )
	{
		UseTargetSkill_TransMon( skillIdx, pTarget );
		return;
	}
	else
	{
		if( IsRideVehicle() == true )
		{
			MSGROOT msg;
			::memset( &msg, 0, sizeof(msg) );
			msg.Category = NM_PLAYER;
			msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
			NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

			AlightVehicle();
		}
	}

	AutoActionClear();
	SKILLEXECUTER->ChargeOff( skillIdx );

	if( IsWeaponChanging() == true )
		return;

	/// 적용 타겟 검증
	if( SKILLEXECUTER->IsPossibleUseSkillTarget( skillIdx, pTarget ) == false )
		return;

	/// 발동 검증
	if( SKILLEXECUTER->IsPossibleUseSkill( skillIdx ) == false )
		return;

	if( pTarget )
	{
		/// 바라봐야할 방향 설정
		NiPoint3 charDir = mDesiredDir;
		NiPoint3 viewDir = pTarget->GetPos() - GetPos();
		viewDir.z = 0.0f;
		if( viewDir != NiPoint3::ZERO )
		{
			if( viewDir.SqrLength() >= 0.001f )	
			{
				viewDir.Unitize();

				/// 회전 Matrix 산출
				NiMatrix3 matA, matB, matRot;
				matA.SetCol( 0, charDir );
				matA.SetCol( 1, -NiPoint3::UNIT_Z );
				matA.SetCol( 2, charDir.UnitCross( -NiPoint3::UNIT_Z ) );

				matB.SetCol( 0, viewDir );
				matB.SetCol( 1, -NiPoint3::UNIT_Z );
				matB.SetCol( 2, viewDir.UnitCross( -NiPoint3::UNIT_Z ) );

				matRot = matB * matA.Transpose();
				mpObjectSceneNode->SetRotate( mpObjectSceneNode->GetRotate()*matRot );
				mDesiredDir = viewDir;
			}
		}
	}

	unsigned long key = SKILLMAN->CreateHeroSkillObject( skillIdx, this, pTarget );
	if( key )
	{
		sPlayerSkillBaseInfo* pInfo = SKILLSCRIPT->GetPlayerSkillInfo( skillIdx );
		if( pInfo )
		{
			if( mMageJobType == true )
				SetState( eOBJECT_STATE_IDLE, false );
			else
				SetState( eOBJECT_STATE_ATTACK );
		}
		else
		{
			SetState( eOBJECT_STATE_ATTACK );
		}

		/// 상태 설정
		SetReadyUseSkill( false );

		/// 070712 PKH 스킬 요청 메세지 발송
		MSG_REQ_SKILL_USED Msg;
		Msg.Category = NM_SKILL;

		/// casting이 있는 경우
		if( SKILLSCRIPT->IsCastSkill( skillIdx, 0 ) == true )
			Msg.Protocol = NM_SKILL_CAST_USED_REQ;
		else
			Msg.Protocol = NM_SKILL_USED_REQ;

		Msg.mSkillClassIndex = skillIdx;
		Msg.mManagedKey = key;
		Msg.mClientPosX = HERO->GetXPos();
		Msg.mClientPosY = HERO->GetYPos();


		if( pTarget )
		{
			Msg.mTarget.index = pTarget->GetObjectID();
			Msg.mTarget.type = pTarget->GetObjectType();
		}
		else
		{
			Msg.mTarget.index = 0;
			Msg.mTarget.type = eOBJECTTYPE_NONE;
		}

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}
}

void cHero::UseFieldSkill( unsigned long skillIdx, NiPoint3 fieldPos )
{
#ifdef _GMTOOL
	if( CHEATMAN->GetHideMode() )
		return;
#endif

	if( IsTransformMonster() )
	{
		UseFieldSkill_TransMon( skillIdx, fieldPos );
		return;
	}
	else
	{
		if( IsRideVehicle() == true )
		{
			MSGROOT msg;
			::memset( &msg, 0, sizeof(msg) );
			msg.Category = NM_PLAYER;
			msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
			NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

			AlightVehicle();
		}
	}

	AutoActionClear();
	SKILLEXECUTER->ChargeOff( skillIdx );

	if( IsWeaponChanging() == true )
		return;

	/// 발동 검증
	if( SKILLEXECUTER->IsPossibleUseSkill( skillIdx ) == false )
		return;

	/// 바라봐야할 방향 설정
	NiPoint3 charDir = mDesiredDir;
	NiPoint3 viewDir = fieldPos - GetPos();
	viewDir.z = 0.0f;
	if( viewDir != NiPoint3::ZERO )
	{
		if( viewDir.SqrLength() >= 0.001f )	
		{
			viewDir.Unitize();

			/// 회전 Matrix 산출
			NiMatrix3 matA, matB, matRot;
			matA.SetCol( 0, charDir );
			matA.SetCol( 1, -NiPoint3::UNIT_Z );
			matA.SetCol( 2, charDir.UnitCross( -NiPoint3::UNIT_Z ) );

			matB.SetCol( 0, viewDir );
			matB.SetCol( 1, -NiPoint3::UNIT_Z );
			matB.SetCol( 2, viewDir.UnitCross( -NiPoint3::UNIT_Z ) );

			matRot = matB * matA.Transpose();
			mpObjectSceneNode->SetRotate( mpObjectSceneNode->GetRotate()*matRot );
			mDesiredDir = viewDir;
		}
	}

	unsigned long key = SKILLMAN->CreateHeroSkillObject( skillIdx, this, fieldPos );
	if( key )
	{
		sPlayerSkillBaseInfo* pInfo = SKILLSCRIPT->GetPlayerSkillInfo( skillIdx );
		if( pInfo )
		{
			if( mMageJobType == true )
				SetState( eOBJECT_STATE_IDLE, false );
			else
				SetState( eOBJECT_STATE_ATTACK );
		}
		else
		{
			SetState( eOBJECT_STATE_ATTACK );
		}

		/// 상태 설정
		SetReadyUseSkill( false );

		/// 070712 PKH 스킬 요청 메세지 발송

		MSG_REQ_POS_USED Msg;
		Msg.Category = NM_SKILL;

		/// casting이 있는 경우
		if( SKILLSCRIPT->IsCastSkill( skillIdx, 0 ) == true )
			Msg.Protocol = NM_SKILL_POS_CAST_USED_REQ;
		else
			Msg.Protocol = NM_SKILL_POS_USED_REQ;

		Msg.mSkillClassIndex = skillIdx;
		Msg.mManagedKey = key;
		Msg.mClientPosX = HERO->GetXPos();
		Msg.mClientPosY = HERO->GetYPos();

		Msg.mTargetPosX = fieldPos.x;
		Msg.mTargetPosY = fieldPos.y;

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}
}

void cHero::UseMapChangeSkill( unsigned long skillIdx, unsigned int slotIdx )
{
	if( PVPMAN->IsEnableBattle() == true )
	{
		CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText(842) );
		return;
	}

	if( IsTransformMonster() == false )
	{
		if( IsRideVehicle() == true )
		{
			MSGROOT msg;
			::memset( &msg, 0, sizeof(msg) );
			msg.Category = NM_PLAYER;
			msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
			NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

			AlightVehicle();
		}
	}

	AutoActionClear();
	if( IsReadyUseSkill() == false )
		return;

	SetWaitMoveFlag( false );

	SKILLEXECUTER->ChargeOff( SKILLEXECUTER->GetChargeSkill() );
	if( skillIdx < NORMAL_ATTACK_SKILL_MAX )
	{
		assert(0);
		return;
	}

	/// 상태 검사
	if( !(GetState() == eOBJECT_STATE_IDLE || GetState() == eOBJECT_STATE_MOVE) )
		return;

	if( IsWeaponChanging() == true )
		return;

	/// 스킬 정보를 획득한다.
	sPlayerSkillBaseInfo* pInfo = SKILLSCRIPT->GetPlayerSkillInfo( skillIdx );
	if( pInfo == 0 )
	{
		assert(0);
		return;
	}
	sPlayerSkillStepInfo* pStepInfo = &pInfo->mpSetpInfoArray[0];

	/// 사용 가능 여부 체크
	if( pInfo->mUseItem )
	{
		unsigned int count = ITEMMAN->GetItemCount( pInfo->mUseItem );
		if( count < pStepInfo->mUseItemCount )
			return;
	}

	if( GetLevel() < pStepInfo->mPlayerLevel )
		return;

	/// 스킬 발동
	unsigned long key = SKILLMAN->CreateHeroMapChangeSkillObject( skillIdx, this );
	if( key )
	{
		SetState( eOBJECT_STATE_ATTACK );

		/// 상태 설정
		SetReadyUseSkill( false );

		/// 070712 PKH 스킬 요청 메세지 발송
		MSG_REQ_SKILL_ITEMMAPCHANGE Msg;
		Msg.Category = NM_SKILL;

		/// casting이 있는 경우
		Msg.Protocol = NM_SKILL_ITEMMAPCHANGE_REQ;
		Msg.mSkillClassIndex = skillIdx;
		Msg.mManagedKey = key;
		Msg.mClientPosX = HERO->GetXPos();
		Msg.mClientPosY = HERO->GetYPos();

		Msg.mTarget.index = HERO->GetObjectID();
		Msg.mTarget.type = eOBJECTTYPE_HERO;
		Msg.mSlotNumber = slotIdx;
		Msg.mUseItemIndex = pInfo->mUseItem;

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}
}

void cHero::UseVehicleSkill( unsigned long skillIdx, unsigned int slotIdx )
{
	AutoActionClear();

	if( IsTransformMonster() == true )
		return;
	if( IsReadyUseSkill() == false )
		return;

	SetWaitMoveFlag( false );

	SKILLEXECUTER->ChargeOff( SKILLEXECUTER->GetChargeSkill() );
	if( skillIdx < NORMAL_ATTACK_SKILL_MAX )
	{
		assert(0);
		return;
	}

	/// 상태 검사
	if( !(GetState() == eOBJECT_STATE_IDLE || GetState() == eOBJECT_STATE_MOVE) )
		return;

//	if( IsWeaponChanging() == true )
//		return;

	/// 스킬 정보를 획득한다.
	sPlayerSkillBaseInfo* pInfo = SKILLSCRIPT->GetPlayerSkillInfo( skillIdx );
	if( pInfo == 0 )
	{
		assert(0);
		return;
	}
	sPlayerSkillStepInfo* pStepInfo = &pInfo->mpSetpInfoArray[0];

	if( IsRideVehicle() == true )
	{
		MSGROOT msg;
		::memset( &msg, 0, sizeof(msg) );
		msg.Category = NM_PLAYER;
		msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
		NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

		AlightVehicle();

		///
		cInfluenceMap::cIterator i = mBuffMap.Begin();
		cInfluenceMap::cIterator end = mBuffMap.End();
		for( ; i != end; ++i )
		{
			cInfluenceObject* p = (cInfluenceObject*)(*i).mSecond;
			if( p )
			{
				if( p->GetInfluenceIdx() == pStepInfo->mInfulenceIdx )
					return;
			}
		}
	}

	/// 사용 가능 여부 체크
	if( pInfo->mUseItem )
	{
		unsigned int count = ITEMMAN->GetItemCount( pInfo->mUseItem );
		if( count < pStepInfo->mUseItemCount )
			return;
	}

	if( GetLevel() < pStepInfo->mPlayerLevel )
		return;

	/// 스킬 발동
	unsigned long key = SKILLMAN->CreateHeroVehicleSkillObject( skillIdx, this );
	if( key )
	{
		SetState( eOBJECT_STATE_ATTACK );

		/// 상태 설정
		SetReadyUseSkill( false );

		/// 070712 PKH 스킬 요청 메세지 발송
		MSG_REQ_SKILL_VEHICLE Msg;
		Msg.Category = NM_SKILL;

		/// casting이 있는 경우
		Msg.Protocol = NM_SKILL_ITEMVEHICLE_REQ;
		Msg.mSkillClassIndex = skillIdx;
		Msg.mManagedKey = key;
		Msg.mClientPosX = HERO->GetXPos();
		Msg.mClientPosY = HERO->GetYPos();

		Msg.mTarget.index = HERO->GetObjectID();
		Msg.mTarget.type = eOBJECTTYPE_HERO;
		Msg.mSlotNumber = slotIdx;
		Msg.mUseItemIndex = pInfo->mUseItem;

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}
}


void cHero::AutoActionClear()
{
	mWaitMove = false;
	mWaitSkillIdx = 0;

	mAfterAction.mActionType = eAfterAction_None;
}

void cHero::StartNpcTalk( unsigned long npcIdx )
{ 
	assert(!mNpcIdx);
	mNpcIdx = npcIdx; 

	SetStopFlag( eSTOP_NPCSPEECH );
}

void cHero::EndNpcTalk()
{
	mNpcIdx = 0;
}

void cHero::SendEndNpcTalk()
{
	/// 죽은 경우에는 메세지를 보내지 않는다.
	if( HERO->GetState() != eOBJECT_STATE_DIE )
	{
		SetStopFlag( eSTOP_NONE );
		SetState( eOBJECT_STATE_IDLE );

		MSGROOT reqMsg;
		reqMsg.Category = NM_NPC;
		reqMsg.Protocol = NM_NPC_CLOSE_REQ;
		NETWORK->SendNetworkMsg( (char*)&reqMsg, sizeof(reqMsg) );
	}
}

/// 애니메이션 관계 처리
void cHero::Interpret( unsigned long time )
{
	if( mpObjectSceneNode->GetType() == cSceneNode::eVEHICLE )
	{
		return InterpretVehicle( time );
	}

	cActorManagerForPartition*	pActor = mpObjectSceneNode->GetActorManager();
	if( !pActor ) return;

	unsigned int id = mpObjectSceneNode->GetTargetAnimation();
	float checkTime = (float)mpObjectSceneNode->GetScaleAccumTime() * 0.001f;

	float endTime = pActor->GetNextEventTime( cActorManagerForPartition::TEXT_KEY_EVENT, id, "end" );
	if( endTime == NI_INFINITY )
		return;

	if( endTime == cActorManagerForPartition::INVALID_TIME )
	{
		endTime = mpObjectSceneNode->GetTargetAnimationScaleEndTime() * 0.001f;
	}

	InterpretEvent( id, checkTime, pActor );

	if( endTime > 0 && endTime <= checkTime )
	{
		if( id >= ANITYPE_IDLE && id < ANITYPE_IDLE2 )
		{
			return;
		}
		else if( id >= ANITYPE_IDLE2 && id < ANITYPE_RUN )
		{
			ChangeAnimation( ANITYPE_IDLE );
		}
		else if( id >= ANITYPE_VEHICLE_IDLE2 && id < ANITYPE_VEHICLE_RUN )
		{
			ChangeAnimation( ANITYPE_IDLE );
		}
		else if( id>= ANITYPE_RUN && id < ANITYPE_ATTACK1 )
		{
			return;
		}
		else if( id >= ANITYPE_ATTACK1 && id < ANITYPE_CASTING1 )
		{
			SetReadyUseSkill( true );
			ChangeAnimation( ANITYPE_IDLE );
		}
		else if( id >= ANITYPE_SKILL_BEGIN && id < ANITYPE_SKILL_END )
		{
			if( mMageJobType )
			{
				/// switch weapon check
				if( mWaitWeaponSwitch )
				{
					if( HERO->IsWeaponChanging() == false )
					{
						if( ITEMMAN->TryToSwitchWeapon() )
							HERO->SetWeaponChangeFlag( true );
					}
					mWaitWeaponSwitch = false;
				}

				SetReadyUseSkill( true );
				SetState( eOBJECT_STATE_IDLE );

				cStringT str;
				str.Format( _T("\nTime Check : %f"), endTime );
				OutputDebugString( str.Cstr() );
			}
			else
			{
				SetReadyUseSkill( true );
				ChangeAnimation( ANITYPE_IDLE );
			}
		}
		else if( mPlayerExrInfo.mState == ePLAYER_STATE_ITEMPICK )
		{
			SetRemoveDropItem( UINT_MAX );
			SetState( eOBJECT_STATE_IDLE );
			ShowWeapon( true );
		}
		else if( id == ANITYPE_PEACE_IDLE2 )
		{
			ChangeAnimation( ANITYPE_PEACE_IDLE );
		}
		else if( id >= ANITYPE_DAMAGE1 && id < ANITYPE_DIE1 )
		{
			if( !(ANITYPE_CASTING1 <= mDamageAfterAnim && mDamageAfterAnim < ANITYPE_DAMAGE1) )
			{
				SetReadyUseSkill( true );
				ChangeAnimation( ANITYPE_IDLE );
			}
			else
			{
				ChangeAnimation( (ePLAYER_ANIMATIONTYPE)mDamageAfterAnim );
			}
		}
		else if( id == ANITYPE_STANDUP )
		{
			SetState( eOBJECT_STATE_IDLE );
		}
		else if( id == ANITYPE_SITDOWN )
		{
			ChangeAnimation( ANITYPE_SITDOWN_IDLE );
			mSitDownFlag = false;
		}
		else if( id >= ANITYPE_EMOTE_START && id < ANITYPE_VEHICLE_IDLE )
		{
			SetState( eOBJECT_STATE_IDLE );
			ShowWeapon( true );
		}
		else if( id >= ANITYPE_ENHANCEDSUCCESS && id <= ANITYPE_ENHANCEDFAIL )
		{
			ChangeAnimation( ANITYPE_IDLE );
			ShowWeapon( true );
		}
	}
	else if( endTime == cActorManagerForPartition::INVALID_TIME )
	{
		return;
	}
}

/*
void cHero::ViewPlayerStatus()
{
	/// 캐릭터 경험치
	sExpTable*	pExp = LEVELSCRIPT->GetExpTable( GetLevel() );
	unsigned int exp = GetPlayerExrInfo()->Exp;
	sSxpTable*	pSxp = LEVELSCRIPT->GetSxpTable( GetSkillLevel() );
	unsigned int sxp = GetPlayerExrInfo()->SkillExp;

	//////////////////////////////////////////////////////////////
	/// 070216 kkr 캐릭터 스테이트창 세팅 ( 화면 왼쪽 상단 )
	cHeroStateWindow* pStateWindow = (cHeroStateWindow*)UIMAN->GetContainer( eUIID_GAME_HERO_STATEWINDOW );

	/// 레벨 세팅
	pStateWindow->SetLevel( GetLevel() );

	unsigned char percent = static_cast<unsigned char>(exp * 100 / pExp->mExp );
	pStateWindow->SetEXPLabel( percent );

	pStateWindow->SetMAXEXP1( 10 );
	pStateWindow->SetMAXEXP2( 100 );
	pStateWindow->SetEXP1( (float)(percent % 10) );
	pStateWindow->SetEXP2( (float)(percent / 10 * 10) );	

	/// 캐릭터 스킬 경험치
	pStateWindow->SetMAXSXP( (float)pSxp->mSxp );
	pStateWindow->SetSXP( static_cast<float>(sxp) );
}



void cHero::ViewPlayerInfo()
{
	/// 캐릭터 경험치
	sExpTable*	pExp = LEVELSCRIPT->GetExpTable( GetLevel() );
	unsigned int exp = GetPlayerExrInfo()->Exp;
	sSxpTable*	pSxp = LEVELSCRIPT->GetSxpTable( GetSkillLevel() );
	unsigned int sxp = GetPlayerExrInfo()->SkillExp;

	//////////////////////////////////////////////////////////////
	/// 070122 PKH 캐릭터 정보창 얻어오기
	cHeroInfoWindow* pInfoWindow = (cHeroInfoWindow*)UIMAN->GetContainer( eUIID_GAME_HERO_INFOWINDOW );

	/// 캐릭터 레벨
	pInfoWindow->SetLevel( GetLevel() );

	SetExp( pExp->mExp, exp );
	pInfoWindow->SetMAXEXP( (float)pExp->mExp );
	pInfoWindow->SetEXP( (float)exp );

	/// 캐릭터 스킬 레벨
	pInfoWindow->SetSkillLevel( mPlayerExrInfo.SkillLevel );
	/// 캐릭터 스킬 포인트
	pInfoWindow->SetSkillPoint( GetSkillPointRemain() );
	/// 캐릭터 스킬 경험치
	pInfoWindow->SetMAXSXP( (float)pSxp->mSxp );
	pInfoWindow->SetSXP( static_cast<float>(sxp) );

	/// 캐릭터 Weight , MAX Weight 세팅 
	pInfoWindow->SetMAXWeight( (float)GetMaxWeight(), eCOLOR_BLACK );
	pInfoWindow->SetWeightNoAni( 55 );

	/// 클래스
	pInfoWindow->SetClass( GetJob() );


	/// 명성
	//pInfoWindow->SetFame( 123456 );
	/// 길드
	//pInfoWindow->SetGuild( "임시길드" );
	/// 직위
	//pInfoWindow->SetRank( "임시직위" );

	/// 힘
	pInfoWindow->SetStr( static_cast<int>(GetBaseStatus( ePLAYER_STATUS_BASE_STR )), eCOLOR_WHITE );	
	/// 민첩
	pInfoWindow->SetDex( static_cast<int>(GetBaseStatus( ePLAYER_STATUS_BASE_DEX )), eCOLOR_WHITE );	
	/// 체력
	pInfoWindow->SetCon( static_cast<int>(GetBaseStatus( ePLAYER_STATUS_BASE_CON )), eCOLOR_WHITE );	
	/// 지능
	pInfoWindow->SetInt( static_cast<int>(GetBaseStatus( ePLAYER_STATUS_BASE_INT )), eCOLOR_WHITE );	
	/// 지혜
	pInfoWindow->SetWis( static_cast<int>(GetBaseStatus( ePLAYER_STATUS_BASE_WIS )), eCOLOR_WHITE );	

	/// 스테이터스 추가량 출력( 노말 상태는 있을수 없음)
	pInfoWindow->SetPlusStr( static_cast<int>(GetBaseStatusPlus( ePLAYER_STATUS_BASE_STR )), GetStatusColor( eSTATUSCOLORPOS_STRENGTH ) );
	pInfoWindow->SetPlusAgi( static_cast<int>(GetBaseStatusPlus( ePLAYER_STATUS_BASE_DEX )), GetStatusColor( eSTATUSCOLORPOS_DEXTERITY ) );
	pInfoWindow->SetPlusCon( static_cast<int>(GetBaseStatusPlus( ePLAYER_STATUS_BASE_CON )), GetStatusColor( eSTATUSCOLORPOS_CONSTITUTION ) );
	pInfoWindow->SetPlusInt( static_cast<int>(GetBaseStatusPlus( ePLAYER_STATUS_BASE_INT )), GetStatusColor( eSTATUSCOLORPOS_INTELLIGENCE ) );
	pInfoWindow->SetPlusWis( static_cast<int>(GetBaseStatusPlus( ePLAYER_STATUS_BASE_WIS )), GetStatusColor( eSTATUSCOLORPOS_WISDOM ) );

	/// 공격력
	pInfoWindow->SetAtt( static_cast<int>(GetStatusAtk()), GetStatusColor( eSTATUSCOLORPOS_EXTATK ) );	
	/// 방어력
	pInfoWindow->SetDef( static_cast<int>(GetStatusDef()), GetStatusColor( eSTATUSCOLORPOS_EXTDEF ) );	
	/// 마법력
	pInfoWindow->SetMag( static_cast<int>(GetStatusMAtk()), GetStatusColor( eSTATUSCOLORPOS_EXTMATK ) );	
	/// 저항력
	pInfoWindow->SetRes( static_cast<int>(GetStatusMDef()), GetStatusColor( eSTATUSCOLORPOS_EXTMDEF ) );	
	/// 명중
	pInfoWindow->SetHit( static_cast<int>(GetStatusHit()), GetStatusColor( eSTATUSCOLORPOS_EXTHIT ) );	
	/// 회피
	pInfoWindow->SetAvoid( static_cast<int>(GetStatusAvoid()), GetStatusColor( eSTATUSCOLORPOS_EXTAVOID ) );	
}
*/

NiPoint3 cHero::GetDirection() const
{
	return mDesiredDir;//mpObjectSceneNode->GetWorldRotate() * -NiPoint3::UNIT_Y;
}

const NiMatrix3& cHero::GetRotate() const
{
	return mpObjectSceneNode->GetWorldRotate();
}

void cHero::AttachListener()
{
	//	((NiNode*)mpObjectSceneNode->GetNiObject())->AttachChild( SOUNDSYS->GetListener() );
}

cDynamicSceneNode* cHero::CreateSceneNode( cPlayerSceneNodeParam* param )
{
	return SCENEMAN->CreateHero( *param );
}

void cHero::AddBuff( unsigned long uniqueIdx, cInfluenceObject* p )
{
	if( p == 0 )
		return;

	cPlayer::AddBuff( uniqueIdx, p );

	/// buff 윈도우에 등록
	cBuffWindow* pWin = GAMEUI->GetBuffWindow();
	if( pWin )
	{
		pWin->AddIcon( uniqueIdx, p->GetInfluenceIdx() );
	}

	if( DUELMAN->IsDuelEnable() )
	{
		cDuelWindow* win = GAMEUI->GetDuelWindow();
		if( win )
			win->AddBuff( false, uniqueIdx, p->GetInfluenceIdx() );
	}

}

void cHero::DelBuff( unsigned long uniqueIdx )
{
	cInfluenceObject* p = (cInfluenceObject*)mBuffMap.GetAt( uniqueIdx );
	if( p )
	{
		cBuffWindow* pWin = GAMEUI->GetBuffWindow();
		if( pWin )
		{
			pWin->DelIcon( uniqueIdx );
		}

		if( DUELMAN->IsDuelEnable() )
		{
			cDuelWindow* win = GAMEUI->GetDuelWindow();
			if( win )
				win->DelBuff( false, uniqueIdx );
		}
	}
	cPlayer::DelBuff( uniqueIdx );
}

void cHero::AddDeBuff( unsigned long uniqueIdx, cInfluenceObject* p )
{
	if( p == 0 )
		return;

	cPlayer::AddDeBuff( uniqueIdx, p );

	/// debuff 윈도우에 등록
	cBuffWindow* pWin = GAMEUI->GetDeBuffWindow();
	if( pWin )
	{
		pWin->AddIcon( uniqueIdx, p->GetInfluenceIdx() );
	}

	if( DUELMAN->IsDuelEnable() )
	{
		cDuelWindow* win = GAMEUI->GetDuelWindow();
		if( win )
			win->AddDeBuff( false, uniqueIdx, p->GetInfluenceIdx() );
	}
}

void cHero::DelDeBuff( unsigned long uniqueIdx )
{
	cInfluenceObject* p = (cInfluenceObject*)mDeBuffMap.GetAt( uniqueIdx );
	if( p )
	{
		cBuffWindow* pWin = GAMEUI->GetDeBuffWindow();
		if( pWin )
		{
			pWin->DelIcon( uniqueIdx );
		}

		if( DUELMAN->IsDuelEnable() )
		{
			cDuelWindow* win = GAMEUI->GetDuelWindow();
			if( win )
				win->DelDeBuff( false, uniqueIdx );
		}
	}

	cPlayer::DelDeBuff( uniqueIdx );
}

void cHero::ClearAllBuff()
{
	cInfluenceMap::cIterator i = mBuffMap.Begin();
	cInfluenceMap::cIterator end = mBuffMap.End();

	cBuffWindow* pWin = GAMEUI->GetBuffWindow();

	for( ; i != end; ++i )
	{
		cInfluenceObject* p = (cInfluenceObject*)(*i).mSecond;
		if( p )
		{
			if( pWin )
				pWin->DelIcon( p->GetManagedKey() );

			SKILLMAN->DeleteInfluenceObject( p );
		}
	}
	mBuffMap.Clear();
}

void cHero::ClearAllDeBuff()
{
	cInfluenceMap::cIterator i = mDeBuffMap.Begin();
	cInfluenceMap::cIterator end = mDeBuffMap.End();

	cBuffWindow* pWin = GAMEUI->GetDeBuffWindow();

	for( ; i != end; ++i )
	{
		cInfluenceObject* p = (cInfluenceObject*)(*i).mSecond;
		if( p )
		{
			if( pWin )
				pWin->DelIcon( p->GetManagedKey() );

			SKILLMAN->DeleteInfluenceObject( p );
		}
	}
	mDeBuffMap.Clear();
}

void cHero::ClearDieBuff()
{
	cInfluenceMap::cIterator i = mBuffMap.Begin();
	cInfluenceMap::cIterator end = mBuffMap.End();

	cBuffWindow* pWin = GAMEUI->GetBuffWindow();

	while( i != end )
	{
		cInfluenceObject* p = (cInfluenceObject*)(*i++).mSecond;
		if( p && p->DieDelete() )
		{
			if( pWin )
				pWin->DelIcon( p->GetManagedKey() );

			SKILLMAN->DeleteInfluenceObject( p );

			mBuffMap.Erase( p->GetManagedKey() );
		}
	}


	cInfluenceMap::cIterator i2 = mDeBuffMap.Begin();
	cInfluenceMap::cIterator end2 = mDeBuffMap.End();

	cBuffWindow* pWin2 = GAMEUI->GetDeBuffWindow();

	while( i2 != end2 )
	{
		cInfluenceObject* p = (cInfluenceObject*)(*i2++).mSecond;
		if( p && p->DieDelete() )
		{
			if( pWin2 )
				pWin2->DelIcon( p->GetManagedKey() );

			SKILLMAN->DeleteInfluenceObject( p );

			mDeBuffMap.Erase( p->GetManagedKey() );
		}
	}
}

void cHero::DuelRequest( unsigned long playerIdx )
{
	cBaseObject* pTarget = NULL;

	try
	{
		if( playerIdx == 0 )
			pTarget = HERO->GetTargetObject();
		else
			pTarget = OBJECTMAN->GetPlayer( playerIdx );

		if( pTarget == NULL )
			throw 1;

		/// 타겟은 플레이어
		if( pTarget->GetObjectType() != eOBJECTTYPE_PLAYER )
			throw 1;
	}
	catch ( ... )
	{
		cStringT Message;
		Message.Format( GAMERESOURCEMAN->GetGameText( 808 ) );
		CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, (LPCTSTR)Message.Cstr() );

		return;
	}

	MSG_REQ_DUEL_ADD msg;
	msg.Category = NM_DUEL;
	msg.Protocol = NM_DUEL_ADD_REQ;
	msg.mTargetIdx = pTarget->GetObjectID();

	NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

	/// 신청
	cStringT Message;
	Message.Format( GAMERESOURCEMAN->GetGameText( 800 ), pTarget->GetName() );
	CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, (LPCTSTR)Message.Cstr() );
}

void cHero::DuelGiveUp()
{
	/// 결투 포기 메세지 발송
	MSGROOT msg;
	msg.Category = NM_DUEL;
	msg.Protocol = NM_DUEL_GIVEUP_RES;

	NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );
}

void cHero::ViewMsgDuelReq( unsigned long duelReqPlayerIdx )
{ 
	cPlayer* pPlayer = OBJECTMANAGER->GetPlayer( duelReqPlayerIdx );
	if( pPlayer == NULL )
		return;

	cDuelMsgWindow* pDuelMsgWin = GAMEUI->GetDuelMsgWindow();
	if( pDuelMsgWin == NULL )
		return;

	/// 결투 요청자 설정
	pDuelMsgWin->SetTargetIdx( duelReqPlayerIdx );

	/// 결투 요청창 열림
	pDuelMsgWin->ShowModal();
}

void cHero::DuelNoStartMsg( int errorCode )
{
	cStringT Message;

	switch( errorCode )
	{
	case ERR_DUEL_ACCEPT_REQ_ERROR:		/// 요청자 상태 불가
		Message.Format( GAMERESOURCEMAN->GetGameText( 803 ) );
		CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, (LPCTSTR)Message.Cstr() );
		break;
	case ERR_DUEL_ACCEPT_RES_REJECTION:	/// 대상자 거절
		Message.Format( GAMERESOURCEMAN->GetGameText( 804 ) );
		CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, (LPCTSTR)Message.Cstr() );
		break;
	case ERR_DUEL_ACCEPT_RES_ERROR:		/// 대상자 상태 불가
		Message.Format( GAMERESOURCEMAN->GetGameText( 806 ) );
		CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, (LPCTSTR)Message.Cstr() );
		break;
	}					

	///// 결투 신청창 닫힘
	cDuelMsgWindow* pWin = GAMEUI->GetDuelMsgWindow();
	if( pWin != NULL )
		pWin->Hide();
}

void cHero::BlinkBuffIcon( unsigned long uniqueIdx, unsigned long influenceIdx )
{
	cBuffWindow* pWin = GAMEUI->GetBuffWindow();
	if( pWin )
		pWin->BlinkIcon( uniqueIdx, influenceIdx );
}

void cHero::BlinkDeBuffIcon( unsigned long uniqueIdx, unsigned long influenceIdx )
{
	cBuffWindow* pWin = GAMEUI->GetDeBuffWindow();
	if( pWin )
		pWin->BlinkIcon( uniqueIdx, influenceIdx );
}

void cHero::ToggleIdleFlag()
{
	if( mPeaceToggleFlag == false )
		return;
	if( GetState() == eOBJECT_STATE_DIE ||
		GetState() == eOBJECT_STATE_ATTACK ||
		GetState() == eOBJECT_STATE_STOP ||
		GetState() == eOBJECT_STATE_MOVE )
		return;

	eIDLEFLAG flag = (mPlayerExrInfo.mStateIdle == eIDLE_NORMAL)? eIDLE_PEACE:eIDLE_NORMAL;
	cPlayer::SetIdleFlag( flag );

	/// Network Msg;
	MSG_REQ_PLAYER_STATE_IDLE Msg;
	Msg.Category = NM_PLAYER;
	Msg.Protocol = NM_PLAYER_STATE_IDLE_REQ;

	Msg.mPlayerStateIdle = (unsigned char)flag;
	NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );

	mPeaceToggleFlag = false;
}

void cHero::ToggleSitDown()
{
	if( IsPossibleSitDown() == false )
		return;

	if( GatheringCancel() == false )
		return;

	/// 앉기/서기 전환
	if( GetState() == ePLAYER_STATE_SITDOWN )
	{
		StandUp();

		/// Network Msg
		MSG_REQ_PLAYER_STATE_SITDWON Msg;
		Msg.Category = NM_PLAYER;
		Msg.Protocol = NM_PLAYER_STATE_SITDOWN_REQ;
		Msg.mSitDown = false;

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}
	else
	{
		if( IsRideVehicle() == true )
		{
			MSGROOT msg;
			::memset( &msg, 0, sizeof(msg) );
			msg.Category = NM_PLAYER;
			msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
			NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

			AlightVehicle();
		}

		SitDown();

		/// 081209 PKH 앉기 시작시간 설정 - 10초 후부터 자연회복 2배표시
		mSitDownStartTime = THEAPP->GetWorldAccumTime();
		mSitDownStatusCalc = true;

		/// Network Msg
		MSG_REQ_PLAYER_STATE_SITDWON Msg;
		Msg.Category = NM_PLAYER;
		Msg.Protocol = NM_PLAYER_STATE_SITDOWN_REQ;
		Msg.mSitDown = true;

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}
}


void cHero::SitDownUpdate()
{
	/// 081209 PKH 회복 2배 스텟 계산
	if( mSitDownStartTime != 0 && mSitDownStatusCalc == true && mPlayerExrInfo.mState == ePLAYER_STATE_SITDOWN &&
		mSitDownStartTime + NATURE_RECOVERY_TIME < THEAPP->GetWorldAccumTime() )
	{
		mSitDownStatusCalc = false;
		CalcStatus();
	}
}


bool cHero::IsSitDownCalc()
{
	/// 081209 PKH 회복 두배 상태 확인
	if( mSitDownStartTime != 0 && mPlayerExrInfo.mState == ePLAYER_STATE_SITDOWN && 
		mSitDownStartTime + NATURE_RECOVERY_TIME < THEAPP->GetWorldAccumTime() )
		return true;

	return false;
}

void cHero::SetWeaponChangeFlag( bool val ) 
{
	if( mSwitchWeaponFlag == true && val == false )
	{
		/// 기본공격을 위한 이동인지 체크한다.
		if( mAfterAction.mActionType == eAfterAction_Attack )
		{
			/// 기본공격을 위한 이동중에 장비교체시 재장착한다.
			SKILLEXECUTER->ReCharge();

			mAfterAction.mExData1 = SKILLEXECUTER->GetChargeSkill();
			mAfterAction.mRange = SKILLEXECUTER->GetChargeSkillRange();

			SKILLEXECUTER->ChargeOff( SKILLEXECUTER->GetChargeSkill() );
		}
	}

	mSwitchWeaponFlag = val;

	if( val == true )
		mSwitchWeaponTime = 600;
}

bool cHero::EquipWeaponItem( sPlayerWeaponInfo* info )
{
	SetWeaponChangeFlag( false );

	return cPlayer::EquipWeaponItem( info );
}

void cHero::ChangeWeaponState( eWEAPON_STATE state )
{
	cPlayer::ChangeWeaponState( state );

	/// 기본공격을 위한 이동인지 체크한다.
	if( mAfterAction.mActionType == eAfterAction_Attack )
	{
		/// 기본공격을 위한 이동중에 장비교체시 재장착한다.
		SKILLEXECUTER->ReCharge();

		mAfterAction.mExData1 = SKILLEXECUTER->GetChargeSkill();
		mAfterAction.mRange = SKILLEXECUTER->GetChargeSkillRange();

		SKILLEXECUTER->ChargeOff( SKILLEXECUTER->GetChargeSkill() );
	}
}

// ResetArmorSets Method.
void cHero::ResetArmorSets()
{
	// CLEAR - ARMOR SETS
	mArmorSets = ARMOR_SETS_NONE;

	//// ARMOR SETS - HAT
	const cItem& hat = ITEMMAN->GetItem( INVENTORY_WEAR_HAT );
	cItemAbility* hatAbil = ITEMMAN->GetItemAbility( hat.GetIndex() );
	if( hatAbil )
	{
		switch( hatAbil->GetDefenseType() )
		{
		case ITEM_MATERIAL_CLOTH:		/// 천
			mArmorSets = mArmorSets | ROBES_HAT;
			break;
		case ITEM_MATERIAL_LEATHER:		/// 가죽
			mArmorSets = mArmorSets | LIGHT_ARMOR_HAT;
			break;
		case ITEM_MATERIAL_METAL:		/// 금속
			mArmorSets = mArmorSets | HEAVY_ARMOR_HAT;
			break;
		}
	}

	//// ARMOR SETS - UPPER(BODY1)
	const cItem& uppper = ITEMMAN->GetItem( INVENTORY_WEAR_BODY1 );
	cItemDefine* upperDefine = ITEMMAN->GetItemDefine( uppper.GetIndex() );
	cItemAbility* uppperAbil = ITEMMAN->GetItemAbility( uppper.GetIndex() );

	if( uppperAbil != NULL )
	{
		if( uppperAbil )
		{
			if( upperDefine->GetSubType() == ITEM_WEAR_ONEPIECE )
			{
				switch ( uppperAbil->GetDefenseType() )
				{
				case 1:
					mArmorSets = mArmorSets | ROBES_ONEPIECE;
					break; // 천
				case 2:
					mArmorSets = mArmorSets | LIGHT_ARMOR_ONEPIECE;
					break; // 가죽
				case 3:
					mArmorSets = mArmorSets | HEAVY_ARMOR_ONEPIECE;
					break; // 금속
				}
			}
			else
			{
				switch ( uppperAbil->GetDefenseType() )
				{
				case 1:
					mArmorSets = mArmorSets | ROBES_UPPER;
					break; // 천
				case 2:
					mArmorSets = mArmorSets | LIGHT_ARMOR_UPPER;
					break; // 가죽
				case 3:
					mArmorSets = mArmorSets | HEAVY_ARMOR_UPPER;
					break; // 금속
				}
			}
		}
	}

	//// ARMOR SETS - LOWER(BODY2)
	const cItem& lower = ITEMMAN->GetItem( INVENTORY_WEAR_BODY2 );
	cItemDefine* lowerDefine = ITEMMAN->GetItemDefine( lower.GetIndex() );
	cItemAbility* lowerAbil = ITEMMAN->GetItemAbility( lower.GetIndex() );

	if( lowerAbil != NULL )
	{
		if( lowerAbil )
		{
			if( lowerDefine->GetSubType() == ITEM_WEAR_ONEPIECE )
			{
				switch ( lowerAbil->GetDefenseType() )
				{
				case 1:
					mArmorSets = mArmorSets | ROBES_ONEPIECE;
					break; // 천
				case 2:
					mArmorSets = mArmorSets | LIGHT_ARMOR_ONEPIECE;
					break; // 가죽
				case 3:
					mArmorSets = mArmorSets | HEAVY_ARMOR_ONEPIECE;
					break; // 금속
				}
			}
			else
			{
				switch ( lowerAbil->GetDefenseType() )
				{
				case 1:
					mArmorSets = mArmorSets | ROBES_LOWER;
					break; // 천
				case 2:
					mArmorSets = mArmorSets | LIGHT_ARMOR_LOWER;
					break; // 가죽
				case 3:
					mArmorSets = mArmorSets | HEAVY_ARMOR_LOWER;
					break; // 금속
				}
			}
		}
	}

	//// ARMOR SETS - HANDS(HAND)
	const cItem& hand = ITEMMAN->GetItem( INVENTORY_WEAR_HAND );
	cItemAbility* handAbil = ITEMMAN->GetItemAbility( hand.GetIndex() );
	if( handAbil )
	{
		switch( handAbil->GetDefenseType() )
		{
		case ITEM_MATERIAL_CLOTH:		/// 천
			mArmorSets = mArmorSets | ROBES_HANDS;
			break;
		case ITEM_MATERIAL_LEATHER:		/// 가죽
			mArmorSets = mArmorSets | LIGHT_ARMOR_HANDS;
			break;
		case ITEM_MATERIAL_METAL:		/// 금속
			mArmorSets = mArmorSets | HEAVY_ARMOR_HANDS;
			break;
		}
	}

	//// ARMOR SETS - FEET(HAND)
	const cItem& feet = ITEMMAN->GetItem( INVENTORY_WEAR_FOOT );
	cItemAbility* feetAbil = ITEMMAN->GetItemAbility( feet.GetIndex() );
	if( feetAbil )
	{
		switch( feetAbil->GetDefenseType() )
		{
		case ITEM_MATERIAL_CLOTH:		/// 천
			mArmorSets = mArmorSets | ROBES_FEET;
			break;
		case ITEM_MATERIAL_LEATHER:		/// 가죽
			mArmorSets = mArmorSets | LIGHT_ARMOR_FEET;
			break;
		case ITEM_MATERIAL_METAL:		/// 금속
			mArmorSets = mArmorSets | HEAVY_ARMOR_FEET;
			break;
		}
	}
}

void cHero::SetAreaNameIdx( unsigned long idx )
{
	mAreaNameIdx = idx;

	cMiniMapWindow* win = (cMiniMapWindow*)UIMAN->GetContainer( eUIID_MINIMAP_WINDOW );
	if( win )
		win->SetZoneName( GAMERESOURCEMAN->GetMapName( mAreaNameIdx ) );
}

void cHero::ApplyDamageDrama( unsigned int damage, unsigned int damageType, cBaseObject* pAttacker )
{
	if( mPlayerExrInfo.mChgMonsterIdx != 0 )
	{
		return ApplyDamageDrama_TransMon( damage, damageType, pAttacker );
	}

	cBaseObject::ApplyDamageDrama( damage, damageType, pAttacker );

	if( GetState() == eOBJECT_STATE_DIE )
		return;

	if( damageType < eDAMAGEPRINT_HEAL_OUR )
		CAMERAMAN->Shake();

	mDamageAfterAnim = UINT_MAX;
//	mDamageAfterAnim = ANITYPE_IDLE;

	if( mpObjectSceneNode->GetTargetAnimation() >= ANITYPE_SKILL_BEGIN && 
		mpObjectSceneNode->GetTargetAnimation() <= ANITYPE_SKILL_END )
	{
		return;
	}

	switch( mAnimationType )
	{
	case ANITYPE_SITDOWN:
	case ANITYPE_SITDOWN_IDLE:
	case ANITYPE_STANDUP:
	case ANITYPE_RUN:
	case ANITYPE_ITEMPICK:
	case ANITYPE_DAMAGE1:
	case ANITYPE_DAMAGE2:
		return;
	}

	if( mpObjectSceneNode->GetTargetAnimation() >= ANITYPE_CASTING1 && 
		mpObjectSceneNode->GetTargetAnimation() < ANITYPE_DAMAGE1 )
	{
		mDamageAfterAnim = (mpObjectSceneNode->GetTargetAnimation()/10)*10;
	}

	if( mpObjectSceneNode->GetTargetAnimation() >= ANITYPE_ATTACK1 && 
		mpObjectSceneNode->GetTargetAnimation() < ANITYPE_CASTING1 )
	{
		if( GetTargetObject() != pAttacker )
			return;

		unsigned long time = mpObjectSceneNode->GetTargetAnimationEndTime() - 200;
		if( THEAPP->GetWorldAccumTime() < time )
			return;
	}

	/// damage동작 적용
	if( damageType < eDAMAGEPRINT_HEAL_OUR )
	{
		mIdleTime = 0;

		if( damageType == eDAMAGEPRINT_CRITICAL_ENEMY || damageType == eDAMAGEPRINT_CRITICAL_OUR )
			ChangeAnimation( ANITYPE_DAMAGE2 );
		else
			ChangeAnimation( ANITYPE_DAMAGE1 );

		/// change dir
		if( mpObjectSceneNode->GetTargetAnimation() >= ANITYPE_IDLE && 
			mpObjectSceneNode->GetTargetAnimation() <= ANITYPE_IDLE2 )
		{
			if( pAttacker )
				SetDesiredDir( GetPos(), pAttacker->GetPos() ); //, 200 );
		}
		if( mAnimationType == ANITYPE_PEACE_IDLE || mAnimationType == ANITYPE_PEACE_IDLE2 )
		{
			if( pAttacker )
				SetDesiredDir( GetPos(), pAttacker->GetPos() ); //, 200 );
		}
	}
}

const cSphere& cHero::GetBoundSphere() const
{
	assert( mpObjectSceneNode );
	return mpObjectSceneNode->GetBoundSphere();
}

const NiPoint3& cHero::GetCenter() const
{
	assert( mpObjectSceneNode );
	return mpObjectSceneNode->GetCenter();
}

void cHero::SetFixedObjectSizeScale( unsigned short size )
{
	cPlayer::SetFixedObjectSizeScale( size );
	mCameraOffset = mDefaultCameraOffset * (float)size * 0.01f;
}

void cHero::ActiveQuestText( LPCTSTR msg, unsigned long color )
{
	if( mPlayerExrInfo.mChgMonsterIdx == 0 )
	{
		if( mpObjectSceneNode->GetType() == cSceneNode::eVEHICLE )
			((cVehicleSceneNode*)mpObjectSceneNode)->ActiveQuestText( msg, color );
		else
			((cHeroSceneNode*)mpObjectSceneNode)->ActiveQuestText( msg, color );
	}
	else
		((cTransMonSceneNode*)mpObjectSceneNode)->ActiveQuestText( msg, color );
}

void cHero::ActiveNoticeText( LPCTSTR msg, unsigned long color )
{
	if( mPlayerExrInfo.mChgMonsterIdx == 0 )
	{
		if( mpObjectSceneNode->GetType() == cSceneNode::eVEHICLE )
			((cVehicleSceneNode*)mpObjectSceneNode)->ActiveQuestText( msg, color );
		else
			((cHeroSceneNode*)mpObjectSceneNode)->ActiveNoticeText( msg, color );
	}
	else
		((cTransMonSceneNode*)mpObjectSceneNode)->ActiveNoticeText( msg, color );
}

void cHero::SetStaticFaceReq( unsigned long faceIdx )
{
	MSG_REQ_PLAYER_STATICFACE msg;
	msg.Category = NM_PLAYER;
	msg.Protocol = NM_PLAYER_STATICFACE_REQ;
	msg.mStaticFaceIdx = faceIdx;

	NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );
}

void cHero::ProcessDelayFlag( unsigned long deltaTime )
{
	/// 평화(Z) 버튼 delay설정
	if( mPeaceToggleFlag == false )
	{
		if( mPeaceToggleTime > 1000 )
		{
			mPeaceToggleFlag = true;
			mPeaceToggleTime = 0;
		}
		else
		{
			mPeaceToggleTime += deltaTime;
		}
	}

	if( mSwitchWeaponTime )
	{
		if( mSwitchWeaponTime <= deltaTime )
			mSwitchWeaponTime = 0;
		else
			mSwitchWeaponTime -= deltaTime;
	}
}

bool cHero::IsPossibleSitDown()
{
	if( IsWeaponChanging() )
		return false;
	if( IsReadyUseSkill() == false )
		return false;
	if( GetState() == eOBJECT_STATE_DIE ||
		GetState() == eOBJECT_STATE_STOP )
		return false;

	if( mSitDownFlag == false )
		return true;

	return false;
}

void cHero::SetPartyIndex( unsigned long partyIndex )
{
	cPlayer::SetPartyIndex( partyIndex );

	if( partyIndex > 0 )
		PartyIn_Init();
	else
		PartyOut_Init();
}

void cHero::SetGuild( unsigned long guildIndex, LPCTSTR guildName, unsigned char guildPosition, unsigned long markIndex )
{
	cPlayer::SetGuild( guildIndex, guildName, guildPosition, markIndex );

	cHeroInfoWindow* win = GAMEUI->GetHeroInfoWindow();
	if( win )
		win->SetGuild( guildName );
}

void cHero::CheckTabPartyObject()
{
	if( GetPartyIndex() == 0 )
		return;

	cBaseObject* curTarget = GetTargetObject();
	if( curTarget )
	{
		if( curTarget->GetObjectType() == eOBJECTTYPE_PLAYER )
		{
			if( PARTYMAN->IsHeroParty( curTarget->GetObjectID() ) == true )
			{
				/// next party user -> SetTarget
				PARTYMAN->ChangeTabTarget( curTarget->GetObjectID() );
				return;
			}
		}
	}

	/// first party user -> SetTarget
	PARTYMAN->ChangeTabTarget( 0 );
}

void cHero::CheckTabTargetObject()
{
	tArray<void*>& vArr = SCENEMAN->GetVisibleDynamicSceneArray();
	if( vArr.IsEmpty() )
		return;

	NiCamera* cam = CAMERAMAN->GetCurrentNi();
	NiPoint3 camDir = cam->GetWorldDirection();
	NiPoint3 heroPos = GetPos();

	/// 등록 타겟이 5명을 넘으면, 제외인원이 있는지 체크한다.
	if( mTabTargetList.GetSize() >= 5 )
	{
		NiTListIterator kIter = mTabTargetList.GetHeadPos();
		while( kIter )
		{
			NiTListIterator kOldIter = kIter;
			sObject* data = mTabTargetList.GetNext(kIter);
			cBaseObject* o = OBJECTMAN->GetObject( data->type, data->index );
			if( o == 0 )
			{
				sObject* rp = mTabTargetList.RemovePos(kOldIter);
				SAFE_NIDELETE(rp);
				continue;
			}

			/// 거리 체크
			NiPoint3 targetPos = o->GetPos();
			NiPoint3 calcPos = (heroPos - targetPos);
			calcPos.z = 0.0f;
			float len  = calcPos.Length();
			if( len > 1500 )
			{
				sObject* rp = mTabTargetList.RemovePos(kOldIter);
				SAFE_NIDELETE(rp);
				continue;
			}

			/// 방향 체크
			NiPoint3 dir = targetPos - heroPos;
			dir.Unitize();
			float checkPos = camDir.Dot(dir);
			if( checkPos < 0.0f )
			{
				sObject* rp = mTabTargetList.RemovePos(kOldIter);
				SAFE_NIDELETE(rp);
				continue;
			}
			else
			{
				if( len > 500 && checkPos < 0.7f )
				{
					sObject* rp = mTabTargetList.RemovePos(kOldIter);
					SAFE_NIDELETE(rp);
					continue;
				}
			}

			if( o->GetObjectType() == eOBJECTTYPE_PLAYER )
			{
				if( DUELMAN->IsDuelEnemy( (cPlayer*)o ) == false &&
					PVPMAN->IsEnableAttack( (cPlayer*)o ) == false )
				{
					sObject* rp = mTabTargetList.RemovePos(kOldIter);
					SAFE_NIDELETE(rp);
					continue;
				}
			}
			else if( o->GetObjectType() != eOBJECTTYPE_MONSTER )
			{
				sObject* rp = mTabTargetList.RemovePos(kOldIter);
				SAFE_NIDELETE(rp);
				continue;
			}
		}
	}

	/// 신규 타겟이 있는지 체크한다.
	if( mTabTargetList.GetSize() < 5 )
	{
		for( int i=vArr.GetSize()-1; i>=0; --i )
		{
			cDynamicSceneNode* n = (cDynamicSceneNode*)vArr[i];
			cBaseObject* obj = n->GetGameObject();
			if( obj == 0 )
				continue;
			if( obj == GetTargetObject() )
				continue;
			if( obj == this )
				continue;
			if( obj->GetObjectType() == eOBJECTTYPE_NPC )
				continue;

			/// 거리 체크
			NiPoint3 targetPos = obj->GetPos();
			NiPoint3 calcPos = (heroPos - targetPos);
			calcPos.z = 0.0f;
			float len = calcPos.Length();
			if( len > 1500 )
				break;

			/// 방향 체크
			NiPoint3 dir = targetPos - heroPos;
			dir.Unitize();
			float checkPos = camDir.Dot(dir);
			if( checkPos > 0.0f )
			{
				if( len > 500 && checkPos < 0.7f )
					continue;
			}
			else
				continue;

			/// 객체 검사
			switch( obj->GetObjectType() )
			{
			case eOBJECTTYPE_PLAYER:
				{
					if( DUELMAN->IsDuelEnemy( (cPlayer*)obj ) == false &&
						PVPMAN->IsEnableAttack( (cPlayer*)obj ) == false )
					{
						/// 플레이어인 경우는 적이 아니면 리스트 검사후 제외 시킨다.
						NiTListIterator kIter = mTabTargetList.GetHeadPos();
						while (kIter)
						{
							NiTListIterator kOldIter = kIter;
							sObject* data = mTabTargetList.GetNext(kIter);
							if( obj->GetObjectID() == data->index && data->type == obj->GetObjectType() )
							{
								sObject* rp = mTabTargetList.RemovePos(kOldIter);
								SAFE_NIDELETE(rp);
								break;
							}
						}
						continue;
					}
				}
				break;
			case eOBJECTTYPE_MONSTER:
				break;
			case eOBJECTTYPE_NPC:
			default:
				continue;
			}

			/// 객체 검사가 끝났다면 선행 5명 선택 목록에 있는지 검사
			bool addTarget = true;
			NiTListIterator kIter = mTabTargetList.GetHeadPos();
			while (kIter)
			{
				sObject* data = mTabTargetList.Get(kIter);
				if( obj->GetObjectID() == data->index && obj->GetObjectType() == data->type )
				{
					addTarget = false;
					break;
				}
				kIter = mTabTargetList.GetNextPos(kIter);
			}

			/// 모든 검사를 통과 - 새로운 타겟 설정
			if( addTarget == true )
			{
				sObject* target = new sObject;
				target->index = obj->GetObjectID();
				target->type = obj->GetObjectType();
				mTabTargetList.AddTail( target );

				if( mTabTargetList.GetSize() > 5 )
				{
					sObject* rp = mTabTargetList.RemoveHead();
					SAFE_NIDELETE(rp);
				}

				/// set target
				HERO->SetTargetObject( target->type, target->index );
				return;
			}
		}
	}

	/// 마지막으로 5명중에 검사해서 처리한다.
 	NiTListIterator kIter = mTabTargetList.GetHeadPos();
	while (kIter)
	{
		NiTListIterator kOldIter = kIter;
		sObject* data = mTabTargetList.GetNext(kIter);
		cBaseObject* o = OBJECTMAN->GetObject( data->type, data->index );
		if( o == 0 )
		{
			sObject* rp = mTabTargetList.RemovePos(kOldIter);
			SAFE_NIDELETE(rp);
			continue;
		}

		/// 거리 체크
		NiPoint3 targetPos = o->GetPos();
		NiPoint3 calcPos = (heroPos - targetPos);
		calcPos.z = 0.0f;
		float len = calcPos.Length();
		if( len > 1500 )
		{
			sObject* rp = mTabTargetList.RemovePos(kOldIter);
			SAFE_NIDELETE(rp);
			continue;
		}

		/// 방향 체크
		NiPoint3 dir = targetPos - heroPos;
		dir.Unitize();
		float checkPos = camDir.Dot(dir);
		if( checkPos < 0.0f )
		{
			sObject* rp = mTabTargetList.RemovePos(kOldIter);
			SAFE_NIDELETE(rp);
			continue;
		}
		else
		{
			if( len > 500 && checkPos < 0.7f )
			{
				sObject* rp = mTabTargetList.RemovePos(kOldIter);
				SAFE_NIDELETE(rp);
				continue;
			}
		}

		switch( o->GetObjectType() )
		{
		case eOBJECTTYPE_PLAYER:
			{
				if( DUELMAN->IsDuelEnemy( (cPlayer*)o ) == false &&
					PVPMAN->IsEnableAttack( (cPlayer*)o ) == false )
				{
					sObject* rp = mTabTargetList.RemovePos(kOldIter);
					SAFE_NIDELETE(rp);
					continue;
				}
			}
			break;
		case eOBJECTTYPE_MONSTER:
			break;
		case eOBJECTTYPE_NPC:
		default:
			continue;
		}

		mTabTargetList.RemovePos(kOldIter);
		mTabTargetList.AddTail(data);

		/// set target
		HERO->SetTargetObject( o->GetObjectType(), o->GetObjectID() );
		return;
	}
}

void cHero::ClearTabTargetList()
{
	NiTListIterator kIter = mTabTargetList.GetHeadPos();
	while (kIter)
	{
		sObject* data = mTabTargetList.Get(kIter);
		SAFE_NIDELETE(data);

		kIter = mTabTargetList.GetNextPos(kIter);
	}
	mTabTargetList.RemoveAll();
}

void cHero::SetAlphaBlended( float alpha )
{
	cDynamicSceneNode* n = GetSceneNode();
	if( n == 0 )
		return;

#ifdef _GMTOOL
	if( CHEATMAN->GetHideMode() )
	{
		n->SetAlphaBlended( 0.25f );
		return;
	}
#endif
	{
		n->SetAlphaBlended( alpha );
	}
}

bool cHero::TransformMonster( unsigned long monsterClassIdx )
{
	/// 일어나는중 or 앉는중 변신했을때 보정
	if( mAnimationType == ANITYPE_STANDUP )
		SetState(eOBJECT_STATE_IDLE);
	else if( mAnimationType == ANITYPE_SITDOWN )
		mSitDownFlag = false;

	if( SKILLEXECUTER->IsSkillCharge() == true )
		SKILLEXECUTER->ChargeOff( SKILLEXECUTER->GetChargeSkill() );

	if( cPlayer::TransformMonster( monsterClassIdx ) == false )
		return false;

	ShowNameGauge( true );
	
	/// 몬스터 스킬 세팅
	QUICKMAN->ChangeMonster( monsterClassIdx );
	QUICKMAN->CheckSkillIcon();

	/// 
	cLookAtCamera* pCamera = (cLookAtCamera*)CAMERAMAN->GetCurrent();

	NiPoint3 sFoot = mpObjectSceneNode->GetObjectFoot();
	NiPoint3 sCenter = mpObjectSceneNode->GetObjectCenter();
	mDefaultCameraOffset = sCenter.z - sFoot.z + 22.0f;
	mCameraOffset = mDefaultCameraOffset;

	pCamera->SetLookAt( NiPoint3( sFoot.x, sFoot.y, sFoot.z + mCameraOffset ) );

	if( GetTargetObject() == this )
		SetTargetObject( eOBJECTTYPE_NONE,0 );

	SKILLEXECUTER->SetMonsterSkillInfo( monsterClassIdx );
	return true;
}

void cHero::RestoreMonster()
{
	if( SKILLEXECUTER->IsSkillCharge() == true )
		SKILLEXECUTER->ChargeOff( SKILLEXECUTER->GetChargeSkill() );

	cPlayer::RestoreMonster();

	ShowNameGauge( true );
	
	/// 몬스터 스킬 세팅
	QUICKMAN->ChangeMonster( 0 );
	QUICKMAN->CheckSkillIcon();

	/// 
	cLookAtCamera* pCamera = (cLookAtCamera*)CAMERAMAN->GetCurrent();

	NiPoint3 sFoot = mpObjectSceneNode->GetObjectFoot();
	NiPoint3 sCenter = mpObjectSceneNode->GetObjectCenter();
	mDefaultCameraOffset = sCenter.z - sFoot.z + 22.0f;
	mCameraOffset = mDefaultCameraOffset;

	pCamera->SetLookAt( NiPoint3( sFoot.x, sFoot.y, sFoot.z + mCameraOffset ) );

	if( GetTargetObject() == this )
		SetTargetObject( eOBJECTTYPE_NONE,0 );
}


///
void cHero::Update_TransMon( unsigned long deltaTime, unsigned long accumTime )
{
	if( mpObjectSceneNode  == NULL ) return;

	ProcessDelayFlag( deltaTime );
	DesiredDirProcess( deltaTime );

	/// 정보가 업데이트 될때마다 세팅
	if( mIsCalcStatus )
	{
		STATUSCALC->CalcPlayerExtensionGlobal();

		mpObjectSceneNode->UpdateNameCardGaugeValue();
		GAMEUI->UpdateHeroInfo();

		mIsCalcStatus  = false;
	}
	/// 스킬겨험치나 경험치가 업데이트될때마다 세팅
	else if( mIsUdateHeroInfo )
	{
		GAMEUI->UpdateExpSxp();

		mIsUdateHeroInfo = false;
	}

	/// 상태에 맞는 처리를 한다.
	switch( mPlayerExrInfo.mState )
	{
	case eOBJECT_STATE_IDLE:
		{
			IdleUpdate( deltaTime );
		}
		break;
	case eOBJECT_STATE_MOVE:
		{
			MoveUpdate( deltaTime );
		}
		break;
	case eOBJECT_STATE_ATTACK:
		{
			AttackUpdate( deltaTime, accumTime );
		}
		break;
	case eOBJECT_STATE_DIE:
		{
			DieUpdate();
		}
		break;
	case ePLAYER_STATE_SITDOWN:
		{
			SitDownUpdate();
		}
		break;
	}

	/// 애니메이션 연결에 대한 처리
	Interpret_TransMon( accumTime );

	/// 카메라 처리
	cLookAtCamera* pCamera = (cLookAtCamera*)CAMERAMAN->GetCurrent();
	if( pCamera )
	{
		const NiPoint3& pos = mpObjectSceneNode->GetTranslate();
		pCamera->SetLookAt( pos.x, pos.y, pos.z + mCameraOffset );
	}

	///
	WORLDMAN->MoveSkyDome( pCamera->GetWorldTranslate() );
}

void cHero::Interpret_TransMon( unsigned long time )
{
	cActorManagerForPartition*	pActor = mpObjectSceneNode->GetActorManager();
	if( !pActor ) return;

	unsigned int id = mpObjectSceneNode->GetTargetAnimation();

	float endTime = pActor->GetNextEventTime( cActorManagerForPartition::TEXT_KEY_EVENT, id, "end" );
	if( endTime == NI_INFINITY )
		return;

	if( endTime == cActorManagerForPartition::INVALID_TIME )
	{
		endTime = mpObjectSceneNode->GetTargetAnimationScaleEndTime() * 0.001f;
	}

	/// 현재 애니메이션 종료 시점 획득
	float checkTime = (float)mpObjectSceneNode->GetScaleAccumTime() * 0.001f;
	if( endTime > 0 && endTime <= checkTime )
	{
		if( M_ANITYPE_IDLE2 == id )
		{
			ChangeAnimation_TransMon( M_ANITYPE_IDLE1 );
		}
		else if( M_ANITYPE_ATTACK1 <= id && id <= M_ANITYPE_ATTACK3 )
		{
			SetReadyUseSkill( true );
			ChangeAnimation_TransMon( M_ANITYPE_IDLE1 );
		}
		else if( M_ANITYPE_SKILL1 <= id && id <= M_ANITYPE_SKILL2 )
		{
			SetReadyUseSkill( true );
			ChangeAnimation_TransMon( M_ANITYPE_IDLE1 );
		}
		else if( id == M_ANITYPE_DAMAGE1 || id == M_ANITYPE_DAMAGE2 )
		{
			/// 공격받는 애니메이션이 끝나면 기본 애니메이션으로 바꾼다.
			if( mDamageAfterAnim == M_ANITYPE_CASTING )
				UpdateSkillAnimation( M_ANITYPE_CASTING );
			else
			{
				ChangeAnimation_TransMon( mDamageAfterAnim );
			}
				
//				SetState( eOBJECT_STATE_IDLE );

			SetReadyUseSkill( true );
		}
	}
	else if( endTime == cActorManagerForPartition::INVALID_TIME )
	{
		return;
	}
}

void cHero::ApplyDamageDrama_TransMon( unsigned int damage, unsigned int damageType, cBaseObject* pAttacker )
{
	cBaseObject::ApplyDamageDrama( damage, damageType, pAttacker );

	if( GetState() == eOBJECT_STATE_DIE )
		return;

	if( GetState() == eOBJECT_STATE_STOP )
	{
		switch( mPlayerExrInfo.mStateStop )
		{
		case eSTOP_READYTAROT:
		case eSTOP_OPENTAROT:
		case eSTOP_OPENSTALL:
			return;
		}
	}

	if( damageType < eDAMAGEPRINT_HEAL_OUR )
		CAMERAMAN->Shake();

	unsigned int ani = mpObjectSceneNode->GetTargetAnimation();

	mDamageAfterAnim = UINT_MAX;
//	mDamageAfterAnim = M_ANITYPE_IDLE1;

	switch( ani )
	{
	case M_ANITYPE_WORK:
	case M_ANITYPE_RUN:
	case M_ANITYPE_SKILL1:
	case M_ANITYPE_SKILL2:
	case M_ANITYPE_DAMAGE1:
	case M_ANITYPE_DAMAGE2:
	case M_ANITYPE_DIE1:
	case M_ANITYPE_DIE2:
		return;
	}

	if( ani >= M_ANITYPE_MODEANI )
		return;

	if( M_ANITYPE_ATTACK1 <= ani && ani <= M_ANITYPE_ATTACK3 )
	{
		if( GetTargetObject() != pAttacker )
			return;

		unsigned long time = mpObjectSceneNode->GetTargetAnimationEndTime() - 200;
		if( THEAPP->GetWorldAccumTime() < time )
			return;
	}

	if( ani == M_ANITYPE_CASTING )
		mDamageAfterAnim = M_ANITYPE_CASTING;

	/// damage동작 적용
	if( damageType < eDAMAGEPRINT_HEAL_OUR )
	{
		mIdleTime = 0;

		if( damageType == eDAMAGEPRINT_CRITICAL_ENEMY || damageType == eDAMAGEPRINT_CRITICAL_OUR )
			mpObjectSceneNode->SetTargetAnimation( M_ANITYPE_DAMAGE2 );
		else
			mpObjectSceneNode->SetTargetAnimation( M_ANITYPE_DAMAGE1 );

		if( mpObjectSceneNode->GetTargetAnimation() == M_ANITYPE_IDLE1 || 
			mpObjectSceneNode->GetTargetAnimation() == M_ANITYPE_IDLE2 )
		{
			if( pAttacker )
				SetDesiredDir( GetPos(), pAttacker->GetPos() ); //, 10 );
		}
	}
}

bool cHero::StartAttack_Mon( unsigned long attackIdx, cBaseObject* pTarget )
{
	/// 기본 스킬 생성..
	unsigned long key =  SKILLMAN->CreateHeroSkillObject( attackIdx, this, pTarget, mPlayerExrInfo.mAttackSpeed );
	if( key )
	{
		/// 상태 설정
		SetState( eOBJECT_STATE_ATTACK );
		SetReadyUseSkill( false );

		/// 070712 PKH 스킬 요청 메세지 발송
		MSG_REQ_SKILL_USED Msg;
		Msg.Category = NM_SKILL;
		Msg.Protocol = NM_SKILL_USED_MON_REQ;//NM_SKILL_USED_REQ;
		Msg.mTarget.index = pTarget->GetObjectID();
		Msg.mTarget.type = pTarget->GetObjectType();

		Msg.mSkillClassIndex = attackIdx;

		Msg.mManagedKey = key;
		Msg.mClientPosX = HERO->GetXPos();
		Msg.mClientPosY = HERO->GetYPos();

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}

	return true;
}

void cHero::UseTargetSkill_TransMon( unsigned long skillIdx, cBaseObject* pTarget )
{
	AutoActionClear();
	SKILLEXECUTER->ChargeOff( skillIdx );

	if( IsReadyUseSkill() == false )
	{
		SetState( eOBJECT_STATE_ATTACK );
		return;
	}

	if( IsWeaponChanging() == true )
		return;


	/// 적용 타겟 검증
	if( SKILLEXECUTER->IsPossibleUseSkillTarget( skillIdx, pTarget ) == false )
		return;

	/// 발동 검증
	if( SKILLEXECUTER->IsPossibleUseSkill( skillIdx ) == false )
		return;

	if( pTarget )
	{
		/// 바라봐야할 방향 설정
		NiPoint3 charDir = mDesiredDir;
		NiPoint3 viewDir = pTarget->GetPos() - GetPos();
		viewDir.z = 0.0f;
		if( viewDir != NiPoint3::ZERO )
		{
			if( viewDir.SqrLength() >= 0.001f )	
			{
				viewDir.Unitize();

				/// 회전 Matrix 산출
				NiMatrix3 matA, matB, matRot;
				matA.SetCol( 0, charDir );
				matA.SetCol( 1, -NiPoint3::UNIT_Z );
				matA.SetCol( 2, charDir.UnitCross( -NiPoint3::UNIT_Z ) );

				matB.SetCol( 0, viewDir );
				matB.SetCol( 1, -NiPoint3::UNIT_Z );
				matB.SetCol( 2, viewDir.UnitCross( -NiPoint3::UNIT_Z ) );

				matRot = matB * matA.Transpose();
				mpObjectSceneNode->SetRotate( mpObjectSceneNode->GetRotate()*matRot );
				mDesiredDir = viewDir;
			}
		}
	}

	unsigned long key = SKILLMAN->CreateHeroSkillObject( skillIdx, this, pTarget );
	if( key )
	{
		unsigned long monsterClassIdx = GetTransMonsterClassIdx();
		if( monsterClassIdx == 0 )
			return ;
		sMonsterSkillScript* pInfo = SKILLSCRIPT->GetMonsterSkillInfo( monsterClassIdx, (eMONSTERATTACK_TYPE)skillIdx );
		if( pInfo == 0 )
			return;

		SetState( eOBJECT_STATE_ATTACK );

		/// 상태 설정
		SetReadyUseSkill( false );

		/// 070712 PKH 스킬 요청 메세지 발송
		MSG_REQ_SKILL_USED Msg;
		Msg.Category = NM_SKILL;

		/// casting이 있는 경우
		if( pInfo->mShotType == eMONSTERSHOTTYPE_CASTING )		/// 090213 PKH 
			Msg.Protocol = NM_SKILL_CAST_USED_MON_REQ;
		else
			Msg.Protocol = NM_SKILL_USED_MON_REQ;

		Msg.mSkillClassIndex = skillIdx;
		Msg.mManagedKey = key;
		Msg.mClientPosX = HERO->GetXPos();
		Msg.mClientPosY = HERO->GetYPos();

		if( pTarget )
		{
			Msg.mTarget.index = pTarget->GetObjectID();
			Msg.mTarget.type = pTarget->GetObjectType();
		}
		else
		{
			Msg.mTarget.index = 0;
			Msg.mTarget.type = eOBJECTTYPE_NONE;
		}

		NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
	}
}

void cHero::UseFieldSkill_TransMon( unsigned long skillIdx, NiPoint3 fieldPos )
{

}

void cHero::SetForceType( unsigned char forceType )
{
	cPlayer::SetForceType( forceType );

	/// 세력 UI 세팅
	cHeroInfoWindow* info = GAMEUI->GetHeroInfoWindow();
	if( info )
		info->SetForceType( forceType );
}

void cHero::SetWindPoint( unsigned long windPoint )
{ 
	mHeroInfo.mWindPoint = windPoint; 
}

void cHero::SetWindFriendly( unsigned long windFriendly )
{ 
	mHeroInfo.mWindFriendly = windFriendly; 
}

void cHero::SetEarthPoint( unsigned long earthPoint )
{ 
	mHeroInfo.mEarthPoint = earthPoint; 
}

void cHero::SetEarthFriendly( unsigned long earthFriendly )
{ 
	mHeroInfo.mEarthFriendly = earthFriendly; 
}

void cHero::SetWaterPoint( unsigned long waterPoint )
{ 
	mHeroInfo.mWaterPoint = waterPoint; 
}

void cHero::SetWaterFriendly( unsigned long waterFriendly )
{ 
	mHeroInfo.mWaterFriendly = waterFriendly; 
}

void cHero::SetFirePoint( unsigned long firePoint )
{ 
	mHeroInfo.mFirePoint = firePoint; 
}

void cHero::SetFireFriendly( unsigned long fireFriendly )
{ 
	mHeroInfo.mFireFriendly = fireFriendly; 
}

void cHero::SetFieldEffRadius( float r )
{
	if( mpObjectSceneNode == 0 )
		return;

	if( IsTransformMonster() == true )
		((cTransMonSceneNode*)mpObjectSceneNode)->SetFieldEffRadius( r );
	else
		((cHeroSceneNode*)mpObjectSceneNode)->SetFieldEffRadius( r );
}

void cHero::UpdateFieldTargetEffect( NiPoint3 center )
{
	if( mpObjectSceneNode == 0 )
		return;

	if( IsTransformMonster() == true )
		((cTransMonSceneNode*)mpObjectSceneNode)->UpdateFieldTargetEffect( center );
	else
		((cHeroSceneNode*)mpObjectSceneNode)->UpdateFieldTargetEffect( center );
}

void cHero::SetLevel( unsigned char level )
{
	cPlayer::SetLevel( level);

	STATUSCALC->CalcPlayerBaseGlobal(); 

	mpObjectSceneNode->UpdateNameCardGaugeValue();
	
	GAMEUI->UpdateHeroInfo();
	GAMEUI->UpdateExpSxp();
}

void cHero::NowCalcStatus()
{
	STATUSCALC->CalcPlayerExtensionGlobal();

	mpObjectSceneNode->UpdateNameCardGaugeValue();
	GAMEUI->UpdateHeroInfo();

	mIsCalcStatus  = false;
}

/// 전직
void cHero::SetJob( ePLAYER_JOB job )
{
	/// 전직세팅
	cPlayer::SetJob( job );

	/// UI 업데이트
	GAMEUI->UpdateHeroInfo();

	/// 스킬창 셋팅
	cSkillWindow* skillWindow = GAMEUI->GetSkillWindow();
	if( skillWindow )
	{
		skillWindow->AddJobTab( mPlayerInfo.Job );
		skillWindow->SetCurrent( 0 );
	}

	/// 스킬 정보 세팅
	SKILLMAN->UpdateAllSkill();
}

void cHero::SetJobUsedSkillPoint( unsigned int count, unsigned short* values )
{
	if( count == 0 || values == 0 )
		return;

	if( count > mJobUsedSkillPoint.GetSize() )
	{
		assert(0);
		return;
	}

	for( unsigned int i=0; i<count; i++ )
		mJobUsedSkillPoint[i] = values[i];
}

unsigned short	cHero::GetJobUsedSkillPoint( unsigned int jobLevel )
{
	if( jobLevel > mJobUsedSkillPoint.GetSize() )
	{
		assert(0);
		return 0;
	}
	return mJobUsedSkillPoint[jobLevel];
}

bool cHero::GatheringStart( unsigned long gaderIdx, unsigned long gatherClassIdx, NiPoint3 targetPos )
{
	if( IsRideVehicle() == true )
	{
		MSGROOT msg;
		::memset( &msg, 0, sizeof(msg) );
		msg.Category = NM_PLAYER;
		msg.Protocol = NM_PLAYER_VEHICLE_END_REQ;
		NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

		AlightVehicle();
	}

	sGatheringList* info = GATHERINGSCRIPT->GetGatheringInfo( gatherClassIdx );
	if( info == 0 )
	{
		assert(0);
		return false;
	}

	/// check
	if( GetLevel() < info->mNeedLevel )
	{
		CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText( 602 ) );
		return false;
	}

	if( info->mNeedItemIdx )
	{
		if( ITEMMAN->FindSlotIndex( info->mNeedItemIdx ) == 0 )
		{
			CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText( 603 ) );
			return false;
		}
	}

	if( info->mNeedQuestIdx )
	{
		if( QUESTMAN->IsCompleteGathering( info->mNeedQuestIdx ) == true )
		{
			CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText( 606 ) );
			return false;
		}
		/*if( QUESTMAN->GetQuestArrIdx( info->mNeedQuestIdx ) == -1 )
		{
			CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText( 604 ) );
			return false;
		}*/
	}

	if( info->mNeedMakeIdx )
	{
		if( CheckHeroMakeSkill( info->mNeedMakeIdx ) == false )
		{
			CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText( 604 ) );
			return false;
		}
	}

	/// casting start
	cCastingBar* pWin = (cCastingBar*)UIMAN->GetContainer( eUIID_GAME_CASTINGBAR );
	if( pWin )
		pWin->SetTime( gatherClassIdx, eCasting_Gather, GATHER_CASTING_TIME );

	/// 서버에 msg전송
	MSG_REQ_GATHERING_START msg;
	msg.Category = NM_GATHERING;
	msg.Protocol = NM_GATHERING_START_REQ;
	msg.mObjectIdx = gaderIdx;
	NETWORK->SendNetworkMsg( (char*)&msg, sizeof(msg) );

	/// type에 따라 애니 설정 및 추가 링크
	if( IsTransformMonster() == false )
	{
		if( info->mType == eGATHERINGTYPE_COMMON )
			ChangeAnimation( ANITYPE_GATHERING1 );
		else if( info->mType == eGATHERINGTYPE_MINING )
		{
			ChangeAnimation( ANITYPE_GATHERING2 );

			if( mGatheringToolObj != UINT_MAX )
			{
				mpObjectSceneNode->UnLinkObject( mGatheringToolObj );
				mGatheringToolObj = UINT_MAX;
			}

			/// 
			mGatheringToolObj = mpObjectSceneNode->LinkObject( eLINK_RHAND, "g_fool_001.nif" );
		}
		else
			ChangeAnimation( ANITYPE_GATHERING1 );
	}

	SetState( ePLAYER_STATE_GATHERING );

	SetDesiredDir( GetPos(), targetPos );

	return true;
}

void cHero::GatheringEnd()
{
	cCastingBar* pWin = (cCastingBar*)UIMAN->GetContainer( eUIID_GAME_CASTINGBAR );
	if( pWin )
		pWin->Hide();

	SetState( eOBJECT_STATE_IDLE );

	if( mGatheringToolObj != UINT_MAX )
	{
		mpObjectSceneNode->UnLinkObject( mGatheringToolObj );
		mGatheringToolObj = UINT_MAX;
	}
}

bool cHero::ItemMixCancel()
{
	if( GetStopFlag() == eSTOP_ITEMMIX )
	{
		ITEMMIXMAN->ItemMixCancel( false );
	}
	return true;
}

bool cHero::ItemEnhanceCancel()
{
	if( GetStopFlag() == eSTOP_ENHANCED )
	{
		ITEMMAN->TryToEnhancedCancel( false );
	}
	return true;
}

bool cHero::GatheringCancel()
{
	if( GetState() == ePLAYER_STATE_GATHERING )
	{
		cCastingBar* pWin = (cCastingBar*)UIMAN->GetContainer( eUIID_GAME_CASTINGBAR );
		if( pWin )
		{
			if( pWin->GetPercent() < 98.0f )
			{
				pWin->Hide();
				HERO->SetState( eOBJECT_STATE_IDLE );

				MSGROOT Msg;
				Msg.Category = NM_GATHERING;
				Msg.Protocol = NM_GATHERING_CANCEL_REQ;

				NETWORK->SendNetworkMsg( (char*)&Msg, sizeof(Msg) );
			}
			else
			{
				CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText( 605 ) );
				return false;
			}
		}
		else 
		{
			CHATMANAGER->AddSystemMsg( eSYSTEM_NORMAL, GAMERESOURCEMAN->GetGameText( 605 ) );
			return false;
		}

		if( mGatheringToolObj != UINT_MAX )
		{
			mpObjectSceneNode->UnLinkObject( mGatheringToolObj );
			mGatheringToolObj = UINT_MAX;
		}
	}

	return true;
}

bool cHero::IsCantSkill( unsigned long skillIdx )
{
	if( mIsSleep == true || mIsStun == true )
		return true;

	if( mCantSkill == true )
	{
		if( IsTransformMonster() )
		{
			if( skillIdx >= eMONSTERATTACK_SKILL1 )
				return true;
		}
		else
		{
			if( skillIdx > NORMAL_ATTACK_SKILL_MAX )
				return true;
		}
	}

	return false;
}

void cHero::RideVehicle( unsigned long vehicleIdx )
{
	cPlayer::RideVehicle( vehicleIdx );

	ShowNameGauge( true );
}

void cHero::AlightVehicle()
{
	cPlayer::AlightVehicle();

	ShowNameGauge( true );
}

void cHero::PreviewTodayWord( LPCTSTR word, unsigned long color )
{
	if( mpObjectSceneNode )
	{
		cPlayerNameCard* nameCard = (cPlayerNameCard*)mpObjectSceneNode->GetNameCard();
		if( nameCard )
			nameCard->SetTodayWord( (LPTSTR)word, color );
	}
}

void cHero::PreviewGuildMark( NiTexture* tex )
{
	if( mpObjectSceneNode )
	{
		cPlayerNameCard* nameCard = (cPlayerNameCard*)mpObjectSceneNode->GetNameCard();
		if( nameCard )
			nameCard->PreviewGuildMark( tex );
	}
}

void cHero::UpdateGuildMark()
{
	if( mpObjectSceneNode )
	{
		cPlayerNameCard* nameCard = (cPlayerNameCard*)mpObjectSceneNode->GetNameCard();
		if( nameCard )
			nameCard->ChangeGuildMark();
	}
}

bool cHero::CheckHeroMakeSkill( unsigned long makeSkillIdx )
{
	if( makeSkillIdx == 0 )
		return true;

	if( makeSkillIdx == mHeroInfo.mMakeSkill1 )
		return true;

	if( makeSkillIdx == mHeroInfo.mMakeSkill2 )
		return true;

	return false;
}

void cHero::SetMixSkillFirst( unsigned char skill1 )
{
	mHeroInfo.mMakeSkill1 = skill1;
}

void cHero::SetMixSkillSecond( unsigned char skill2 )
{
	mHeroInfo.mMakeSkill2 = skill2;
}

bool cHero::CheckGatheringEnable( unsigned long gaderClassIdx )
{
	sGatheringList* info = GATHERINGSCRIPT->GetGatheringInfo( gaderClassIdx );
	if( info == 0 )
		return false;

	if( GetLevel() < info->mNeedLevel )
	{
		return false;
	}

	if( info->mNeedItemIdx )
	{
		if( ITEMMAN->FindSlotIndex( info->mNeedItemIdx ) == 0 )
		{
			return false;
		}
	}

	if( info->mNeedQuestIdx )
	{
		//if( QUESTMAN->GetQuestArrIdx( info->mNeedQuestIdx ) == -1 )
		if( QUESTMAN->IsCompleteGathering( info->mNeedQuestIdx ) == true )
			return false;
	}

	if( info->mNeedMakeIdx )
	{
		if( HERO->CheckHeroMakeSkill( info->mNeedMakeIdx ) == false )
		{
			return false;
		}
	}

	return true;
}
