#include "StdAfx.h"
#include "gamesrv.h"

#include "Protocol.h"

#include "DropScript.h"
#include "Drop.h"
#include "Drop_Common.h"

#include "Monster.h"
#include "ObjectManager.h"
#include "AppTimer.h"
#include "Monster.h"
#include "Player.h"
#include "LevelManager.h"
#include "MonsterScript.h"
#include "item.h"
#include "TakeDamagePool.h"
#include "Gathering.h"


cDrop* cDrop::mpDrop = NULL;



cDrop::cDrop(void)
{
	/// 싱글톤
	if( mpDrop )
	{
		Verbose->WriteLog( "Drop new error");
	}
	else
	{
		mpDrop = this;
	}
}



cDrop::~cDrop(void)
{
}



bool cDrop::Init()
{

	mpDropScript = new cDropScript;
	if( mpDropScript->Init() == false )
	{
		assert(NULL);
		return false;
	}	

	return true;
}



void cDrop::Release()
{
	SAFE_RELEASE_DELETE( mpDropScript );

}



void cDrop::MonsterDrop( unsigned long monsterIdx, unsigned long playerIdx, unsigned long partyIdx )
{

	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster == NULL )  
	{ 
		assert(NULL);
		NETWORK2->PostServerEvent("drop monster idx error[%d]", monsterIdx );
		return; 
	}

	unsigned long monsterClassIdx = pMonster->GetRaceGender();

	sMonsterScript* pMonsterScript = MONSTERSCRIPT->GetMonsterListInfo( monsterClassIdx );
	if( pMonsterScript == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("drop monsterScript idx error[%d]", monsterClassIdx );
		return;
	}

	/// money, exp, sxp 드롭
	MonsterDropValue( pMonster );
	/// 일반드롭 - 아이템 인덱스를 mDropItemCntMap에 담아옴
	MonsterDropNType( monsterClassIdx );
	/// 특수드롭 - 아이템 인덱스를 mDropItemCntMap에 담아옴
	MonsterDropSType( monsterClassIdx ); 
	/// 월드드롭 - 아이템 인덱스를 mDropItemCntMap에 담아옴
	MonsterDropWType( pMonster->GetLevel(), pMonster->GetMonsterInfo()->mType ); 

	if( mDropItemCntMap.GetSize() == 0 )
		return;

	cHashMap::cConstIterator begin = mDropItemCntMap.Begin();
	cHashMap::cConstIterator end = mDropItemCntMap.End();

	unsigned long ownerIdx[MAX_PARTY] = {0,};
	unsigned char ownerCnt = 0;

	cPlayer* pPlayer = NULL;

	/// 선공한 유저 파티 유/무 
	cParty* pParty = PARTYMAN->GetParty( partyIdx );
	if( pParty == NULL )
	{
		/// 파티가 사라졌으면 선공유저에게 우선권을 준다.
		if( OBJECTMANAGER->GetPlayer( playerIdx ) != NULL )
		{
			ownerIdx[0] = playerIdx;
			++ownerCnt;
		}
	}
	else
	{
		switch( pParty->GetDivideType() )
		{
		case PARTY_DIVIDE_TURN:		/// 순서대로
			{
				ownerIdx[0] = pParty->GetTurnUser( pMonster->GetMapNumber(), pMonster->GetXPos(), pMonster->GetYPos() );
				ownerCnt = 1;
			}
			break;
		case PARTY_DIVIDE_RANDOM:	/// 랜덤
			{
				ownerIdx[0] = pParty->GetRandomUser( pMonster->GetMapNumber(), pMonster->GetXPos(), pMonster->GetYPos() );
				ownerCnt = 1;
			}
			break;
		case PARTY_DIVIDE_DAMAGE:	/// 데미지
			{
				unsigned long* pUser = pParty->GetUserArr();
				unsigned int partyCnt = pParty->GetCount();
				unsigned long maxDamagePlayerIdx = playerIdx;
				unsigned long maxDamage = 0;
				unsigned long tempDamage = 0;

				for( unsigned int i = 0; i < partyCnt; ++i )
				{
					pPlayer = OBJECTMANAGER->GetPlayer( pUser[i] );
					if( pPlayer )
					{
						/// 조건 체크
						if( MonsterCheckDrop( pMonster, pPlayer ) == false )
							continue;

						tempDamage = pMonster->GetTakeDamage( pUser[i] );
						if( tempDamage > maxDamage )
						{
							maxDamagePlayerIdx = pUser[i];
							maxDamage = tempDamage;
						}
					}
				}
				ownerIdx[0] = maxDamagePlayerIdx;
				ownerCnt = 1;
			}
			break;
		case PARTY_DIVIDE_FREE:		/// 자유분배 (획득권한자유)
			{
				unsigned long* pUser = pParty->GetUserArr();
				unsigned int partyCnt = pParty->GetCount();

				for( unsigned int i = 0; i < partyCnt; ++i )
				{
					pPlayer = OBJECTMANAGER->GetPlayer( pUser[i] );
					if( pPlayer )
					{
						/// 조건 체크
						if( MonsterCheckDrop( pMonster, pPlayer ) == false )
							continue;

						ownerIdx[ownerCnt] = pUser[i];
						++ownerCnt;
					}
				}

			}
			break;
		}
	}

	unsigned short applyCnt;
	bool isApply = false;

	unsigned itemIdx = ITEMMANAGER->MakeItemIdx( );
	cItem*   item    = OBJECTMANAGER->AddItem( itemIdx, pMonster );
	if ( item != NULL )
	{
		for( unsigned char i = 0 ; i < ownerCnt ; ++i )
			item->PushOwner( ownerIdx[i] );

		for( ; begin != end ; ++begin )
		{
			applyCnt = 0;

			while( applyCnt < (*begin).mSecond )
			{
				unsigned short cnt = item->PushItem( (*begin).mFirst, (unsigned short)(*begin).mSecond );
				if( cnt == 0 )
				{
					NETWORK2->PostServerEvent("cDrop::Drop item(%d,%d)->PushItem cnt == 0", (*begin).mFirst, (*begin).mSecond);
					break;
				}

				applyCnt = applyCnt + cnt;
				isApply = true;
			}
		}
	}

	if( isApply == true )
	{
		/// 몬스터의 현재 좌표에 떨어트릴수 없는 경우
		if( AIMANAGER->IsPassible( item->GetMapNumber(), item->GetXPos(), item->GetYPos(), item->GetObject() ) == false )
		{
			for( unsigned char i = 0 ; i < ownerCnt ; ++i )
			{
				/// 소유자의 좌표에 떨어트림
				pPlayer = OBJECTMANAGER->GetPlayer( ownerIdx[i] );
				if( AIMANAGER->IsPassible( pPlayer->GetMapNumber(), pPlayer->GetXPos(), pPlayer->GetYPos(), pPlayer->GetObject()  ) == true )
				{
					item->SetMapNumber( pPlayer->GetMapNumber() );
					item->SetPos( pPlayer->GetXPos(), pPlayer->GetYPos() );
					break;
				}
			}
		}
		GRIDMANAGER->AddItem( item );
	}
	else
		OBJECTMANAGER->RemoveItem( itemIdx );

	mDropItemCntMap.Clear();
}


void cDrop::MonsterQuestDrop( unsigned long monsterIdx, unsigned long playerIdx, unsigned long partyIdx )
{

	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster == NULL )  
	{ 
		assert(NULL);
		NETWORK2->PostServerEvent("QuestDrop monster idx error[%d]", monsterIdx );
		return; 
	}

	unsigned long monsterClassIdx = pMonster->GetRaceGender();

	sMonsterScript* pMonsterScript = MONSTERSCRIPT->GetMonsterListInfo( monsterClassIdx );
	if( pMonsterScript == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("QuestDrop monsterScript idx error[%d]", monsterClassIdx );
		return;
	}

	cPlayer* pPlayer = NULL;

	/// 파티 유/무
	cParty* pParty = PARTYMAN->GetParty( partyIdx );
	if( pParty == NULL )
	{
		/// 파티가 사라졌으면 선공한 플레이어에게 드롭을 한다.
		pPlayer = OBJECTMANAGER->GetPlayer( playerIdx );
		if( pPlayer != NULL )
			MonsterDropQType( pMonster, pPlayer );
	}
	else
	{
		unsigned long* pUser = pParty->GetUserArr();
		unsigned int partyCnt = pParty->GetCount();

		/// 파티원별 퀘스트 드롭
		for( unsigned int i = 0; i < partyCnt; ++i )
		{
			pPlayer = OBJECTMANAGER->GetPlayer( pUser[i] );
			if( pPlayer != NULL )
			{
				/// 조건 체크
				if( MonsterCheckDrop( pMonster, pPlayer ) == false )
					continue;

				MonsterDropQType( pMonster, pPlayer );
			}
		}
	}
}


void cDrop::MonsterDropValue( cMonster* pMonster )
{
	unsigned long monsterKind = pMonster->GetRaceGender();
	unsigned long maxMoney = 0;
	unsigned long minMoney = 0;
	int moneyDropPer = 0;

	/// 드롭 스크립트에서 몬스터 종류에 해당하는 드롭정보 읽어 옴
	sDropNType* pDropNType = mpDropScript->GetDropNType( monsterKind );
	if( pDropNType != NULL ) 
	{ 
		maxMoney = pDropNType->mMaxMoney;
		minMoney = pDropNType->mMinMoney;
		moneyDropPer = FloatToInt( pDropNType->mMoneyPercent );
	}

	/// 몬스터 정보
	sMonsterScript* pMonsterScript = MONSTERSCRIPT->GetMonsterListInfo( monsterKind );
	if( pMonsterScript == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropValue pMonsterScript[%d] == NULL", monsterKind);
		return;
	}

	/// 확률 토탈값 오류
	long moneyGap = maxMoney - minMoney;
	if( moneyGap > RANDOM_MAX )
	{
		NETWORK2->PostServerEvent("cDrop::GatherDropValue[%d] moneyGap > RANDOM_MAX", monsterKind, moneyGap );
		moneyGap = RANDOM_MAX;
	}

	/// 몬스터를 공격한 데미지 맵
	TakeDamageRoot* pTakeDamageRoot = pMonster->GetTakeDamageRoot();
	if( pTakeDamageRoot == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropValue pTakeDamageMap == NULL");
		return;
	}

	/// 랜덤값 추출
	unsigned long randValue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
	int dropMoney = 0;
	unsigned long dropExp = pMonsterScript->mExp;
	unsigned long dropSxp = pMonsterScript->mSxp;

	/// 드롭 금액 랜덤 추출
	int droprand = ( rand() % 100 ) + 1;

	/// 확률계산해서 드랍 성공 인경우만 금액 계산
	if( droprand <= moneyDropPer ) 
	{
		if( moneyGap > 0 )
			dropMoney = minMoney + ( randValue % moneyGap ); 
		else
			dropMoney = minMoney;
	}

	unsigned long playerIdx;		/// 공격자 정보
	unsigned long damage;			/// 공격자가준 데미지
	float partyTotalMoney;			/// 개인/파티 분배 할 전체량
	float partyTotalExp;
	float partyTotalSxp;

	unsigned long partyIdx;			/// 파티 고유번호

	cPlayer* pPlayer = NULL;
	cParty* pParty = NULL;

	unsigned long calcExp;
	unsigned long calcSxp;

	/// 공격한 플레이어(데미지)전체 합산
	unsigned long totalDamage = 0;

	/// 몬스터가 공격받은 전체 데미지 값
	PerTakeDamage* per = (PerTakeDamage*)pTakeDamageRoot->pool;
	while( per )
	{
		totalDamage += per->takeDamage;
		per = (PerTakeDamage*)per->next;
	}

	partyIdxSet.Clear();

	per = (PerTakeDamage*)pTakeDamageRoot->pool;
	while( per )
	{
		playerIdx = per->playerIdx;
		damage = per->takeDamage;

		/// 데미지를 주지않는 형태의 디버프
		if( damage == 0 )
		{
			per = (PerTakeDamage*)per->next;
			continue;
		}

		/// 플레이어 객체가 없으면 money를 주지 않음
		pPlayer = OBJECTMANAGER->GetPlayer( playerIdx );
		if( pPlayer == NULL )
		{
			per = (PerTakeDamage*)per->next;
			continue;
		}

		/// 데미지 비율로 개인/파티(전체) 에게 돌아갈 money 계산
		partyTotalMoney = (float)(dropMoney * damage / totalDamage);
		/// 증감치 계산을 하기전 순수 경험치
		partyTotalExp = (float)(dropExp * damage / totalDamage);
		partyTotalSxp = (float)(dropSxp * damage / totalDamage);

		bool IsParty = false;

		/// 파티정보를 읽어옴
		partyIdx = pPlayer->GetPartyIndex();
		if( partyIdx != 0 )
		{
			pParty = PARTYMAN->GetParty( partyIdx );
			if( pParty != NULL )
				IsParty = true;
		}

		/// 파티가 아닌 경우
		if( IsParty == false )
		{
			if( pPlayer->GetStateDie() == true )
			{
				per = (PerTakeDamage*)per->next;
				continue;
			}

			/// 공격 데미지가 30% 미만인경우 보상 없음
			if( pMonster->GetMaxHP() * (float)0.3 > damage )	
			{
				per = (PerTakeDamage*)per->next;
				continue; 
			}

			/// 드롭 조건이 타당한지 체크
			if( MonsterCheckDrop( pMonster, pPlayer ) == false )
			{
				per = (PerTakeDamage*)per->next;
				continue;
			}

			calcExp = FloatToInt( MonsterDropExpIncrease( partyTotalExp, pPlayer->GetLevel(), pMonsterScript->mLevel ) );
			calcSxp = FloatToInt( MonsterDropExpIncrease( partyTotalSxp, pPlayer->GetLevel(), pMonsterScript->mLevel ) );

			///------ 드랍 통계자료 출력시 주석
			pPlayer->AddExpSxp( calcExp, calcSxp );

			unsigned long money = FloatToInt( partyTotalMoney );
			if( money != 0 )
				pPlayer->AddMoney( pMonster->GetObject(), money );
			///---------------------------------

		}
		else
		{
			/// 파티 인경우 파티처리용 Set에 기록한 후 처리
			/// ※ 한파티 여러명이 친경우 insert 시도는 여러 번 있을 수 있지만 1개만 들어감
			partyIdxSet.Insert( partyIdx );

			/// 파티 객체에 드롭 값 누적 입력
			pParty->AddDropValue( pMonster->GetMaxHP(), damage, partyTotalMoney,  partyTotalExp, partyTotalSxp );
		}

		per = (PerTakeDamage*)per->next;
	}

	cHashSet::cIterator setIter = partyIdxSet.Begin();
	cHashSet::cIterator setIterEnd = partyIdxSet.End();

	/// 따로 처리 할 파티 경험치 분배 처리
	while( setIter != setIterEnd )
	{
		partyIdx = (*setIter++);
		pParty = PARTYMAN->GetParty( partyIdx );

		/// 파티원에게 money, exp, sxp를 분배
		pParty->DivideDropValue( pMonster->GetObjectID(), pMonsterScript->mLevel );
	}
}



void cDrop::MonsterDropNType( unsigned long monsterClassIdx )
{

	/// 드롭 스크립트에서 몬스터 종류에 해당하는 드롭정보 읽어 옴
	sDropNType* pDropNType = mpDropScript->GetDropNType( monsterClassIdx );
	if( pDropNType == NULL ) 
		return; 

	/// 드롭확률 계산
	int droprand = ( rand() % 100 ) + 1;
	if( droprand > pDropNType->mDropPercent ) 
		return; 

	// 드롭 갯수 설정
	unsigned long dropCnt;

	unsigned long dropTotalPer = pDropNType->mDropClass1Per + pDropNType->mDropClass2Per + pDropNType->mDropClass3Per;

	/// 확률 토탈값 오류 - 드롭 안함
	if( dropTotalPer == 0 || dropTotalPer > RANDOM_MAX )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropNType dropTotalPer[%d] ERROR", dropTotalPer );
		return;
	}

	/// 랜덤값 추출
	unsigned long randValue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );

	unsigned long selectRand = ( randValue % dropTotalPer ) + 1;
	if( selectRand <= pDropNType->mDropClass1Per )
		dropCnt = pDropNType->mDropClass1Cnt;
	else if( selectRand <= pDropNType->mDropClass1Per + pDropNType->mDropClass2Per )
		dropCnt = pDropNType->mDropClass2Cnt;
	else
		dropCnt = pDropNType->mDropClass3Cnt;

	mDropItemCntMap.Clear();

	for( unsigned char i = 0 ; i < dropCnt ; ++i )
		MonsterDropNTypeSelect( pDropNType );
}


void cDrop::MonsterDropNTypeSelect( sDropNType* pDropNType )
{
	unsigned long totalPer = pDropNType->mItemTotalPer;

	/// 확률 토탈값 오류 - 드롭 안함
	if( totalPer == 0 || totalPer > RANDOM_MAX )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropNTypeSelect totalPer[%d] ERROR", totalPer );
		return;
	}

	/// 랜덤함수를 돌려 확률 전체 값중 임의의 값 1개를 정한다.
	unsigned long randValue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
	unsigned long randnum = ( randValue % totalPer ) + 1;

	cHashMap::cIterator hashIter;

	cAry* pAry = pDropNType->mpData;
	if( pAry == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropNTypeSelect[%d] pDropNType->mpData == NULL", pDropNType->mClassIdx );   
		return;
	}

	sDropNTypeData* pData = NULL;
	/// 임의의 값을 순서대로 비교해 해당위치에 맞는 값을 드롭리스트로 선정한다.
	for( unsigned long i = 0 ; i < pAry->GetSize() ; ++i )
	{
		pData = (sDropNTypeData*)(*pAry)[i];
		if( pData == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cDrop::DropNTypeSelect[%d] pData == NULL", pDropNType->mClassIdx );   
			return;
		}
		
		if( randnum <= pData->mItemPer )
		{
			/// 입력을 했는데 이미 있는 아이템이면 값을 + 시킨다
			if( mDropItemCntMap.Insert( pData->mItemIdx, pData->mItemCnt ) == false )
			{
				if( pData->mItemCnt == 0 )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::DropNTypeSelect itemCnt( %d, %d ) == 0", pData->mItemCnt, pData->mItemCnt );   
					return;
				}

				hashIter = mDropItemCntMap.Find( pData->mItemIdx );
				if( hashIter == mDropItemCntMap.End() )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::DropNTypeSelect hashIter == mDropItemCntMap.End()" );   
					return;
				}

				unsigned long* cnt = &(hashIter->mSecond);
				if( cnt == NULL )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("DropNTypeSelect2 itemIdx[%d] cnt == NULL", pData->mItemIdx);   
					return;
				}

				*cnt += pData->mItemCnt;
			}
			return;
		}

		randnum = randnum - pData->mItemPer;
	}

	/// 다돌았는데 결정이안되면 오류
	if( randnum <= 0 )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("DropNTypeSelect2 randnum <= 0");        
	}
}


void cDrop::MonsterDropSType( unsigned long monsterClassIdx )
{
	sDropSWType* pSType = DROPSCRIPT->GetDropSType( monsterClassIdx );
	if( pSType == NULL )
		return;

	MonsterDropSWTypeSelect( pSType, true );
}


void cDrop::MonsterDropSWTypeSelect( sDropSWType* pDropSWType, bool isSType )
{

	unsigned long accunTime = NETWORK2->GetAccumTime();

	/// 드랍 확률 체크
	unsigned long randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
	unsigned long randSelect = ( randvalue % 100 ) + 1;
	if( pDropSWType->mDropPercent < randSelect )
		return;

	float totalPer = pDropSWType->mItemTotalPer * DROP_PER_FLOAT_CONVERT;

	/// 확률 토탈값 오류 - 드롭 안함
	if( totalPer == 0 || totalPer > RANDOM_MAX )
	{
		NETWORK2->PostServerEvent("cDrop::DropSWTypeSelect[%d] totalPer[%d][%d] ERROR", pDropSWType->mIdx, totalPer, isSType );
		return;
	}

	/// 100%이상인 경우는 무조건 선정되고
	/// 100%미만 인경우는 확률별로 선택 공백을 선택한경우는 드롭 안함
	if( totalPer < 100.0f * DROP_PER_FLOAT_CONVERT )
		totalPer = 100.0f * DROP_PER_FLOAT_CONVERT;

	/// 랜덤값 추출
	randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );

	/// 랜덤함수를 돌려 확률 전체 확률값중 임의의 값 1개를 정한다.
	unsigned long randnum = ( randvalue % (unsigned long)totalPer ) + 1;

	cAry* pAry = pDropSWType->mpData;
	if( pAry == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropSWTypeSelect[%d][%d] pAry == NULL", pDropSWType->mIdx, isSType );   
		return;
	}

	cHashMap::cIterator hashIter;
	sDropSWTypeData* pData = NULL;
	/// 임의의 값을 순서대로 비교해 해당위치에 맞는 값을 드롭리스트로 선정한다.
	for( unsigned long i = 0 ; i < pAry->GetSize() ; ++i )
	{
		pData = (sDropSWTypeData*)(*pAry)[i];
		if( pData == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cDrop::DropSWTypeSelect[%d][%d] pData == NULL", pDropSWType->mIdx, isSType );   
			return;
		}

		/// 선택된 랜덤값이 아이템 확률에 걸린경우
		if( randnum <= pData->mItemPer * DROP_PER_FLOAT_CONVERT )
		{
			/// 추가 확률값 증가
			if( pData->mEnoughCntNow < pData->mEnoughCnt )
				++pData->mEnoughCntNow;

			/// 시간 제약 조건에 걸림
			if( pData->mEnoughTimeEnd > accunTime )
				return;

			/// 추가 확률 조건 비교
			randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
            randSelect = ( randvalue % pData->mEnoughCnt ) + 1;
			if( pData->mEnoughCntNow < randSelect )
				return;

			/// 입력을 했는데 이미 있는 아이템이면 값을 + 시킨다
			if( mDropItemCntMap.Insert( pData->mItemIdx, pData->mItemCnt ) == false )
			{
				if( pData->mItemCnt == 0 )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::DropSTypeSelect itemCnt( %d, %d )[%d] == 0", pData->mItemIdx, pData->mItemCnt, isSType );   
					return;
				}

				hashIter = mDropItemCntMap.Find( pData->mItemIdx );
				if( hashIter == mDropItemCntMap.End() )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::DropNTypeSelect[%d] hashIter == mDropItemCntMap.End()", isSType );   
					return;
				}

				unsigned long* cnt = &(hashIter->mSecond);
				if( cnt == NULL )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::DropSTypeSelect itemIdx[%d] cnt == NULL[%d]", pData->mItemIdx, isSType);   
					return;
				}

				*cnt += pData->mItemCnt;
			}
			
			/// 선정 된경우 제약사항 업데이트
			pData->mEnoughCntNow = 0;
			pData->mEnoughTimeEnd = accunTime + pData->mEnoughTime;

			return;
		}

		randnum = randnum - FloatToInt( pData->mItemPer * DROP_PER_FLOAT_CONVERT );
	}
}


void cDrop::MonsterDropWType( unsigned char monsterLv, unsigned char monsterType )
{
	cAry* pAry = DROPSCRIPT->GetDropWType( monsterLv );
	if( pAry == NULL )
		return;
    
	sDropWTypeLink* pWTypeLink = NULL;
	sDropSWType* pWType = NULL;
	for( unsigned long i = 0 ; pAry->GetSize() > i ; ++i )
	{
		pWTypeLink = (sDropWTypeLink*)(*pAry)[i];

		if( pWTypeLink == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cDrop::DropWType pWType == NULL");
			continue;
		}

		if( pWTypeLink->mMonsterType != monsterType && pWTypeLink->mMonsterType != 0 )
			continue;

		pWType = pWTypeLink->mpWType;
		if( pWType == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cDrop::DropWType pSType == NULL");
			continue;
		}

        MonsterDropSWTypeSelect( pWType, false );
	}
}


void cDrop::MonsterDropQType( cMonster* pMonster, cPlayer* pPlayer )
{
	unsigned long monsterClassIdx = pMonster->GetRaceGender();

	TB_QUEST_PROGRESS* pQuest = NULL;
	cAry* pAry = NULL;
	sDropQType* pDropQ = NULL;
	unsigned long selectRand = 0;
	//	unsigned long itemIdx = 0;
	unsigned long dropNeedCnt = 0;

	/// 퀘스트 전체 검사
	for( unsigned long i = 0 ; i < MAX_KEEPQUEST ; ++i )
	{
		/// 퀘스트 정보 로드
		pQuest = pPlayer->GetQuest( i );
		if( pQuest == NULL )
			break;

		if( pQuest->idx == 0 )
			return;

		cQuestDefine* questDefine = QUESTMAN->GetQuestDefine( pQuest->questIdx );
		if( !questDefine )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("DropQType questDefine[%d] == NULL", pQuest->questIdx );
			return;
		}

		/// 퀘스트 진행 중 체크
		if( pQuest->check >= questDefine->mComplete )
			continue;

		/// 몬스터가 플레이어의 퀘스트에 해당하는 드롭아이템 종류를 읽어옴
		pAry = DROPSCRIPT->GetDropQtype( monsterClassIdx, pQuest->questIdx );
		if( pAry == NULL )
			continue;

		/// 드롭 아이템 비교
		for( unsigned long itemI = 0 ; itemI < pAry->GetSize() ; ++itemI )
		{
			pDropQ = (sDropQType*)(*pAry)[itemI];

			if( pDropQ == NULL )
			{
				assert(NULL);
				NETWORK2->PostServerEvent("DropQType pDropQ == NULL [%d]", itemI );
				continue;
			}

			/// 드롭 확률 비교
			unsigned long randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
			selectRand = ( randvalue % 100 ) + 1;
			if( selectRand > pDropQ->mDropPer )
				continue;

			/// 드롭 필요 수량 확인
			dropNeedCnt = pPlayer->GetDropItemCount( i, pDropQ->mItemIdx );
			if( dropNeedCnt == 0 )
				continue;

			/// 퀘스트 제한 검사 - 착용정보 검사
			if( pPlayer->IsEquipQuestItem( pQuest->questIdx ) == false )
				continue;

			/// 플레이어 자동 습득
			if( pPlayer->ItemGetQuest( pDropQ->mItemIdx, 1 ) == true )
			{
				/// 퀘스트 아이템 수집 관련 업데이트
				pPlayer->UpdateDutyItem();
			}
			else
			{
				assert(NULL);
				NETWORK2->PostServerEvent("Drop::DropQType ItemGetQuest[%d] ERROR", pDropQ->mItemIdx );
			}
		}
	}
}


float cDrop::MonsterDropExpIncrease( float takeExp, unsigned char playerLevel, unsigned char monsterLevel )
{

	char levelDifferent;
	float increase;
	float exp = 0.0f;

	/// 레벨차
	levelDifferent = monsterLevel - playerLevel;
	/// 캐릭터와 몬스터의 레벨차가 -6보다 작은경우 경험치 주지 않음
	if( levelDifferent < DROPSCRIPT->GetMinLevelDifferent() )	
		return 0; 

	/// 캐릭터와 몬스터가 레벨차가 +5 이상인 경우 +5로 설정
	if( levelDifferent > DROPSCRIPT->GetMaxLevelDifferent() )
		levelDifferent = DROPSCRIPT->GetMaxLevelDifferent();

	/// 레벨차에 따른 증감치를 얻어옴
	increase = DROPSCRIPT->GetDropExpIncrease( levelDifferent );

	/// 경험치 * 증가치
	exp = takeExp * increase;
	if( exp < 0.0f )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropExpIncrease exp < 0.0f");
		return 0.0f;
	}
	return exp;
}



bool cDrop::MonsterCheckDrop( cMonster* pMonster, cPlayer* pPlayer )
{

	if( pMonster->GetMapNumber() != pPlayer->GetMapNumber() )
		return false;

	NiPoint2 monsterPos = pMonster->GetPos();
	NiPoint2 playerPos = pPlayer->GetPos();

	cRangeCheck rangeCheck( PARTY_RANGE );
	if( rangeCheck.IsRange( monsterPos, playerPos ) == false )
		return false;

	return true;
}


void cDrop::GatherDrop( cGathering* pGathering, unsigned long playerIdx )
{
	if( pGathering == NULL )
		return;

	cPlayer* pPlayer = OBJECTMANAGER->GetPlayer( playerIdx );
	if( pPlayer == NULL )
		return;

	/// money 드롭
	GatherDropValue( pGathering, playerIdx );
	/// 퀘스트드롭
	GatherDropQType( pGathering, playerIdx );

	/// 일반드롭 - 아이템 인덱스를 mDropItemCntMap에 담아옴
	GatherDropNType( pGathering );
	/// 특수드롭 - 아이템 인덱스를 mDropItemCntMap에 담아옴
	GatherDropSType( pGathering ); 
	if( mDropItemCntMap.GetSize() == 0 )
		return;

	/// 드롭 아이템 처리
	cHashMap::cConstIterator begin = mDropItemCntMap.Begin();
	cHashMap::cConstIterator end = mDropItemCntMap.End();
	bool                     apply = false;

	for( ; begin != end ; ++begin )
	{
		unsigned long itemIdx = (*begin).mFirst;
		unsigned long itemCnt = (*begin).mSecond;

		unsigned short applyCnt = 0;
		while( applyCnt < itemCnt )
		{
			unsigned short cnt = pPlayer->ItemGetAutoPush( itemIdx, (unsigned short)itemCnt );
			if( cnt == 0 )
			{
				assert(0);
				NETWORK2->PostServerEvent("cDrop::GatherDrop item(%d,%d)->ItemGetAutoPush cnt == 0", itemIdx, itemCnt);
				break;
			}

			applyCnt = applyCnt + cnt;
			apply    = true;
		}
	}
	pPlayer->ItemGetAutoEnd( apply );

	mDropItemCntMap.Clear();
}


void cDrop::GatherDropValue( cGathering* pGathering, unsigned long playerIdx )
{
	unsigned long gatherClassIdx = pGathering->GetClassIdx();

	unsigned long maxMoney = 0;
	unsigned long minMoney = 0;
	int moneyDropPer = 0;

	/// 드롭 스크립트에서 채집에 해당하는 드롭정보 읽어옴
	sDropNTypeGather* pDropNTypeGather = mpDropScript->GetDropNTypeGather( gatherClassIdx );
	if( pDropNTypeGather != NULL ) 
	{ 
		maxMoney = pDropNTypeGather->mMaxMoney;
		minMoney = pDropNTypeGather->mMinMoney;
		moneyDropPer = FloatToInt( pDropNTypeGather->mMoneyPercent );
	}

	/// 확률 토탈값 오류
	unsigned long moneyGap = maxMoney - minMoney;
	if( moneyGap > RANDOM_MAX )
	{
		NETWORK2->PostServerEvent("cDrop::GatherDropValue[%d] moneyGap > RANDOM_MAX", gatherClassIdx, moneyGap );
		moneyGap = RANDOM_MAX;
	}

	/// 드롭 금액 랜덤 추출
	int droprand = ( rand() % 100 ) + 1;

	/// 랜덤값 추출
	unsigned long randValue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
	unsigned long dropMoney = 0;
	/// 확률계산해서 드랍 성공 인경우만 금액 계산
	if( droprand <= moneyDropPer ) 
	{
		if( moneyGap > 0 )
			dropMoney = minMoney + ( randValue % moneyGap ); 
		else
			dropMoney = minMoney;
	}

	cPlayer* pPlayer = NULL;
	pPlayer = OBJECTMANAGER->GetPlayer( playerIdx );
	if( pPlayer == NULL )
		return;

	if( dropMoney != 0 )
		pPlayer->AddMoney( pGathering->GetObject(), dropMoney );
}


void cDrop::GatherDropNType( cGathering* pGathering )
{
	unsigned long gatherClassIdx = pGathering->GetClassIdx();

	/// 드롭 스크립트에서 몬스터 종류에 해당하는 드롭정보 읽어 옴
	sDropNTypeGather* pDropNTypeGather = mpDropScript->GetDropNTypeGather( gatherClassIdx );
	if( pDropNTypeGather == NULL ) 
		return; 

	/// 드롭확률 계산
	int droprand = ( rand() % 100 ) + 1;
	if( droprand > pDropNTypeGather->mDropPercent ) 
		return; 

	// 드롭 갯수 설정
	unsigned long dropCnt;

	unsigned long dropTotalPer = pDropNTypeGather->mDropClass1Per + pDropNTypeGather->mDropClass2Per + pDropNTypeGather->mDropClass3Per;

	/// 확률 토탈값 오류 - 드롭 안함
	if( dropTotalPer == 0 || dropTotalPer > RANDOM_MAX )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropNType dropTotalPer[%d] ERROR", dropTotalPer );
		return;
	}

	/// 랜덤값 추출
	unsigned long randValue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );

	unsigned long selectRand = ( randValue % dropTotalPer ) + 1;
	if( selectRand <= pDropNTypeGather->mDropClass1Per )
		dropCnt = pDropNTypeGather->mDropClass1Cnt;
	else if( selectRand <= pDropNTypeGather->mDropClass1Per + pDropNTypeGather->mDropClass2Per )
		dropCnt = pDropNTypeGather->mDropClass2Cnt;
	else
		dropCnt = pDropNTypeGather->mDropClass3Cnt;

	mDropItemCntMap.Clear();

	for( unsigned char i = 0 ; i < dropCnt ; ++i )
		GatherDropNTypeSelect( pDropNTypeGather );
}


void cDrop::GatherDropNTypeSelect( sDropNTypeGather* pDropNTypeGather )
{
	unsigned long totalPer = pDropNTypeGather->mItemTotalPer;

	/// 확률 토탈값 오류 - 드롭 안함
	if( totalPer == 0 || totalPer > RANDOM_MAX )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::GatherDropNTypeSelect totalPer[%d] ERROR", totalPer );
		return;
	}

	/// 랜덤함수를 돌려 확률 전체 값중 임의의 값 1개를 정한다.
	unsigned long randValue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
	unsigned long randnum = ( randValue % totalPer ) + 1;

	cHashMap::cIterator hashIter;

	cAry* pAry = pDropNTypeGather->mpData;
	if( pAry == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::DropNTypeSelectGather[%d] pDropNType->mpData == NULL", pDropNTypeGather->mClassIdx );   
		return;
	}

	sDropNTypeDataGather* pData = NULL;
	/// 임의의 값을 순서대로 비교해 해당위치에 맞는 값을 드롭리스트로 선정한다.
	for( unsigned long i = 0 ; i < pAry->GetSize() ; ++i )
	{
		pData = (sDropNTypeDataGather*)(*pAry)[i];
		if( pData == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cDrop::GatherDropNTypeSelect[%d] pData == NULL", pDropNTypeGather->mClassIdx );   
			return;
		}

		if( randnum <= pData->mItemPer )
		{
			/// 입력을 했는데 이미 있는 아이템이면 값을 + 시킨다
			if( mDropItemCntMap.Insert( pData->mItemIdx, pData->mItemCnt ) == false )
			{
				if( pData->mItemCnt == 0 )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::GatherDropNTypeSelect itemCnt( %d, %d ) == 0", pData->mItemCnt, pData->mItemCnt );   
					return;
				}

				hashIter = mDropItemCntMap.Find( pData->mItemIdx );
				if( hashIter == mDropItemCntMap.End() )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::GatherDropNTypeSelect hashIter == mDropItemCntMap.End()" );   
					return;
				}

				unsigned long* cnt = &(hashIter->mSecond);
				if( cnt == NULL )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("GatherDropNTypeSelect itemIdx[%d] cnt == NULL", pData->mItemIdx);   
					return;
				}

				*cnt += pData->mItemCnt;
			}
			return;
		}

		randnum = randnum - pData->mItemPer;
	}

	/// 다돌았는데 결정이안되면 오류
	if( randnum <= 0 )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("DropNTypeSelect randnum <= 0");        
	}
}


void cDrop::GatherDropSType( cGathering* pGathering )
{
	unsigned long gatherClassIdx = pGathering->GetClassIdx();

	sDropSTypeGather* pDropSTypeGather = DROPSCRIPT->GetDropSTypeGather( gatherClassIdx );
	if( pDropSTypeGather == NULL )
		return;

	unsigned long accunTime = NETWORK2->GetAccumTime();

	/// 드랍 확률 체크
	unsigned long randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
	unsigned long randSelect = ( randvalue % 100 ) + 1;
	if( pDropSTypeGather->mDropPercent < randSelect )
		return;

	float totalPer = pDropSTypeGather->mItemTotalPer * DROP_PER_FLOAT_CONVERT;

	/// 확률 토탈값 오류 - 드롭 안함
	if( totalPer == 0 || totalPer > RANDOM_MAX )
	{
		NETWORK2->PostServerEvent("cDrop::GatherDropSType[%d] totalPer[%d] ERROR", pDropSTypeGather->mIdx, totalPer );
		return;
	}

	/// 100%이상인 경우는 무조건 선정되고
	/// 100%미만 인경우는 확률별로 선택 공백을 선택한경우는 드롭 안함
	if( totalPer < 100.0f * DROP_PER_FLOAT_CONVERT )
		totalPer = 100.0f * DROP_PER_FLOAT_CONVERT;

	/// 랜덤값 추출
	randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );

	/// 랜덤함수를 돌려 확률 전체 확률값중 임의의 값 1개를 정한다.
	unsigned long randnum = ( randvalue % (unsigned long)totalPer ) + 1;

	cAry* pAry = pDropSTypeGather->mpData;
	if( pAry == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cDrop::GatherDropSType[%d] pAry == NULL", pDropSTypeGather->mIdx );   
		return;
	}

	cHashMap::cIterator hashIter;
	sDropSWTypeData* pData = NULL;
	/// 임의의 값을 순서대로 비교해 해당위치에 맞는 값을 드롭리스트로 선정한다.
	for( unsigned long i = 0 ; i < pAry->GetSize() ; ++i )
	{
		pData = (sDropSTypeDataGather*)(*pAry)[i];
		if( pData == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cDrop::GatherDropSType[%d] pData == NULL", pDropSTypeGather->mIdx );   
			return;
		}

		/// 선택된 랜덤값이 아이템 확률에 걸린경우
		if( randnum <= pData->mItemPer * DROP_PER_FLOAT_CONVERT )
		{
			/// 추가 확률값 증가
			if( pData->mEnoughCntNow < pData->mEnoughCnt )
				++pData->mEnoughCntNow;

			/// 시간 제약 조건에 걸림
			if( pData->mEnoughTimeEnd > accunTime )
				return;

			/// 추가 확률 조건 비교
			randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
			randSelect = ( randvalue % pData->mEnoughCnt ) + 1;
			if( pData->mEnoughCntNow < randSelect )
				return;

			/// 입력을 했는데 이미 있는 아이템이면 값을 + 시킨다
			if( mDropItemCntMap.Insert( pData->mItemIdx, pData->mItemCnt ) == false )
			{
				if( pData->mItemCnt == 0 )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::GatherDropSType itemCnt( %d, %d ) == 0", pData->mItemIdx, pData->mItemCnt );   
					return;
				}

				hashIter = mDropItemCntMap.Find( pData->mItemIdx );
				if( hashIter == mDropItemCntMap.End() )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::GatherDropSType hashIter == mDropItemCntMap.End()" );   
					return;
				}

				unsigned long* cnt = &(hashIter->mSecond);
				if( cnt == NULL )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("cDrop::GatherDropSType itemIdx cnt == NULL", pData->mItemIdx );   
					return;
				}

				*cnt += pData->mItemCnt;
			}

			/// 선정 된경우 제약사항 업데이트
			pData->mEnoughCntNow = 0;
			pData->mEnoughTimeEnd = accunTime + pData->mEnoughTime;

			return;
		}

		randnum = randnum - FloatToInt( pData->mItemPer * DROP_PER_FLOAT_CONVERT );
	}
}


void cDrop::GatherDropQType( cGathering* pGathering, unsigned long playerIdx )
{
	unsigned long gatherClassIdx = pGathering->GetClassIdx();

	TB_QUEST_PROGRESS* pQuest = NULL;
	cAry* pAry = NULL;
	sDropQType* pDropQ = NULL;
	unsigned long selectRand = 0;
	unsigned long dropNeedCnt = 0;

	cPlayer* pPlayer = OBJECTMANAGER->GetPlayer( playerIdx );
	if( pPlayer == NULL )
		return;

	/// 퀘스트 전체 검사
	for( unsigned long i = 0 ; i < MAX_KEEPQUEST ; ++i )
	{
		/// 퀘스트 정보 로드
		pQuest = pPlayer->GetQuest( i );
		if( pQuest == NULL )
			break;

		if( pQuest->idx == 0 )
			return;

		cQuestDefine* questDefine = QUESTMAN->GetQuestDefine( pQuest->questIdx );
		if( !questDefine )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("GatherDropQType questDefine[%d] == NULL", pQuest->questIdx );
			return;
		}

		/// 퀘스트 진행 중 체크
		if( pQuest->check >= questDefine->mComplete )
			continue;

		/// 몬스터가 플레이어의 퀘스트에 해당하는 드롭아이템 종류를 읽어옴
		pAry = DROPSCRIPT->GetDropQtypeGather( gatherClassIdx, pQuest->questIdx );
		if( pAry == NULL )
			continue;

		/// 드롭 아이템 비교
		for( unsigned long itemI = 0 ; itemI < pAry->GetSize() ; ++itemI )
		{
			pDropQ = (sDropQTypeGather*)(*pAry)[itemI];

			if( pDropQ == NULL )
			{
				assert(NULL);
				NETWORK2->PostServerEvent("GatherDropQType pDropQ == NULL [%d]", itemI );
				continue;
			}

			/// 드롭 확률 비교
			unsigned long randvalue = (unsigned long)( RANDOMTABLE->Get( ) * 1000000 );
			selectRand = ( randvalue % 100 ) + 1;
			if( selectRand > pDropQ->mDropPer )
				continue;

			/// 드롭 필요 수량 확인
			dropNeedCnt = pPlayer->GetDropItemCount( i, pDropQ->mItemIdx );
			if( dropNeedCnt == 0 )
				continue;

			/// 퀘스트 제한 검사 - 착용정보 검사
			if( pPlayer->IsEquipQuestItem( pQuest->questIdx ) == false )
				continue;

			/// 플레이어 자동 습득
			if( pPlayer->ItemGetQuest( pDropQ->mItemIdx, 1 ) == true )
			{
				/// 퀘스트 아이템 수집 관련 업데이트
				pPlayer->UpdateDutyItem();
			}
			else
			{
				assert(NULL);
				NETWORK2->PostServerEvent("Drop::GatherDropQType ItemGetQuest[%d] ERROR", pDropQ->mItemIdx );
			}
		}
	}
}
