#include "gamesrv.h"

#include "stdafx.h"
#include "SkillManager.h"
#include "ObjectManager.h"

#include "../Common/Protocol.h"
#include "Player_Common.h"
#include "Monster_Common.h"
#include "Status_Common.h"

#include "BaseObject.h"
#include "Monster.h"
#include "Npc.h"
#include "Item.h"
#include "Totem.h"
#include "AIManager.h"
#include "StatusCalc_Server.h"
#include "DamageCalc.h"
#include "StatusScript.h"
#include "MonsterScript.h"
#include "Drop.h"
#include "LevelManager.h"
#include "NpcScript.h"
#include "TotemScript.h"
#include "PvPScript.h"
#include "VehicleScript.h"
#include "Gathering.h"
#include "VehicleScript.h"
#include "ShortcutScript.h"

#include "AppTimer.h"

const unsigned char TARGET_CNT_MAX = 50;

cObjectManager* cObjectManager::mpObjectManager = NULL;

cObjectManager::cObjectManager(void) 
{
	mpObjectManager = this;

	mpPlayerNamePool = NULL;

	mpPlayerPool  = NULL;
	mpMonsterPool = NULL;
	mpNpcPool     = NULL;
	mpItemPool    = NULL;
	mpGatheringPool = NULL;

	mpPlayerMap  = NULL;
	mpMonsterMap = NULL;
	mpNpcMap     = NULL;
	mpItemMap    = NULL;
	mpGatheringMap = NULL;

	mpDrop       = NULL;
	mpShortCutScript = NULL;
}

cObjectManager::~cObjectManager(void)
{
	Release( );

	mpObjectManager = NULL;
}

// Init Method
bool cObjectManager::Init( int serverType )
{
	// Memory Pool - cPlayerNamePool class.
	mpPlayerNamePool = new cPlayerNamePool( );

	// memorypool
	mpPlayerPool  = new cPlayerPool( 1000 );
	mpMonsterPool = new cMonsterPool( 1000 );
	mpNpcPool     = new cNpcPool( 100 );
	mpItemPool    = new cItemPool( 1000 );
	mpTotemPool   = new cTotemPool( 1000 );
	mpGatheringPool = new cGatheringPool( 1000 );

	// hashtable
	mpPlayerMap  = new cPlayerMap( 1000 );
	mpMonsterMap = new cMonsterMap( 1000 );	
	mpNpcMap     = new cNpcMap( 100 );
	mpItemMap    = new cItemMap( 1000 );
	mpTotemMap    = new cTotemMap( 1000 );
	mpGatheringMap = new cGatheringMap( 1000 );

	/// 070115 PKH 스텟 계산기
	mpStatusScript = new cStatusScript;
	if ( !mpStatusScript->Init( ) )
	{
		return false;
	}
	mpStatusCalc = new cStatusCalc_Server;
	mpDamageCalc = new cDamageCalc;


	/// 070130 PKH 몬스터 스크립트
	mpMonsterScript = new cMonsterScript;
	if ( !mpMonsterScript->Init( ) )
	{
		return false;
	}

	/// 070220 PKH 드롭 시스템 로드
	mpDrop = new cDrop;
	if ( !mpDrop->Init( ) )
	{
		return false;
	}

	/// 070515 PKH 레벨 관리 
	mpLevelManager = new cLevelManager;
	if ( !mpLevelManager->Init( ) )
	{
		return false;
	}

	/// 070626 PKH NPC정보 로드
	mpNpcScript = new cNPCScript;
	if ( !mpNpcScript->Init( ) )
	{
		return false;
	}

	/// 081023 PKH 토템 스크립트
	mpTotemScript = new cTotemScript;
	if ( !mpTotemScript->Init( ) )
	{
		return false;
	}

	/// 081114 PKH pvp 스크립트
	mpPvPScript = new cPvPScript;
	if ( !mpPvPScript->Init( ) )
	{
		return false;
	}

	/// 090106 PKH 탈것
	mpVehicleScript = new cVehicleScript;
	if ( !mpVehicleScript->Init( ) )
	{
		return false;
	}

	/// 
	mpShortCutScript = new cShortCutScript;
	if( !mpShortCutScript->Init() )
	{
		return false;
	}

	switch( serverType )
	{
	case _E_ST_NORMAL_MAP_:
		LoadNpcInfo();
		break;
	case _E_ST_ID_PVP_:
		break;
	default:
		return false;
	}

	return true;
}

// Release Method
void cObjectManager::Release( )
{
	SAFE_DELETE( mpShortCutScript );

	SAFE_DELETE( mpVehicleScript );

	SAFE_RELEASE_DELETE( mpPvPScript );

	/// 토템 리소스 제거
	SAFE_RELEASE_DELETE( mpTotemScript );

	SAFE_RELEASE_DELETE( mpNpcScript );

	/// 레벨메니저 제거
	SAFE_RELEASE_DELETE( mpLevelManager );

	/// 드롭 시스템 제거
	SAFE_RELEASE_DELETE( mpDrop );

	/// 몬스터 리소스 제거
	SAFE_RELEASE_DELETE( mpMonsterScript );

	/// 070117 PKH 스텟 계산기
	SAFE_DELETE( mpDamageCalc );
	SAFE_DELETE( mpStatusCalc );
	SAFE_RELEASE_DELETE( mpStatusScript );

	if ( mpGatheringMap )
	{
		cGatheringMap::cIterator begin = mpGatheringMap->Begin( );
		cGatheringMap::cIterator end   = mpGatheringMap->End( );
		cGathering*              gathering;
		while ( begin != end )
		{
			gathering = (cGathering*)(*begin++).mSecond;
			delete gathering;
		}
	}
	SAFE_DELETE( mpGatheringMap );
	SAFE_DELETE( mpGatheringPool );

	if ( mpTotemMap )
	{
		cTotemMap::cIterator begin = mpTotemMap->Begin( );
		cTotemMap::cIterator end   = mpTotemMap->End( );
		cTotem*              totem;
		while ( begin != end )
		{
			totem = (*begin++).mSecond;
			delete totem;
		}
	}
	SAFE_DELETE( mpTotemMap );
	SAFE_DELETE( mpTotemPool );

	/// 071105 KCJ 맵에 들어 있는 내용물 소멸자 호출
	if ( mpItemMap )
	{
		cItemMap::cIterator begin = mpItemMap->Begin( );
		cItemMap::cIterator end   = mpItemMap->End( );
		cItem*              item;
		while ( begin != end )
		{
			item = (*begin++).mSecond;
			delete item;
		}
	}
	SAFE_DELETE( mpItemMap );
	SAFE_DELETE( mpItemPool );

	/// 061201 PKH 맵에 들어 있는 내용물 소멸자 호출
	if ( mpNpcMap )
	{
		cNpcMap::cIterator begin = mpNpcMap->Begin( );
		cNpcMap::cIterator end   = mpNpcMap->End( );
		cNpc*              npc;
		while ( begin != end )
		{
			npc = (*begin++).mSecond;
			delete npc;
		}
	}
	SAFE_DELETE( mpNpcMap );
	SAFE_DELETE( mpNpcPool );

	/// 061201 PKH 맵에 들어 있는 내용물 소멸자 호출
	if ( mpMonsterMap )
	{
		cMonsterMap::cIterator begin = mpMonsterMap->Begin( );
		cMonsterMap::cIterator end   = mpMonsterMap->End( );
		cMonster*              monster;
		while ( begin != end )
		{
			monster = (cMonster*)(*begin++).mSecond;
			delete monster;
		}
	}
	SAFE_DELETE( mpMonsterMap );
	SAFE_DELETE( mpMonsterPool );

	/// 070221 PKH 맵에 들어 있는 내용물 소멸자 호출
	if ( mpPlayerMap )
	{
		cPlayerMap::cIterator begin = mpPlayerMap->Begin( );
		cPlayerMap::cIterator end   = mpPlayerMap->End( );
		cPlayer*              player;
		while ( begin != end )
		{
			player = (*begin++).mSecond;
			delete player;	
		}
	}
	SAFE_DELETE( mpPlayerMap );
	SAFE_DELETE( mpPlayerPool );

	// Memory Pool - cPlayerNamePool class.
	SAFE_DELETE( mpPlayerNamePool );
}

// AllocPlayer Method
cPlayer* cObjectManager::AllocPlayer()
{
	return mpPlayerPool->Alloc();
}

// FreePlayer Method
void cObjectManager::FreePlayer( cPlayer* ptr )
{
	mpPlayerPool->Free( ptr );
}

// AllocMonster Method
cMonster* cObjectManager::AllocMonster( )
{
	return mpMonsterPool->Alloc();
}

// FreeMonster Method
void cObjectManager::FreeMonster( cMonster* ptr )
{
	mpMonsterPool->Free( ptr );
}

// AllocNpc Method
cNpc* cObjectManager::AllocNpc()
{
	return mpNpcPool->Alloc();
}

// FreeNpc Method
void cObjectManager::FreeNpc( cNpc* ptr )
{
	mpNpcPool->Free( ptr );
}

// AllocItem Method
cItem* cObjectManager::AllocItem( )
{
	return mpItemPool->Alloc();
}

// FreeItem Method
void cObjectManager::FreeItem(cItem* ptr)
{
	mpItemPool->Free( ptr );
}

// AllocTotem Method
cTotem* cObjectManager::AllocTotem( )
{
	return mpTotemPool->Alloc();
}

// FreeTotem Method
void cObjectManager::FreeTotem(cTotem* ptr)
{
	mpTotemPool->Free( ptr );
}

// AllocGathering Method
cGathering* cObjectManager::AllocGathering( )
{
	return mpGatheringPool->Alloc();
}

// FreeGathering Method
void cObjectManager::FreeGathering(cGathering* ptr)
{
	mpGatheringPool->Free( ptr );
}

// AddPlayer Method
cPlayer* cObjectManager::AddPlayer(unsigned long connectionIdx, unsigned long playerIdx, wchar_t* playerName, unsigned long money, unsigned long deposit)
{
	cPlayer* player = NULL;
	if ( mpPlayerMap->GetAt( playerIdx ) == NULL )
	{
		player = new cPlayer( );
		if( player == 0 )
			return 0;

		if( player->Init( connectionIdx, playerIdx, money, deposit ) == false )
		{
			SAFE_DELETE( player );
			return 0;
		}

		if( mpPlayerMap->Insert( playerIdx, player ) == false )
		{
			SAFE_DELETE( player );
			return 0;
		}

		PerPlayerName* name = mpPlayerNamePool->Search( player->GetName( ) );
		if ( name != NULL )
			mpPlayerNamePool->ReleasePlayerName( name );
		mpPlayerNamePool->GetPlayerName( connectionIdx, playerIdx, playerName );
	}
	return player;
}

// RemovePlayer Method
bool cObjectManager::RemovePlayer(unsigned long playerIdx)
{
	cPlayer* player = (cPlayer*)mpPlayerMap->GetAt( playerIdx );
	if ( player != NULL )
	{
		PerPlayerName* name = mpPlayerNamePool->Search( player->GetName( ) );
		if ( name != NULL )
			mpPlayerNamePool->ReleasePlayerName( name );

		//player->ReleaseSkillInfluenceSet();
		mpPlayerMap->Erase( playerIdx );
		SAFE_DELETE( player );
		return true;
	}
	return false;
}

// AddMonster Method - 몬스터 생성은 풀에서 할당 받는 형태로 오버 라이드됨
cMonster* cObjectManager::AddMonster( unsigned long monsterIdx, unsigned long classIdx, unsigned long regenIdx )
{
	cMonster* monster = NULL;
	// 061126 PKH 맵에 이미 만들어져 있는 몬스터가 있는지 확인
	if ( mpMonsterMap->GetAt( monsterIdx ) == NULL )
	{
		// 061126 PKH 몬스터 객체생성(monster new override)
		monster = new cMonster( );

		// 061126 PKH 몬스터 초기화 && 생성된 몬스터를 맵에 추가
		if ( !monster )
		{
			SAFE_DELETE( monster );
		}
		else if( !monster->Init( monsterIdx, classIdx, regenIdx ) )
		{
			SAFE_DELETE( monster );
		}
		else if ( !mpMonsterMap->Insert( monsterIdx, monster ) )
		{
			SAFE_DELETE( monster );
		}
	}
	return monster;
}

// RemoveMonster Method
bool cObjectManager::RemoveMonster(unsigned long monsterIdx)
{
	cMonster* monster = (cMonster*)mpMonsterMap->GetAt( monsterIdx );
	if ( monster != NULL )
	{
		mpMonsterMap->Erase( monsterIdx );
		SAFE_DELETE( monster );
		return true;
	}
	return false;
}

void cObjectManager::InsertDeleteMonster( unsigned long monsterIdx )
{
	mDelMonster.PushBack( monsterIdx );
}

void cObjectManager::MonJobProcess( cMonster* pMonster, eACTION_ID actionid, eACTION_CHANGE actionstate )
{
	/// eACTCH_CONTINUE일경우 액션상태 안바낌
	if(actionstate!=eACTCH_CONTINUE)
	{
		/// 플레이어 비정상위치 도망
		if ( actionstate == eACTCH_ESCAPE )
		{
			pMonster->ActionChange( eACT_ESCAPE ); 
			return;
		}

		/// 현재 액션다음 행동에 해당하는 액션으로 교체
		switch( actionid )
		{
			/// 이동상태
		case eACT_MOVE:
			{
				/// 이동이 끝난경우 추적
				if ( actionstate == eACTCH_MOVE_END ) { pMonster->ActionChange( eACT_FOLLOW ); }
			}
			break;

			/// 추적 상태 인경우
		case eACT_FOLLOW:	
			{
				/// 추적 포기 이동
				if ( actionstate == eACTCH_FOLLOW_END ) { pMonster->ActionChange( eACT_MOVE ); }
				/// 추적중 전투 재시작
				if ( actionstate == eACTCH_FOLLOW_ATTACK ) { pMonster->ActionChange( eACT_ATTACK ); }
			}
			break;

			/// 전투 끝 처리
		case eACT_ATTACK:	
			{
				/// 전투끝 몬스터 승리
				if ( actionstate == eATTCH_ATTACK_END ) { pMonster->ActionChange( eACT_MOVE ); }
				/// 플레이어 도망
				if ( actionstate == eATTCH_ATTACK_FOLLOW ) { pMonster->ActionChange( eACT_FOLLOW ); }
			}
			break;

			/// 도망이 끝난경우 주위이동
		case eACT_ESCAPE:
			{
				/// 도망 끝(타겟 죽음) 자유이동
				if ( actionstate == eACTCH_ESCAPE_END ) { pMonster->ActionChange( eACT_MOVE ); }
				/// 목적지까지 도망 다시 전투 시작
				if ( actionstate == eACTCH_ESCAPE_FOLLOW ) { pMonster->ActionChange( eACT_FOLLOW ); }
			}
			break;

			/// 대기 시간이 끝난경우 전투 추적 상태
		case eACT_IDLE:
			{
				if ( actionstate == eACTCH_IDLE_END ) { pMonster->ActionChange( eACT_ATTACK ); }
			}
			break;

		default: break;
		} 
	} 
}


// AddNpc Method
cNpc* cObjectManager::AddNpc(unsigned long npcIdx)
{
	cNpc* npc = NULL;
	// 070627 PKH 맵에 이미 만들어져 있는 NPC가 있는지 확인
	if ( mpNpcMap->GetAt( npcIdx ) == NULL )
	{
		// 070627 PKH NPC 객체생성(monster new override)
		npc = new cNpc( );

		// 070627 PKH NPC 초기화 && 생성된 NPC를 맵에 추가
		if ( !npc )
		{
			SAFE_DELETE( npc );
		}
		else if ( !npc->Init( npcIdx ) )
		{
			SAFE_DELETE( npc );
		}
		else if ( !mpNpcMap->Insert( npcIdx, npc ) )
		{
			SAFE_DELETE( npc );
		}
	}
	return npc;
}

// RemoveNpc Method
bool cObjectManager::RemoveNpc(unsigned long npcIdx)
{
	cNpc* npc = mpNpcMap->GetAt( npcIdx );
	if ( npc != NULL )
	{
		mpNpcMap->Erase( npcIdx );
		SAFE_DELETE( npc );
		return true;
	}
	return false;
}

// AddItem Method
cItem* cObjectManager::AddItem(unsigned long itemIdx, cBaseObject* baseObject, bool getAuto)
{
	cItem* item = NULL;
	if ( mpItemMap->GetAt( itemIdx ) == NULL )
	{
		item = new cItem( );
		if ( !item )
		{
			SAFE_DELETE( item );
		}
		else if ( !item->Init( itemIdx, baseObject, getAuto ) )
		{
			SAFE_DELETE( item );
		}
		else if ( !mpItemMap->Insert( itemIdx, item ) )
		{
			SAFE_DELETE( item );
		}
	}
	return item;
}

// RemoveItem Method
bool cObjectManager::RemoveItem(unsigned long itemIdx)
{
	cItem* item = mpItemMap->GetAt( itemIdx );
	if ( item != NULL )
	{
		mpItemMap->Erase( itemIdx );
		SAFE_DELETE( item );
		return true;
	}
	return false;
}

// AddTotem Method
cTotem* cObjectManager::AddTotem( unsigned long calssIdx, unsigned long partyIdx, NiPoint2 pos, sObject attacker, 
								 eAPPLYTYPE applyType, eATTRIBUTETYPE attributeType )
{
	unsigned long objectIdx = mTotemIdxGen.GeneratIdx();

	cTotem* pTotem = NULL;
	if ( mpTotemMap->GetAt( objectIdx ) == NULL )
	{
		pTotem = new cTotem( );

		if ( !pTotem )
		{
			SAFE_DELETE( pTotem );
		}
		else if ( !pTotem->TotemInit( attacker, objectIdx, calssIdx, partyIdx, pos, applyType, attributeType ) )
		{
			SAFE_DELETE( pTotem );
		}
		else if ( !mpTotemMap->Insert( objectIdx, pTotem ) )
		{
			SAFE_DELETE( pTotem );
		}
	}
	return pTotem;
}

// AddTotem Method
cTotem* cObjectManager::PvPAddTotem( unsigned char teamType, unsigned long calssIdx, unsigned short pvpIdx, NiPoint2 pos, 
								 eAPPLYTYPE applyType, eATTRIBUTETYPE attributeType )
{
	unsigned long objectIdx = mTotemIdxGen.GeneratIdx();

	cTotem* pTotem = NULL;
	if ( mpTotemMap->GetAt( objectIdx ) == NULL )
	{
		pTotem = new cTotem( );

		if ( pTotem == NULL )
		{
			SAFE_DELETE( pTotem );
			return NULL;
		}

		if ( pTotem->PvPTotemInit( (ePVPDM_TEAM_TYPE)teamType, objectIdx, calssIdx, pvpIdx, pos, applyType, attributeType ) == false )
		{
			SAFE_DELETE( pTotem );
			return NULL;
		}

		if ( mpTotemMap->Insert( objectIdx, pTotem ) == NULL )
		{
			SAFE_DELETE( pTotem );
			return NULL;
		}
	}
	return pTotem;
}

// RemoveTotem Method
bool cObjectManager::RemoveTotem( unsigned long objectIdx )
{
	cTotem* pTotem = mpTotemMap->GetAt( objectIdx );
	if ( pTotem != NULL )
	{
		mpTotemMap->Erase( objectIdx );
		SAFE_DELETE( pTotem );
		return true;
	}
	return false;
}

void cObjectManager::InsertDeleteTotem( unsigned long totemIdx )
{
	mDelTotem.PushBack( totemIdx );
}

// AddGathering Method
cGathering* cObjectManager::AddGathering( unsigned long objectIdx, unsigned long regenIdx )
{
	cGathering* pGathering = NULL;

	if ( mpGatheringMap->GetAt( objectIdx ) == NULL )
	{
		pGathering = new cGathering( );
		if( pGathering == 0 )
			return 0;

		if( pGathering->GatheringInit( objectIdx, regenIdx ) == false )
		{
			SAFE_DELETE( pGathering );
			return 0;
		}

		if( mpGatheringMap->Insert( objectIdx, pGathering ) == false )
		{
			SAFE_DELETE( pGathering );
			return 0;
		}
	}
	return pGathering;
}

// RemoveGathering Method
bool cObjectManager::RemoveGathering( unsigned long objectIdx )
{
	cGathering* pGathering = (cGathering*)mpGatheringMap->GetAt( objectIdx );
	if ( pGathering != NULL )
	{
		mpGatheringMap->Erase( objectIdx );
		SAFE_DELETE( pGathering );
		return true;
	}
	return false;
}

void cObjectManager::InsertDeleteGathering( unsigned long objectIdx )
{
	mDelGathering.PushBack( objectIdx );
}

// SendMsgMonsterModelNumberToPlayer Method
void cObjectManager::SendMsgMonsterModelNumberToPlayer( unsigned long connectionIdx, unsigned long mapNum )
{
	if ( !mpMonsterMap ) assert(NULL);

	tHashSet<unsigned long>* pHashSet = NULL;
	unsigned long            classIdx;

	/// 070622 PKH 맵번호에 해당하는 몬스터 모델 넘버set을 읽어옴
	pHashSet = MONSTERSCRIPT->GetMonsterModelNumber( mapNum );

	HANDLE           handle  = NULL;
	MSG_MODELNUMBER* sendMsg = (MSG_MODELNUMBER*)NETWORK2->GetMsgRoot( &handle, connectionIdx );
	unsigned long    length  = 0;

	sendMsg->Category = NM_MONSTER;
	sendMsg->Protocol = NM_MONSTER_MODELNUMBER_RES;
	sendMsg->mCount   = 0;

	if ( pHashSet != NULL )
	{
		tHashSet<unsigned long>::cIterator start = pHashSet->Begin( );
		tHashSet<unsigned long>::cIterator end   = pHashSet->End( );

		while ( start != end )
		{
			classIdx = (*start);

			if ( classIdx )
			{
				/// 070622 PKH 모델정보 로드에 필요한 idx를 담는다.
				sendMsg->mClassIdx[sendMsg->mCount] = classIdx;
				++(sendMsg->mCount);
			}
			else
			{
				assert(NULL);
				NETWORK2->PostServerEvent("cObjectManager::SendMsgMonsterModelNumberToPlayer modelNumber[%d]==0", (*start) );
				return;
			}

			start++;
		}
	}

	length  = sizeof(MSG_MODELNUMBER)-sizeof(sendMsg->mClassIdx);
	length += sendMsg->mCount * sizeof(sendMsg->mClassIdx);

	NETWORK2->SendMsgRoot( handle, length );
}

// SendMsgNpcModelNumberToPlayer Method
void cObjectManager::SendMsgNpcModelNumberToPlayer( unsigned long connectionIdx, unsigned long mapNum )
{
	if ( !mpNpcMap ) assert(NULL);

	tHashSet<unsigned long>* pHashSet = NULL;
	unsigned long            classIdx;

	/// 070622 PKH 맵번호에 해당하는 Npc 모델 넘버set을 읽어옴
	pHashSet = NPCSCRIPT->GetNpcModelNumber( mapNum );

	HANDLE           handle  = NULL;
	MSG_MODELNUMBER* sendMsg = (MSG_MODELNUMBER*)NETWORK2->GetMsgRoot( &handle, connectionIdx );
	unsigned long    length  = 0;

	sendMsg->Category = NM_NPC;
	sendMsg->Protocol = NM_NPC_MODELNUMBER_RES;
	sendMsg->mCount   = 0;

	if ( pHashSet != NULL )
	{
		tHashSet<unsigned long>::cIterator start = pHashSet->Begin( );
		tHashSet<unsigned long>::cIterator end   = pHashSet->End( );

		while ( start != end )
		{
			classIdx = (*start);

			if ( classIdx )
			{
				/// 070622 PKH 모델정보 로드에 필요한 idx를 넣는다.
				sendMsg->mClassIdx[sendMsg->mCount] = classIdx;
				sendMsg->mCount++;
			}
			else
			{
				assert(NULL);
				NETWORK2->PostServerEvent("cObjectManager::SendMsgNpcModelNumberToPlayer modelNumber[%d]==0", (*start) );
				return;
			}

			start++;
		}
	}

	length  = sizeof(MSG_MODELNUMBER)-sizeof(sendMsg->mClassIdx);
	length += sendMsg->mCount * sizeof(sendMsg->mClassIdx);

	NETWORK2->SendMsgRoot( handle, length );
}

// GetObject Method
cBaseObject* cObjectManager::GetObject(unsigned char objectType, unsigned long idx)
{
	switch( objectType )
	{
	case eOBJECTTYPE_PLAYER:
	case eOBJECTTYPE_HERO:
		return GetPlayer( idx );

	case eOBJECTTYPE_MONSTER:
		return GetMonster( idx );

	case eOBJECTTYPE_NPC:
		return GetNpc( idx );

	case eOBJECTTYPE_TOTEM:
		return GetTotem( idx );

	case eOBJECTTYPE_GATHERING:
		return GetGathering( idx );


	default:
		return NULL;
	}
}

// Process Method
void cObjectManager::Process(unsigned long elapsedTime, unsigned long accumTime)
{

	/*-- 061208 PKH 클라이언트&서버 플레이어 이동속도 동기화 --*/

	cPlayerMap::cIterator playerBegin = mpPlayerMap->Begin();
	cPlayerMap::cIterator playerEnd   = mpPlayerMap->End();
	cPlayer*              player;

	// 플레이어 정보 처리
	while ( playerBegin != playerEnd )
	{
		player = (*playerBegin++).mSecond;
		player->Update( elapsedTime, accumTime );
	}

	/*-- PKH Monster Object 처리. --*/

	for( cPointHashMap::cIterator iter = mpMonsterMap->Begin(); iter != mpMonsterMap->End() ; ++iter )
	{
		cMonster* pMonster = (cMonster*)(*iter).mSecond;

		if( !pMonster )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cObjectManager::Process pMonster == NULL" );
			continue;
		}

		/// 몬스터 상태 업데이트
		eACTION_CHANGE actionstate = pMonster->Update( elapsedTime, accumTime  );
		eACTION_ID actionid = pMonster->GetActionID();

		MonJobProcess( pMonster, actionid, actionstate );
	}

	/*-- Begin Item Object 처리. --*/

	cItemMap::cIterator itemBegin = mpItemMap->Begin( );
	cItemMap::cIterator itemEnd   = mpItemMap->End( );
	cItem*              item;
	unsigned long       itemIdx;

	// 아이템 관리.
	while ( itemBegin != itemEnd )
	{
		item    = (cItem*)(*itemBegin++).mSecond;
		itemIdx = item->GetObjectID( );

		if ( !item->Update( elapsedTime, accumTime ) )
		{
			if ( item->IsGetAuto( ) != true )
			{
				GRIDMANAGER->RemoveItem( item );
			}
			RemoveItem( itemIdx );
		}
	}

	/*-- PKH Totem Object 처리. --*/

	cTotemMap::cIterator totemBegin = mpTotemMap->Begin();
	cTotemMap::cIterator totemEnd = mpTotemMap->End();
	cTotem* pTotem;

	// 토템 정보 처리
	while ( totemBegin != totemEnd )
	{
		pTotem = (*totemBegin++).mSecond;
		pTotem->TotemProcess( elapsedTime, accumTime );
	}

	/*-- PKH Gathering Object 처리. --*/

	for( cPointHashMap::cIterator iter = mpGatheringMap->Begin(); iter != mpGatheringMap->End() ; ++iter )
	{
		cGathering* pGathering = (cGathering*)(*iter).mSecond;

		if( !pGathering )
		{
			NETWORK2->PostServerEvent("cObjectManager::Process pGathering  == NULL" );
			continue;
		}

		/// 채집 상태 업데이트
		pGathering->Update( elapsedTime, accumTime  );
	}

	for( unsigned long i = 0 ; mDelMonster.GetSize() > i ; ++i )
	{
		RemoveMonster( mDelMonster[i] );
	}
	mDelMonster.Clear();

	for( unsigned long i = 0 ; mDelTotem.GetSize() > i ; ++i )
	{
		cTotem* pTotem = GetTotem( mDelTotem[i] );
		if( pTotem != NULL )
		{
			GRIDMANAGER->RemoveTotem( pTotem );
		}
		RemoveTotem( mDelTotem[i] );
	}
	mDelTotem.Clear();

	for( unsigned long i = 0 ; mDelGathering.GetSize() > i ; ++i )
	{
		RemoveGathering( mDelGathering[i] );
	}
	mDelGathering.Clear();
}

// LoadNpcInfo Method
void cObjectManager::LoadNpcInfo( )
{
	typedef const tPointerHashMap<unsigned long, sNPCRegen*> cNpcRegenMap;
	cNpcRegenMap* pNpcRegenMap = NPCSCRIPT->GetNPCRegenMap( );

	cNpcRegenMap::cConstIterator begin = pNpcRegenMap->Begin( );
	cNpcRegenMap::cConstIterator end   = pNpcRegenMap->End( );

	cNpc* pNpc = NULL;
	sNPCRegen* pNpcRegenScript = NULL;
	unsigned long npcIdx;
	while( begin != end )
	{
		npcIdx = (*begin).mFirst;
		pNpcRegenScript = (*begin).mSecond;
		if( pNpcRegenScript == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cObjectManager::LoadNpcInfo pNpcRegenScript[%d] == NULL", npcIdx );
			return;
		}

		pNpc = AddNpc( npcIdx );	
		GRIDMANAGER->AddNpc( pNpc );

		++begin;
	}
}

// PlayerInCenterPoint Method
void cObjectManager::PlayerInCenterPoint( NiPoint3 centerPoint, float range, tArray<sMultiTarget>* targetAry, bool applyHP )
{
	cRangeCheck rangeCheck;
	NiPoint3    targetPoint;

	cPlayerMap::cIterator playerStart = mpPlayerMap->Begin( );
	cPlayerMap::cIterator playerEnd   = mpPlayerMap->End( );
	cPlayer*              pPlayer     = NULL;

	sObject target;
	unsigned char targetCnt = 0;

	while ( playerStart != playerEnd )
	{
		pPlayer     = (*playerStart++).mSecond;
		if( pPlayer == NULL || pPlayer->GetStateDie() == true )
			continue;

		targetPoint.x = pPlayer->GetXPos( );
		targetPoint.y = pPlayer->GetYPos( );
		targetPoint.z = pPlayer->Height( );

		float tempRange = ObjectSizeRange( NULL, pPlayer, range );

		rangeCheck.SetRadius( tempRange );

		/// 플레이어가 범위 안에 들어왔는지 체크
		if ( rangeCheck.IsRange( centerPoint, targetPoint ) )
		{
			target.index = pPlayer->GetObjectID( );
			target.type = eOBJECTTYPE_PLAYER;
			sMultiTarget multiTarget;
			multiTarget.mTarget = target;
			multiTarget.mApplyHP = applyHP;
			targetAry->PushBack( multiTarget );
			++targetCnt;

			if( targetCnt >= TARGET_CNT_MAX )
				return;
		}
	}
}



void cObjectManager::PlayerInCenterPoint( NiPoint3		centerPoint,
										 float			range,
										 tArray<sMultiTarget>* targetAry,
										 unsigned long	attackerIdx,
										 bool applyHP )
{
	cRangeCheck rangeCheck;
	NiPoint3    targetPoint;

	cPlayerMap::cIterator playerStart = mpPlayerMap->Begin( );
	cPlayerMap::cIterator playerEnd   = mpPlayerMap->End( );
	cPlayer*              pPlayer     = NULL;

	sObject target;
	unsigned char targetCnt = 0;

	while ( playerStart != playerEnd )
	{
		pPlayer     = (*playerStart++).mSecond;
		if( pPlayer == NULL || pPlayer->GetStateDie() == true )
			continue;

		/// 자신 제외
		if( attackerIdx == pPlayer->GetObjectID() )
			continue;

		/// 결투 대상 확인
		if( DUELMANAGER->IsAttack( pPlayer->GetDuelIdx(), attackerIdx, pPlayer->GetObjectID() ) == false )
			continue;

		targetPoint.x = pPlayer->GetXPos( );
		targetPoint.y = pPlayer->GetYPos( );
		targetPoint.z = pPlayer->Height( );

		float tempRange = ObjectSizeRange( NULL, pPlayer, range );

		rangeCheck.SetRadius( tempRange );

		/// 플레이어가 범위 안에 들어왔는지 체크
		if ( rangeCheck.IsRange( centerPoint, targetPoint ) )
		{
			target.index = pPlayer->GetObjectID( );
			target.type = eOBJECTTYPE_PLAYER;
			sMultiTarget multiTarget;
			multiTarget.mTarget = target;
			multiTarget.mApplyHP = applyHP;
			targetAry->PushBack( multiTarget );
			++targetCnt;

			if( targetCnt >= TARGET_CNT_MAX )
				return;
		}
	}
}



void cObjectManager::MonsterInCenterPoint( NiPoint3 centerPoint, float range, tArray<sMultiTarget>* targetAry, bool applyHP )
{
	cRangeCheck rangeCheck;
	NiPoint3    targetPoint;

	sObject target;
	unsigned char targetCnt = 0;

	cMonsterMap::cIterator monsterStart = mpMonsterMap->Begin( );
	cMonsterMap::cIterator monsterEnd   = mpMonsterMap->End( );
	cMonster*              pMonster     = NULL;

	while ( monsterStart != monsterEnd )
	{
		pMonster    = (cMonster*)(*monsterStart++).mSecond;
		if( pMonster == NULL || pMonster->GetStateDie() == true )
			continue;

		targetPoint.x = pMonster->GetXPos( );
		targetPoint.y = pMonster->GetYPos( );
		targetPoint.z = pMonster->Height( );

		float tempRange = ObjectSizeRange( NULL, pMonster, range );

		rangeCheck.SetRadius( tempRange );

		if ( rangeCheck.IsRange( centerPoint, targetPoint ) )
		{
			target.index = pMonster->GetObjectID( );
			target.type = eOBJECTTYPE_MONSTER;
			sMultiTarget multiTarget;
			multiTarget.mTarget = target;
			multiTarget.mApplyHP = applyHP;
			targetAry->PushBack( multiTarget );
			++targetCnt;

			if( targetCnt >= TARGET_CNT_MAX )
				return;
		}
	}
}


float cObjectManager::ObjectSizeRange( cBaseObject* object1, cBaseObject* object2, float range )
{
	float object1size = 0.0f;
	float object2size = 0.0f;

	/// 오브젝트 사이즈 만큼의 수정된 거리
	if( object1 != NULL )
		object1size = object1->GetFixedObjectSize();

	if( object2 != NULL )
		object2size = object2->GetFixedObjectSize();

	return range + object1size + object2size;
}


NiPoint2 cObjectManager::CalcFollowPos( unsigned char followPos, NiPoint2 centerPos )
{
	if( followPos >= FOLLOW_POS_MAX )
		return centerPos;

	NiPoint2 followPosValue = FOLLOW_POS_VALUE[followPos];
	NiPoint2 followPosTemp = centerPos - followPosValue;
	return followPosTemp;
}



unsigned long cObjectManager::IsNpcTalk( unsigned long npcIdx, cPlayer* pPlayer, eNPC_TALK_TYPE npcTalkType, unsigned long value )
{

	if( pPlayer == NULL )
		return ERROR_NPC_OPEN_PLAYERSTATE;

	pPlayer->MoveStop();

	// npc 정보확인
	cNpc* pNpc = GRIDMANAGER->GetNpc( npcIdx );
	if( pNpc == NULL )
	{
		NETWORK2->PostServerEvent("cObjectManager::IsNpcTalk[%d,%d,%d,%d,%d] pNpc == NULL", 
			npcIdx, pPlayer->GetNpcIdx(), pPlayer->GetObjectID(), pPlayer->GetState(), pPlayer->GetStateStop() );
		return ERROR_NPC_OPEN_NPC;
	}

	unsigned long npcClassIdx = pNpc->GetRaceGender();

	/// 거리 체크
	float talkRange = NPC_VALID_DISTANCE + SYNC_MOVE_RANGE;
	float tempRange = OBJECTMANAGER->ObjectSizeRange( pPlayer, pNpc, talkRange );
	cRangeCheck rangeCheck( tempRange );
	if( rangeCheck.IsNotRange( pPlayer->GetPos(), pNpc->GetPos() ) == true )
		return ERROR_NPC_OPEN_RANGE;


	/// 대화 타입별 조건 체크(공통)
	if( npcTalkType != eNPCTALK_OPEN )
	{
		/// 대화 상대가 틀림
		if( pPlayer->GetNpcIdx() != npcIdx )
		{
			NETWORK2->PostServerEvent("cObjectManager::IsNpcTalk[%d,%d,%d,%d,%d] pPlayer->GetNpcIdx() != npcIdx && npcTalkType != eNPCTALK_OPEN", 
				npcIdx, pPlayer->GetNpcIdx(), pPlayer->GetObjectID(), pPlayer->GetState(), pPlayer->GetStateStop() );
			return ERROR_NPC_OPEN_NPC;
		}

		/// 플레이어가 npc대화 상태가 아님
		if( pPlayer->GetStateStop() != eSTOP_NPCSPEECH )
			return ERROR_NPC_OPEN_PLAYERSTATE;
	}

	/// 대화 타입별 체크 & 설정(개별)
	switch( npcTalkType )
	{
		/// 대화 시작
	case eNPCTALK_OPEN:
		{

			if( pPlayer->GetNpcIdx() == npcIdx )
				return ERROR_NPC_OPEN_ALREADY;

			if( pPlayer->GetNpcIdx() != 0 )
			{
				NETWORK2->PostServerEvent("cObjectManager::IsNpcTalk[%d,%d,%d,%d,%d] pPlayer->GetNpcIdx() != npcIdx && && npcTalkType == eNPCTALK_OPEN", 
					npcIdx, pPlayer->GetNpcIdx(), pPlayer->GetObjectID(), pPlayer->GetState(), pPlayer->GetStateStop() );
				return ERROR_NPC_OPEN_NPC;
			}

			/// 결투, 메세지 수락/대기 상태등 일경우 불가
			if( pPlayer->IsRequestRejection() == true )
				return ERROR_NPC_OPEN_PLAYERSTATE;

			/// 플레이어 상태 체크
			if( pPlayer->ChangeState( eOBJECT_STATE_STOP ) == false )
				return ERROR_NPC_OPEN_PLAYERSTATE;

			/// 정지 상태 중 npc 대화 상태로 변경
			pPlayer->SetStateStop( eSTOP_NPCSPEECH );

			/// 대화중인 npc인덱스를 설정
			pPlayer->SetNpcIdx( npcIdx );

			/// 퀘스트 신규가능 목록 리스트 보내기
			if( pPlayer->SendNpcNewQuestList( npcIdx ) == false )
				NETWORK2->PostServerEvent( "GameIn : failed to send NPC QUEST LIST" );
		}
		break;
		/// 스킬 습득
	case eNPCTALK_SKILLADD:
		{
			if( NPCSCRIPT->IsHaveNpcFunc( npcClassIdx, NPC_FUNC_SKILLSTORE ) == false )
				return ERROR_NPC_OPEN_DEALLIST; 

			/// npc가 보유한 스킬 목록의 직업군을 읽어옴
			tArray<unsigned long>* ary = SKILLSCRIPT->GetStoreJobList( npcClassIdx );
			if( ary == NULL )
				return ERROR_NPC_OPEN_NPC;

			cSkillTree* pSkillTree;
			unsigned long jobIdx;
			unsigned long playerJobIdx;
			unsigned long skillidx;

			bool jobSame = false;

			/// 직업 군을 비교
			for( unsigned long i = 0 ; i < ary->GetSize() ; ++i )
			{
				/// npc의 직업군
				jobIdx = (*ary)[i];

				/// 플레이어의 직업
				playerJobIdx = pPlayer->GetJob();

				/// npc와 플레이어의 직업 비교
				if( jobIdx == playerJobIdx )
					jobSame = true;

				/// 플레이어와 npc의 직업 군이 틀릴경우
				if( jobSame == false )
				{
					/// 플레이어의 상위 직업군 로드
					tArray<unsigned long>* playerJobParent = STATUSSCRIPT->GetParentJob( playerJobIdx );
					if( playerJobParent == NULL )
						continue;

					/// 플레이어의 상위직업군과 npc의 직업 비교
					for( unsigned int i = 0 ; i < playerJobParent->GetSize() ; ++i )
					{
						if( (*playerJobParent)[i] == jobIdx )
							jobSame = true;
					}
				}

				if( jobSame == true )
				{
					/// 직업 군에 해당하는 스킬들을 읽어옴
					pSkillTree = SKILLSCRIPT->GetSkillTree( jobIdx );
					for( unsigned long j = 0 ; pSkillTree->mIconCount > j ; ++j )
					{
						skillidx = pSkillTree->mIconList[j].skillIdx;

						/// 배우려는 스킬과 npc가 보유한 스킬 리스트가 일치하면 정상 종료
						if( skillidx == value )
							return ERROR_NPC_OPEN_SUCCESS;
					}
				}
			}

			/// 전체 검사해도 나오지 않는 경우 에러 리턴
			return ERROR_NPC_OPEN_DEALLIST;
		}
		break;
		/// 아이템 구매
	case eNPCTALK_ITEMBUY:
		{
			if( NPCSCRIPT->IsHaveNpcFunc( npcClassIdx, NPC_FUNC_STORE ) == false )
				return ERROR_NPC_OPEN_DEALLIST; 

			/// npc에 해당하는 item 목록을 읽어옴
			cItemNpcStore* pItemStore = ITEMMANAGER->GetItemNpcStore( npcClassIdx );
			if( pItemStore == NULL )
				return ERROR_NPC_OPEN_DEALLIST;

			if( pItemStore->IsItemIdx( value ) == false )
				return ERROR_NPC_OPEN_DEALLIST;
		}
		break;
		/// 아이템 판매
	case eNPCTALK_ITEMSELL:
		{
			if( NPCSCRIPT->IsHaveNpcFunc( npcClassIdx, NPC_FUNC_STORE ) == false )
				return ERROR_NPC_OPEN_DEALLIST; 
		}
		break;
		/// 창고 이용
	case eNPCTALK_INVENTORY:
		{
			if( NPCSCRIPT->IsHaveNpcFunc( npcClassIdx, NPC_FUNC_WAREHOUSE ) == false )
				return ERROR_NPC_OPEN_DEALLIST; 
		}
		break;
		/// 퀘스트 추가
	case eNPCTALK_QUESTADD:
		{
			/// 퀘스트번호 유효성 검사
			cQuestDefine* define = QUESTMAN->GetQuestDefine( value );
			if( !define )
				return ERROR_NPC_OPEN_DEALLIST;

			/// 해당 퀘스트와 퀘스트 발생 npc 유효성 검사
			if( define->mGiveNpcIndex > 0 && define->mGiveNpcIndex != npcClassIdx )
				return ERROR_NPC_OPEN_DEALLIST;
		}
		break;
		/// 퀘스트 보상
	case eNPCTALK_QUESTREWARD:
		{
			/// 퀘스트번호 유효성 검사
			cQuestDefine* define = QUESTMAN->GetQuestDefine( value );
			if( !define )
				return ERROR_NPC_OPEN_DEALLIST;

			/// 보상요청한 npc 유효성 검사
			if( define->mTakeNpcIndex != npcClassIdx )
				return ERROR_NPC_OPEN_DEALLIST;
		}
		break;
		/// 길드 생성
	case eNPCTALK_GUILDCREATE:
		{
			if( NPCSCRIPT->IsHaveNpcFunc( npcClassIdx, NPC_FUNC_GUILD ) == false )
				return ERROR_NPC_OPEN_DEALLIST; 
		}
		break;
		/// NPC 점집
	case eNPCTALK_NPCTAROT:
		{
			sNpcFuncData* data = NPCSCRIPT->GetNpcFuncData( npcClassIdx );
			if( !data )
				return ERROR_NPC_TAROT_JOIN_FAIL;

			if( NPCSCRIPT->IsHaveNpcFunc( npcClassIdx, NPC_FUNC_TAROT ) == false )
				return ERROR_NPC_TAROT_JOIN_FAIL;

			if( !(pPlayer->GetMoney() > data->mTarotPrice) )
				return ERROR_NPC_TAROT_JOIN_MONEY;
		}
		break;
	default:
		assert(NULL);
		NETWORK2->PostServerEvent( "IsNpcTalk eNPC_TALK_TYPE error[%d]", npcTalkType );
		break;
	}

	return ERROR_NPC_OPEN_SUCCESS;
}

cPlayer* cObjectManager::GetPlayer( wchar_t* charName )
{
	PerPlayerName* name = mpPlayerNamePool->Search( charName );
	if ( name != NULL )
	{
		return GetPlayer( name->characterIdx );
	}
	return NULL;
}

void cObjectManager::SendNotice( char* msg, unsigned long size )
{
	cPlayerMap::cIterator start = mpPlayerMap->Begin();
	cPlayerMap::cIterator end = mpPlayerMap->End();

	cPlayer* pPlayer;

	while( start != end )
	{
		pPlayer = (*start++).mSecond;
		if( pPlayer == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cObjectManager::SendNotice player == NULL");
			continue;
		}

		if( pPlayer->GetGameIn() == false )
			continue;

		NETWORK2->SendToUser( pPlayer->GetConnectionIdx(), msg, size );
	}
}

/// 치트
void cObjectManager::CheatRemoveItemRange( cBaseObject* obj, float radius )
{
	obj;radius;
}

void cObjectManager::CheatRemoveMonsterRange( cBaseObject* pCenter, float size )
{
	if( pCenter == NULL )
		return;

	cRangeCheck rangeCheck( size );

	cMonster* pMonster = GRIDMANAGER->FindFirstMonster( pCenter );
	/// 각각의 몬스터를 검색함
	while( pMonster != NULL )
	{
		if( pMonster->GetStateDie() == true ) 
		{
			pMonster = GRIDMANAGER->FindNextMonster();
			continue;
		}

		if( pMonster->GetMapNumber() != pCenter->GetMapNumber() )
		{
			pMonster = GRIDMANAGER->FindNextMonster();
			continue;
		}

		if( rangeCheck.IsRange( pCenter->GetPos(), pMonster->GetPos() ) == true )
			CheatRemoveMonster( pMonster->GetObjectID() );		

		pMonster = GRIDMANAGER->FindNextMonster();
	}
}


void cObjectManager::CheatRemoveMonster( unsigned long monsterIdx )
{
	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster != NULL )
	{
		pMonster->OutsideDestroyTrue();
		pMonster->ActionChange( eACT_DIE );

		MSG_SYN_MONSTER_HP synMsg;
		synMsg.Category			= NM_MONSTER;
		synMsg.Protocol			= NM_MONSTER_HP_SYN;
		synMsg.mMonsterIdx	    = monsterIdx;
		synMsg.mHP				= 0;
		synMsg.mMaxHP			= pMonster->GetMaxHP();
		synMsg.mDie				= true;

		NETWORK2->QuickSendExcept( pMonster, (char*)&synMsg, sizeof(MSG_SYN_MONSTER_HP) );
	}
}

void cObjectManager::CheatKillMonster( unsigned long monsterIdx, unsigned long playerIdx )
{
	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster != NULL )
	{
		sObject player = { eOBJECTTYPE_PLAYER, playerIdx };
		if( pMonster->GetfirstAttack() == 0 )
		{
			pMonster->SetFirstAttack( playerIdx );
			pMonster->AddTakeDamage( player, pMonster->GetMaxHP(), 0, eTAKEDAMAGETYPE_DAMAGE );
		}		

		pMonster->ActionChange( eACT_DIE );

		MSG_SYN_MONSTER_HP synMsg;
		synMsg.Category			= NM_MONSTER;
		synMsg.Protocol			= NM_MONSTER_HP_SYN;
		synMsg.mMonsterIdx	    = monsterIdx;
		synMsg.mHP				= 0;
		synMsg.mMaxHP			= pMonster->GetMaxHP();
		synMsg.mDie				= true;

		NETWORK2->QuickSendExcept( pMonster, (char*)&synMsg, sizeof(MSG_SYN_MONSTER_HP) );
	}
}

void cObjectManager::QuestRemoveMonster( unsigned long monsterIdx )
{
	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster != NULL )
		pMonster->OutsideDestroy();
}

// SendMsgSynPVPJoin Method.
void cObjectManager::SendMsgSynPVPJoin( )
{
	cPlayerMap::cIterator begin = mpPlayerMap->Begin();
	cPlayerMap::cIterator end   = mpPlayerMap->End();
	cPlayer* pPlayer = NULL;

	while ( begin != end )
	{
		pPlayer = (*begin++).mSecond;
		if ( pPlayer != NULL )
		{
			if ( pPlayer->GetGameIn( ) )
			{
				if( pPlayer->CheckPvPJoin() == false )
					continue;				

				sDMInfo* pDMInfo = PVPSCRIPT->GetDmInfo( 0, pPlayer->GetLevel() );
				if( pDMInfo != NULL && pPlayer->GetForceType() != eFORCETYPE_NONE )
				{
					HANDLE               handle  = NULL;
					MSG_SYN_PVP_DM_JOIN* sendMsg = NULL;
					sendMsg = (MSG_SYN_PVP_DM_JOIN*)NETWORK2->GetMsgRoot( &handle, pPlayer->GetConnectionIdx( ), NM_PVP, NM_PVP_DM_JOIN_SYN );
					if ( sendMsg != NULL )
					{
						sendMsg->mMinLevel		= pDMInfo->mMinLevel;
						sendMsg->mMaxLevel		= pDMInfo->mMaxLevel;
						sendMsg->mLeftTime      = PVP_DM_JOIN_TIME;
						NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PVP_DM_JOIN) );
					}
				}
			}
		}
		else
			NETWORK2->PostServerEvent( "cObjectManager::SendMsgSynPVPJoin:player is NULL." );
	}
}


void cObjectManager::SendPVPNotice( unsigned char leftMinute )
{
	cPlayerMap::cIterator start = mpPlayerMap->Begin();
	cPlayerMap::cIterator end = mpPlayerMap->End();

	cPlayer* pPlayer;

	while( start != end )
	{
		pPlayer = (*start++).mSecond;
		if( pPlayer == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cObjectManager::SendNotice player == NULL");
			continue;
		}

		if( pPlayer->GetGameIn() == false )
			continue;

		HANDLE               handle  = NULL;
		MSG_SYN_PVP_DM_NOTICE* sendMsg = NULL;
		sendMsg = (MSG_SYN_PVP_DM_NOTICE*)NETWORK2->GetMsgRoot( &handle, pPlayer->GetConnectionIdx( ), NM_PVP, NM_PVP_DM_NOTICE_SYN );
		if ( sendMsg != NULL )
		{
			sendMsg->mLeftTime = leftMinute;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PVP_DM_NOTICE) );
		}
	}
}
