#include "StdAfx.h"
#include "PlayerSceneNode.h"

#include "ResourceManager.h"

#include "ObjectNameCard.h"
#include "SceneManager.h"
#include "cameraManager.h"
#include "OptionManager.h"
#include "RenderSystem.h"

#include "ChatBubble.h"
#include "CenterMsgPlane.h"
#include "Signboard.h"
#include "BaseObject.h"
#include "PartyManager.h"
#include "NoticeMsgPlane.h"

#include "ObjectManager.h"
#include "Hero.h"

#include "ShadowGeometry.h"
#include "StageManager.h"

#include "EffectSceneNode.h"

#include "LightAgent.h"
#include "FogAgent.h"

#ifdef _DEVSYS
#include "DevSystem.h"
#endif


cPlayerSceneNode::cPlayerSceneNode( eType type )
: cDynamicSceneNode( type )
{
	mpTarotBoard = 0;
	mpUserSellBoard = 0;
}

cPlayerSceneNode::~cPlayerSceneNode()
{
	SAFE_DELETE( mpUserSellBoard );
	SAFE_DELETE( mpTarotBoard );
}

bool cPlayerSceneNode::Init( const cPlayerSceneNodeParam& param )
{
	if( cDynamicSceneNode::Init( param ) == false )
	{
		return false;
	}

	/// parts 정보 등록
	const char* partName = 0;
	for( unsigned int i=0; i<ePART_MAX; i++ )
	{
		partName = RESOURCEMAN->GetManagedPartName( i );
		if( strlen(partName) == 0 )
		{
			assert(0);
			continue;
		}
		mpActorManager->AddPartsObject( i, partName );
	}

	/// 말풍선용 객체 생성
	mpChatBubble = new cChatBubble;
	mpTarotBoard = new cTarotSignboard;
	mpUserSellBoard = new cUseSellSignboard;
	return true;
}

bool cPlayerSceneNode::InitLinkInfo()
{
	// 이부분은 적당한 메모리 할당을 한다. (나중에 좀더 정확하게 계산하여 추가하거나 줄일것..)
	// 대략 계산 : 양손 무기 2개 + 방패 1개
	mpLinkInfo = NiNew NiTPrimitiveArray<sLinkInfo*>(15, 5);
	assert(mpLinkInfo);
	if( mpLinkInfo == 0 ) 
	{
		return false;
	}

	for( unsigned int i=0; i<mpLinkInfo->GetAllocatedSize(); ++i )
	{
		mpLinkInfo->SetAt( i,0 );
	}

	return true;
}

/* ------------------------------------------------------------------
* 함수이름 :	InitializeRandomIdle()
* 목    적 :	동작의 다양성을 제공하기 위해서
* 주의사항 :	
* ------------------------------------------------------------------ */
void cPlayerSceneNode::InitializeRandomIdle()
{
	mpRandomIdle = NiNew NiTPrimitiveArray<cActorManagerForPartition::EventCode>(1);

	mpRandomIdle->SetAt( 0, 0 );
}

void cPlayerSceneNode::ProcessTrail( unsigned long delta )
{
	unsigned long scaleDelta = (unsigned long)(delta * mScaleFactor);

	///
	NiTListIterator pos = mRibbonList.GetHeadPos();
	while( pos )
	{
		sTrailInfo* p = mRibbonList.GetNext(pos);
		cRibbonGeometry* geom = p->mRibbonGeom;
		if( p->mLoop == false )
		{
			if( p->mLifeTime <= 5 )
			{
				p->mLifeTime = 0;
				geom->SetPushEnabled( false );

				if( geom->GetNumVerts() == 0 )
				{
					/// delete
					mDeleteRibbonList.AddTail(p);
					continue;
				}
			}
			else
				p->mLifeTime -= 5;
		}

		if( geom )
		{
			/// push point
			NiAVObject* obj1 = GetDummyObject( p->mStartLink );
			NiAVObject* obj2 = GetDummyObject( p->mEndLink );

			if( obj1 && obj2 )
			{
				const NiPoint3& pos1 = obj1->GetWorldTranslate();
				const NiPoint3& pos2 = obj2->GetWorldTranslate();

				geom->SetPushPoint( pos1, pos2 );
			}

			geom->Process( float(delta) * 0.001f );
		}
	}
}

const NiPoint3& cPlayerSceneNode::GetObjectHead()
{
	NiNode* spine = GetDummyObject( eLINK_HEAD );

	if( spine )	
		return spine->GetWorldTranslate();
	else
		return NiPoint3::ZERO;
}

const NiPoint3& cPlayerSceneNode::GetObjectFoot()
{
	NiNode* spine = GetDummyObject( eLINK_FOOT );

	if( spine )	
		return spine->GetWorldTranslate();
	else
		return NiPoint3::ZERO;
}

const NiPoint3& cPlayerSceneNode::GetObjectCenter()
{
	NiNode* spine = GetDummyObject( eLINK_BODY );

	if( spine )	
		return spine->GetWorldTranslate();
	else
		return NiPoint3::ZERO;
}

/* ------------------------------------------------------------------
* 함수이름 :	ChangeParts( unsigned int index, const cString& pathName )
* 목    적 :	파트 교체용 인터페이스 제공
* 주의사항 :	
* ------------------------------------------------------------------ */
bool cPlayerSceneNode::ChangeParts( unsigned int partIdx, const char* fileName )
{
	if( fileName == 0 )
		return false;

	///
	cString pathName;
	pathName.Format( "./Data/Character/%s", fileName );
	RESOURCEMAN->LoadNIF( pathName );

	///
	NiNode* pNode = RESOURCEMAN->CloneObjectByName( fileName );
	assert(pNode);
	if( !pNode ) 
	{
		return false;
	}

	if( partIdx == ePART_FACE )
		return cPlayerSceneNode::ChangeParts( partIdx, pNode );
	else
		return cDynamicSceneNode::ChangeParts( partIdx, pNode );
}

bool cPlayerSceneNode::ChangeParts( unsigned int partsIdx, NiNode* pNode )
{
	if( partsIdx != ePART_FACE )
		return cDynamicSceneNode::ChangeParts( partsIdx, pNode );


	//////////////////////////////////////////////////////////////////////////
	/// Clone한 Object에 있던 Bone Root정보를 삭제한다.
	if( pNode->DetachChild( pNode->GetObjectByName("foot_dummy") ) == 0)
	{
		assert(0);
	}


	/// 2개 이상의 Geom객체가 있는 경우에 대한 처리
	NiAVObject* oldFace = GetPartObject( partsIdx );
	if( oldFace == 0 )
	{
		assert(0);
		return false;
	}

	NiAVObject* oldEar = GetPartObject( ePART_EAR );

	//////////////////////////////////////////////////////////////////////////
	/// face process
	const char* partsName1 =RESOURCEMAN->GetManagedPartName( partsIdx );
	if( partsName1 == 0 || strlen(partsName1) == 0 )
	{
		assert(0);
		return false;
	}
	NiAVObject* newFace = pNode->GetObjectByName( partsName1 );
	if( newFace == 0 )
	{
		assert(0);
		return false;
	}
	else
	{
		NiTriBasedGeom* pOldGeom = NULL;
		if( NiIsKindOf(NiTriBasedGeom, oldFace) )
			pOldGeom = (NiTriBasedGeom*)oldFace;
		else
			pOldGeom = NiDynamicCast( NiTriBasedGeom, GetGeom(oldFace) );

		if( !pOldGeom )
		{
			assert(0);
			return false;
		}

		/// 스킨정보 획득
		NiSkinInstance* pOldSkinInst = pOldGeom->GetSkinInstance();
		assert(pOldSkinInst);
		if( !pOldSkinInst )
		{
			return false;
		}
		NiSkinData* pOldSkinData = pOldSkinInst->GetSkinData();
		assert(pOldSkinData);
		if( !pOldSkinData )
		{
			return false;
		}

		//	/// 교체 객체의 Geom객체 정보
		NiTriBasedGeom* pGeom = NiDynamicCast(NiTriBasedGeom, GetGeom( newFace ) );
		assert(pGeom);
		if( !pGeom )
		{
			return false;
		}

		/// 교체 객체의 스킨정보
		NiSkinInstance* pSkinInst = pGeom->GetSkinInstance();
		assert(pSkinInst);
		if( !pSkinInst )
		{
			return false;
		}
		NiSkinData* pSkinData = pSkinInst->GetSkinData();
		assert(pSkinData);
		if( !pSkinData )
		{
			return false;
		}

		/// 교체되어질 Bone정보
		NiAVObject*const* ppBones = pSkinInst->GetBones();
		assert(ppBones);
		if( !ppBones )
		{
			return false;
		}
		NiSkinData::BoneData* pBoneData = pSkinData->GetBoneData();
		assert(pBoneData);
		if( !pBoneData )
		{
			return false;
		}

		/// 새로운 모델의 Bone정보를 현재 Bone Hierachy의 Bone Node들로 교체한다.
		/// 하나의 본셋에서 분할한 것이므로 본은 기존것을 공유한다.
		/// 분할 객체 자체도 독립객체로 출력이 가능하도록 본 정보를 갖는다.
		/// 주의!! 이 본정보는 모두 같지만, 참조 위치를 기존 포인터로 변경해야 한다.
		/// (스킨정보가 참조하는 메모리 주소로의 변경이다.)
		unsigned int i;
		NiAVObject *pBone = NULL;
		NiAVObject *pNewBone = NULL;
		NiTPrimitiveSet<NiAVObject*> newBones;
		NiTPrimitiveSet<sBoneDataObj*> newBoneDataObjs;
		sBoneDataObj* pBoneDataObj = NULL;

		//	NiAVObject* pRoot= GetNiObj();
		for( i=0; i<pSkinData->GetBoneCount(); ++i )
		{
			pBone = ppBones[i];
			if( !pBone )
			{
				assert(0);
				continue;
			}

			pNewBone = GetNiObj()->GetObjectByName( pBone->GetName() );
			if( !pNewBone )
			{
				assert(0);
				continue;
			}

			newBones.Add( pNewBone );
			pBoneDataObj = NiNew sBoneDataObj;
			pBoneDataObj->pBoneData = &pBoneData[i];
			for( unsigned int k = 0; k < pBoneData[i].m_usVerts; ++k )
			{
				pBoneDataObj->BoneVertData.Add( &pBoneData[i].m_pkBoneVertData[k] );
			}
			newBoneDataObjs.Add( pBoneDataObj );
		}

		/// Create the new arrays.
		unsigned int numNewBones = newBones.GetSize();
		NiAVObject** ppNewBones = NiAlloc(NiAVObject*, numNewBones);
		NiSkinData::BoneData* pNewBoneData = NiNew NiSkinData::BoneData[numNewBones];

		/// Fill the new arrays.
		for( i = 0; i<numNewBones; ++i )
		{
			ppNewBones[i] = newBones.GetAt( i );

			pBoneDataObj = newBoneDataObjs.GetAt( i );
			pNewBoneData[i].m_kBound = pBoneDataObj->pBoneData->m_kBound;
			pNewBoneData[i].m_kSkinToBone = pBoneDataObj->pBoneData->m_kSkinToBone;

			/// 구동상의 문제는 아니지만, 별도의 체크를 요한다!!
			assert( pBoneDataObj->BoneVertData.GetSize() < (unsigned int) (unsigned short) ~0 );
			pNewBoneData[i].m_usVerts = (unsigned short)pBoneDataObj->BoneVertData.GetSize();
			pNewBoneData[i].m_pkBoneVertData = NiNew NiSkinData::BoneVertData[pNewBoneData[i].m_usVerts];

			for ( unsigned short k = 0; k<pNewBoneData[i].m_usVerts; ++k )
			{
				pNewBoneData[i].m_pkBoneVertData[k].m_fWeight = pBoneDataObj->BoneVertData.GetAt( k )->m_fWeight;
				pNewBoneData[i].m_pkBoneVertData[k].m_usVert = pBoneDataObj->BoneVertData.GetAt( k )->m_usVert;
			}
		}

		/// Create the new NiSkinData and NiSkinInstance objects.
		NiSkinData* pNewSkinData = NiNew NiSkinData( numNewBones, pNewBoneData, 
			pSkinData->GetRootParentToSkin(), pGeom->GetVertices() );
		pNewSkinData->SortAndMergeBoneData();
		NiSkinInstance* pNewSkinInst = NiNew NiSkinInstance( pNewSkinData,
			pOldSkinInst->GetRootParent(), ppNewBones );

		/// Delete allocated memory.
		for( i = 0; i< newBoneDataObjs.GetSize(); ++i )
		{
			NiDelete newBoneDataObjs.GetAt( i );
		}

		/// Skin Instance 갱신
		pGeom->SetSkinInstance( pNewSkinInst );

		/// Bone LOD 정보의 갱신..
		NiBoneLODController* pOldCtrl = NULL;
		pOldCtrl = mpActorManager->GetBoneLODController();
		if( pOldCtrl )
		{
			int curLOD = pOldCtrl->GetBoneLOD();
			pOldCtrl->ProcessScene( GetNiNode() );
			pOldCtrl->ReplaceSkin( pOldGeom, pGeom );

			mpActorManager->RefreshControllerManager();

			NiBoneLODController* pNewCtrl = mpActorManager->GetBoneLODController();
			pNewCtrl->SetBoneLOD( curLOD );
		}
	}


	//////////////////////////////////////////////////////////////////////////
	/// ear process
	const char* partsName2 =RESOURCEMAN->GetManagedPartName( ePART_EAR );
	if( partsName2 == 0 || strlen(partsName2) == 0 )
	{
		assert(0);
		return false;
	}
	NiAVObject* newEar = pNode->GetObjectByName( partsName2 );
	if( newEar )
	{
		NiTriBasedGeom* pOldGeom = NULL;
		if( oldEar )
		{
			if( NiIsKindOf(NiTriBasedGeom, oldEar) )
				pOldGeom = (NiTriBasedGeom*)oldEar;
			else
				pOldGeom = NiDynamicCast( NiTriBasedGeom, GetGeom(oldEar) );
		}
		else
		{
			if( NiIsKindOf(NiTriBasedGeom, oldFace) )
				pOldGeom = (NiTriBasedGeom*)oldFace;
			else
				pOldGeom = NiDynamicCast( NiTriBasedGeom, GetGeom(oldFace) );
		}

		if( !pOldGeom )
		{
			assert(0);
			return false;
		}

		/// 스킨정보 획득
		NiSkinInstance* pOldSkinInst = pOldGeom->GetSkinInstance();
		assert(pOldSkinInst);
		if( !pOldSkinInst )
		{
			return false;
		}
		NiSkinData* pOldSkinData = pOldSkinInst->GetSkinData();
		assert(pOldSkinData);
		if( !pOldSkinData )
		{
			return false;
		}

		//	/// 교체 객체의 Geom객체 정보
		NiTriBasedGeom* pGeom = NiDynamicCast(NiTriBasedGeom, GetGeom( newEar ) );
		assert(pGeom);
		if( pGeom )
		{
			/// 교체 객체의 스킨정보
			NiSkinInstance* pSkinInst = pGeom->GetSkinInstance();
			assert(pSkinInst);
			if( !pSkinInst )
			{
				return false;
			}
			NiSkinData* pSkinData = pSkinInst->GetSkinData();
			assert(pSkinData);
			if( !pSkinData )
			{
				return false;
			}

			/// 교체되어질 Bone정보
			NiAVObject*const* ppBones = pSkinInst->GetBones();
			assert(ppBones);
			if( !ppBones )
			{
				return false;
			}
			NiSkinData::BoneData* pBoneData = pSkinData->GetBoneData();
			assert(pBoneData);
			if( !pBoneData )
			{
				return false;
			}

			/// 새로운 모델의 Bone정보를 현재 Bone Hierachy의 Bone Node들로 교체한다.
			/// 하나의 본셋에서 분할한 것이므로 본은 기존것을 공유한다.
			/// 분할 객체 자체도 독립객체로 출력이 가능하도록 본 정보를 갖는다.
			/// 주의!! 이 본정보는 모두 같지만, 참조 위치를 기존 포인터로 변경해야 한다.
			/// (스킨정보가 참조하는 메모리 주소로의 변경이다.)
			unsigned int i;
			NiAVObject *pBone = NULL;
			NiAVObject *pNewBone = NULL;
			NiTPrimitiveSet<NiAVObject*> newBones;
			NiTPrimitiveSet<sBoneDataObj*> newBoneDataObjs;
			sBoneDataObj* pBoneDataObj = NULL;

			//	NiAVObject* pRoot= GetNiObj();
			for( i=0; i<pSkinData->GetBoneCount(); ++i )
			{
				pBone = ppBones[i];
				if( !pBone )
				{
					assert(0);
					continue;
				}

				pNewBone = GetNiObj()->GetObjectByName( pBone->GetName() );
				if( !pNewBone )
				{
					assert(0);
					continue;
				}

				newBones.Add( pNewBone );
				pBoneDataObj = NiNew sBoneDataObj;
				pBoneDataObj->pBoneData = &pBoneData[i];
				for( unsigned int k = 0; k < pBoneData[i].m_usVerts; ++k )
				{
					pBoneDataObj->BoneVertData.Add( &pBoneData[i].m_pkBoneVertData[k] );
				}
				newBoneDataObjs.Add( pBoneDataObj );
			}

			/// Create the new arrays.
			unsigned int numNewBones = newBones.GetSize();
			NiAVObject** ppNewBones = NiAlloc(NiAVObject*, numNewBones);
			NiSkinData::BoneData* pNewBoneData = NiNew NiSkinData::BoneData[numNewBones];

			/// Fill the new arrays.
			for( i = 0; i<numNewBones; ++i )
			{
				ppNewBones[i] = newBones.GetAt( i );

				pBoneDataObj = newBoneDataObjs.GetAt( i );
				pNewBoneData[i].m_kBound = pBoneDataObj->pBoneData->m_kBound;
				pNewBoneData[i].m_kSkinToBone = pBoneDataObj->pBoneData->m_kSkinToBone;

				/// 구동상의 문제는 아니지만, 별도의 체크를 요한다!!
				assert( pBoneDataObj->BoneVertData.GetSize() < (unsigned int) (unsigned short) ~0 );
				pNewBoneData[i].m_usVerts = (unsigned short)pBoneDataObj->BoneVertData.GetSize();
				pNewBoneData[i].m_pkBoneVertData = NiNew NiSkinData::BoneVertData[pNewBoneData[i].m_usVerts];

				for ( unsigned short k = 0; k<pNewBoneData[i].m_usVerts; ++k )
				{
					pNewBoneData[i].m_pkBoneVertData[k].m_fWeight = pBoneDataObj->BoneVertData.GetAt( k )->m_fWeight;
					pNewBoneData[i].m_pkBoneVertData[k].m_usVert = pBoneDataObj->BoneVertData.GetAt( k )->m_usVert;
				}
			}

			/// Create the new NiSkinData and NiSkinInstance objects.
			NiSkinData* pNewSkinData = NiNew NiSkinData( numNewBones, pNewBoneData, 
				pSkinData->GetRootParentToSkin(), pGeom->GetVertices() );
			pNewSkinData->SortAndMergeBoneData();
			NiSkinInstance* pNewSkinInst = NiNew NiSkinInstance( pNewSkinData,
				pOldSkinInst->GetRootParent(), ppNewBones );

			/// Delete allocated memory.
			for( i = 0; i< newBoneDataObjs.GetSize(); ++i )
			{
				NiDelete newBoneDataObjs.GetAt( i );
			}

			/// Skin Instance 갱신
			pGeom->SetSkinInstance( pNewSkinInst );

			/// Bone LOD 정보의 갱신..
			NiBoneLODController* pOldCtrl = NULL;
			pOldCtrl = mpActorManager->GetBoneLODController();
			if( pOldCtrl )
			{
				int curLOD = pOldCtrl->GetBoneLOD();
				pOldCtrl->ProcessScene( GetNiNode() );
				pOldCtrl->ReplaceSkin( pOldGeom, pGeom );

				mpActorManager->RefreshControllerManager();

				NiBoneLODController* pNewCtrl = mpActorManager->GetBoneLODController();
				pNewCtrl->SetBoneLOD( curLOD );
			}
		}
	}

	/// 새로운 객체로 바꾼다.
	mpActorManager->ChangePartsObject( partsIdx, pNode );

	/// 기하 정보를 갱신
	mUpdateGeomInfo = true;

	return true;


/*
	/// 1. 기존 파트에 대한 정보를 수집한다.
	NiAVObject* pOldNode = mpActorManager->GetParts( partsIdx );
	if( !pOldNode )
	{
		assert(0);
		return false;
	}

	/// Geom객체를 획득한다.

	NiTriBasedGeom* pOldGeom = NULL;
	if( NiIsKindOf(NiTriBasedGeom, pOldNode) )
	{
		pOldGeom = (NiTriBasedGeom*)pOldNode;
	}
	else
	{
		pOldGeom = NiDynamicCast( NiTriBasedGeom, GetGeom(pOldNode) );
	}

	if( !pOldGeom )
	{
		assert(0);
		return false;
	}

	/// 스킨정보 획득
	NiSkinInstance* pOldSkinInst = pOldGeom->GetSkinInstance();
	assert(pOldSkinInst);
	if( !pOldSkinInst )
	{
		return false;
	}
	NiSkinData* pOldSkinData = pOldSkinInst->GetSkinData();
	assert(pOldSkinData);
	if( !pOldSkinData )
	{
		return false;
	}

	/// 2. 교체 되어질 파트에 대한 정보 수집
	const char* partsName =RESOURCEMAN->GetManagedPartName( partsIdx );
	if( partsName == 0 || strlen(partsName) == 0 )
	{
		assert(0);
		return false;
	}

	//	/// 교체 객체의 Geom객체 정보
	NiTriBasedGeom* pGeom = NiDynamicCast(NiTriBasedGeom, GetGeom( pNode ) );
	assert(pGeom);
	if( !pGeom )
	{
		return false;
	}

	/// 교체 객체의 스킨정보
	NiSkinInstance* pSkinInst = pGeom->GetSkinInstance();
	assert(pSkinInst);
	if( !pSkinInst )
	{
		return false;
	}
	NiSkinData* pSkinData = pSkinInst->GetSkinData();
	assert(pSkinData);
	if( !pSkinData )
	{
		return false;
	}

	/// 교체되어질 Bone정보
	NiAVObject*const* ppBones = pSkinInst->GetBones();
	assert(ppBones);
	if( !ppBones )
	{
		return false;
	}
	NiSkinData::BoneData* pBoneData = pSkinData->GetBoneData();
	assert(pBoneData);
	if( !pBoneData )
	{
		return false;
	}

	/// 새로운 모델의 Bone정보를 현재 Bone Hierachy의 Bone Node들로 교체한다.
	/// 하나의 본셋에서 분할한 것이므로 본은 기존것을 공유한다.
	/// 분할 객체 자체도 독립객체로 출력이 가능하도록 본 정보를 갖는다.
	/// 주의!! 이 본정보는 모두 같지만, 참조 위치를 기존 포인터로 변경해야 한다.
	/// (스킨정보가 참조하는 메모리 주소로의 변경이다.)
	unsigned int i;
	NiAVObject *pBone = NULL;
	NiAVObject *pNewBone = NULL;
	NiTPrimitiveSet<NiAVObject*> newBones;
	NiTPrimitiveSet<sBoneDataObj*> newBoneDataObjs;
	sBoneDataObj* pBoneDataObj = NULL;

	//	NiAVObject* pRoot= GetNiObj();
	for( i=0; i<pSkinData->GetBoneCount(); ++i )
	{
		pBone = ppBones[i];
		if( !pBone )
		{
			assert(0);
			continue;
		}

		pNewBone = GetNiObj()->GetObjectByName( pBone->GetName() );
		if( !pNewBone )
		{
			assert(0);
			continue;
		}

		newBones.Add( pNewBone );
		pBoneDataObj = NiNew sBoneDataObj;
		pBoneDataObj->pBoneData = &pBoneData[i];
		for( unsigned int k = 0; k < pBoneData[i].m_usVerts; ++k )
		{
			pBoneDataObj->BoneVertData.Add( &pBoneData[i].m_pkBoneVertData[k] );
		}
		newBoneDataObjs.Add( pBoneDataObj );
	}

	/// Create the new arrays.
	unsigned int numNewBones = newBones.GetSize();
	NiAVObject** ppNewBones = NiAlloc(NiAVObject*, numNewBones);
	NiSkinData::BoneData* pNewBoneData = NiNew NiSkinData::BoneData[numNewBones];

	/// Fill the new arrays.
	for( i = 0; i<numNewBones; ++i )
	{
		ppNewBones[i] = newBones.GetAt( i );

		pBoneDataObj = newBoneDataObjs.GetAt( i );
		pNewBoneData[i].m_kBound = pBoneDataObj->pBoneData->m_kBound;
		pNewBoneData[i].m_kSkinToBone = pBoneDataObj->pBoneData->m_kSkinToBone;

		/// 구동상의 문제는 아니지만, 별도의 체크를 요한다!!
		assert( pBoneDataObj->BoneVertData.GetSize() < (unsigned int) (unsigned short) ~0 );
		pNewBoneData[i].m_usVerts = (unsigned short)pBoneDataObj->BoneVertData.GetSize();
		pNewBoneData[i].m_pkBoneVertData = NiNew NiSkinData::BoneVertData[pNewBoneData[i].m_usVerts];

		for ( unsigned short k = 0; k<pNewBoneData[i].m_usVerts; ++k )
		{
			pNewBoneData[i].m_pkBoneVertData[k].m_fWeight = pBoneDataObj->BoneVertData.GetAt( k )->m_fWeight;
			pNewBoneData[i].m_pkBoneVertData[k].m_usVert = pBoneDataObj->BoneVertData.GetAt( k )->m_usVert;
		}
	}

	/// Create the new NiSkinData and NiSkinInstance objects.
	NiSkinData* pNewSkinData = NiNew NiSkinData( numNewBones, pNewBoneData, 
		pSkinData->GetRootParentToSkin(), pGeom->GetVertices() );
	pNewSkinData->SortAndMergeBoneData();
	NiSkinInstance* pNewSkinInst = NiNew NiSkinInstance( pNewSkinData,
		pOldSkinInst->GetRootParent(), ppNewBones );

	/// Delete allocated memory.
	for( i = 0; i< newBoneDataObjs.GetSize(); ++i )
	{
		NiDelete newBoneDataObjs.GetAt( i );
	}

	/// Clone한 Object에 있던 Bone Root정보를 삭제한다.
	if( pNode->DetachChild( pNode->GetObjectByName("foot_dummy") ) == 0)
		assert(0);

	/// Skin Instance 갱신
	pGeom->SetSkinInstance( pNewSkinInst );

	/// 새로운 객체로 바꾼다.
	mpActorManager->ChangePartsObject( partsIdx, pNode );

	/// Bone LOD 정보의 갱신..
	NiBoneLODController* pOldCtrl = NULL;
	pOldCtrl = mpActorManager->GetBoneLODController();
	if( pOldCtrl )
	{
		int curLOD = pOldCtrl->GetBoneLOD();
		pOldCtrl->ProcessScene( GetNiNode() );
		pOldCtrl->ReplaceSkin( pOldGeom, pGeom );

		mpActorManager->RefreshControllerManager();

		NiBoneLODController* pNewCtrl = mpActorManager->GetBoneLODController();
		pNewCtrl->SetBoneLOD( curLOD );
	}

	/// 기하 정보를 갱신
	mUpdateGeomInfo = true;

	return true;
*/
	return true;
}


bool cPlayerSceneNode::ChangePartsTexture( unsigned int partsIdx, const char* fileName )
{
	cString pathName;
	pathName.Format( "./Data/Character/%s", fileName );
	NiTexture* pTexture = RESOURCEMAN->LoadTexture( pathName );
	if( pTexture == 0 )
	{
		return false;
	}

	return cDynamicSceneNode::ChangePartsTexture( partsIdx, pTexture );
}


/* ------------------------------------------------------------------
* 함수이름 :	LinkToDummy( unsigned int index, const cString& pathName )
* 목    적 :	객체 링크용 인터페이스
* 주의사항 :	
* ------------------------------------------------------------------ */
unsigned int cPlayerSceneNode::LinkObject( unsigned int dummyIdx, const char* fileName )
{
	if( fileName == 0 )
		return UINT_MAX;

	///
	cString pathName;
	pathName.Format( "./Data/Character/%s", fileName );
	if( RESOURCEMAN->LoadNIF( pathName ) == false )
		return UINT_MAX;

	///
	NiNode* n = RESOURCEMAN->GetObjectByName( fileName );
	if( n == 0 ) 
	{
		assert(0);
		return UINT_MAX;
	}

	return cDynamicSceneNode::LinkObject( dummyIdx, n );
}

NiAVObject* cPlayerSceneNode::GetWeaponDummy( unsigned int objectIdx, bool left )
{
	NiAVObject* pObj = GetLinkObject( objectIdx );
	if( pObj == 0 )
		return 0;

	unsigned int dummyIdx = (left == true)? eLINK_LWEAPON:eLINK_RWEAPON;
	const char* dummyName = RESOURCEMAN->GetManagedDummyName( dummyIdx );
	if( strlen( dummyName ) == 0 )
		return 0;

	return pObj->GetObjectByName( dummyName );
}

cObjectNameCard* cPlayerSceneNode::CreateNameCard( cBaseObject* pObj )
{
	if( !pObj )
	{
		assert(0);
		return 0;
	}
	return new cPlayerNameCard( pObj );
}

void cPlayerSceneNode::AddDepthPlaneObject()
{
	bool showName = true;
	unsigned int optionHeight = 0;

	/// 타롯 & 점집 간판 추가
	if( mpTarotBoard && mpTarotBoard->IsActive() )
	{
		SCENEMAN->AddPlane( mpTarotBoard );

		mpTarotBoard->SetPos( mHeadScreenPosX, mHeadScreenPosY );
		optionHeight += mpTarotBoard->GetBoardHeight();

		showName = false;
	}
	else if( mpUserSellBoard && mpUserSellBoard->IsActive() )
	{
		SCENEMAN->AddPlane( mpUserSellBoard );
		mpUserSellBoard->SetPos( mHeadScreenPosX, mHeadScreenPosY );

		optionHeight += mpUserSellBoard->GetBoardHeight();
		showName = false;
	}

	if( mpHead == 0 )
	{
		assert( 0 );
		return;
	}

	NiPoint3 pos = this->GetWorldTranslate();
	pos.z = mpHead->GetWorldTranslate().z;
	pos.z += mHeadDist;

	NiPoint3 camPos = CAMERAMAN->GetCurrentNi()->GetWorldTranslate();

	float distCam = (pos - camPos).Length();
	float distHero = distCam;
	if( HERO )
	{
		distHero = (GetWorldTranslate() - HERO->GetPos()).Length();
	}

	/// 말풍선 보이기 여부
	if( OPTIONMAN->IsShowBubble() )
	{
		if( mpChatBubble && mpChatBubble->IsActive() )
		{
			if( distCam <= 3000.0f)
			{
				/// 말풍선 등록
				SCENEMAN->AddPlane( mpChatBubble );
				mpChatBubble->SetPos( mHeadScreenPosX, mHeadScreenPosY - (optionHeight+5) );

				optionHeight += mpChatBubble->GetBubbleHeight();
			}
		}
	}

	/// NameCard
	if( mpNameCard && showName == true )
	{
		if( distHero <= 1500 )
		{
			if( mShowNameGauge )
			{
				mpNameCard->SetEnableGauge( true );
				if( distHero > 500.0f )
				{
					if( HERO->GetTargetObject() != mpGameObject )
						mpNameCard->SetEnableGauge( false );
				}
			}
			else
			{
				mpNameCard->SetEnableGauge( false );
			}

			if( mpGameObject->IsDie() )
				mpNameCard->SetEnableGauge( false );

			mpNameCard->Set2DPos( mHeadScreenPosX, mHeadScreenPosY - (optionHeight+5), mFootScreenPosX, mFootScreenPosY+20 );
			SCENEMAN->AddPlane( mpNameCard );
		}
		else
		{
			mpNameCard->SetEnableGauge( false );

			/// over인 경우
			cBaseObject* o = OBJECTMAN->GetOverObject();
			if( o == mpGameObject && distHero <= 5000 )
			{
				mpNameCard->SetEnableGauge( false );

				mpNameCard->Set2DPos( mHeadScreenPosX, mHeadScreenPosY - (optionHeight+5), mFootScreenPosX, mFootScreenPosY+20 );
				SCENEMAN->AddPlane( mpNameCard );
			}
		}
	}
}

void cPlayerSceneNode::ActiveTarotBubble( LPTSTR title, unsigned long color )
{
	if( mpTarotBoard )
		mpTarotBoard->Active( mpGameObject->GetName(), title, color, true );
}

void cPlayerSceneNode::DeActiveTarotBubble()
{
	if( mpTarotBoard )
		mpTarotBoard->DeActive();
}

void cPlayerSceneNode::ActiveUserSellBubble( LPTSTR title, unsigned long color )
{
	if( mpUserSellBoard )
		mpUserSellBoard->Active( mpGameObject->GetName(), title, color, true );
}

void cPlayerSceneNode::DeActiveUserSellBubble()
{
	if( mpUserSellBoard )
		mpUserSellBoard->DeActive();
}

void cPlayerSceneNode::UpdateLinkEffect()
{
	if( mEffectList.IsEmpty() )
		return;

	cLinkIndexList::cIterator i = mEffectList.Begin();
	cLinkIndexList::cIterator end = mEffectList.End();
	for( ; i != end; ++i )
	{
		unsigned long idx = (unsigned long)*i;
		cEffectSceneNode* n = SCENEMAN->GetEffectSceneNode( idx );
		if( n )
		{
			unsigned int dummyIdx = n->GetDummyIdx();

			if( dummyIdx != eLINK_RWEAPON && dummyIdx != eLINK_LWEAPON )
				continue;

			NiNode* parent = n->GetNiObj()->GetParent();
			NiNode* check = GetDummyObject( dummyIdx );
			if( check )
			{
				if( check != parent )
				{
					/// new dummy
					if( parent )
						parent->DetachChild( n->GetNiObj() );
					else
						n->GetNiObj()->DetachParent();

					check->AttachChild( n->GetNiObj() );
				}
			}
			else
			{
				/// hand dummy check
				if( dummyIdx == eLINK_RWEAPON )
					check = GetDummyObject( eLINK_RHAND );
				else
					check = GetDummyObject( eLINK_LHAND );

				assert(check);
				if( check )
				{
					if( check != parent )
					{
						if( parent )
							parent->DetachChild( n->GetNiObj() );
						else
							n->GetNiObj()->DetachParent();

						check->AttachChild( n->GetNiObj() );
					}
				}
			}
		}
	}

}

void cPlayerSceneNode::OnProcessVehicle( unsigned long deltaTime, unsigned long accumTime )
{
	if( mUpdateGeomInfo )
	{
		/// Clear collect info
		ClearCollectInfo();

		/// collect info
		CollectGeomInfo( GetNiObj() );

		mUpdateGeomInfo = false;
	}

	/// 임의로 뼈대정보를 변경하는 경우에 사용한다.
	UpdateBipedRootTransforms( accumTime );

	/// Alpha Fade In/Out 처리
	ProcessAlpha( deltaTime );

	if( mAlpha < 0.000001f )
		mCheckFrustum = false;
	else
		mCheckFrustum = true;

	/// 경과 시간 * 속도 조절자
	unsigned long delta = accumTime - mLastTime;
	delta = (unsigned long)(delta * mScaleFactor);

	/// actormanager 전용 시간 계산
	if( mLastTime == ULONG_MAX )
		mScaleAccumTime = accumTime;
	else
		mScaleAccumTime += delta;

	float fScaleTime = mScaleAccumTime*0.001f;

	/// 객체의 Update
	mpActorManager->Update( fScaleTime );

	GetNiNode()->Update( fScaleTime );

	/// 경계구를 설정
	mBoundSphere.Set( GetCenter(), GetRadius() );

	/// 장면 트리를 갱신
	if( mContainer )
		mContainer->Update( this );

	ProcessTrail( accumTime - mLastTime );

	/// delete trail....
	if( mDeleteRibbonList.GetSize() )
	{
		NiTListIterator pos = mDeleteRibbonList.GetHeadPos();
		while( pos )
		{
			sTrailInfo* p = mDeleteRibbonList.GetNext(pos);
			mRibbonList.Remove(p);

			SAFE_DELETE(p);
		}
		mDeleteRibbonList.RemoveAll();
	}

	///
	if( mpChatBubble )
		mpChatBubble->Update( accumTime );

	///
	if( mNeedUpdateTransform )
	{
		mNeedUpdateTransform = false;
		mNeedUpdateShadow = true;
	}

	/// 누적 시간을 설정
	mLastTime = accumTime;
}

bool cPlayerSceneNode::OnVisibleVehicle( bool update, NiPoint3 pos )
{
	if( SCENEMAN->IsScreenShotMode() == true )
		return false;

	if( mAlpha < 0.00001f )
		return false;

	/// 그림자
	if( mpGameObject )
	{
		if( mpGameObject->IsDie() == false && mShadowGeom )
		{
			if( mAlpha > 0.00001f )
				mShadowGeom->Process( update, pos, mAlpha );

			mNeedUpdateShadow = false;
		}
	}
	return true;
}

void cPlayerSceneNode::HideHair( bool hide )
{
	if( mHideHair == hide )
		return;

	mHideHair = hide;

	NiAVObject* obj = GetPartObject( ePART_HAIR );
	if( obj )
		obj->SetAppCulled( mHideHair );

	/// 귀가 나누어진 경우는 별도처리
	obj = GetPartObject( ePART_EAR );
	if( obj )
		obj->SetAppCulled( mHideHair );
}



//////////////////////////////////////////////////////////////////////////
/// Hero SceneNode


cHeroSceneNode::cHeroSceneNode( eType type )
: cPlayerSceneNode( type )
, mpQuestPlane(0)
, mpNoticePlane(0)
, mpFieldTargetEffect(0)
{
	mAlphaSpeed = 4.0f;
	mFieldEffectOn = false;
	mAlpha = 1.0f;
}

cHeroSceneNode::~cHeroSceneNode()
{
	SAFE_DELETE( mpFieldTargetEffect );
	SAFE_DELETE( mpNoticePlane );
	SAFE_DELETE( mpQuestPlane );
}

bool cHeroSceneNode::Init( const cPlayerSceneNodeParam& param )
{
	if( cPlayerSceneNode::Init( param ) == false )
		return false;

	mpQuestPlane = new cCenterMsgPlane;
	if( mpQuestPlane == 0 )
	{
		assert(mpQuestPlane);
		return false;
	}

	mpNoticePlane = new cNoticeMsgPlane;
	if( mpNoticePlane == 0 )
	{
		assert(mpNoticePlane);
		return false;
	}

	mpFieldTargetEffect = new cShadowGeometry();
	if( mpFieldTargetEffect->Init( RESOURCEMAN->GetFieldTargetTexture(), 1000.0f, 1.0f ) == false )
	{
		assert(mpFieldTargetEffect);
		return false;
	}

	mAlpha = 1.0f;
	mTargetAlpha = 1.0f;

	return true;
}


void cHeroSceneNode::OnProcess( unsigned long deltaTime, unsigned long accumTime )
{
	cPlayerSceneNode::OnProcess( deltaTime, accumTime );

	///
	if( mpQuestPlane )
		mpQuestPlane->Update( deltaTime );

	if( mpNoticePlane )
		mpNoticePlane->Update( deltaTime, accumTime );
}

void cHeroSceneNode::ActiveQuestText( LPCTSTR msg, unsigned long color )
{
	if( mpQuestPlane == 0 )
		return;

	if( mpHead == 0 )
	{
		assert(0);
		return;
	}

	NiPoint3 pos = mpFoot->GetWorldTranslate();
	pos.z += mpGameObject->GetStatureValue() + mHeadDist;

	mpQuestPlane->ActiveMsg( msg, pos, color );
}

void cHeroSceneNode::ActiveNoticeText( LPCTSTR msg, unsigned long color )
{
	if( mpNoticePlane == 0 )
		return;

	mpNoticePlane->ActiveMsg( msg, color );
}

void cHeroSceneNode::AddToVisibleArray()
{
#ifdef _DEVSYS
	DEVSYSTEM->mShowHero = true;
#endif 

	if( SCENEMAN->IsScreenShotMode() == true )
		return;

	cDynamicSceneNode::AddToVisibleArray();
}

void cHeroSceneNode::AddDepthPlaneObject()
{
	if( mpQuestPlane && mpQuestPlane->IsActive() )
	{
		SCENEMAN->AddPlane( mpQuestPlane );
	}

	if( mpNoticePlane && mpNoticePlane->IsActive() )
	{
		SCENEMAN->AddPlane( mpNoticePlane );
	}

	///
	bool showName = true;
	unsigned int optionHeight = 0;

	/// 타롯 & 노점 간판 추가
	if( mpTarotBoard && mpTarotBoard->IsActive() )
	{
		SCENEMAN->AddPlane( mpTarotBoard );

		mpTarotBoard->SetPos( mHeadScreenPosX, mHeadScreenPosY );
		optionHeight += mpTarotBoard->GetBoardHeight()+5;

		showName = false;
	}
	else if( mpUserSellBoard && mpUserSellBoard->IsActive() )
	{
		SCENEMAN->AddPlane( mpUserSellBoard );
		mpUserSellBoard->SetPos( mHeadScreenPosX, mHeadScreenPosY );

		optionHeight += mpUserSellBoard->GetBoardHeight()+5;
		showName = false;
	}

	NiPoint3 pos = this->GetWorldTranslate();
	pos.z = mpHead->GetWorldTranslate().z;
	pos.z += mHeadDist;

	NiPoint3 camPos = CAMERAMAN->GetCurrentNi()->GetWorldTranslate();
	float distCam = (pos - camPos).Length();

	/// 말풍선 보이기 여부
	if( OPTIONMAN->IsShowBubble() )
	{
		if( mpChatBubble && mpChatBubble->IsActive() )
		{
			/// 말풍선 등록
			SCENEMAN->AddPlane( mpChatBubble );
			mpChatBubble->SetPos( mHeadScreenPosX, mHeadScreenPosY - (optionHeight+5) );

			optionHeight += mpChatBubble->GetBubbleHeight()+5;
		}
	}

	if( STAGEMAN->GetCurrentStage() == eStage_Select )
	{
		if( mpNameCard && showName == true )
		{
			if( mShowNameGauge )
				mpNameCard->SetEnableGauge( true );
			else
				mpNameCard->SetEnableGauge( false );

			if( mpGameObject->IsDie() )
				mpNameCard->SetEnableGauge( false );

			mpNameCard->Set2DPos( mHeadScreenPosX, mHeadScreenPosY - (optionHeight+5), mFootScreenPosX, mFootScreenPosY+20 );
			SCENEMAN->AddPlane( mpNameCard );
		}
	}
	else
	{
		if( OPTIONMAN->IsShowHeroNameCard() || OPTIONMAN->IsShowGauge() )
		{
			if( mpNameCard && showName == true )
			{
				if( mShowNameGauge )
					mpNameCard->SetEnableGauge( true );
				else
					mpNameCard->SetEnableGauge( false );

				if( mpGameObject->IsDie() )
					mpNameCard->SetEnableGauge( false );

				mpNameCard->Set2DPos( mHeadScreenPosX, mHeadScreenPosY - (optionHeight+5), mFootScreenPosX, mFootScreenPosY+20 );
				SCENEMAN->AddPlane( mpNameCard );
			}
		}
	}
}

void cHeroSceneNode::SetFieldEffRadius( float r )
{
	if( mpFieldTargetEffect == 0 )
		return;
	mpFieldTargetEffect->SetRadius( r );
}

void cHeroSceneNode::UpdateFieldTargetEffect( NiPoint3 center )
{
	if( mpFieldTargetEffect == 0 )
		return;

	mFieldEffectOn = true;
	mFieldEffectCenter = center;
}

bool cHeroSceneNode::OnVisible()
{
	bool check = cPlayerSceneNode::OnVisible();

	if( check && mFieldEffectOn )
	{
		mFieldEffectOn = false;
		mpFieldTargetEffect->Process( true, mFieldEffectCenter, 0.9f );
	}
	return check;
}