#include "StdAfx.h"
#include "AiManager.h"

#include "Monster_Common.h"

#include "GameSrv.h"

#include "NaviField.h"
#include "Pathfinder.h"
#include "ObjectManager.h"
#include "MonsterScript.h"
#include "Monster.h"
#include "Gathering.h"


cAIManager* cAIManager::mpAIManager = NULL;


cAIManager::cAIManager()
{
	if( !mpAIManager )
	{
		mpAIManager = this;
	}

	mpRegenGroupMap = new cPointHashMap( 999 );
	mpRegenGatherGroupMap = new cPointHashMap( 999 );
	mpRegenSwitchMap = new cPointHashMap( 999 );
}

cAIManager::~cAIManager()
{
	if( mServerType == _E_ST_NORMAL_MAP_ )
	{
		for( unsigned int i = 0; i < MAP_COUNT; ++i )
		{
			/// 네비필드를 해제
			delete mNaviField[i];

			/// 길찾기를 해제
			delete mPathFinder[i];
		}
	}
	else
	{
		/// 081106 PKH 인던
		delete mInstDgNaviField;

		delete mInstDgPathFinder;
	}


	SAFE_DELETE( mpRegenSwitchMap );
	SAFE_DELETE( mpRegenGroupMap );
	SAFE_DELETE( mpRegenGatherGroupMap );
}

bool cAIManager::Init( int serverType )
{
	mNextCheckTime = 0;

	cString fileName;

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

	mServerType = serverType;

	mServerOnRegen = true;

	if( mServerType == _E_ST_NORMAL_MAP_ )
	{
		for( unsigned int i = 0; i < MAP_COUNT; ++i )
		{
			/// 네비필드를 생성
			mNaviField[i] = new cNaviField;
			assert( mNaviField[i] );

			/// 길찾기를 생성
			mPathFinder[i] = new cPathFinder( 4096 );
			assert( mPathFinder[i] );

			unsigned int mapNum = MAP_MIN + i;
			unsigned long folderIdx = STAGESCRIPT->GetMapFolderIdx( mapNum );

			/// 네비 필드를 로딩
			cNaviField* naviField = mNaviField[i];
			if( naviField == NULL )
			{
				assert(NULL);
				NETWORK2->PostServerEvent("naviField[%d] == NULL", i );
				return false;
			}


			fileName.Format( "./Script/Resource/Map/%03d.navifield", folderIdx );

			if( naviField->Load( fileName.Cstr() ) == false )
			{
				assert( 0 && "failed to load navifield" );
				return false;
			}

			/// 길찾기를 생성 및 설정
			cPathFinder* pathFinder = mPathFinder[i];
			if( pathFinder == NULL )
			{
				assert(NULL);
				NETWORK2->PostServerEvent("pathFinder[%d] == NULL", i );
				return false;
			}

			pathFinder->Init( naviField );

			/// x좌표 타일갯수, y좌표 타일갯수, 타일1칸사이즈							
			mMapSize[i] = naviField->GetCellCount() * 100.0f;
		}

		cPointHashMap::cIterator iter;

		//////////////////////////////////////////////////////////////////////////
		/// 스크립트로 로드된 몬스터, 몬스터 그룹 정보를 보관
		/// 목록을 보유한 상태로 objectmanager의 몬스터 객체만 지웠다 추가했다가 한다.
		cPointHashMap* pRegenGroupMap = MONSTERSCRIPT->GetRegenGroupMap();
		if( pRegenGroupMap == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("pRegenGroupMap == NULL" );
			return false;
		}

		sRegenGroup* pRegenGroup = NULL;
		for( iter = pRegenGroupMap->Begin() ; iter != pRegenGroupMap->End() ; ++iter )
		{
			/// 그룹 정보 추가
			pRegenGroup = new sRegenGroup;		/// 서버 생성 시점에만 생성(new)함
			pRegenGroup->mpRegenGroup = (sRegenGroupScript*)(*iter).mSecond;
			pRegenGroup->mGroupIdx = (*iter).mFirst;
			pRegenGroup->mIsRegen = false;
			pRegenGroup->mRegenTime = 0;
			pRegenGroup->mMonsterAliveCnt = 0;
			if( mpRegenGroupMap->Insert( pRegenGroup->mpRegenGroup->mGroupIdx, pRegenGroup ) == false )
			{
				assert(NULL);
				NETWORK2->PostServerEvent("mpRegenGroupMap Insert duplicate[%d]", pRegenGroup->mpRegenGroup->mGroupIdx );
				return false;
			}

			/// 그룹 안에 몬스터 목록도 넣는다.
			for( unsigned long i = 0 ; i < pRegenGroup->mpRegenGroup->mMonsterAry.GetSize() ; ++i )
			{
				sRegenMonster* pRM = new sRegenMonster;
				
				/// 몬스터 객체 regen 하지 않은 상태로 플래그 설정
				pRM->mObjectIdx = 0;
				pRM->mRegenTime = 0;
				pRM->mDie = true;
				pRM->mRegenIdx = pRegenGroup->mpRegenGroup->mMonsterAry[i];

				pRegenGroup->mMonsterList.PushBack( pRM );
			}
		}

		//////////////////////////////////////////////////////////////////////////
		/// 스크립트로 로드된 채집, 리젠위치 그룹 정보를 보관
		/// 목록을 보유한 상태로 objectmanager의 채집 객체만 지웠다 추가했다가 한다.
		cPointHashMap* pRegenGatheringMap = GATHERINGSCRIPT->GetRegenGatheringMap();
		if( pRegenGatheringMap == NULL )
		{
			NETWORK2->PostServerEvent( "pRegenGatheringMap == NULL" );
			return false;
		}

		sRegenGatherGroup* pGroup = 0;
		for( iter = pRegenGatheringMap->Begin() ; iter != pRegenGatheringMap->End() ; ++iter )
		{
			/// 그룹 정보 추가
			pGroup = new sRegenGatherGroup;		/// 서버 생성 시점에만 생성(new)함
			pGroup->mGroupIdx = (*iter).mFirst;
			pGroup->mpGetheringRegenHeader = (sGatheringRegenHeader*)(*iter).mSecond;


			unsigned long randTime = pGroup->mpGetheringRegenHeader->mRandTime;
			if( randTime != 0 )
				randTime = rand() % pGroup->mpGetheringRegenHeader->mRandTime;
			pGroup->mRegenTime = NETWORK2->GetAccumTime() + pGroup->mpGetheringRegenHeader->mRegenWaitTime + randTime;
			pGroup->mRegenReady = true;

			if( mpRegenGatherGroupMap->Insert( pGroup->mGroupIdx, pGroup ) == false )
			{
				assert(NULL);
				NETWORK2->PostServerEvent("mpRegenGatherGroupMap Insert duplicate[%d]", pGroup->mGroupIdx );
				return false;
			}
		}
	}
	else
	{
		/// 081106 PKH 인던

		mInstDgNaviField = new cNaviField;
		assert( mInstDgNaviField );

		mInstDgPathFinder = new cPathFinder( 4096 );
		assert( mInstDgPathFinder );

		cNaviField* naviField = mInstDgNaviField;
		if( naviField == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("InstDgNaviField == NULL" );
			return false;
		}

		fileName.Format( "./Script/Resource/Map/%03d.navifield", PVP_MAP_NO );

		if( naviField->Load( fileName.Cstr() ) == false )
		{
			assert( 0 && "failed to load navifield" );
			return false;
		}

		cPathFinder* pathFinder = mInstDgPathFinder;
		if( pathFinder == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("InstPathFinder == NULL" );
			return false;
		}

		pathFinder->Init( naviField );

		mInstDgMapSize = naviField->GetCellCount() * 100.0f;
	}		

	/// 스위치 형태로 리젠하는 리젠그룹들 정보 셋팅
	cPointHashMap* pMap = MONSTERSCRIPT->GetSwitchGroup();
	if( pMap != NULL )
	{
		cPointHashMap::cIterator begin = pMap->Begin();
		cPointHashMap::cIterator end = pMap->End();

		for( ; begin != end ; ++begin )
		{
			/// 스크립트에 기록된 스위치 그룹을 ai에 등록
			unsigned long swichGroupIdx = (*begin).mFirst;
			cArray* pAry = (cArray*)(*begin).mSecond;
			unsigned long arySize = pAry->GetSize();

			/// 스크립트 정보 ai에 기록
			sSwichGroup* pSwitch = new sSwichGroup;
			pSwitch->mSwitchGroup = swichGroupIdx;
			pSwitch->mpGroupIdxAry = pAry;
			pSwitch->mGroupIdx = 0;
			pSwitch->mRegenTime = 0;
			pSwitch->mRegenStop = false;
			/// [리젠그룹] 랜덤 선택
			if( pAry != NULL && arySize != 0 )
			{
				unsigned long randSelect = rand() % arySize;
				unsigned long selectGroupIdx = (*pAry)[randSelect];
                
				sRegenGroup* pRegenGroup = (sRegenGroup*)mpRegenGroupMap->GetAt( selectGroupIdx );
				if( pRegenGroup == NULL )
					continue;

				/// 선택된 [리젠그룹]의 정보를 기본정보로 셋팅
				pSwitch->mGroupIdx = selectGroupIdx;
				pSwitch->mRegenTime = pRegenGroup->mpRegenGroup->mRegenTime + ( rand() % 1000 ) + NETWORK2->GetAccumTime();
			}			

			/// 정보 기록
			if( mpRegenSwitchMap->Insert( pSwitch->mSwitchGroup, pSwitch ) == false )
			{
				NETWORK2->PostServerEvent("AIManager::Init mpRegenSwitchMap->Insert( %d ) == false", pSwitch->mSwitchGroup );
				return false;
			}
		}
	}

	return true;
}



void cAIManager::Release()
{
	//SAFE_DELETE( mPathFinder );
	cPointHashMap::cIterator iter;

	for( iter = mpRegenSwitchMap->Begin() ; iter != mpRegenSwitchMap->End() ; ++iter )
	{
		sSwichGroup* pSwitch = (sSwichGroup*)(*iter).mSecond;
		SAFE_DELETE( pSwitch );
	}
	mpRegenSwitchMap->Clear();

	

	for( iter = mpRegenGatherGroupMap->Begin() ; iter != mpRegenGatherGroupMap->End() ; ++iter )
	{
		sRegenGatherGroup* pGroup = (sRegenGatherGroup*)(*iter).mSecond;
		SAFE_DELETE( pGroup );
	}
	mpRegenGatherGroupMap->Clear();


	for( iter = mpRegenGroupMap->Begin() ; iter != mpRegenGroupMap->End() ; ++iter )
	{
		sRegenGroup* pGroup = (sRegenGroup*)(*iter).mSecond;
				
		for( unsigned long i = 0 ; i < pGroup->mMonsterList.GetSize() ; ++i )
		{
			sRegenMonster* pMonster = (sRegenMonster*)pGroup->mMonsterList[i];
			SAFE_DELETE( pMonster );
		}
		SAFE_DELETE( pGroup );
	}
	mpRegenGroupMap->Clear();
}



void cAIManager::Process( unsigned long elapsedTime, unsigned long accumTime )
{

	if( mNextCheckTime >= accumTime )
		return;

	mNextCheckTime = AI_REGEN_PROCESS_DELAY + accumTime;

	/// 몬스터
	MonRegenProcess( elapsedTime, accumTime );

	/// 채집
	GatRegenProcess( elapsedTime, accumTime );
}


void cAIManager::MonRegenProcess( unsigned long /*elapsedTime*/, unsigned long accumTime )
{
	cPointHashMap::cIterator iter;

	/// 리젠 그룹 프로세스할 포인터
	sRegenGroup* pGroupList = NULL;

	/// 그룹안의 몬스터 배열의 총수량
	unsigned long totalCnt;

	sRegenGroupScript* pRegenGroupScript;

	for( iter = mpRegenGroupMap->Begin() ; iter != mpRegenGroupMap->End() ; ++iter )
	{
		pGroupList = (sRegenGroup*)(*iter).mSecond;

		if( !pGroupList )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::pGroupList == NULL" );
			continue;
		}

		/// 선택 리젠 통과
		if( pGroupList->mpRegenGroup->mMapGroupIdx != 0 )
			continue;

		/// 개별 몬스터 리젠
		for( unsigned long i = 0 ; pGroupList->mMonsterList.GetSize() > i ; ++i )
		{
			sRegenMonster* pRM = (sRegenMonster*)pGroupList->mMonsterList[i];
			if( pRM != NULL && pRM->mDie == true && pRM->mObjectIdx == 0 && pRM->mRegenTime < accumTime )
				RegenMonster( pGroupList->mGroupIdx, pRM );
		}

		pRegenGroupScript = pGroupList->mpRegenGroup;
		if( pRegenGroupScript == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::pRegenGroupScript == NULL" );
			continue;
		}

		/// 몬스터 전체 수
		totalCnt = pRegenGroupScript->mMonsterAry.GetSize();

		/// 그룹 리젠 조건에 만족 하지 않는 경우
		///	살아있는수					* 100	/ 전체 마리수 > 리젠 퍼센트 
		if( pGroupList->mMonsterAliveCnt * 100 / totalCnt > pGroupList->mpRegenGroup->mRegenPer ) 
		{
			/// 리젠안함으로 설정
			pGroupList->mIsRegen = false; 
			continue;
		}

		/// 조건을 만족하는데 리젠 안함으로 된경우 - 처음으로 들어온경우
		if( pGroupList->mIsRegen == false )
		{
			/// 리젠 함으로 바꾸고
			pGroupList->mIsRegen = true;
			/// 리젠 수행 시간 설정
			pGroupList->mRegenTime = accumTime + pRegenGroupScript->mRegenTime;
		}

		if( mServerOnRegen == false )
		{
			/// 리젠 시간 확인
			if( pGroupList->mRegenTime > accumTime )
				continue;
		}

		/// isRegen 초기화
		pGroupList->mIsRegen = false;

		/// 모든 조건을 만족하는 경우 리젠을 하고자하는 그룹을 읽어옴
		sRegenGroup* pRegenGroup = (sRegenGroup*)mpRegenGroupMap->GetAt( pRegenGroupScript->mRegenGroupIdx );
		if( pRegenGroup == NULL )
		{
			NETWORK2->PostServerEvent("cAIManager::pRegenGroup == NULL mRegenGroupIdx[%d]", pRegenGroupScript->mRegenGroupIdx );
			continue;
		}

		cVoidAry* pMonsterAry = &pRegenGroup->mMonsterList;
		if( pMonsterAry == NULL )
		{
			NETWORK2->PostServerEvent("cAIManager::pMonsterAry == NULL mRegenGroupIdx[%d]", pRegenGroupScript->mRegenGroupIdx );
			continue;
		}

		/// 그룹에 해당하는 모든 몬스터에게 개별 리젠
		for( unsigned int i = 0 ; pMonsterAry->GetSize() > i ; ++i )
		{
			sRegenMonster* pRM = (sRegenMonster*)(*pMonsterAry)[i];
			if( pRM != NULL && pRM->mDie == true && pRM->mObjectIdx == 0 )
				RegenMonster( pGroupList->mGroupIdx, pRM );
		}
	}

	/// 스위치 그룹 처리
	cPointHashMap::cIterator begin = mpRegenSwitchMap->Begin();
	cPointHashMap::cIterator end = mpRegenSwitchMap->End();
    for( ; begin != end ; ++begin )
	{
		unsigned long switchGroupIdx = (*begin).mFirst;
		sSwichGroup* pSwitch = (sSwichGroup*)(*begin).mSecond;
		if( pSwitch != NULL )
		{

			/// 현재 리젠중인 [리젠그룹] 끝 체크
			if( pSwitch->mGroupIdx != 0 )
			{
				sRegenGroup* pRegenGroup = (sRegenGroup*)mpRegenGroupMap->GetAt( pSwitch->mGroupIdx );
				if( pRegenGroup == NULL )
					continue;

				/// 몬스터 전체 수
				totalCnt = pRegenGroup->mpRegenGroup->mMonsterAry.GetSize();

				/// 새로 리젠을 해야하는지 현재 [리젠그룹] 리젠 조건 확인
				if( pRegenGroup->mMonsterAliveCnt * 100 / totalCnt > pRegenGroup->mpRegenGroup->mRegenPer ) 
					continue;

				/// 다음 리젠 시간 설정
				pSwitch->mGroupIdx = 0;
				/// 메세지 발송 시간차를 고려해 랜덤시간을 준다.
				pSwitch->mRegenTime = pRegenGroup->mpRegenGroup->mRegenTime + ( rand() % 1000 ) + accumTime;
				pSwitch->mRegenStop = false;
				/// 같은 서버군에 같은 스위치그룹군에 리젠설정을 다시해줌
				if( pRegenGroup->mpRegenGroup->mChannelCheck == true )
					NETWORK2->PostMonRegenStart( switchGroupIdx );
			}
			else	/// [리젠그룹]이 선택 처리
			{
				/// 리젠 가능시간 체크 && 다른곳에서 리젠된것 있으면 안됨
				if( pSwitch->mRegenTime < accumTime && pSwitch->mRegenStop == false )
				{
					/// 스위치 그룹의 [리젠그룹]목록 로드
					cArray* pAry = (cArray*)pSwitch->mpGroupIdxAry;
					if( pAry != NULL && pAry->GetSize() > 0 )
					{
						/// [리젠그룹] 임의 선택
						unsigned long selectRand = rand() % pAry->GetSize();
						unsigned long groupIdx = (*pAry)[selectRand];

						/// 선택된 [리젠그룹] 데이타 로드
						sRegenGroup* pRegenGroup = (sRegenGroup*)mpRegenGroupMap->GetAt( groupIdx );
						if( pRegenGroup == NULL )
							continue;

						/// [리젠그룹]의 몬스터 정보 로드
						cVoidAry* pMonsterAry = &pRegenGroup->mMonsterList;
						if( pMonsterAry == NULL )
							continue;

						/// 그룹에 해당하는 모든 몬스터에게 개별 리젠
						for( unsigned int i = 0 ; pMonsterAry->GetSize() > i ; ++i )
						{
							/// 몬스터 개별 리젠
							sRegenMonster* pRM = (sRegenMonster*)(*pMonsterAry)[i];
							if( pRM != NULL && pRM->mDie == true && pRM->mObjectIdx == 0 )
								RegenMonster( groupIdx, pRM );
						}

                        /// 선택한 [리젠그룹] 기록
						pSwitch->mGroupIdx = groupIdx;
						pSwitch->mRegenStop = true;
						/// 같은 서버군에 같은 스위치그룹군에 리젠을 하지 말라고알림
						if( pRegenGroup->mpRegenGroup->mChannelCheck == true )
							NETWORK2->PostMonRegenStop( switchGroupIdx );
					}
				}
			}
		}
	}

	mServerOnRegen = false;
}


void cAIManager::RegenMonster( unsigned long groupIdx, sRegenMonster* pRM )
{
	if( pRM == NULL )
		return;

	unsigned long objectIdx = mMonsterIdxGen.GeneratIdx();
	if( objectIdx == 0 )
	{
		NETWORK2->PostServerEvent("mMonsterIdxGen == 0");
		return;
	}

	/// 몬스터가 만들어져 있을수가 없다.
	if( OBJECTMANAGER->GetMonster( objectIdx ) )
	{
		NETWORK2->PostServerEvent("cAIManager::Process monsterIdx[%d] duplicate", objectIdx );
		return;
	}

	sRegenScript* pRegen = MONSTERSCRIPT->GetRegenMonsterInfo( pRM->mRegenIdx );
	if( pRegen == NULL )
	{
		NETWORK2->PostServerEvent("cAIManager::Process pRegen[%d,%d] == NULL", objectIdx, pRM->mRegenIdx );
		return;
	}

	/// 몬스터 추가
	cMonster* pMonster = OBJECTMANAGER->AddMonster( objectIdx, pRegen->mMonsterClassIdx, pRM->mRegenIdx );
	if( !pMonster )
	{
		//NETWORK2->PostServerEvent("cAIManager::RegenMonster OBJECTMANAGER->AddMonster( %d, %d, %d )  == false", objectIdx, pRegen->mMonsterClassIdx, pRM->mRegenIdx );
		return;
	}

	/// 그리드 추가한다.
	if( GRIDMANAGER->AddMonster( pMonster ) == false )
	{
		OBJECTMANAGER->RemoveMonster( pMonster->GetObjectID() );
		NETWORK2->PostServerEvent("cAIManager::RegenMonster GRIDMANAGER->AddMonster( pMonster ) == false" );
		return;
	}

	/// 길찾기 설정
	unsigned int mapIndex = pMonster->GetMapNumber() - MAP_MIN;
	if( mapIndex > MAP_MAX )
	{
		NETWORK2->PostServerEvent("mapIndex[%d] > MAP_MAX", pMonster->GetMapNumber() );
		return;
	}

	if( mServerType == _E_ST_NORMAL_MAP_ )
		pMonster->SetPathFinder( mPathFinder[mapIndex] );
	else	/// 081106 PKH 인던
		pMonster->SetPathFinder( mInstDgPathFinder );

	pRM->mDie = false;
	pRM->mObjectIdx = objectIdx;
	pRM->mRegenTime = 0;

	sRegenGroup* pRegenGroup = (sRegenGroup*)mpRegenGroupMap->GetAt( groupIdx );
	if( pRegenGroup == NULL )
	{
		NETWORK2->PostServerEvent("cAIManager::RegenMonster groupIdx[%d] == pRegenGroup", groupIdx );
		return;
	}

	/// 그룹 리젠 몬스터 수 증가
	if( pRegenGroup->mMonsterAliveCnt <= pRegenGroup->mpRegenGroup->mMonsterAry.GetSize() )
		++pRegenGroup->mMonsterAliveCnt;
	else
		NETWORK2->PostServerEvent("cAIManager::RegenMonster pRegenGroup->mMonsterAliveCnt[%d] == mMonsterAry.GetSize()", pRegenGroup->mMonsterAliveCnt );
}



void cAIManager::GatRegenProcess( unsigned long /*elapsedTime*/, unsigned long accumTime )
{
	cPointHashMap::cIterator iter;
	
	for( iter = mpRegenGatherGroupMap->Begin() ; iter != mpRegenGatherGroupMap->End() ; ++iter )
	{
		sRegenGatherGroup* pGroup = (sRegenGatherGroup*)(*iter).mSecond;
		if( !pGroup )
		{
			NETWORK2->PostServerEvent("cAIManager::pGroupHeader == NULL" );
			continue;
		}

		if( pGroup->mRegenReady == false || pGroup->mRegenTime > accumTime )
			continue;

		unsigned long objectIdx = mGatheringIdxGen.GeneratIdx();
		if( objectIdx == 0 )
		{
			NETWORK2->PostServerEvent("mGatheringIdxGen == 0");
			continue;
		}

		/// 만들어져 있을수가 없다.
		if( OBJECTMANAGER->GetGathering( objectIdx ) )
		{
			NETWORK2->PostServerEvent("cAIManager::Process gatheringIdx[%d] duplicate", objectIdx );
			continue;
		}

		/// 객체 리젠
		cGathering* pGathering = OBJECTMANAGER->AddGathering( objectIdx, pGroup->mGroupIdx );
		if( pGathering == NULL )
			continue;

		if( GRIDMANAGER->AddGathering( pGathering ) == false )
		{
			OBJECTMANAGER->RemoveGathering( pGathering->GetObjectID() );
            continue;
		}

		pGroup->mRegenReady = false;
		pGroup->mRegenTime = 0;
		pGathering->SendRegenSync();
	}
}


float cAIManager::GetPathMaxSideSize( unsigned int mapNum )
{
	if( mServerType == _E_ST_NORMAL_MAP_ )
	{
		if( mapNum > MAP_MAX )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::GetPathMaxSideSize mapNum[%d] ERR", mapNum );
			return 0.0f;
		}

		return mMapSize[mapNum - MAP_MIN];
	}
	else
	{
		/// 081106 PKH 인던
		//if( mapNum > MAP_MAX )
		//{
		//	assert(NULL);
		//	NETWORK2->PostServerEvent("cAIManager::GetPathMaxSideSize mapNum[%d] ERR", mapNum );
		//	return 0.0f;
		//}

		return mInstDgMapSize;
	}
}



bool cAIManager::CalcHeight( unsigned short mapNumber, float* height, float x, float y ) const
{
	bool retValue = false;

	if( mServerType == _E_ST_NORMAL_MAP_ )
	{
		if( mapNumber > MAP_MAX )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::CalcHeight mapNumber[%d] ERR", mapNumber );
			return false;
		}

		retValue = mNaviField[mapNumber-MAP_MIN]->CalcHeight( height, x, y );
		if( retValue == false )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager CalcHeight Error m[%d], x[%f] y[%f] h[%f]", mapNumber, x, y, *height);
			return false;
		}
	}
	else
	{
		/// 081106 PKH 인던
		//if( mapNumber > MAP_MAX )
		//{
		//	assert(NULL);
		//	NETWORK2->PostServerEvent("cAIManager::CalcHeight mapNumber[%d] ERR", mapNumber );
		//	return false;
		//}

		retValue = mInstDgNaviField->CalcHeight( height, x, y );
		if( retValue == false )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager InstDgCalcHeight Error m[%d], x[%f] y[%f] h[%f]", mapNumber, x, y, *height);
			return false;
		}
	}

	return retValue;
}



/// 이동 가능지역 체크
bool cAIManager::IsPassible( unsigned short mapNumber, float x, float y, sObject object ) const
{
	if( mServerType == _E_ST_NORMAL_MAP_ )
	{
		if( mapNumber > MAP_MAX )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::IsPassible mapNumber[%d] x[%f], y[%f] object[%d,%d]ERR", mapNumber, x, y, object.type, object.index );
			return false;
		}

		float mapMax = mMapSize[mapNumber - MAP_MIN];

		if( mapMax < x || mapMax < y || x < 0.0f || y < 0.0f )
		{
			NETWORK2->PostServerEvent("cAIManager::IsPassible PosError mapNumber[%d] x[%f], y[%f] object[%d,%d]", mapNumber, x, y, object.type, object.index);
			return false;
		}

		return mPathFinder[mapNumber-MAP_MIN]->IsPassible( x, y );
	}
	else
	{
		/// 081106 PKH 인던
		//if( mapNumber > MAP_MAX )
		//{
		//	assert(NULL);
		//	NETWORK2->PostServerEvent("cAIManager::InstDgIsPassible mapNumber[%d] x[%f], y[%f] object[%d,%d]ERR", mapNumber, x, y, object.type, object.index );
		//	return false;
		//}

		float mapMax = mInstDgMapSize;

		if( mapMax < x || mapMax < y || x < 0.0f || y < 0.0f )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::InstDgIsPassible PosError mapNumber[%d] x[%f], y[%f] object[%d,%d]", mapNumber, x, y, object.type, object.index);
			return false;
		}

		return mInstDgPathFinder->IsPassible( x, y );
	}
}


bool cAIManager::IsPassible( unsigned short mapNumber, NiPoint2 start, NiPoint2 goal, sObject object ) const
{
	if( mServerType == _E_ST_NORMAL_MAP_ )
	{
		if( mapNumber > MAP_MAX )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::IsPassible mapNumber[%d] start[%f,%f] goal[%f,%f] object[%d,%d]ERR", mapNumber, start.x, start.y, goal.x, goal.y, object.type, object.index );
			return false;
		}

		float mapMax = mMapSize[mapNumber - MAP_MIN];

		if( mapMax < start.x || mapMax < start.y || start.x < 0.0f || start.y < 0.0f ||
			mapMax < goal.x || mapMax < goal.y || goal.x < 0.0f || goal.y < 0.0f )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::IsPassible PosError mapNumber[%d] start[%f,%f] goal[%f,%f] object[%d,%d]ERR", mapNumber, start.x, start.y, goal.x, goal.y, object.type, object.index );
			return false;
		}

		return mPathFinder[mapNumber-MAP_MIN]->IsPassible( start, goal );
	}
	else
	{
		/// 081106 PKH 인던
		//if( mapNumber > MAP_MAX )
		//{
		//	assert(NULL);
		//	NETWORK2->PostServerEvent("cAIManager::IsPassible mapNumber[%d] start[%f,%f] goal[%f,%f] object[%d,%d]ERR", mapNumber, start.x, start.y, goal.x, goal.y, object.type, object.index );
		//	return false;
		//}

		float mapMax = mInstDgMapSize;

		if( mapMax < start.x || mapMax < start.y || start.x < 0.0f || start.y < 0.0f ||
			mapMax < goal.x || mapMax < goal.y || goal.x < 0.0f || goal.y < 0.0f )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::InstDgIsPassible PosError mapNumber[%d] start[%f,%f] goal[%f,%f] object[%d,%d]ERR", mapNumber, start.x, start.y, goal.x, goal.y, object.type, object.index );
			return false;
		}

		return mInstDgPathFinder->IsPassible( start, goal );
	}
}



void cAIManager::InsertReadyMonster( unsigned long monsterIdx, unsigned long regenIdx, unsigned long groupIdx )
{
	sRegenGroup* pRegenGroup = (sRegenGroup*)mpRegenGroupMap->GetAt( groupIdx );
	if( pRegenGroup == NULL )
	{
		NETWORK2->PostServerEvent("cAIManager::InsertReadyMonster pMonsterAry[%d] == NULL", groupIdx );
		return;
	}

	/// 그룹안에 몬스터 생존 카운트
	if( pRegenGroup->mMonsterAliveCnt != 0 )
		--pRegenGroup->mMonsterAliveCnt;
	else
		NETWORK2->PostServerEvent("cAIManager::GroupRegenAliveDown pRegenGroup->mMonsterAliveCnt[%d] == 0", pRegenGroup->mMonsterAliveCnt );

	mMonsterIdxGen.DelIdx( monsterIdx );

	cVoidAry* pAry = &pRegenGroup->mMonsterList;
	if( pAry == NULL )
	{
		NETWORK2->PostServerEvent("cAIManager::InsertReadyMonster pAry == NULL[%d]", groupIdx );
		return;
	}

	unsigned long size = pAry->GetSize();
	for( unsigned long i = 0 ; i < size ; ++i )
	{
		sRegenMonster* pRM = (sRegenMonster*)(*pAry)[i];
		if( pRM && pRM->mRegenIdx == regenIdx && pRM->mObjectIdx == monsterIdx )
		{
			sRegenScript* pRegen = MONSTERSCRIPT->GetRegenMonsterInfo( regenIdx );
			if( pRegen != NULL )
			{
				pRM->mDie = true;
				pRM->mObjectIdx = 0;
				pRM->mRegenTime = NETWORK2->GetAccumTime() + pRegen->mRegenTime;
			}
		}
	}

}


void cAIManager::SwitchRegenStart( unsigned long switchGroupIdx )
{
	sSwichGroup* pSwitchGroup = (sSwichGroup*)mpRegenSwitchMap->GetAt( switchGroupIdx );
	if( pSwitchGroup == NULL )
		return;

	pSwitchGroup->mRegenStop = false;
	if( pSwitchGroup->mGroupIdx == 0 )
	{
		/// 스위치 그룹의 [리젠그룹]목록 로드
		cArray* pAry = (cArray*)pSwitchGroup->mpGroupIdxAry;
		if( pAry != NULL && pAry->GetSize() > 0 )
		{
			/// [리젠그룹] 임의 선택
			unsigned long selectRand = rand() % pAry->GetSize();
			unsigned long groupIdx = (*pAry)[selectRand];

			pSwitchGroup->mGroupIdx = groupIdx;			

			sRegenGroup* pRegenGroup = (sRegenGroup*)mpRegenGroupMap->GetAt( pSwitchGroup->mGroupIdx );
			if( pRegenGroup != NULL )	/// 메세지 발송 처리 시간차에 따른 랜덤 시간값 설정
				pSwitchGroup->mRegenTime = pRegenGroup->mpRegenGroup->mRegenTime + ( rand() % 1000 ) + NETWORK2->GetAccumTime();
		}
	}
}


void cAIManager::SwitchRegenStop( unsigned long switchGroupIdx )
{
	sSwichGroup* pSwitchGroup = (sSwichGroup*)mpRegenSwitchMap->GetAt( switchGroupIdx );
	if( pSwitchGroup == NULL )
		return;

	pSwitchGroup->mRegenStop = true;
}


unsigned long cAIManager::CalcPathCnt( unsigned short mapNumber, NiPoint2 start, NiPoint2 goal )
{
	unsigned int pathCnt = 0;

	if( mServerType == _E_ST_NORMAL_MAP_ )
	{
		if( mapNumber > MAP_MAX )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::CalcPathCnt mapNumber[%d] ERR", mapNumber );
			return false;
		}

		mPathFinder[mapNumber-MAP_MIN]->FindPath( mTempPathAry, &pathCnt, MAX_PATH_COUNT, start, goal );

		return pathCnt;
	}
	else
	{
		/// 081106 PKH 인던
		//if( mapNumber > MAP_MAX )
		//{
		//	assert(NULL);
		//	NETWORK2->PostServerEvent("cAIManager::CalcPathCnt mapNumber[%d] ERR", mapNumber );
		//	return false;
		//}

		mInstDgPathFinder->FindPath( mTempPathAry, &pathCnt, MAX_PATH_COUNT, start, goal );

		return pathCnt;
	}
}


void cAIManager::BossSummonMonRegen( cMonster* pBoss, unsigned short mapNumber, float x , float y, unsigned long genRange, unsigned long genCnt, unsigned long monsterClassIdx )
{
	if( pBoss == NULL )
	{
		NETWORK2->PostServerEvent("cAIManager::BossSummonMonRegen pBoss == NULL" );
		return;
	}

	cMonster* pMonster = NULL;

	unsigned long monsterIdx;

	for( unsigned long i = 0 ; i < genCnt ; ++i )
	{
		monsterIdx = mMonsterIdxGen.GeneratIdx();
		if( monsterIdx == 0 )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::CheatAddMonster mMonsterIdxGen == 0");
			return;
		}

		/// 몬스터가 만들어져 있을수가 없다.
		if( OBJECTMANAGER->GetMonster( monsterIdx ) )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::CheatAddMonster monsterIdx[%d] duplicate", monsterIdx );
			return;
		}

		if( mapNumber < MAP_MIN || mapNumber > MAP_MAX )
			return;

		float mapMaxSize = AIMANAGER->GetPathMaxSideSize( mapNumber );
		if( mapMaxSize < x || mapMaxSize < y )
			return;

		/// 몬스터 추가
		pMonster = OBJECTMANAGER->AddMonster( monsterIdx, monsterClassIdx, 0 );
		if( !pMonster )
		{
			NETWORK2->PostServerEvent("cAIManager::CheatAddMonster pMonster == NULL" );
			return;
		}
		/// 소환 몬스터 설정		
		pMonster->SetSummonInfo( pBoss, mapNumber, x, y, genRange, 0 );

		/// 소환된 몬스터를 보스에 등록
		pBoss->AddSummonMonster( monsterIdx );

		/// 길찾기 설정
		if( mServerType == _E_ST_NORMAL_MAP_ )
			pMonster->SetPathFinder( mPathFinder[pMonster->GetMapNumber()-MAP_MIN] );	
		else	/// 081106 PKH 인던
			pMonster->SetPathFinder( mInstDgPathFinder );	
	}
}


void cAIManager::CheatSummonMonRegen( unsigned short mapNumber, float x , float y, unsigned long monsterClassIdx, unsigned long genCnt )
{
	cMonster* pMonster = NULL;

	unsigned long monsterIdx;

	for( unsigned long i = 0 ; i < genCnt ; ++i )
	{
		monsterIdx = mMonsterIdxGen.GeneratIdx();
		if( monsterIdx == 0 )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::CheatAddMonster mMonsterIdxGen == 0");
			return;
		}

		/// 몬스터가 만들어져 있을수가 없다.
		if( OBJECTMANAGER->GetMonster( monsterIdx ) )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cAIManager::CheatAddMonster monsterIdx[%d] duplicate", monsterIdx );
			return;
		}

		if( mapNumber < MAP_MIN || mapNumber > MAP_MAX )
			return;

		float mapMaxSize = AIMANAGER->GetPathMaxSideSize( mapNumber );
		if( mapMaxSize < x || mapMaxSize < y )
			return;

		/// 몬스터 추가
		pMonster = OBJECTMANAGER->AddMonster( monsterIdx, monsterClassIdx, 0 );
		if( !pMonster )
		{
			NETWORK2->PostServerEvent("cAIManager::CheatAddMonster pMonster == NULL" );
			return;
		}	
		/// 소환 몬스터 설정		
		pMonster->SetSummonInfo( 0, mapNumber, x, y, 500, 0 );

		/// 길찾기 설정
		if( mServerType == _E_ST_NORMAL_MAP_ )
			pMonster->SetPathFinder( mPathFinder[pMonster->GetMapNumber()-MAP_MIN] );	
		else	/// 081106 PKH 인던
			pMonster->SetPathFinder( mInstDgPathFinder );	
	}
}

/// 퀘스트로 인한 몬스터 add
unsigned long cAIManager::QuestSummonMonRegen( unsigned short mapNumber, float x , float y, unsigned long monsterClassIdx, unsigned long destroyTime )
{
	cMonster* pMonster = NULL;
	unsigned long monsterIdx = mMonsterIdxGen.GeneratIdx();
	if( monsterIdx == 0 )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cAIManager::QuestAddMonster mMonsterIdxGen == 0");
		return 0;
	}

	/// 몬스터가 만들어져 있을수가 없다.
	if( OBJECTMANAGER->GetMonster( monsterIdx ) )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cAIManager::QuestAddMonster monsterIdx[%d] duplicate", monsterIdx );
		return 0;
	}

	if( mapNumber < MAP_MIN || mapNumber > MAP_MAX )
		return 0;

	float mapMaxSize = AIMANAGER->GetPathMaxSideSize( mapNumber );
	if( mapMaxSize < x || mapMaxSize < y )
		return 0;

	/// 몬스터 추가
	pMonster = OBJECTMANAGER->AddMonster( monsterIdx, monsterClassIdx, 0 );
	if( !pMonster )
	{
		NETWORK2->PostServerEvent("cAIManager::QuestAddMonster pMonster == NULL" );
		return 0;
	}	
	/// 소환 몬스터 설정
	pMonster->SetSummonInfo( 0, mapNumber, x, y, 500, destroyTime );

	/// 길찾기 설정
	if( mServerType == _E_ST_NORMAL_MAP_ )
		pMonster->SetPathFinder( mPathFinder[pMonster->GetMapNumber()-MAP_MIN] );	
	else	/// 081106 PKH 인던
		pMonster->SetPathFinder( mInstDgPathFinder );	

	return monsterIdx;
}

void cAIManager::InsertReadyGathering( unsigned long objectIdx, unsigned long groupIdx )
{
	sRegenGatherGroup* pGroup = (sRegenGatherGroup*)mpRegenGatherGroupMap->GetAt( groupIdx );
	if( pGroup == NULL )
	{
		NETWORK2->PostServerEvent("cAIManager::InsertReadyMonster pMonsterAry[%d] == NULL", groupIdx );
		return;
	}

	mGatheringIdxGen.DelIdx( objectIdx );

	/// 초기화
	pGroup->mRegenReady = true;

	const sGatheringRegenHeader* pScript = pGroup->mpGetheringRegenHeader;
	if( pScript == NULL )
	{
		pGroup->mRegenTime = 0;
		return;
	}

	/// 다음 리젠 설정
	unsigned long randTime = pScript->mRandTime;
	if( randTime != 0 )
		randTime = rand() % pScript->mRandTime;

	pGroup->mRegenTime = NETWORK2->GetAccumTime() + pScript->mRegenWaitTime + randTime;
}
