#include "gamesrv.h"

#include "stdafx.h"

#include "Player.h"
#include "BaseObject_Common.h"
#include "ObjectManager.h"
#include "Protocol.h"
#include "Skill_Common.h"
#include "SkillManager.h"
#include "Status_Common.h"
#include "DamageCalc.h"
#include "StatusCalc_Server.h"
#include "StatusScript.h"
#include "RangeCheck.h"
#include "SkillManager.h"
#include "PartyManager.h"
#include "LevelManager.h"
#include "Drop.h"

#include "ItemManager.h"
#include "Tarot_Common.h"
#include "Monster.h"
#include "Npc.h"
#include "Item.h"
#include "Communityscript.h"
#include "StageScript.h"
#include "LevelScript.h"
#include "MonsterScript.h"
#include "Cheat_Common.h"
#include "Protocol_Cheat.h"
#include "PVP_Common.h"
#include "StatusCalc_Server.h"
#include "Gathering.h"
#include "VehicleScript.h"
#include "Vehicle_Common.h"
#include "VehicleScript.h"
#include "MakeSkill_Common.h"
#include "MakeSkillScript.h"

/// 치트
#include "Protocol_Cheat.h"
#include "Cheat_Common.h"

int StallSellItemCompare( const void *arg1, const void *arg2 )
{
	StallSellItem* item1 = (StallSellItem*)arg1;
	StallSellItem* item2 = (StallSellItem*)arg2;

	// 내림차순 정렬
	return (item2->status != StallSellItemNone) - (item1->status != StallSellItemNone);
}

cPlayer::cPlayer() : cBaseObject( eOBJECTTYPE_PLAYER )
{
	//mpSkill = NULL;
	mLoseTime = 0;
	mPlayerWeaponState = eWEAPON_STATE_NONE;
	mNpcIdx = 0;

	mRange = 0.0f;
	mRangeTargetPos = NiPoint2::ZERO;
	mRangeTarget.type = eOBJECTTYPE_NONE;
	mRangeTarget.index = 0;

	mPartyState = ePARTY_NONE;
	mIsGuildAddReq = false;
	mMonsterImportance = 0;

	mLastMaxHP = 0;
	mLastMaxMP = 0;

	memset( mStatus2Plus, 0 , sizeof(mStatus2Plus) );
	memset( mStatus2Per, 0 , sizeof(mStatus2Per) );

	mNextNatureRecoveryTime = 0;
	mGameIn = false;

	memset( mFollowPos, 0, sizeof(mFollowPos) );

	mStatus2.mMoveSpeed = 500;
	mStatus2.mAttackSpeed = 1.0f;
	mPlayerExrInfo.mFixedObjectSizePer = 0;

	mIsRequestRejection = eREQREJCT_NONE;

	mDuelPlayerIdx = 0;
	mIsDuelRequester = false;
	mDuelStraightWin = 0;

	mPlayerExrInfo.mBaseFixedSize = FIXED_OBJECT_SIZE;
	SetFixedObjectSizePer( mPlayerExrInfo.mFixedObjectSizePer );

	mBlinkPos = NiPoint2::ZERO;

	mTitleUniqIndex = 0;

	memset( &mOptionData1, 0, sizeof(mOptionData1) );

	/// 치트
	mCheatHideMode = false;
	mCheatUndeadMode = false;
	mCheatChatDuration = 0;
	mCheatChatAccumTime = 0;
	mCheatStopDuration = 0;
	mCheatSpeedUp = eCHEATSPEEDUP_NONE;

	mJobUsedSpArr.Resize(5);

	mSitDownStartTime = 0;
	mSitDownStatusCalc = false;

	for( unsigned int i=0;i<mJobUsedSpArr.GetSize();i++ )
		mJobUsedSpArr[i] = 0;

	mGatheringIdx = 0;
	mVehicleInfIdx = 0;

	mItemMixEndTime = 0;
	mMixRecipeIdx = 0;
	mMixMakeSkill = 0;
}


cPlayer::~cPlayer()
{
	ItemCountRoot* root = GetItemCountRoot( );
	while ( root->pool != NULL )
	{
		ITEMCOUNTPOOL->ReleaseItemCount( &mItemCountRoot, (PerItemCount*)root->pool );
	}
	mObject.index = 0;
}


void* cPlayer::operator new( size_t n )
{
	if( n != sizeof(cPlayer) ) 
	{
		assert(0);
		return NULL;
	}

	return OBJECTMANAGER->AllocPlayer();
}


void cPlayer::operator delete( void* ptr, size_t n )
{
	/// NULL 포인터 검사
	if( ptr == 0 )
	{
		assert(0);
		return;
	}

	if( n != sizeof(cPlayer) ) 
	{
		assert(0);
		return;
	}

	OBJECTMANAGER->FreePlayer( static_cast<cPlayer*>(ptr) );

	return;
}

// Init Method
bool cPlayer::Init(unsigned long connectionIdx, unsigned long playerIdx, unsigned long money, unsigned long deposit)
{
	// sObject & sPlayerInfo 초기화.
	cBaseObject::SetObjectID(playerIdx);
	mPlayerInfo.CharacterIdx = playerIdx;

	// ConnectionIdx 초기화.
	mConnectionIdx = connectionIdx;

	/// 기본정보셋팅 - 초기화
	mPlayerExrInfo.RestHP = 0;	
	mPlayerExrInfo.MaxHP =  0;	
	mPlayerExrInfo.MaxMP =  0;	
	mPlayerInfo.Level = 0;
	mPlayerInfo.Job = ePLAYER_NONEJOB;

	mIsSkillResurrection = false;
	mSkillResurrectionHP = 0;
	mSkillResurrectionMP = 0;

	mPlayerExrInfo.mStateIdle = eIDLE_NORMAL;
	mPlayerExrInfo.mState = eOBJECT_STATE_IDLE;
	mPlayerExrInfo.mStateStop = eSTOP_NONE;

	mMapChange = false;

	mMapTargetPos.mMapNumber = 0;
	mMapTargetPos.mPosX = 0.0f;
	mMapTargetPos.mPosY = 0.0f;
	mMapTargetPos.mRotAngle = 0.0f;

	/// 소지금액 & 예치금액 설정.
	mMoney   = money;
	mDeposit = deposit;

	mSitDownStartTime = 0;
	mSitDownStatusCalc = false;

	memset( mODDITY, 0, sizeof(mODDITY) );

	/// 아이템 관련 변수 초기화.
	memset( mInventory, 0, sizeof(mInventory) );

	mItemActiveWeapon = ItemActiveFront;

	mItemWeight = 0;
	memset( &mEquipAbility, 0, sizeof(mEquipAbility) );
	mArmorSets  = ARMOR_SETS_NONE;

	mItemEnhancedSeed = RANDOMTABLE->GetSeed( );
	mItemChangeSeed   = RANDOMTABLE->GetSeed( );

	mItemCountRoot.pool = NULL;
	mItemCountRoot.root = NULL;

	mCooltimeRoot.pool = NULL;
	mCooltimeRoot.root = NULL;

	/// 아이템줍기 관련 변수 초기화.
	mItemGetOpen = false;
	mItemGetIdx  = 0;

	mItemGetCount = 0;
	memset( mItemGetData, 0, sizeof(mItemGetData) );

	/// 거래 관련 변수 초기화.
	mExchangeStatus = ItemExchangeNone;
	mExchangeItems  = 0;
	mExchangeMoney  = 0;

	/// 노점 관련 변수 초기화.
	mStallSell      = false;

	memset( mStallSellTitle, 0, sizeof(mStallSellTitle) );
	memset( mStallSellItems, 0, sizeof(mStallSellItems) );
	mStallSellItemOffset = 0;

	memset( mStallSellGuests, 0, sizeof(mStallSellGuests) );
	mStallSellGuestOffset = 0;

	memset( &mStallSellOwner, 0, sizeof(mStallSellOwner) );

	/// 타로카드 관련 변수 초기화.
	memset( mTarotTitle, 0, sizeof(mTarotTitle) );
	mTarotPrice         = 0;
	mTarotUserCount     = 0;
	mTarotItemCount     = 0;
	mTarotCloseReserved = false;

	memset( mTarotCards, 0, sizeof(mTarotCards) );
	memset( mSpread,     0, sizeof(mSpread)     );
	mSpreadOffset = 0;

	mTarotReaderIdx = 0;

	memset( mTarotGuests, 0, sizeof(mTarotGuests) );
	mTarotGuestOffset = 0;

	mTarotResultIndex = 0;
	mTarotResultValue = 0;

	mDuelIdx = 0;

	mPvPDMIdx = 0;

	mIsSleep = false;
	mIsStun = false;

	/// 퀘스트 관련 변수 초기화
	memset( mQuest, 0, sizeof(TB_QUEST_PROGRESS) * MAX_KEEPQUEST );

	///
	TIMESTAMP_STRUCT timestamp;
	timestamp.year		= 1900;
	timestamp.month		= 1;
	timestamp.day		= 1;
	timestamp.hour		= 0;
	timestamp.minute	= 0;
	timestamp.second	= 0;
	timestamp.fraction  = 0;

	for( int i = 0; i < 5; ++i )
	{
		mFortuneThru[i] = timestamp;
	}

	/// 커뮤니티 관련 변수 초기화.
	ClearFriends( );


	// 블록 관련 변수 초기화.
	mBlockApply     = false;
	mBlockValidThru = 0;
	return true;
}


void cPlayer::Update( unsigned long elapsedTime, unsigned long accumTime )
{
	/// 플레이어 이동 처리
	switch( GetState() )
	{
	case eOBJECT_STATE_MOVE:
		{
			/// 이동불가 상태 이상인 경우
			if( IsCantMove() == true )
				return;

			float moveElapsedTime = (float)(elapsedTime) / SECOND_THOUSAND;

			/// 플레이어의 이동속도
			NiPoint2 pos = mObjectPos;
			float speed = static_cast<float>(GetMoveSpeed());

			/// 방향 설정
			NiPoint2 dir1;
			dir1.x = mGotoX - GetXPos();
			dir1.y = mGotoY - GetYPos();
			dir1.Unitize();

			/// 속도 * 경과시간 만큼의 예상 이동 위치 계산
			pos.x += (speed * moveElapsedTime * dir1.x);
			pos.y += (speed * moveElapsedTime * dir1.y);

			/// 목적지 좌표와 예상 이동 위치를 비교
			NiPoint2 dir2;
			dir2.x = mGotoX - pos.x;
			dir2.y = mGotoY - pos.y;
			dir2.Unitize();

			/// 예상 이동위치가 목적지 좌표를 넘어가면 목적지 도달로 판정
			if( dir2.Dot(dir1) <= 0.0f )
			{
				/// 플레이어가 목적지까지 도달 했다고 판정이 난경우
				pos.x = mGotoX;
				pos.y = mGotoY;

				/// 대기 상태로 바꿈
				if( ChangeState( eOBJECT_STATE_IDLE ) == false )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("Player[%d] Update ChangeState( eOBJECT_STATE_IDLE ) == false", mObject.index );
				}
			}
			else
			{

				if( FloatToInt(mRange) != 0 )
				{
					/// 타겟 좌표 설정
					NiPoint3 targetPos( mRangeTargetPos.x, mRangeTargetPos.y, 0.0f );
					NiPoint3 playerGuessPos( pos.x, pos.y, 0.0f );
					AIMANAGER->CalcHeight( mMapNumber, &playerGuessPos.z, playerGuessPos.x, playerGuessPos.y );
					NiPoint3 playerNowPos( GetXPos(), GetYPos(), Height() );

					cBaseObject* pTarget = OBJECTMANAGER->GetObject( mRangeTarget.type, mRangeTarget.index );
					if( pTarget != NULL )
					{
						targetPos.x = pTarget->GetXPos();
						targetPos.y = pTarget->GetYPos();
						targetPos.z = pTarget->Height();
					}
					else
					{
						AIMANAGER->CalcHeight( mMapNumber, &targetPos.z, targetPos.x, targetPos.y );
					}

					float rangeFix = OBJECTMANAGER->ObjectSizeRange( this, pTarget, mRange );

					cRangeCheck range( rangeFix );

					if( range.IsRange( playerGuessPos, targetPos ) )
					{
						NiPoint3 p;

						NiPoint3 d = playerNowPos - targetPos;
						d.Unitize();

						p.x = targetPos.x + (rangeFix-1.0f)*d.x;
						p.y = targetPos.y + (rangeFix-1.0f)*d.y;

						pos.x = p.x;
						pos.y = p.y;

						SetMoveTargetPos( p.x, p.y );
						mRange = 0.0f;

						/// 대기 상태로 바꿈
						if( ChangeState( eOBJECT_STATE_IDLE ) == false )
						{
							assert(NULL);
							NETWORK2->PostServerEvent("Player[%d] Update ChangeState( eOBJECT_STATE_IDLE ) == false", mObject.index );
						}
					}
				}
			}

			SetPos( pos.x, pos.y );
		}
		break;
	case eOBJECT_STATE_ATTACK:
		{
			if( SKILLMANAGER->IsUsingSkill( mObject.type, mObject.index ) == false )
			{
				if( ChangeState( eOBJECT_STATE_IDLE ) == false )
				{
					assert(NULL);
					NETWORK2->PostServerEvent("Player[%d] Update ChangeState( eOBJECT_STATE_IDLE ) == false", mObject.index );
				}
			}
		}
		break;
	case eOBJECT_STATE_STOP:
		if ( GetStateStop( ) == eSTOP_OPENTAROT )
		{
			if ( (IsTarotReaderClose( ) == true) && !(GetTarotItemCount( ) > 0) )
			{
				TarotReaderClose( ERROR_TAROT_READER_CLOSE_ITEM );
			}
		}
		if ( GetStateStop() == eSTOP_CHEATSTOP )
		{
			if( mCheatStopDuration < accumTime )
			{
				mCheatStopDuration = 0;
				ChangeState( eOBJECT_STATE_IDLE );
				SetStateStop( eSTOP_NONE );
			}
		}
		break;
	}

	/// 자연 회복
	CalcNatureRecovery();

	/// 생활 스킬 종료 체크
	if( mPlayerExrInfo.mCommunitySkillIdx != 0 )
	{
		if( mCommunitySkillEndTime < accumTime || GetState() != eOBJECT_STATE_IDLE )
		{
			mPlayerExrInfo.mCommunitySkillIdx = 0;
			mCommunitySkillEndTime = 0.0f;
		}
	}

	/// 일괄 저장
	if( mBatchSaveTime <= accumTime )
	{
		/// 다음 저장시간 설정
		mBatchSaveTime = accumTime + BATCH_SAVE_TIME;

		DBUpdate( false );
	}

}

/// 직업 체인지
void cPlayer::SetJob(ePLAYER_JOB job, unsigned long* skillList, unsigned char skillListCnt )
{ 
	mPlayerInfo.Job = job;

	/// 전직 관련 퀘스트 트리거 검사
	SendNpcQuestStatus();

	if( GetGuildIndex() > 0 )
	{
		GUILDMAN->RequestUpdateUser( GetGuildIndex(), GetObjectID(), GetLevel(), GetJob() );
	}

	HANDLE              handle = NULL;
	MSG_RES_JOB* resMsg = (MSG_RES_JOB*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, 
		NM_PLAYER, NM_PLAYER_JOB_RES );
	if ( resMsg != NULL )
	{
		resMsg->mJob = (unsigned short)mPlayerInfo.Job;
		resMsg->mSkillListCnt = skillListCnt;
		memcpy( resMsg->mSkillClassIdx, skillList, sizeof(unsigned long) * skillListCnt );
		NETWORK2->SendMsgRoot( handle, resMsg->GetMsgLength() );
	}

	for( int i = 0 ; i < skillListCnt ; ++i )
	{
		if( SKILLMANAGER->AddPlayerHaveSkill( mObject.index, skillList[i], 0, 0 ) == false )
		{
			NETWORK2->PostServerEvent("cPlayer[%d]::SetJob[%d,%d] AddPlayerHaveSkill == false", 
				mObject.index, skillList[i], 0 );
		}
	}

	MSG_SYN_JOB synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_JOB_SYN;
    synMsg.mPlayerIdx = mObject.index;
	synMsg.mJob = (unsigned short)job;
	NETWORK2->QuickSendExcept( this, (char*)&synMsg, sizeof(synMsg) );

	/// 파티에 전직 싱크 보내기
	PARTYMAN->SendToPartyChangeJob( this );
}

void cPlayer::SkillLevelUp( unsigned char upLevel )
{ 
	/// 만렙 체크
	sSxpTable* pSxpTable = LEVELSCRIPT->GetSxpTable( mHeroInfo.SkillLevel + upLevel );
	if( pSxpTable == NULL )	
		return; 

	mHeroInfo.SkillLevel = mHeroInfo.SkillLevel + upLevel; 
	mHeroInfo.SkillPointRemain	+= SKILL_LEVELUP_POINT * upLevel;
	mHeroInfo.SkillPointTotal	+= SKILL_LEVELUP_POINT * upLevel;

	/// 레벨업 메세지 발송
	HANDLE              handle = NULL;
	MSG_RES_SKILLLEVEL* resMsg = (MSG_RES_SKILLLEVEL*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_SKILLLEVEL_RES );
	if ( resMsg != NULL )
	{
		resMsg->mSkillLevel = mHeroInfo.SkillLevel;
		resMsg->mSkillPoint = mHeroInfo.SkillPointRemain;
		resMsg->mSkillPointTotal = mHeroInfo.SkillPointTotal;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_SKILLLEVEL) );
	}

	CHARACTER_SKILLLEVEL* characterSkillLevel = (CHARACTER_SKILLLEVEL*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_SKILLLEVEL );
	if ( characterSkillLevel == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer[%d]::LevelUp characterSkillLevel == NULL", mObject.index );
		return;
	}

	characterSkillLevel->idx = mObject.index;
	characterSkillLevel->skillLevel = mHeroInfo.SkillLevel;
	characterSkillLevel->levelUpPoint = upLevel;
	characterSkillLevel->skillPoint = mHeroInfo.SkillPointRemain;
	characterSkillLevel->skillTotal = mHeroInfo.SkillPointTotal;
	characterSkillLevel->skillUpPoint = SKILL_LEVELUP_POINT * upLevel;
	characterSkillLevel->retvalue = -1;

	NETWORK2->SendSQL( handle, sizeof(CHARACTER_SKILLLEVEL) );

}


unsigned char cPlayer::SkillReset( unsigned short itemSlotNum )
{
	/*-- 아이템 사용시 검사할 내용들.
	*/
	if( itemSlotNum < INVENTORY_BAG_BEGIN || itemSlotNum > INVENTORY_BAG_END )
		return ERROR_SKILLRESET_ITEM;

	TB_INVENTORY* inventory = SelectInventory( itemSlotNum );
	if( IsInventory( inventory ) == false )
		return ERROR_SKILLRESET_ITEM;

	if( inventory->apply != InventoryApplyNone )
		return ERROR_SKILLRESET_ITEM;

	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
	if( itemDefine == NULL )
		return ERROR_SKILLRESET_ITEM;

	/*-- Item Limit Table(TB_ITEM_LIMIT) 적용
	*/
	TB_ITEM_LIMIT* itemLimit = ITEMMANAGER->GetItemLimit( inventory->itemDefineIdx );
	if( itemLimit != NULL )
	{
		// 종족; TB_ITEM_LIMIT::charRace 와 sPlayerInfo::Race 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charRace != ITEM_RACE_ALL )
		{
			if ( itemLimit->charRace != mPlayerInfo.Race )
				return ERROR_SKILLRESET_ITEM;
		}

		// 성별; TB_ITEM_LIMIT::charGender 와 sPlayerInfo::Gender 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charGender != ITEM_GENDER_ALL )
		{
			if ( itemLimit->charGender != mPlayerInfo.Gender )
				return ERROR_SKILLRESET_ITEM;
		}

		// 직업; TB_ITEM_LIMIT::charJob 와 sPlayerInfo::Job 은 정의는 다르나 같은 값을 사용.
		if( itemLimit->charJob != ITEM_JOB_ALL )
		{
			long charJob = mPlayerInfo.Job - (mPlayerInfo.Job % ITEM_JOB_FIGHTER);
			if ( itemLimit->charJob != charJob )
				return ERROR_SKILLRESET_ITEM;
		}

		// 레벨; 이하일 경우만 제어를 한다.
		if(  itemLimit->charLevel > (BYTE)mPlayerInfo.Level )
			return ERROR_SKILLRESET_ITEM;
	}

	/// 스킬 초기화 아이템 인지 확인
	if( !(itemDefine->type == ITEM_ETC1 && itemDefine->subType == ITEM_ETC1_SKILLRESET) )
		return ERROR_SKILLRESET_ITEM;

	/// 상태 설정
	if( ChangeState( eOBJECT_STATE_STOP ) == false )
		return ERROR_SKILLRESET_STATE;

	SetStateStop( eSTOP_SKILLRESET );

	/// 아이템 차감
	unsigned long inventoryIdx = inventory->idx;
	if ( inventory->count == 1 )
		RemoveInventory( inventory->number );
	else
		UpdateInventory( inventory, (-1) ); // --inventory->count;

	/// db 저장
	HANDLE handle = NULL;
	CHARACTER_SKILL_RESET* reset = (CHARACTER_SKILL_RESET*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_SKILLRESET );
	reset->mCharacterIdx = mObject.index;
	reset->mRace = mPlayerInfo.Race;
	reset->mJobStep = (unsigned char)STATUSSCRIPT->GetJobStep( mPlayerInfo.Job );
	reset->mInventoryIdx = inventoryIdx;
	reset->mRetvalue = -1;
	unsigned long length = sizeof(CHARACTER_SKILL_RESET) - sizeof(reset->mTable);
	return (NETWORK2->SendSQL( mConnectionIdx, handle, length ) ) ? ERROR_SKILLRESET_SUCCESS : ERROR_SKILLRESET_DBERROR;

}


void cPlayer::AddExpSxp( unsigned long exp, unsigned long sxp )
{
	if( exp == 0 && sxp == 0 )
		return;

	/*--------------- 경험치 ---------------*/
	/// 받을 경험치 계산시 추가 버프에 의한 받을 경험치 증가치
	float addExpPer = (float)mStatusEtc.mExpAddPer;
	float addExpPlus = (float)mStatusEtc.mExpAddPlus;

	/// 경험치 = 경험치 + %효과 + 효과
	unsigned long addExp = FloatToInt( exp + ( exp * addExpPer / 100.0f ) + addExpPlus );

	/// 레벨 관리기를 통해 플레이어의 경험치를 넣어줌
	LEVELMANAGER->AddExp( GetObjectID(), addExp );

	/*--------------- 스킬 경험치 ---------------*/
	/// 받을 스킬 경험치 계산시 추가 버프에 의한 받을 스킬 경험치 증가치
	float addSxpPer = (float)mStatusEtc.mSxpAddPer;
	float addSxpPlus = (float)mStatusEtc.mSxpAddPlus;

	/// 스킬경험치2 = 스킬경험치1 + %효과 + 효과
	unsigned long addSxp = FloatToInt( sxp + ( sxp * addSxpPer / 100.0f ) + addSxpPlus );

	/// 레벨 관리기를 통해 플레이어의 스킬 경험치를 넣어줌
	LEVELMANAGER->AddSxp( GetObjectID(), addSxp );		


	/// 얻은 EXP와 SXP를 발송
	HANDLE           handle = NULL;
	MSG_RES_EXP_SXP* msgExp = (MSG_RES_EXP_SXP*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_EXP_SXP_RES );
	if ( msgExp != NULL )
	{
		msgExp->mExp      = addExp;
		msgExp->mSxp      = addSxp;
		msgExp->mTotalExp = mHeroInfo.Exp;
		msgExp->mTotalSxp = mHeroInfo.SkillExp;

		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_EXP_SXP) );
	}
}

void cPlayer::AddExpSxpNoPer( unsigned long exp, unsigned long sxp )
{
	if( exp == 0 && sxp == 0 )
		return;

	/*--------------- 경험치 ---------------*/
	unsigned long addExp = exp;

	/// 레벨 관리기를 통해 플레이어의 경험치를 넣어줌
	LEVELMANAGER->AddExp( GetObjectID(), addExp );

	/*--------------- 스킬 경험치 ---------------*/
	unsigned long addSxp = sxp;

	/// 레벨 관리기를 통해 플레이어의 스킬 경험치를 넣어줌
	LEVELMANAGER->AddSxp( GetObjectID(), addSxp );		


	/// 얻은 EXP와 SXP를 발송
	HANDLE           handle = NULL;
	MSG_RES_EXP_SXP* msgExp = (MSG_RES_EXP_SXP*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_EXP_SXP_RES );
	if ( msgExp != NULL )
	{
		msgExp->mExp      = addExp;
		msgExp->mSxp      = addSxp;
		msgExp->mTotalExp = mHeroInfo.Exp;
		msgExp->mTotalSxp = mHeroInfo.SkillExp;

		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_EXP_SXP) );
	}
}

void cPlayer::InitHP( unsigned long HP )
{
	mPlayerExrInfo.RestHP = HP;
	mPlayerExrInfo.MaxHP = GetMaxHP();
}

void cPlayer::InitMP( unsigned long MP )
{
	mPlayerExrInfo.RestMP = MP;
	mPlayerExrInfo.MaxMP = GetMaxMP();
}


unsigned long cPlayer::HPDamage( unsigned long damage, bool* die, bool msgSend, bool /*cri*/ )
{ 
	/// 실제 적용된 데미지
	unsigned long realDamage = 0;
	*die = false;

	if( GetStateDie() == true )
	{  
		*die = true;
		return realDamage;
	}


	/// 공격받으면 죽는 경우
	if( mPlayerExrInfo.RestHP <= damage ) 
	{
		/// 치트 무적 여부를 검사
		if( mCheatUndeadMode )
			return 0;

		/// [실제적용된데미지]는 player의 [실제HP값]
		realDamage = mPlayerExrInfo.RestHP;
		/// [실제HP값]은 0
		mPlayerExrInfo.RestHP = 0;
		mPlayerExrInfo.RestMP = 0;

		/// 플레이어의 상태 변경
		if( ChangeState( eOBJECT_STATE_DIE ) == false )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("Player[%d] Update ChangeState( eOBJECT_STATE_DIE ) == false", mObject.index );
		}

		*die = true;

		/// 결투종료
		DuelEnd();
	}
	else /// 죽지 않는 경우
	{
		/// 잠들어 있는 상태라면 풀어 준다.
		if( mODDITY[eODDITYTYPE_SLEEP] != 0 )
		{
			cSkillInfluenceSet::cIterator begin = mInfluenceSet.Begin();
			cSkillInfluenceSet::cIterator end = mInfluenceSet.End();

			cInfluenceObject* pInf = NULL;
			unsigned long influenceIdx = 0;
			while( begin != end )
			{
				influenceIdx = (*begin++);
				pInf = SKILLMANAGER->GetInfluence( influenceIdx );
				if( pInf == NULL )
					continue;

				if( pInf->IsSleep() == true )
					SKILLMANAGER->DeleteInfluenceList( influenceIdx );
			}
		}

		/// 치트 무적 여부를 검사
		if( mCheatUndeadMode )
			return 0;

		/// [실제적용된데미지]는 [파라메타데미지]
		realDamage = damage;
		/// [실제HP값]은 [파라메타데미지]뺀값
		mPlayerExrInfo.RestHP -= damage;	
	}

	if( msgSend == true )
	{
		MSG_SYN_PLAYER_HP synMsg;
		synMsg.Category			= NM_PLAYER;
		synMsg.Protocol			= NM_PLAYER_HP_SYN;
		synMsg.mCharacterIdx	= GetObjectID();
		synMsg.mHp				= mPlayerExrInfo.RestHP;
		synMsg.mMaxHp			= GetMaxHP();
		synMsg.mDie				= GetStateDie();

		NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(MSG_SYN_PLAYER_HP) );
	}

	/// [실제적용된데미지]리턴
	return realDamage;
}


void cPlayer::HPHeal( unsigned long hp, bool msgSend, unsigned long healerIdx, long distressPoint, eTAKEDAMAGE_TYPE type )
{ 
	unsigned long maxHP  = GetMaxHP();
	unsigned long restHP = mPlayerExrInfo.RestHP;

	if( GetStateDie() == true )
		return;

	if ( maxHP == restHP )
		return;

	/// 실제 적용되는 힐량 계산
	unsigned long applyHP = hp;
	if( mPlayerExrInfo.RestHP + hp > maxHP )
        applyHP = maxHP - mPlayerExrInfo.RestHP;

	/// 실제 적용된 수치만 큼만 어그로 비중 곱해서 넣준다.
	if( healerIdx != 0 )
        HealAttackToMonster( healerIdx, applyHP, distressPoint, type );

	unsigned long currentHp = restHP + hp;

	mPlayerExrInfo.RestHP = (currentHp > maxHP) ? maxHP : currentHp;

	if ( msgSend == true )
		SendHPHeal( );
}

bool cPlayer::SendHPHeal( )
{
	MSG_SYN_PLAYER_HP synMsg;
	synMsg.Category			= NM_PLAYER;
	synMsg.Protocol			= NM_PLAYER_HP_SYN;
	synMsg.mCharacterIdx	= GetObjectID();
	synMsg.mHp				= mPlayerExrInfo.RestHP;
	synMsg.mMaxHp			= GetMaxHP();
	synMsg.mDie				= GetStateDie();

	return NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(MSG_SYN_PLAYER_HP) );
}

void cPlayer::SendMaxHP( )
{
	MSG_SYN_PLAYER_MAXHP synMsg;
	synMsg.Category			= NM_PLAYER;
	synMsg.Protocol			= NM_PLAYER_MAXHP_SYN;
	synMsg.mCharacterIdx	= GetObjectID();
	synMsg.mMaxHp			= GetMaxHP();

	NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(MSG_SYN_PLAYER_MAXHP) );
}

void cPlayer::MPDamage( unsigned long mp, bool msgSend )
{ 
	if( mPlayerExrInfo.RestMP < mp ) 
	{
		mPlayerExrInfo.RestMP = 0;
	}
	else
	{
		mPlayerExrInfo.RestMP -= mp;	
	}	

	if( msgSend == true )
	{
		MSG_SYN_PLAYER_MP synMsg;
		synMsg.Category			= NM_PLAYER;
		synMsg.Protocol			= NM_PLAYER_MP_SYN;
		synMsg.mCharacterIdx	= GetObjectID();
		synMsg.mMp				= mPlayerExrInfo.RestMP;
		synMsg.mMaxMp			= GetMaxMP();

		NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
	}
}


void cPlayer::MPHeal( unsigned long mp, bool msgSend )
{
	unsigned long maxMP  = GetMaxMP();
	unsigned long restMP = mPlayerExrInfo.RestMP;

	if( maxMP == restMP )
		return;

	unsigned long currentMp = restMP + mp;

	mPlayerExrInfo.RestMP = (currentMp > maxMP) ? maxMP : currentMp;

	if ( msgSend != NULL )
		SendMPHeal( );
}

bool cPlayer::SendMPHeal( )
{
	MSG_SYN_PLAYER_MP synMsg;
	synMsg.Category			= NM_PLAYER;
	synMsg.Protocol			= NM_PLAYER_MP_SYN;
	synMsg.mCharacterIdx	= GetObjectID();
	synMsg.mMp				= mPlayerExrInfo.RestMP;
	synMsg.mMaxMp			= GetMaxMP();

	return NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(MSG_SYN_PLAYER_MP) );
}

void cPlayer::SendMaxMP( )
{
	MSG_SYN_PLAYER_MAXMP synMsg;
	synMsg.Category			= NM_PLAYER;
	synMsg.Protocol			= NM_PLAYER_MAXMP_SYN;
	synMsg.mCharacterIdx	= GetObjectID();
	synMsg.mMaxMp			= GetMaxMP();

	NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(MSG_SYN_PLAYER_MAXMP) );
}

void cPlayer::SetMoveTargetPos( float x, float y )
{ 
	mGotoX = x; mGotoY = y; 

	float viewPosX, viewPosY;
	viewPosX = mObjectPos.x - x;
	viewPosY = mObjectPos.y - y;

	mDirection = atan2( viewPosX, viewPosY );
}

bool cPlayer::IsMovePossible( NiPoint2 startPos, NiPoint2 goalPos )
{
	/// 이동불가 상태 이상인 경우
	if( IsCantMove() == true )
	{
		return false;
	}

	/// 이동 메세지 시간차 고려
	mRangeCheck.SetRadius( SYNC_MOVE_RANGE );
	if( mRangeCheck.IsNotRange( startPos, mObjectPos ) == true )
		return false;

	/// pvp인경우 
	if( mPvPDMIdx != 0 )
	{
		/// 전투모드전 대기 상태인지 체크
		cDeathMatchObject* pDMObject = PVPMANAGER->GetPvPDMObject( mPvPDMIdx );
		if( pDMObject != NULL && pDMObject->IsFight() == false )
		{
			/// 자신의 팀에 해당하는 기준점 읽어옴
			unsigned long posIdx = PVP_DM_START_POSIDX_A; 
			if( mPlayerExrInfo.mPvPDMTeamType == ePVPDM_TEAMTYPE_B )
				posIdx = PVP_DM_START_POSIDX_B;

			/// 스크립트상의 시작좌표
			sStageChangePos* pScript = (sStageChangePos*)STAGESCRIPT->GetPosScriptInfo( posIdx );
			if( pScript != NULL )
			{
				NiPoint2 pos( pScript->mPosX, pScript->mPosY );
				mRangeCheck.SetRadius( PVP_DM_READY_MOVE_RANGE );
				if( mRangeCheck.IsNotRange( goalPos, pos ) == true )
					return false;
			}
		}
	}

	/// 직선 코스 이동 가능 인지 확인
	if( AIMANAGER->IsPassible( mMapNumber, startPos, goalPos, mObject ) == false )
	{
		/// 직선 불가능 일때 길찾기 해보고 여러 코스가 나오면 서버와 클라가 안맞을 가능성이 크다.
		if( AIMANAGER->CalcPathCnt( mMapNumber, startPos, goalPos ) > 1 )
			return false;
	}

	return true;
}

void cPlayer::LevelUp( unsigned char upLevel ) 
{ 
	/// 만렙 체크
	sExpTable* pExpTable = LEVELSCRIPT->GetExpTable( mPlayerInfo.Level + upLevel );
	if( pExpTable == NULL )	
		return; 

	/// 레벨업전 스텟1 저장
	sStatus1 oldStatus1;
	oldStatus1 = mStatus1;

	mPlayerInfo.Level = mPlayerInfo.Level + upLevel; 
	STATUSCALC->CalcPlayerBaseGlobal( mObject );

	HPHeal( GetMaxHP( ), false, 0, 0, eTAKEDAMAGETYPE_NONE );
	MPHeal( GetMaxMP( ), false );

	/// 
	if( GetGuildIndex() > 0 )
	{
		GUILDMAN->RequestUpdateUser( GetGuildIndex(), GetObjectID(), GetLevel(), GetJob() );
	}

	/// 레벨업 메세지 발송
	HANDLE         handle = NULL;
	MSG_RES_LEVEL* msgExp = (MSG_RES_LEVEL*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_LEVEL_RES );
	if ( msgExp != NULL )
	{
		msgExp->mLevel = GetLevel();
		msgExp->mStr = (unsigned char)(FloatToInt( mStatus1.mStr ) - FloatToInt( oldStatus1.mStr ));
		msgExp->mDex = (unsigned char)(FloatToInt( mStatus1.mDex ) - FloatToInt( oldStatus1.mDex ));
		msgExp->mCon = (unsigned char)(FloatToInt( mStatus1.mCon ) - FloatToInt( oldStatus1.mCon ));
		msgExp->mInt = (unsigned char)(FloatToInt( mStatus1.mInt ) - FloatToInt( oldStatus1.mInt ));
		msgExp->mWis = (unsigned char)(FloatToInt( mStatus1.mWis ) - FloatToInt( oldStatus1.mWis ));
		msgExp->mHP = mPlayerExrInfo.RestHP;
		msgExp->mMaxHP = GetMaxHP();
		msgExp->mMP = mPlayerExrInfo.RestMP;
		msgExp->mMaxMP = GetMaxMP();
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_LEVEL) );
	}

	/// 레벨업 싱크 메세지 발송
	MSG_SYN_LEVEL synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_LEVEL_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mLevel = mPlayerInfo.Level;
	synMsg.mHP = mPlayerExrInfo.RestHP;
	synMsg.mMaxHP = GetMaxHP();
	synMsg.mMP = mPlayerExrInfo.RestMP;
	synMsg.mMaxMP = GetMaxMP();
	NETWORK2->QuickSendExcept( this, (char*)&synMsg, sizeof(synMsg) );

	/// 레벨업 퀘스트 트리거 검사
	SendNpcQuestStatus();

	CHARACTER_LEVEL* characterLevel = (CHARACTER_LEVEL*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_LEVEL );
	if ( characterLevel == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer[%d]::LevelUp characterLevel == NULL", mObject.index );
		return;
	}

	characterLevel->idx = mObject.index;
	characterLevel->level = mPlayerInfo.Level;
	characterLevel->levelUpPoint = upLevel;
	characterLevel->retvalue = -1;

	NETWORK2->SendSQL( handle, sizeof(CHARACTER_LEVEL) );
}


void cPlayer::SetFixedObjectSizePer( short sizePer )
{
	mPlayerExrInfo.mFixedObjectSizePer = 100 + sizePer;
	if( 100 + sizePer < 0 )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer::GetFixedObjectSize() sizePer < -100");
		sizePer = -100;
	}

	float objectSize = mPlayerExrInfo.mBaseFixedSize * ( (float)( 100 + sizePer ) / 100 );
	if( objectSize <= 0.0f )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer::GetFixedObjectSize() <= 0");
		objectSize = 1.0f;
	}

	mFixedObjectSize = objectSize;
}

void cPlayer::SetChgMonster( unsigned long infObjectIdx, unsigned long monsterIdx )
{ 
	/// 몬스터 외형 변경
	sMonsterScript* pMScript = MONSTERSCRIPT->GetMonsterListInfo( monsterIdx );
	if( pMScript == NULL )
	{
		NETWORK2->PostServerEvent("cPlayer::SetChgMonsterIdx[%d] pMScript == NULL", monsterIdx );
		return;
	}

	if( pMScript->mMonsterScale <= 0.0f )
	{
		NETWORK2->PostServerEvent("cPlayer::SetChgMonsterIdx MonsterScale ERROR - %d", pMScript->mMonsterScale );
		return;
	}

	mPlayerExrInfo.mBaseFixedSize = pMScript->mMonsterFixSize * pMScript->mMonsterScale; 

	memset( mChgMonCoolTime, 0, sizeof(mChgMonCoolTime) );

	mChgMonsterInfIdx = infObjectIdx;
	mPlayerExrInfo.mChgMonsterIdx = monsterIdx; 	

	MSG_SYN_PLAYER_CHG_MONSTER synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_CHG_MONSTER_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mMonsterIdx = mPlayerExrInfo.mChgMonsterIdx;
	NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
}


void cPlayer::EndChgMonster()
{
	/// 몬스터 외형 꺼짐	
	mPlayerExrInfo.mBaseFixedSize = FIXED_OBJECT_SIZE;

	mChgMonsterInfIdx = 0;
	mPlayerExrInfo.mChgMonsterIdx = 0; 	

	MSG_SYN_PLAYER_CHG_MONSTER synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_CHG_MONSTER_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mMonsterIdx = mPlayerExrInfo.mChgMonsterIdx;
	NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
}


void cPlayer::SetVehicle( unsigned long infObjectIdx, unsigned long vehicleIdx )
{ 
	/// 탈것 변경
	sVehicleList* pScript = VEHICLESCRIPT->GetVehicleInfo( vehicleIdx );
	if( pScript == NULL )
	{
		NETWORK2->PostServerEvent("cPlayer::SetVehicle[%d,%d] pScript == NULL", mObject.index, vehicleIdx );
		return;
	}

	mPlayerExrInfo.mBaseFixedSize = pScript->mObjectFixSize; 

	mVehicleInfIdx = infObjectIdx;
	mPlayerExrInfo.mVehicleIdx = vehicleIdx; 

	MSG_SYN_PLAYER_VEHICLE synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_VEHICLE_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mVehicleIdx = mPlayerExrInfo.mVehicleIdx;
	NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
}


void cPlayer::SendEndVehicle()
{ 
	/// 탈것 꺼짐
	mPlayerExrInfo.mBaseFixedSize = FIXED_OBJECT_SIZE;
	mVehicleInfIdx = 0;
	mPlayerExrInfo.mVehicleIdx = 0; 

	MSG_SYN_PLAYER_VEHICLE synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_VEHICLE_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mVehicleIdx = mPlayerExrInfo.mVehicleIdx;
	NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
}


void cPlayer::SendEndVehicleExcept()
{ 
	/// 탈것 꺼짐
	mPlayerExrInfo.mBaseFixedSize = FIXED_OBJECT_SIZE;
	mVehicleInfIdx = 0;
	mPlayerExrInfo.mVehicleIdx = 0; 

	MSG_SYN_PLAYER_VEHICLE synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_VEHICLE_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mVehicleIdx = mPlayerExrInfo.mVehicleIdx;
	NETWORK2->QuickSendExcept( this, (char*)&synMsg, sizeof(synMsg) );
}


void cPlayer::EndVehicleClient( bool sendSync )
{
	/// 프로세스 순서 확인하자!
	if( mVehicleInfIdx != 0 )
		SKILLMANAGER->VehicleDeleteInfluence( mObject, mVehicleInfIdx, sendSync );
}


void cPlayer::EndVehicleItem( unsigned long skillClassIdx )
{
	sPlayerSkillBaseInfo* pSkill = SKILLSCRIPT->GetPlayerSkillInfo( skillClassIdx );
	if( pSkill == NULL )
		return;
	
	sInfluenceScript* pScript = SKILLSCRIPT->GetInfluenceInfo( pSkill->mpSetpInfoArray[0].mInfulenceIdx );
	if( pScript == NULL )
		return;
	
	if( mPlayerExrInfo.mVehicleIdx == 0 )
		return;

	if( pScript->mStatusPlusIdx1 == eSTATUSPLUS_VEHICLE )
	{
		if( (long)mPlayerExrInfo.mVehicleIdx != pScript->mValue1 )
			return;

		if( mVehicleInfIdx != 0 )
			SKILLMANAGER->DeleteInfluence( mObject, mVehicleInfIdx );
	}
	else if( pScript->mStatusPlusIdx2 == eSTATUSPLUS_VEHICLE )
	{
		if( (long)mPlayerExrInfo.mVehicleIdx != pScript->mValue2 )
			return;

		if( mVehicleInfIdx != 0 )
			SKILLMANAGER->DeleteInfluence( mObject, mVehicleInfIdx );
	}
	else if( pScript->mStatusPlusIdx3 == eSTATUSPLUS_VEHICLE )
	{
		if( (long)mPlayerExrInfo.mVehicleIdx != pScript->mValue3 )
			return;

		if( mVehicleInfIdx != 0 )
			SKILLMANAGER->DeleteInfluence( mObject, mVehicleInfIdx );
	}
}


void cPlayer::SetForceType( unsigned char forceType, bool msgSend )
{ 
	mPlayerExrInfo.mForceType = forceType; 

	if( msgSend == true )
	{
		MSG_SYN_PLAYER_FORCE msg;
		msg.Category = NM_PLAYER;
		msg.Protocol = NM_PLAYER_FORCE_SYN;
		msg.mPlayerIdx = mObject.index;
		msg.mForceType = forceType;
		NETWORK2->QuickSend( this, (char*)&msg, sizeof(msg) );
	}
}

void cPlayer::WindPointPlus( unsigned long windPoint, bool msgSend )
{ 
	mHeroInfo.mWindPoint += windPoint; 

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_WIND;
			pMsg->mValueType = 1;
			pMsg->mValue = mHeroInfo.mWindPoint;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}

void cPlayer::WindFriendlyPlus( unsigned long windFriendly, bool msgSend )
{ 
	mHeroInfo.mWindFriendly += windFriendly;

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_WIND;
			pMsg->mValueType = 2;
			pMsg->mValue = mHeroInfo.mWindFriendly;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}

void cPlayer::EarthPointPlus( unsigned long earthPoint, bool msgSend )
{ 
	mHeroInfo.mEarthPoint += earthPoint; 

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_EARTH;
			pMsg->mValueType = 1;
			pMsg->mValue = mHeroInfo.mEarthPoint;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}

void cPlayer::EarthFriendlyPlus( unsigned long earthFriendly, bool msgSend )
{ 
	mHeroInfo.mEarthFriendly += earthFriendly; 

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_EARTH;
			pMsg->mValueType = 2;
			pMsg->mValue = mHeroInfo.mEarthFriendly;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}

void cPlayer::WaterPointPlus( unsigned long waterPoint, bool msgSend )
{ 
	mHeroInfo.mWaterPoint += waterPoint; 

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_WATER;
			pMsg->mValueType = 1;
			pMsg->mValue = mHeroInfo.mWaterPoint;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}

void cPlayer::WaterFriendlyPlus( unsigned long waterFriendly, bool msgSend )
{ 
	mHeroInfo.mWaterFriendly += waterFriendly; 

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_WATER;
			pMsg->mValueType = 2;
			pMsg->mValue = mHeroInfo.mWaterFriendly;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}

void cPlayer::FirePointPlus( unsigned long firePoint, bool msgSend )
{ 
	mHeroInfo.mFirePoint += firePoint; 

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_FIRE;
			pMsg->mValueType = 1;
			pMsg->mValue = mHeroInfo.mFirePoint;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}

void cPlayer::FireFriendlyPlus( unsigned long fireFriendly, bool msgSend )
{ 
	mHeroInfo.mFireFriendly += fireFriendly; 

	if( msgSend == true )
	{
		HANDLE         handle = NULL;
		MSG_SYN_PLAYER_FORCEVALUE* pMsg = (MSG_SYN_PLAYER_FORCEVALUE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_FORCEVALUE_SYN );
		if ( pMsg != NULL )
		{
			pMsg->mForceType = eFORCETYPE_FIRE;
			pMsg->mValueType = 2;
			pMsg->mValue = mHeroInfo.mFireFriendly;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_FORCEVALUE) );
		}
	}
}


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

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

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

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


float cPlayer::GetStatus2Plus( ePLAYER_STATUS_EXT extStatus )
{	
	if( extStatus >= ePLAYER_STATUS_EXT2_MAX )
	{
		assert(NULL);
		return 0.0f;
	}

	return mStatus2Plus[extStatus];
}


float cPlayer::GetStatus2Per( ePLAYER_STATUS_EXT extStatus )
{
	if( extStatus >= ePLAYER_STATUS_EXT2_MAX )
	{
		assert(NULL);
		return 0.0f;
	}

	return mStatus2Per[extStatus];
}


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

	switch( extStatus )
	{
	case ePLAYER_STATUS_EXT_ATK:
		break;	/// 최소 최대 값으로 재정의
	case ePLAYER_STATUS_EXT_RATK:
		break;	/// 최소 최대 값으로 재정의
	case ePLAYER_STATUS_EXT_DEF:
		mStatus2.mPhysicDefense = value; break;
	case ePLAYER_STATUS_EXT_MATK:
		break;	/// 최소 최대 값으로 재정의
	case ePLAYER_STATUS_EXT_MDEF:
		mStatus2.mMagicDefense = value; break;
	case ePLAYER_STATUS_EXT_CRI:
		mStatus2.mPhysicCritical = value; break;
	case ePLAYER_STATUS_EXT_MCRI:
		mStatus2.mMagicCritical = value; break;
	case ePLAYER_STATUS_EXT_MAXHP:
		mPlayerExrInfo.MaxHP = FloatToInt( value );
		mStatus2.mMaxHP = value; break;
	case ePLAYER_STATUS_EXT_MAXMP:
		mPlayerExrInfo.MaxMP = FloatToInt( value );
		mStatus2.mMaxMP = value; break;
	case ePLAYER_STATUS_EXT_RECOVHP:
		mStatus2.mRecovHP = value; break;
	case ePLAYER_STATUS_EXT_RECOVMP:
		mStatus2.mRecovMP = value; break;
	case ePLAYER_STATUS_EXT_HIT:
		mStatus2.mPhysicAttackRate = value; break;
	case ePLAYER_STATUS_EXT_MHIT:
		mStatus2.mMagicAttackRate = value; break;
	case ePLAYER_STATUS_EXT_AVOID:
		mStatus2.mAvoid = value; break;
	case ePLAYER_STATUS_EXT_WEIGHT:
		mStatus2.mMaxWeight = value; break;
	case ePLAYER_STATUS_EXT_ASPD:
		mPlayerExrInfo.mAttackSpeed = value;
		mStatus2.mAttackSpeed = (float)value; break;
	case ePLAYER_STATUS_EXT_COOLTIME:
		mStatus2.mCooltime = (float)value; break;
	case ePLAYER_STATUS_EXT_RANGE:
		mStatus2.mInRange = value; break;
	case ePLAYER_STATUS_EXT_RRANGE:
		mStatus2.mOutRange = value; break;
	case ePLAYER_STATUS_EXT_SPD:
		mPlayerExrInfo.mMoveSpeed = FloatToInt( value );
		mStatus2.mMoveSpeed = value; break;
	case ePLAYER_STATUS_EXT_HEAL:
		break;	/// 최소 최대 값으로 재정의

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

	mLastMaxHP = GetMaxHP();
	mLastMaxMP = GetMaxMP();
}


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

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


void cPlayer::SetDamageCalc( ePLAYER_STATUS_DAMAGE statusDamage, unsigned long value )
{
	if( statusDamage >= ePLAYER_STATUS_DAMAGE_MAX )
	{
		assert(NULL);
		return;
	}

	switch( statusDamage )
	{
	case ePLAYER_STATUS_DAMAGE_INSTANTPERHIT:
		mDamageCalc.mInstantPerHit = value; break;
	case ePLAYER_STATUS_DAMAGE_INSTANTPLUSHIT:
		mDamageCalc.mInstantPlusHit = value; break;				
	case ePLAYER_STATUS_DAMAGE_PHYINSTANTPERCRI:
		mDamageCalc.mPhyInstantPerCri = value; break;
	case ePLAYER_STATUS_DAMAGE_PHYINSTANTPLUSCRI:
		mDamageCalc.mPhyInstantPlusCri = value; break;
	case ePLAYER_STATUS_DAMAGE_MAGICINSTANTPERCRI:
		mDamageCalc.mMagicInstantPerCri = value; break;
	case ePLAYER_STATUS_DAMAGE_MAGICINSTANTPLUSCRI:
		mDamageCalc.mMagicInstantPlusCri = value; break;	
	case ePLAYER_STATUS_DAMAGE_PHYINSTANTPERATTACK:
		mDamageCalc.mPhyInstantPerAttack = value; break;
	case ePLAYER_STATUS_DAMAGE_PHYINSTANTPLUSATTACK:
		mDamageCalc.mPhyInstantPlusAttack = value; break;
	case ePLAYER_STATUS_DAMAGE_MAGICINSTANTPERATTACK:
		mDamageCalc.mMagicInstantPerAttack = value; break;
	case ePLAYER_STATUS_DAMAGE_MAGICINSTANTPLUSATTACK:
		mDamageCalc.mMagicInstantPlusAttack = value; break;	
	case ePLAYER_STATUS_DAMAGE_INSTANTPERFINAL:
		mDamageCalc.mInstantPerFinal = value; break;
	case ePLAYER_STATUS_DAMAGE_INSTANTPLUSFINAL:
		mDamageCalc.mInstantPlusFinal = value; break;
	}

}

unsigned long cPlayer::DamageCalc( sObject target, unsigned long /*skillIdx*/, unsigned long aType, unsigned long rType )
{
	return DAMAGECALC->PlayerDamageCalc( mObject.index, target, (eATTRIBUTETYPE)aType, (eRANGETYPE)rType );
}


bool cPlayer::AttackSuccess( sObject target,unsigned long /*skillIdx*/,  unsigned long aType, unsigned short accuracyValue )
{
	return DAMAGECALC->PlayerAttackSuccess( mObject.index, target, (eATTRIBUTETYPE)aType, accuracyValue );
}


bool cPlayer::CriticalSuccess( sObject target, unsigned long /*skillIdx*/, unsigned long aType, unsigned short criticalValue )
{
	return DAMAGECALC->PlayerCritical( mObject.index, target, (eATTRIBUTETYPE)aType, criticalValue );
}


void cPlayer::CalcInstantSkill( unsigned long statusType1, unsigned long value1, unsigned long statusType2, unsigned long value2 )
{
	for( int i = ePLAYER_STATUS_DAMAGE_INSTANTPERHIT ; i < ePLAYER_STATUS_DAMAGE_MAX ; ++i )
		SetDamageCalc( (ePLAYER_STATUS_DAMAGE)i, 0 );

	eSTATUS_PLUS statusType = (eSTATUS_PLUS)statusType1;
	unsigned long value = value1;
	unsigned long arrayNum = 0;

	if( statusType != 0 )
	{
		if( eSTATUSPLUS_INSTANTPERHIT <= statusType && statusType < eSTATUSPLUS_DAMAGECALC_MAX )
		{
			arrayNum = statusType - eSTATUSPLUS_INSTANTPERHIT;
			SetDamageCalc( (ePLAYER_STATUS_DAMAGE)arrayNum, value );
		}
	}

	statusType = (eSTATUS_PLUS)statusType2;
	value = value2;

	if( statusType != 0 )
	{
		/// 타입1, 타입2가 같으면 더해서 넣는다.
		if( statusType1 == statusType2 )
		{
			NETWORK2->PostServerEvent(" Player::CalcInstantSkill [%d,%d] statusType1 == statusType2", statusType1, statusType2 );
			value = value1 + value2;
		}	

		if( eSTATUSPLUS_INSTANTPERHIT <= statusType && statusType < eSTATUSPLUS_DAMAGECALC_MAX )
		{
			arrayNum = statusType - eSTATUSPLUS_INSTANTPERHIT;
			SetDamageCalc( (ePLAYER_STATUS_DAMAGE)arrayNum, value );
		}
	}
}


float cPlayer::CalcStatusSkillRange( float skillRange, eRANGETYPE mRangeType, bool itemPlus )
{
	/// 스킬 사거리
	float range = skillRange;

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

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

	/// 사거리 계산
	if( itemPlus == true )
		range = ( range + mEquipAbility.mAttackRange ) * ( ( 100 + statusRangePer ) / 100 ) + statusRangePlus;
	else
		range = range * ( ( 100 + statusRangePer ) / 100 ) + statusRangePlus;

	return range;
}


long cPlayer::GetMoveSpeed( )
{
	long moveSpeed = FloatToInt( mStatus2.mMoveSpeed );
	if( moveSpeed < 0 )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer::GetMoveSpeed( ) <= 0");
		moveSpeed = 1;
	}

	return moveSpeed;
}


void cPlayer::SetLoseTime( unsigned long loseTime ) 
{ 
	if( loseTime > mLoseTime) 
	{ 
		mLoseTime = loseTime; 
		Verbose->WriteLog("PlayerLoseTime - %d",mLoseTime);
	}

}


bool cPlayer::IsChangeState( unsigned int newState, bool resurrection )
{
	switch( mPlayerExrInfo.mState )
	{
	case eOBJECT_STATE_IDLE:		
	case eOBJECT_STATE_MOVE:		
	case ePLAYER_STATE_SITDOWN:
	case ePLAYER_STATE_ITEMPICK:	/// 아이템 집는 중에 모든 상태 가능
		break;
	case eOBJECT_STATE_STOP:		
		{
			/// 정지중에 죽는 상태, 대기 상태로만 변경가능
			if( newState == eOBJECT_STATE_DIE || newState == eOBJECT_STATE_IDLE )
				break;

			return false;
		}
		break;
	case ePLAYER_STATE_GATHERING:
		{
			/// 채집중에 죽는 상태, 대기 상태로만 변경가능
			if( newState == eOBJECT_STATE_DIE || newState == eOBJECT_STATE_IDLE || newState == ePLAYER_STATE_ITEMPICK )
				break;

			return false;
		}
		break;
	case eOBJECT_STATE_ATTACK:
		{
			/// 공격중에 죽는 상태, 대기 상태로만 변경가능
			if( newState == eOBJECT_STATE_DIE || newState == eOBJECT_STATE_IDLE )
				break;

			return false;
		}
		break;

	case eOBJECT_STATE_DIE:
		{
			/// 죽은 상태에서 살아나는(IDLE) 상태로만 변경가능
			if( newState == eOBJECT_STATE_IDLE && resurrection == true )
				break;

			return false;
		}
		break;
	default:
		return false;
	}

	return true;
}


bool cPlayer::ChangeState( unsigned int newState, bool resurrection )
{
	/// 변경 가능한지 체크
	if( IsChangeState( newState, resurrection ) == false )
		return false;

	/// 기존 정지 상태에서 다른 상태로 변하는 경우
	switch( mPlayerExrInfo.mState )
	{
	case eOBJECT_STATE_STOP:
		{
			if ( newState == eOBJECT_STATE_DIE )
			{
				/// 상태에 따른 정리.
				switch ( GetStateStop( ) )
				{
				case eSTOP_READYTAROT:
				case eSTOP_OPENTAROT:
					TarotReaderDie( );
					break;
				case eSTOP_ENTERTAROT:
				case eSTOP_USETAROT:
					TarotSeekerDie( );
					break;
				case eSTOP_OPENSTALL:
					StallSellOpenDie( );
					break;
				case eSTOP_USESTALL:
					StallSellUseDie( );
					break;
				case eSTOP_EXCHANGE:
					ExchangeDie( );
					break;
				}
			}

			/// 정지상태 세부값을 NONE 으로 바꿈
			SetStateStop( eSTOP_NONE );
		}
		break;
	case eOBJECT_STATE_DIE:
		{
			/// 부활 변수를 초기화
			mIsSkillResurrection = false;
			mSkillResurrectionHP = 0;
			mSkillResurrectionMP = 0;
		}
		break;
	case ePLAYER_STATE_SITDOWN:
		{
			if( mSitDownStartTime != 0 && mPlayerExrInfo.mState == ePLAYER_STATE_SITDOWN && 
				mSitDownStartTime + NATURE_RECOVERY_TIME < NETWORK2->GetAccumTime() )
			{
				STATUSCALC->CalcPlayerExtensionGlobal( mObject );
			}
			mSitDownStartTime = 0;
			mSitDownStatusCalc = false;
		}
		break;
	}

	/// 신규 상태가 이동이 아닌 경우 이동 중이던 목표값 초기화 해준다.
	if( newState != eOBJECT_STATE_MOVE )
	{	
		MoveStop();
	}

	/// 신규 상태처리
	switch( newState )
	{
	case eOBJECT_STATE_DIE:
		{
			/// 몬스터가 쫓아오는 비중을 클리어 시킨다.
			mMonsterImportance = 0;
			/// npc랑 대화 중이었으면 대화를 초기화한다.
			mNpcIdx = 0;

			mPvPDieTime = NETWORK2->GetAccumTime();

			/// 추적 위치 클리어
			memset( mFollowPos, 0, sizeof(mFollowPos) );

			/// 효과를 제거한다.
			DieDeleteInfluence();	

			/// 타겟으로 잡을 몬스터에게 전달
			DietargetingMonster();

			/// 채집 중이던 목록에서 플레이어 제거
			cGathering* pGathering = OBJECTMANAGER->GetGathering( mGatheringIdx );
			if( pGathering != NULL )
				pGathering->EraseGatherPlayer( this );

			mGatheringIdx = 0;
		}
		break;
	case eOBJECT_STATE_ATTACK:
		{
			// 평화 대기 풀림
			mPlayerExrInfo.mStateIdle = eIDLE_NORMAL;

			if( mPlayerExrInfo.mVehicleIdx != 0 )
			{
				NETWORK2->PostServerEvent("cPlayer[%d,%d]::ChangeState eOBJECT_STATE_ATTACK mPlayerExrInfo.mVehicleIdx[%d] != 0",
					mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mVehicleIdx );
				SKILLMANAGER->DeleteInfluence( mObject, mVehicleInfIdx );
			}
		}
		break;
	case ePLAYER_STATE_SITDOWN:
		{
			/// 앉기 시작시간 설정 - 10초 후부터 자연회복 2배
			mSitDownStartTime = NETWORK2->GetAccumTime();
			mSitDownStatusCalc = true;

			if( mPlayerExrInfo.mVehicleIdx != 0 )
			{
				NETWORK2->PostServerEvent("cPlayer[%d,%d]::ChangeState ePLAYER_STATE_SITDOWN mPlayerExrInfo.mVehicleIdx[%d] != 0",
					mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mVehicleIdx );
				SKILLMANAGER->DeleteInfluence( mObject, mVehicleInfIdx );
			}
		}
		break;
	case ePLAYER_STATE_ITEMPICK:
		{
			if( mPlayerExrInfo.mVehicleIdx != 0 )
			{
				NETWORK2->PostServerEvent("cPlayer[%d,%d]::ChangeState ePLAYER_STATE_ITEMPICK mPlayerExrInfo.mVehicleIdx[%d] != 0",
					mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mVehicleIdx );
				SKILLMANAGER->DeleteInfluence( mObject, mVehicleInfIdx );
			}
		}
		break;
	case ePLAYER_STATE_GATHERING:
		{
			if( mPlayerExrInfo.mVehicleIdx != 0 )
			{
				NETWORK2->PostServerEvent("cPlayer[%d,%d]::ChangeState ePLAYER_STATE_GATHERING mPlayerExrInfo.mVehicleIdx[%d] != 0",
					mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mVehicleIdx );
				SKILLMANAGER->DeleteInfluence( mObject, mVehicleInfIdx );
			}
		}
		break;
	}

	/// 상태 변경
	mPlayerExrInfo.mState = (unsigned char)newState;

	return true;
}

void cPlayer::Resurrection( unsigned long resurrectionType )
{

	unsigned char errorCode = ERROR_PLAYER_RESURRECTION_SUCCESS;
	unsigned long expDown = 0;

	/// 죽었는지 확인
	if( GetStateDie() == true )
	{
		/// 스킬에 의한 부활여부
		if( resurrectionType == eRESURRECTIONTYPE_SKILL )
		{
			if( SkillResurrection() == false )
				errorCode = ERROR_PLAYER_RESURRECTION_FAIL;
			else
				expDown = ExpDown( RESURRECTION_EXP_SKILL_MIN, RESURRECTION_EXP_SKILL_MAX );
		}
		else
		{
			if( ChangeState( eOBJECT_STATE_IDLE, true ) == false )
			{
				assert(NULL);
				NETWORK2->PostServerEvent("Player[%d] Resurrection ChangeState( eOBJECT_STATE_IDLE, true ) == false", mObject.index );
			}

			/// 경험치 하락
			switch( resurrectionType )
			{
			case eRESURRECTIONTYPE_STAND:		/// 제자리 부활
				{
					if( mPlayerInfo.Level >= 10 )
					{
						expDown = ExpDown( RESURRECTION_EXP_STAND_MIN, RESURRECTION_EXP_STAND_MAX );

						SKILLMANAGER->AddInfluence( mObject, mObject, RESURRECTION_STAND_INFLUENCE_CLASSIDX, 0, true );		
					}
				}
				break;
			case eRESURRECTIONTYPE_SAFE:		/// 안전지대 부활
				{
					if( mPlayerInfo.Level >= 10 )
						expDown = ExpDown( RESURRECTION_EXP_SAFE_MIN, RESURRECTION_EXP_SAFE_MAX );

					NiPoint2 pos = STAGESCRIPT->CalcNearTargetMapPos( mMapNumber, mObjectPos.x, mObjectPos.y );
					mObjectPos.x = pos.x;
					mObjectPos.y = pos.y;
				}
				break;
			};

			InitHP( 10 );
			InitMP( 10 );
		}
	}
	else
	{
		assert(NULL);
		NETWORK2->PostServerEvent("ERROR alive player resurrection");
		errorCode = ERROR_PLAYER_RESURRECTION_FAIL;
	}

	HANDLE                       handle  = NULL;
	MSG_RES_PLAYER_RESURRECTION* sendMsg = (MSG_RES_PLAYER_RESURRECTION*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, 
		NM_PLAYER, NM_PLAYER_RESURRECTION_RES );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = errorCode;
		sendMsg->mHP = mPlayerExrInfo.RestHP;
		sendMsg->mMaxHP = GetMaxHP();
		sendMsg->mMP = mPlayerExrInfo.RestMP;
		sendMsg->mMaxMP = GetMaxMP();
		sendMsg->mExpDown = expDown;
		sendMsg->mPlayerExp = mHeroInfo.Exp;
		sendMsg->mPosX = mObjectPos.x;
		sendMsg->mPosY = mObjectPos.y;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_RESURRECTION) );
	}

	if( errorCode == 0 )
	{
		MSG_SYN_PLAYER_RESURRECTION synMsg;
		synMsg.Category  = NM_PLAYER;
		synMsg.Protocol  = NM_PLAYER_RESURRECTION_SYN;
		synMsg.mCharacterIdx = mObject.index;
		synMsg.mHP = mPlayerExrInfo.RestHP;
		synMsg.mMaxHP = GetMaxHP();
		synMsg.mMP = mPlayerExrInfo.RestMP;
		synMsg.mMaxMP = GetMaxMP();
		synMsg.mPosX = mObjectPos.x;
		synMsg.mPosY = mObjectPos.y;   
		NETWORK2->QuickSendExcept( this, (char*)&synMsg, sizeof(synMsg) );
	}
}


bool cPlayer::SkillResurrection()
{
	bool retBool = mIsSkillResurrection;
	if( mIsSkillResurrection == true )
	{
		InitHP( mSkillResurrectionHP );
		InitMP( mSkillResurrectionMP );
		if( ChangeState( eOBJECT_STATE_IDLE, true ) == false )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("Player[%d] SkillResurrection ChangeState( eOBJECT_STATE_IDLE, true ) == false", mObject.index );
		}

		mSkillResurrectionHP = 0;
		mSkillResurrectionMP = 0;
		mIsSkillResurrection = false;
	}

	return retBool;
}


void cPlayer::PvPResurrection( bool timeCheckPass )
{
	unsigned long accumTime = NETWORK2->GetAccumTime();

	/// 죽었는지 확인
	if( GetStateDie() == false )
		return;

	if( mPvPDMIdx == 0 )
	{
		NETWORK2->PostServerEvent("cPlayer::PvPResurrection() hero[%d,%d,%d] mPvPDMIdx == 0", 
			mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop, mPvPDieTime, accumTime );
		return;
	}

	if( mPvPDieTime + PVP_DM_RESURRECTION_TIME > accumTime && timeCheckPass == true )
	{
		NETWORK2->PostServerEvent("cPlayer::PvPResurrection() hero[%d,%d,%d] mPvPDieTime[%d] > accumTime[%d]", 
			mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop, mPvPDieTime, accumTime );
	}

	if( ChangeState( eOBJECT_STATE_IDLE, true ) == false )
		NETWORK2->PostServerEvent("cPlayer::PvPResurrection() hero[%d,%d,%d]->ChangeState() == false", 
		mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop );

	NiPoint2 pos;
	unsigned long posIdx = PVP_DM_START_POSIDX_A; 
	if( mPlayerExrInfo.mPvPDMTeamType == ePVPDM_TEAMTYPE_B )
		posIdx = PVP_DM_START_POSIDX_B;

	if( STAGESCRIPT->CalcTargetMapPos( posIdx, &pos.x, &pos.y ) == true )
	{
		mObjectPos.x = pos.x;
		mObjectPos.y = pos.y;
	}

	InitHP( GetMaxHP() );
	InitMP( GetMaxMP() );

	HANDLE                       handle  = NULL;
	MSG_RES_PLAYER_RESURRECTION* sendMsg = (MSG_RES_PLAYER_RESURRECTION*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_RESURRECTION_RES );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = 0;	
		sendMsg->mHP = mPlayerExrInfo.RestHP;
		sendMsg->mMaxHP = GetMaxHP();
		sendMsg->mMP = mPlayerExrInfo.RestMP;
		sendMsg->mMaxMP = GetMaxMP();
		sendMsg->mExpDown = 0;
		sendMsg->mPlayerExp = mHeroInfo.Exp;
		sendMsg->mPosX = mObjectPos.x;
		sendMsg->mPosY = mObjectPos.y;                   
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_RESURRECTION) );
	}

	MSG_SYN_PLAYER_RESURRECTION synMsg;
	synMsg.Category  = NM_PLAYER;
	synMsg.Protocol  = NM_PLAYER_RESURRECTION_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mHP = mPlayerExrInfo.RestHP;
	synMsg.mMaxHP = GetMaxHP();
	synMsg.mMP =mPlayerExrInfo.RestMP;
	synMsg.mMaxMP = GetMaxMP();
	synMsg.mPosX = mObjectPos.x;
	synMsg.mPosY = mObjectPos.y;    
	NETWORK2->QuickSendExcept( this, (char*)&synMsg, sizeof(synMsg) );

	SKILLMANAGER->AddInfluence( mObject, mObject, PVP_DM_NODAMAGE_INFLUENCE, 0, true );
}


bool cPlayer::AddInfluence( unsigned long uniqueIdx, unsigned long influenceClassIdx )
{ 
	if( mInfluenceSet.Insert( uniqueIdx ) == false )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer[%d]::AddSkillInfluence mInfluenceSet.Insert( %d, %d ) == false", mObject.index, uniqueIdx, influenceClassIdx );
		return false;
	}
	return true;
}


void cPlayer::ReleaseInfluenceSet() 
{ 
	cSkillInfluenceSet::cIterator start = mInfluenceSet.Begin();
	cSkillInfluenceSet::cIterator end = mInfluenceSet.End();

	unsigned long infIdx = 0;
	while( start != end )
	{
		infIdx = *start++;
		SKILLMANAGER->DeleteInfluenceList( infIdx );
	}
}


void cPlayer::DieDeleteInfluence()
{
	cSkillInfluenceSet::cIterator start = mInfluenceSet.Begin();
	cSkillInfluenceSet::cIterator end = mInfluenceSet.End();

	unsigned long infIdx = 0;
	cInfluenceObject* pInfluence = NULL;
	while( start != end )
	{
		infIdx = *start++;
		pInfluence = SKILLMANAGER->GetInfluence( infIdx );
		if( pInfluence == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cPlayer[%d]::ReleaseSkillInfluenceSet  pInfluence==NULL", mObject.index );
			continue;
		}

		if( pInfluence->GetDieDelete() == false )
			continue;

		pInfluence->NoSendDelMsg();		/// 클라이언트에 발송을 하지 않는다.
		SKILLMANAGER->DeleteInfluenceList( infIdx );
	}
}


void cPlayer::DeleteInfluence( unsigned long influenceIdx )
{
	/// 플레이어가 가지고 있는 목록에서 제거
	if( mInfluenceSet.Erase( influenceIdx ) == false )
		NETWORK2->PostServerEvent("cPlayer[%d]::EraseSkillInfluence[%d]", mObject.index, influenceIdx );
}

void cPlayer::InfluenceProcessStart()
{
	cInfluenceObject* pInfluenceObject = NULL;

	tHashSet<unsigned long>::cIterator start = mInfluenceSet.Begin();
	tHashSet<unsigned long>::cIterator end = mInfluenceSet.End();

	for( ; start != end ; ++start )
	{
		pInfluenceObject = SKILLMANAGER->GetInfluence( (*start) );
		if ( pInfluenceObject == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cPlayer::InfluenceProcessStart()");
			//-/-//SKILLMANAGER->InsertDeleteInfluenceObject( pInfluenceObject );	/// 객체 삭제 대기열에 넣는다.
			continue;
		}
		pInfluenceObject->ProcessStart();
	}
}


void cPlayer::InfluenceProcessStop()
{
	cInfluenceObject* pInfluenceObject = NULL;

	tHashSet<unsigned long>::cIterator start = mInfluenceSet.Begin();
	tHashSet<unsigned long>::cIterator end = mInfluenceSet.End();

	for( ; start != end ; ++start )
	{
		pInfluenceObject = SKILLMANAGER->GetInfluence( (*start) );
		if ( pInfluenceObject == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cPlayer::InfluenceProcessStart()");
			//-/-//SKILLMANAGER->InsertDeleteInfluenceObject( pInfluenceObject );	/// 객체 삭제 대기열에 넣는다.
			continue;
		}
		pInfluenceObject->ProcessStop();
	}
}


bool cPlayer::IsParentEqual( unsigned long parentUniqueIdx )
{
	cSkillInfluenceSet::cIterator beginAura = mInfluenceSet.Begin();
	cSkillInfluenceSet::cIterator endAura = mInfluenceSet.End();

	cInfluenceObject* pHaveInfluenceObject = NULL;
	while( beginAura != endAura )
	{
		/// 보유 효과 오브젝트
		pHaveInfluenceObject = SKILLMANAGER->GetInfluence( *beginAura++ );
		if( !pHaveInfluenceObject )	
		{ 
			NETWORK2->PostServerEvent("cSkillManager::IsParentEqual pHaveInfluenceObject==NULL Aura" );
			continue; 
		}

		if( pHaveInfluenceObject->IsParentEqual( parentUniqueIdx ) == true )
			return true;
	}

	return false;
}


void cPlayer::SkillResurrectionHP( unsigned long HP )
{
	mIsSkillResurrection = true;
	mSkillResurrectionHP = HP;
}



void cPlayer::SkillResurrectionMP( unsigned long MP )
{
	if( mIsSkillResurrection == true )
	{
		mSkillResurrectionMP = MP;
	}
}

void cPlayer::MonsterInTargeting( tArray<sMultiTarget>* targetAry, float range )
{
	/// 범위 설정
	mRangeCheck.SetRadius( (float)range );

	/// 나를 타겟으로 하는 몬스터 타겟 해제
	cMonster* pMonster = NULL;

	cTargetingMonsterSet::cIterator start = mTargetingMonsterSet.Begin();
	cTargetingMonsterSet::cIterator end = mTargetingMonsterSet.End();
	while( start != end )
	{
		unsigned long monsterIdx = (*start++);
		if( monsterIdx == 0 )
			continue;

		pMonster = GRIDMANAGER->GetMonster( monsterIdx );
		if( pMonster && pMonster->GetTarget() == this )
		{
			/// 범위 체크
			if( mRangeCheck.IsRange( mObjectPos, pMonster->GetPos() ) == true )
			{
				sMultiTarget multiTarget;
				multiTarget.mTarget.type = pMonster->GetObjectType();
				multiTarget.mTarget.index = pMonster->GetObjectID();
				multiTarget.mApplyHP = true;
				targetAry->PushBack( multiTarget );
			}
		}
	}
}

/// 각 직업당 사용한 sp 저장 처리
void cPlayer::UpdateJobUsedSP()
{
	/// 보유스킬 포인터
	typedef tPointerHashMap<unsigned long, void*> cHaveSkillMap;
	cHaveSkillMap* haveSkillMap = (cHaveSkillMap*)SKILLMANAGER->GetPlayerHaveSkill( mObject.index );

	for( unsigned int i=0;i<mJobUsedSpArr.GetSize();i++ )
		mJobUsedSpArr[i] = 0;

	cHaveSkillMap::cIterator begin = haveSkillMap->Begin();
	cHaveSkillMap::cIterator end = haveSkillMap->End();
	for( ; begin != end ; ++begin )
	{
		cHaveSkillObject* pHaveSkill = (cHaveSkillObject*)((*begin).mSecond);
		if( pHaveSkill == NULL )
			continue;

		sPlayerSkillBaseInfo* pSkillInfo = SKILLSCRIPT->GetPlayerSkillInfo( pHaveSkill->GetSkillIdx() );
		if( pSkillInfo == 0 )
			continue;
		if( pSkillInfo->mJobStep == 9 )
			continue;
		if( pSkillInfo->mStepCount <= pHaveSkill->GetSkillStep() )
		{
			assert(0);
			continue;
		}

		for( unsigned int i=0;i<=pHaveSkill->GetSkillStep();i++ )
		{
			sPlayerSkillStepInfo* pSkillStep = &pSkillInfo->mpSetpInfoArray[i];
			if( pSkillStep != NULL )
			{
				if( pSkillStep->mPlayerLevel != 0 )
					mJobUsedSpArr[pSkillInfo->mJobStep] = mJobUsedSpArr[pSkillInfo->mJobStep] + pSkillStep->mRequireSP;
			}
		}
	}
}


void cPlayer::StatusCalc()
{ 
	STATUSCALC->CalcPlayerExtensionGlobal( mObject );
}


bool cPlayer::CancelGathering( unsigned long gatheringIdx )
{ 
	if( mGatheringIdx != 0 && mGatheringIdx != gatheringIdx )
		return false;

	mGatheringIdx = 0;
	return true;
}


unsigned char cPlayer::AddMakeSkill( unsigned short itemSlotNum )
{
	/*-- 아이템 사용시 검사할 내용들.
	*/
	if( GetStateDie() == true )
		return ERROR_MAKESKILL_STATE;

	if( itemSlotNum < INVENTORY_BAG_BEGIN || itemSlotNum > INVENTORY_BAG_END )
		return ERROR_MAKESKILL_ITEM;

	TB_INVENTORY* inventory = SelectInventory( itemSlotNum );
	if( IsInventory( inventory ) == false )
		return ERROR_MAKESKILL_ITEM;

	if( inventory->apply != InventoryApplyNone )
		return ERROR_MAKESKILL_ITEM;

	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
	if( itemDefine == NULL )
		return ERROR_MAKESKILL_ITEM;

	/*-- Item Limit Table(TB_ITEM_LIMIT) 적용
	*/
	TB_ITEM_LIMIT* itemLimit = ITEMMANAGER->GetItemLimit( inventory->itemDefineIdx );
	if( itemLimit != NULL )
	{
		// 종족; TB_ITEM_LIMIT::charRace 와 sPlayerInfo::Race 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charRace != ITEM_RACE_ALL )
		{
			if ( itemLimit->charRace != mPlayerInfo.Race )
				return ERROR_MAKESKILL_ITEM;
		}

		// 성별; TB_ITEM_LIMIT::charGender 와 sPlayerInfo::Gender 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charGender != ITEM_GENDER_ALL )
		{
			if ( itemLimit->charGender != mPlayerInfo.Gender )
				return ERROR_MAKESKILL_ITEM;
		}

		// 직업; TB_ITEM_LIMIT::charJob 와 sPlayerInfo::Job 은 정의는 다르나 같은 값을 사용.
		if( itemLimit->charJob != ITEM_JOB_ALL )
		{
			long charJob = mPlayerInfo.Job - (mPlayerInfo.Job % ITEM_JOB_FIGHTER);
			if ( itemLimit->charJob != charJob )
				return ERROR_MAKESKILL_ITEM;
		}

		// 레벨; 이하일 경우만 제어를 한다.
		if(  itemLimit->charLevel > (BYTE)mPlayerInfo.Level )
			return ERROR_MAKESKILL_ITEM;
	}

	/*-- 아이템 효과 적용 - 스킬과 연결하여 사용한다.
	*/
	TB_ITEM_ABILITY* itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
	if( itemAbility == NULL )
		return ERROR_MAKESKILL_ITEM;

	/// 전문 스킬 아이템인지 확인
	if( !(itemAbility->influence_idx > 0 && (itemDefine->type == ITEM_ETC1 && itemDefine->subType == ITEM_ETC1_MIXSKILL )) )
		return ERROR_MAKESKILL_ITEM;

	unsigned char makeSkill = 0;
	makeSkill = (unsigned char)itemAbility->influence_idx;	// 전문 스킬 인덱스

	/// 이미 보유 하고 있는지 체크
	if( mHeroInfo.mMakeSkill1 == makeSkill )
		return ERROR_MAKESKILL_ALREADY;
	if( mHeroInfo.mMakeSkill2 == makeSkill )
		return ERROR_MAKESKILL_ALREADY;

	/// 빈 공간이 있는지 체크
	unsigned char emptyNum = 0;
	if( mHeroInfo.mMakeSkill1 == 0 )
		emptyNum = 1;
	else if( mHeroInfo.mMakeSkill2 == 0 )
		emptyNum = 2;
	
	if( emptyNum == 0 )
		return ERROR_MAKESKILL_NOTEMPTY;

	unsigned long inventoryIdx = inventory->idx;
	/// 아이템 차감
	if ( inventory->count == 1 )
		RemoveInventory( inventory->number );
	else
		UpdateInventory( inventory, (-1) ); // --inventory->count;

    /// db 저장
	HANDLE handle = NULL;
	MAKESKILL_INSERT* pMakeSkill = (MAKESKILL_INSERT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_MAKESKILL_INSERT );
	pMakeSkill->mCharacterIdx = mObject.index;
	pMakeSkill->mIsFirstSkill = emptyNum == 1 ? true : false;
	pMakeSkill->mMakeSkillIdx = makeSkill;
	pMakeSkill->mInventoryIdx = inventoryIdx;
	pMakeSkill->mRetvalue = -1;
	return (NETWORK2->SendSQL( mConnectionIdx, handle, sizeof(MAKESKILL_INSERT) ) ) ? ERROR_MAKESKILL_SUCCESS : ERROR_MAKESKILL_DBERROR;
}


unsigned char cPlayer::AddRecipe( unsigned short itemSlotNum )
{
	/*-- 아이템 사용시 검사할 내용들.
	*/
	if( GetStateDie() == true )
		return ERROR_MAKESKILL_STATE;

	if( itemSlotNum < INVENTORY_BAG_BEGIN || itemSlotNum > INVENTORY_BAG_END )
		return ERROR_MAKESKILL_ITEM;

	TB_INVENTORY* inventory = SelectInventory( itemSlotNum );
	if( IsInventory( inventory ) == false )
		return ERROR_MAKESKILL_ITEM;

	if( inventory->apply != InventoryApplyNone )
		return ERROR_MAKESKILL_ITEM;

	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
	if( itemDefine == NULL )
		return ERROR_MAKESKILL_ITEM;

	/*-- Item Limit Table(TB_ITEM_LIMIT) 적용
	*/
	TB_ITEM_LIMIT* itemLimit = ITEMMANAGER->GetItemLimit( inventory->itemDefineIdx );
	if( itemLimit != NULL )
	{
		// 종족; TB_ITEM_LIMIT::charRace 와 sPlayerInfo::Race 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charRace != ITEM_RACE_ALL )
		{
			if ( itemLimit->charRace != mPlayerInfo.Race )
				return ERROR_MAKESKILL_ITEM;
		}

		// 성별; TB_ITEM_LIMIT::charGender 와 sPlayerInfo::Gender 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charGender != ITEM_GENDER_ALL )
		{
			if ( itemLimit->charGender != mPlayerInfo.Gender )
				return ERROR_MAKESKILL_ITEM;
		}

		// 직업; TB_ITEM_LIMIT::charJob 와 sPlayerInfo::Job 은 정의는 다르나 같은 값을 사용.
		if( itemLimit->charJob != ITEM_JOB_ALL )
		{
			long charJob = mPlayerInfo.Job - (mPlayerInfo.Job % ITEM_JOB_FIGHTER);
			if ( itemLimit->charJob != charJob )
				return ERROR_MAKESKILL_ITEM;
		}

		// 레벨; 이하일 경우만 제어를 한다.
		if(  itemLimit->charLevel > (BYTE)mPlayerInfo.Level )
			return ERROR_MAKESKILL_ITEM;
	}

	/*-- 아이템 효과 적용 - 스킬과 연결하여 사용한다.
	*/
	TB_ITEM_ABILITY* itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
	if( itemAbility == NULL )
		return ERROR_MAKESKILL_ITEM;

	if( !(itemAbility->influence_idx > 0 && itemDefine->type == ITEM_ETC1 ) )
		return ERROR_MAKESKILL_ITEM;

	/// 퀘스트 의뢰 아이템인지 확인
	if( itemDefine->subType == ITEM_ETC1_RECIPE )
	{
		unsigned long recipeIdx = 0;
		recipeIdx = itemAbility->influence_idx;	// 레시피 인덱스	

		/// 레시피 스크립트 확인
		sMakeSkillScript* pRecipeInfo = MAKESKILLSCRIPT->GetMakeSkill( recipeIdx );
		if( pRecipeInfo == NULL )
			return ERROR_MAKESKILL_SCRIPT;

		/// 보유 전문기술 확인
		if( mHeroInfo.mMakeSkill1 != pRecipeInfo->mMakeSkill && mHeroInfo.mMakeSkill2 != pRecipeInfo->mMakeSkill )
			return ERROR_MAKESKILL_NOTHAVE;

		/// 보유 레시피 인지 확인
		if( mHeroInfo.mMakeSkill1 == pRecipeInfo->mMakeSkill )
		{
			cRecipeGroup::cIterator end = mRecipeGroup1.End();
			if( mRecipeGroup1.Find( recipeIdx ) != end )
				return ERROR_MAKESKILL_ALREADY;
		}
		else
		{
			cRecipeGroup::cIterator end = mRecipeGroup2.End();
			if( mRecipeGroup2.Find( recipeIdx ) != end )
				return ERROR_MAKESKILL_ALREADY;
		}

		unsigned long inventoryIdx = inventory->idx;
		if ( inventory->count == 1 )
			RemoveInventory( inventory->number );
		else
			UpdateInventory( inventory, (-1) ); // --inventory->count;		

		/// db 저장
		HANDLE handle = NULL;
		RECIPE_INSERT* pMakeSkill = (RECIPE_INSERT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_RECIPE_INSERT );
		pMakeSkill->mCharacterIdx = mObject.index;
		pMakeSkill->mMakeSkillIdx = pRecipeInfo->mMakeSkill;	
		pMakeSkill->mRecipeIdx = recipeIdx;
		pMakeSkill->mInventoryIdx = inventoryIdx;
		pMakeSkill->retvalue = -1;
		return (NETWORK2->SendSQL( mConnectionIdx, handle, sizeof(RECIPE_INSERT) ) ) ? ERROR_MAKESKILL_SUCCESS : ERROR_MAKESKILL_DBERROR;

	}
	else if( itemDefine->subType == ITEM_ETC1_RECIPEGROUP )
	{
		unsigned long groupIdx = 0;
		groupIdx = itemAbility->influence_idx;	// 레시피 그룹 인덱스	

		/// 레시피 스크립트 확인
		cAry* pRecipeAry = MAKESKILLSCRIPT->GetRecipeGroup( groupIdx );
		if( pRecipeAry == NULL )
			return ERROR_MAKESKILL_SCRIPT;

		HANDLE handle = NULL;
		RECIPE_GROUP_INSERT* pMakeSkill = (RECIPE_GROUP_INSERT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_RECIPE_GROUP_INSERT );
		pMakeSkill->mRowCnt = 0;

		for( unsigned long i = 0 ; i < pRecipeAry->GetSize() ; ++i )
		{
			sRecipeGroupInfo* pGroupInfo = (sRecipeGroupInfo*)(*pRecipeAry)[i];
			if( pGroupInfo == NULL )
				continue;

			/// 보유 레시피 인지 확인
			if( mHeroInfo.mMakeSkill1 == pGroupInfo->mMakeSkill )
			{
				cRecipeGroup::cIterator end = mRecipeGroup1.End();
				if( mRecipeGroup1.Find( pGroupInfo->mRecipeIdx ) == end )
				{
					pMakeSkill->mRecipeGroup[pMakeSkill->mRowCnt].mRecipeIdx = pGroupInfo->mRecipeIdx;
					pMakeSkill->mRecipeGroup[pMakeSkill->mRowCnt].mMakeSkill = pGroupInfo->mMakeSkill;	
					++pMakeSkill->mRowCnt;
				}
			}
			else if( mHeroInfo.mMakeSkill2 == pGroupInfo->mMakeSkill )
			{
				cRecipeGroup::cIterator end = mRecipeGroup2.End();
				if( mRecipeGroup2.Find( pGroupInfo->mRecipeIdx ) == end )
				{
					pMakeSkill->mRecipeGroup[pMakeSkill->mRowCnt].mRecipeIdx = pGroupInfo->mRecipeIdx;
					pMakeSkill->mRecipeGroup[pMakeSkill->mRowCnt].mMakeSkill = pGroupInfo->mMakeSkill;	
					++pMakeSkill->mRowCnt;
				}
			}
		}

		unsigned long inventoryIdx = inventory->idx;
		if ( inventory->count == 1 )
			RemoveInventory( inventory->number );
		else
			UpdateInventory( inventory, (-1) ); // --inventory->count;		

		/// db 저장
		pMakeSkill->mCharacterIdx = mObject.index;
		pMakeSkill->mInventoryIdx = inventoryIdx;
		pMakeSkill->mRetvalue = -1;
		return (NETWORK2->SendSQL( mConnectionIdx, handle, pMakeSkill->GetMsgLength() ) ) ? ERROR_MAKESKILL_SUCCESS : ERROR_MAKESKILL_DBERROR;

	}
	else
		return ERROR_MAKESKILL_ITEM;


}


void cPlayer::SetMakeSkill( MAKESKILL_INSERT* pInsert )
{
	bool IsfirstSkill = false;
	unsigned char makeSkill = 0;

    if( pInsert != NULL )
	{
		IsfirstSkill = pInsert->mIsFirstSkill;
		makeSkill = pInsert->mMakeSkillIdx;        		
	}
	else
		NETWORK2->PostServerEvent("cPlayer[%d]::SetMakeSkill pInsert == NULL", mObject.index );

	if( IsfirstSkill == true )
		mHeroInfo.mMakeSkill1 = makeSkill;
	else
		mHeroInfo.mMakeSkill2 = makeSkill;
    
    HANDLE handle = NULL;
	MSG_RES_MAKESKILL_ADD* sendMsg = (MSG_RES_MAKESKILL_ADD*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, 
			NM_MAKESKILL, NM_MAKESKILL_ADD_RES );
	if( sendMsg != NULL )
	{
		sendMsg->mMakeSkill = makeSkill;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_MAKESKILL_ADD) );
	}
}


unsigned char cPlayer::ReqDelMakeSkill( unsigned char makeSkill )
{
	if( IsMakeSkill( makeSkill ) == false )
		return ERROR_MAKESKILL_NOTHAVE;

	/// db 저장
	HANDLE handle = NULL;
	MAKESKILL_DELETE* pMakeSkill= (MAKESKILL_DELETE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_MAKESKILL_DELETE );
	pMakeSkill->mCharacterIdx = mObject.index;
	pMakeSkill->mMakeSkillIdx = makeSkill;
	pMakeSkill->mRetvalue = -1;
	return (NETWORK2->SendSQL( mConnectionIdx, handle, sizeof(MAKESKILL_DELETE) ) ) ? ERROR_MAKESKILL_SUCCESS : ERROR_MAKESKILL_DBERROR;
}


void cPlayer::DelMakeSkill( unsigned char makeSkillIdx )
{
	if( mHeroInfo.mMakeSkill1 == makeSkillIdx )
	{
		mHeroInfo.mMakeSkill1 = 0;        
		mRecipeGroup1.Clear();
	}
	else if( mHeroInfo.mMakeSkill2 == makeSkillIdx )
	{
		mHeroInfo.mMakeSkill2 = 0;
		mRecipeGroup2.Clear();
	}

	HANDLE handle  = NULL;
	MSG_RES_MAKESKILL_DELETE* sendMsg = (MSG_RES_MAKESKILL_DELETE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_MAKESKILL, NM_MAKESKILL_DEL_RES );
	if( sendMsg != NULL )
	{
		sendMsg->mMakeSkill = makeSkillIdx;
		sendMsg->ErrorCode = ERROR_MAKESKILL_SUCCESS;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_MAKESKILL_DELETE) );
	}
}


void cPlayer::SetRecipe( unsigned char makeSkill, unsigned long recipeIdx )
{
	if( makeSkill == mHeroInfo.mMakeSkill1 )
	{
		if( mRecipeGroup1.Insert( recipeIdx, 0 ) == false )
			NETWORK2->PostServerEvent("cPlayer[%d,%d]::SetRecipe mRecipeGroup1.Insert( %d ) == false", mObject.index, makeSkill, recipeIdx );
	}
	else if( makeSkill == mHeroInfo.mMakeSkill2 )
	{
		if( mRecipeGroup2.Insert( recipeIdx, 0 ) == false )
			NETWORK2->PostServerEvent("cPlayer[%d,%d]::SetRecipe mRecipeGroup2.Insert( %d ) == false", mObject.index, makeSkill, recipeIdx );
	}
	else
		NETWORK2->PostServerEvent("cPlayer[%d,%d]::SetRecipe else( %d ) == false", mObject.index, makeSkill, recipeIdx );

	HANDLE handle = NULL;
	MSG_RES_MAKESKILL_RECIPE_ADD * sendMsg = (MSG_RES_MAKESKILL_RECIPE_ADD *)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, 
		NM_MAKESKILL, NM_MAKESKILL_RECIPE_ADD_RES );
	if( sendMsg != NULL )
	{
		sendMsg->mGroupInfo[sendMsg->mCount].mMakeSkill = makeSkill;
		sendMsg->mGroupInfo[sendMsg->mCount].mRecipeIdx = recipeIdx;
		sendMsg->mCount = 1;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_MAKESKILL_RECIPE_ADD) );
	}
}


void cPlayer::SetRecipeGroup( sRecipeGroupInfo* groupInfo, unsigned long cnt )
{
	HANDLE handle = NULL;
	MSG_RES_MAKESKILL_RECIPE_ADD* sendMsg = (MSG_RES_MAKESKILL_RECIPE_ADD*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, 
		NM_MAKESKILL, NM_MAKESKILL_RECIPE_ADD_RES );
	if( sendMsg != NULL )
	{
        for( unsigned long i = 0 ; i < cnt ; ++i )
		{
			if( groupInfo[i].mMakeSkill == mHeroInfo.mMakeSkill1 )
			{
				if( mRecipeGroup1.Insert( groupInfo[i].mRecipeIdx, 0 ) == false )
					NETWORK2->PostServerEvent("cPlayer[%d,%d]::SetRecipe mRecipeGroup1.Insert( %d ) == false", mObject.index, groupInfo[i].mMakeSkill, groupInfo[i].mRecipeIdx );
			}
			else if( groupInfo[i].mMakeSkill == mHeroInfo.mMakeSkill2 )
			{
				if( mRecipeGroup2.Insert( groupInfo[i].mRecipeIdx, 0 ) == false )
					NETWORK2->PostServerEvent("cPlayer[%d,%d]::SetRecipe mRecipeGroup2.Insert( %d ) == false", mObject.index, groupInfo[i].mMakeSkill, groupInfo[i].mRecipeIdx );
			}
			else
			{
				NETWORK2->PostServerEvent("cPlayer[%d,%d]::SetRecipe else( %d ) == false", mObject.index, groupInfo[i].mMakeSkill, groupInfo[i].mRecipeIdx );
				continue;
			}

			sendMsg->mGroupInfo[sendMsg->mCount].mMakeSkill = groupInfo[i].mMakeSkill;
			sendMsg->mGroupInfo[sendMsg->mCount].mRecipeIdx = groupInfo[i].mRecipeIdx;
			++sendMsg->mCount;
		}

		NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength() );
	}
}


bool cPlayer::InitMakeSkill( MAKESKILL_SELECT* pMakeSkill )
{
	TB_MAKESKILL* pTable = NULL;
	for( long i = 0 ; i < pMakeSkill->mRowCount ; ++i )
	{
		pTable = &pMakeSkill->mTable[i];
		if( pTable == NULL )
		{
			NETWORK2->PostServerEvent("cPlayer[%d]::InitMakeSkill pTable == NULL", mObject.index );
			continue;
		}

		if( pTable->mMakeSkill != 0 && pTable->mMakeSkill == mHeroInfo.mMakeSkill1 )		/// 제조스킬 비교
		{
			unsigned long endTime = ( pTable->mCoolTime * SECOND ) + NETWORK2->GetAccumTime();
			if( mRecipeGroup1.Insert( pTable->mRecipeIdx, endTime ) == false )
			{
				NETWORK2->PostServerEvent("cPlayer[%d]::InitMakeSkill mRecipeGroup1[%d].Insert[%d]",
					mObject.index, mHeroInfo.mMakeSkill1, pTable->mRecipeIdx );
				return false;
			}
		}
		else if( pTable->mMakeSkill != 0 && pTable->mMakeSkill == mHeroInfo.mMakeSkill2 )
		{
			unsigned long endTime = ( pTable->mCoolTime * SECOND ) + NETWORK2->GetAccumTime();
			if( mRecipeGroup2.Insert( pTable->mRecipeIdx, endTime ) == false )
			{
				NETWORK2->PostServerEvent("cPlayer[%d]::InitMakeSkill mRecipeGroup2.Insert[%d]",
					mObject.index, mHeroInfo.mMakeSkill2, pTable->mRecipeIdx );
				return false;
			}
		}
		else
		{
			NETWORK2->PostServerEvent("cPlayer[%d]::InitMakeSkill pTable->mMakeSkill[%d] != ERROR", 
				mObject.index, pTable->mMakeSkill );
			continue;
		}
	}

	SendMsgRecipeList();

	return true;
}


void cPlayer::SendMsgRecipeList()
{
	HANDLE handle  = NULL;

	cRecipeGroup::cIterator start = mRecipeGroup1.Begin();
	cRecipeGroup::cIterator end = mRecipeGroup1.End();
	MSG_RES_MAKESKILL_LIST* sendMsg = (MSG_RES_MAKESKILL_LIST*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, 
		NM_MAKESKILL, NM_MAKESKILL_LIST1_RES );
	if( sendMsg != NULL )
	{
		for( unsigned short i = 0 ; start != end ; ++start, ++i )
		{
			sendMsg->mRecipeInfo[i].mRecipeIdx = (*start).mFirst;
			unsigned long endCoolTime = (*start).mSecond;
			unsigned long accumTime = NETWORK2->GetAccumTime();
			if( endCoolTime > accumTime )
				sendMsg->mRecipeInfo[i].mCoolTime = endCoolTime - accumTime;
			else
				sendMsg->mRecipeInfo[i].mCoolTime = 0;
		}

		sendMsg->mCount = i;
		NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
	}

	start = mRecipeGroup2.Begin();
	end = mRecipeGroup2.End();
	sendMsg = (MSG_RES_MAKESKILL_LIST*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_MAKESKILL, NM_MAKESKILL_LIST2_RES );
	if( sendMsg != NULL )
	{
		for( short i = 0 ; start != end ; ++start, ++i )
		{
			sendMsg->mRecipeInfo[i].mRecipeIdx = (*start).mFirst;
			unsigned long endCoolTime = (*start).mSecond;
			unsigned long accumTime = NETWORK2->GetAccumTime();
			if( endCoolTime > accumTime )
				sendMsg->mRecipeInfo[i].mCoolTime = endCoolTime - accumTime;
			else
				sendMsg->mRecipeInfo[i].mCoolTime = 0;
		}

		sendMsg->mCount = i;
		NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
	}
}


bool cPlayer::IsMakeSkill( unsigned char makeSkillIdx )
{
	if( makeSkillIdx == 0 )
		return false;

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

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

	return false;
}


int cPlayer::ItemMixReq( unsigned char makeSkill, unsigned long mixRecipeIdx )
{
	/// 스크립트 체크
	sMakeSkillScript* pScript = MAKESKILLSCRIPT->GetMakeSkill( mixRecipeIdx );
	if( pScript == NULL )
	{
		NETWORK2->PostServerEvent( "cPlayer[%d]::ItemMixStart pScript[%d] == NULL", mObject.index, mixRecipeIdx );
		return ERROR_MAKESKILL_START_SCRIPT;
	}

	/// 보유 여부 체크
	if( makeSkill == mHeroInfo.mMakeSkill1 )
	{
		/// 쿨타임 체크
		cRecipeGroup::cIterator find = mRecipeGroup1.Find( mixRecipeIdx );
		cRecipeGroup::cIterator end = mRecipeGroup1.End();
        if( find == end || (*find).mSecond > NETWORK2->GetAccumTime() )
			return ERROR_MAKESKILL_START_COOLTIME;
	}
	else if( makeSkill == mHeroInfo.mMakeSkill2 )
	{
		cRecipeGroup::cIterator find = mRecipeGroup2.Find( mixRecipeIdx );
		cRecipeGroup::cIterator end = mRecipeGroup2.End();
		if( find == end || (*find).mSecond > NETWORK2->GetAccumTime() )
			return ERROR_MAKESKILL_START_COOLTIME;
	}
	else
		return ERROR_MAKESKILL_START_HAVESKILL;

	/// 상태 변경
	if( ChangeState( eOBJECT_STATE_STOP ) == false )
		return ERROR_MAKESKILL_START_STATE;

	SetStateStop( eSTOP_ITEMMIX );

	/// 조합 정보 기록
	mMixRecipeIdx = mixRecipeIdx;
	mMixMakeSkill = makeSkill;
	mItemMixEndTime = NETWORK2->GetAccumTime() + pScript->mMakeTime;
	return ERROR_MAKESKILL_START_SUCCESS;
}


int cPlayer::ItemMixEnd()
{
	try
	{
		if( mItemMixEndTime > NETWORK2->GetAccumTime() + 1000 )
			return ERROR_MAKESKILL_END_TIME;

		/// 상태 체크
		if( GetState() != eOBJECT_STATE_STOP )
			return ERROR_MAKESKILL_END_STATE;

		if( GetStateStop() != eSTOP_ITEMMIX )
			return ERROR_MAKESKILL_END_STATE;

		/// 아이템 메니저에 조합요청
		sMakeSkillScript* script = MAKESKILLSCRIPT->GetMakeSkill( mMixRecipeIdx );
		if ( script == NULL )
			return ERROR_MAKESKILL_END_ITEM;

		tArray<void*>* array = script->mpMaterial;

		if ( array == NULL )
			return ERROR_MAKESKILL_END_ITEM;

		// 인벤토리 재료 검사.
		for ( unsigned long i = 0; i < array->GetSize( ); ++i )
		{
			sMaterialScript* material  = (sMaterialScript*)(*array)[i];
			PerItemCount*    itemCount = ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, material->mItemIdx );

			if ( itemCount != NULL )
			{
				if ( itemCount->bag < material->mItemCnt )
					return ERROR_MAKESKILL_END_ITEM;
			}
			else
				return ERROR_MAKESKILL_END_ITEM;
		}

		// 인벤토리 영역 검사 & 조합 확률 처리.
		unsigned long*  makeItem;	// 캐스팅 포인터 - 조합결과 아이템
		unsigned char*  makeCount;	// "             - 조합결과 수량
		unsigned short* makeRate;   // "             - 조합     확률

		int             sumRate;	// 합계 확률.
		int             addRate;	// 누적 확률.
		int             rndRate;	// 램덤 확률.
		long            mixItem;	// 결과.
		short           mixCount;    // 결과.

		makeItem  = script->mMakeItemIdx;
		makeCount = script->mMakeCnt;
		makeRate  = script->mMakeRate;
		sumRate   = 0;

		for ( int i = 0; (i < MAKESKILL_MAKE_ITEM_CNT) && ((*makeItem) > 0) && ((*makeCount) > 0); i++, makeItem++, makeCount++, makeRate++ )
		{
			sumRate += (*makeRate);
		}

		makeItem  = script->mMakeItemIdx;
		makeCount = script->mMakeCnt;
		makeRate  = script->mMakeRate;

		addRate  = 0;
		rndRate  = rand( ) % sumRate;
		mixItem  = 0;
		mixCount = 0;

		for ( int i = 0; (i < MAKESKILL_MAKE_ITEM_CNT) && ((*makeItem) > 0) && ((*makeCount) > 0); i++, makeItem++, makeCount++, makeRate++ )
		{
			if ( CheckInventory( (*makeItem), (*makeCount) ) == false )
				return ERROR_MAKESKILL_END_ITEM;

			addRate += (*makeRate);

			if ( rndRate < addRate )
			{
				mixItem  = (*makeItem);
				mixCount = (*makeCount);
			}
		}

		if ( !( mixItem > 0 && mixCount > 0 ) )
			return ERROR_MAKESKILL_END_ITEM;

		HANDLE    handle   = NULL;
		ITEM_MIX* itemMix  = (ITEM_MIX*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_MIX );
		long      length   = sizeof(ITEM_MIX) - sizeof(itemMix->table);

		TB_INVENTORY* table    = itemMix->table;
		long&         rowCount = itemMix->rowCount;

		itemMix->characterIdx = mObject.index;

		// 재료 아이템 차감.
		for ( unsigned long i = 0; i < array->GetSize( ); ++i )
		{
			sMaterialScript* material    = (sMaterialScript*)(*array)[i];
			TB_INVENTORY*    tempInv     = SelectInventory( INVENTORY_BAG_BEGIN );
			short            remainCount = material->mItemCnt;
			short            shortCount;

			for ( short i = INVENTORY_BAG_BEGIN; (i <= INVENTORY_BAG_END) && (remainCount > 0); i++, tempInv++ )
			{
				if ( (long)material->mItemIdx == tempInv->itemDefineIndex )
				{
					shortCount  = UpdateInventory( tempInv, (-remainCount) );
					remainCount = remainCount + shortCount;

					table->idx             = tempInv->idx;
					table->itemDefineIdx   = tempInv->itemDefineIdx;
					table->itemDefineIndex = tempInv->itemDefineIndex;
					table->number          = tempInv->number;
					table->count           = shortCount;
					table++;

					if ( tempInv->count == 0 )
						RemoveInventory( tempInv );

					rowCount++;
				}
			}
		}

		// 조합 아이템 생성.
		{
			// 겹치기 검사 및 처리.
			TB_ITEM_DEFINE* itemDefine  = ITEMMANAGER->GetItemDefineByIndex( mixItem );
			TB_INVENTORY*   tempInv     = SelectInventory( INVENTORY_BAG_BEGIN );
			short           remainCount = mixCount;
			short           shortCount;

			for ( short i = INVENTORY_BAG_BEGIN; i <= INVENTORY_BAG_END && remainCount > 0; i++, tempInv++ )
			{
				if ( tempInv->itemDefineIndex == mixItem )
				{
					if ( tempInv->count < itemDefine->capacity )
					{
						shortCount  = UpdateInventory( tempInv, remainCount );
						remainCount = remainCount - shortCount;

						table->idx             = tempInv->idx;
						table->itemDefineIdx   = tempInv->itemDefineIdx;
						table->itemDefineIndex = tempInv->itemDefineIndex;
						table->number          = tempInv->number;
						table->count           = (+shortCount);
						table++;

						rowCount++;
					}
				}
			}

			// 생성 검사 및 등록.
			short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );

			while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
			{
				CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

				table->idx             = 0;
				table->itemDefineIdx   = itemDefine->idx;
				table->itemDefineIndex = itemDefine->index;
				table->number          = number;
				table->count           = (+shortCount);
				table++;

				rowCount++;

				remainCount = remainCount - shortCount;
				number      = GetEmptyBagNumber( number+1 );
			}
		}

		length += sizeof(TB_INVENTORY) * itemMix->rowCount;
		NETWORK2->SendSQL( mConnectionIdx, handle, length );

	}
	catch ( int error )
	{
		mItemMixEndTime = 0;
		mMixRecipeIdx = 0;
		mMixMakeSkill = 0;
		
		return error;
	}

	return ERROR_MAKESKILL_END_SUCCESS;
}


void cPlayer::ItemMixCoolTime()
{
	/// 쿨타임 변경
	sMakeSkillScript* pScript = MAKESKILLSCRIPT->GetMakeSkill( mMixRecipeIdx );
	if( pScript == NULL )
		return;

	if( mMixMakeSkill == mHeroInfo.mMakeSkill1 )
	{
		cRecipeGroup::cIterator find = mRecipeGroup1.Find( mMixRecipeIdx );
		cRecipeGroup::cIterator end = mRecipeGroup1.End();
		if( find != end )
			(*find).mSecond = NETWORK2->GetAccumTime() + pScript->mNextMakeTime;
	}
	else if( mMixMakeSkill == mHeroInfo.mMakeSkill2 )
	{
		cRecipeGroup::cIterator find = mRecipeGroup2.Find( mMixRecipeIdx );
		cRecipeGroup::cIterator end = mRecipeGroup2.End();
		if( find != end )
			(*find).mSecond = NETWORK2->GetAccumTime() + pScript->mNextMakeTime;
	}

	mItemMixEndTime = 0;
	mMixRecipeIdx = 0;
	mMixMakeSkill = 0;
}


void cPlayer::ItemMixCancel()
{
	mItemMixEndTime = 0;
	mMixRecipeIdx = 0;
	mMixMakeSkill = 0;
}


bool cPlayer::SaveRecipeCoolTime( RECIPE_COOLTIME* pRecipeCoolTime )
{
	pRecipeCoolTime->mCharacterIdx = mObject.index;
	pRecipeCoolTime->mRetvalue = -1;

	if( mHeroInfo.mMakeSkill1 != 0 )
	{
		cRecipeGroup::cIterator begin = mRecipeGroup1.Begin();
		cRecipeGroup::cIterator end = mRecipeGroup1.End();

		unsigned long row = 0;
		for(  ; begin != end ; ++begin )
		{
			TB_RECIPE_COOLTIME* pCoolTime =	&pRecipeCoolTime->mTable[row];

			if( pCoolTime != NULL && (*begin).mSecond > NETWORK2->GetAccumTime() )
			{
				pCoolTime->mMakeSkill = mHeroInfo.mMakeSkill1;
				pCoolTime->mRecipeIdx = (*begin).mFirst;
				pCoolTime->mLeftCoolTime = (*begin).mSecond - NETWORK2->GetAccumTime();
				++row;				
			}
		}

		pRecipeCoolTime->mRowCount = row;
	}

	if( mHeroInfo.mMakeSkill2 != 0 )
	{
		cRecipeGroup::cIterator begin = mRecipeGroup2.Begin();
		cRecipeGroup::cIterator end = mRecipeGroup2.End();

		unsigned long row = 0;
		for(  ; begin != end ; ++begin )
		{
			TB_RECIPE_COOLTIME* pCoolTime =	&pRecipeCoolTime->mTable[row];

			if( pCoolTime != NULL && (*begin).mSecond > NETWORK2->GetAccumTime() )
			{
				pCoolTime->mMakeSkill = mHeroInfo.mMakeSkill2;
				pCoolTime->mRecipeIdx = (*begin).mFirst;
				pCoolTime->mLeftCoolTime = (*begin).mSecond - NETWORK2->GetAccumTime();
				++row;				
			}
		}

		pRecipeCoolTime->mRowCount = pRecipeCoolTime->mRowCount + row;
	}

	return true;

}

bool cPlayer::SkillPointMinus( unsigned short minusSP )
{
	if( mHeroInfo.SkillPointRemain - minusSP < 0 )
	{
		return false;
	}

	mHeroInfo.SkillPointRemain = mHeroInfo.SkillPointRemain - minusSP;

	return true;
}


bool cPlayer::SkillPointMinusRestore( unsigned short minusSP )
{
	mHeroInfo.SkillPointRemain = mHeroInfo.SkillPointRemain + minusSP;

	return true;
}


bool cPlayer::SkillPointPlus( unsigned short plusSP )
{
	if( mHeroInfo.SkillPointRemain + plusSP > USHRT_MAX )
	{
		return false;
	}

	mHeroInfo.SkillPointRemain = mHeroInfo.SkillPointRemain + plusSP;
	mHeroInfo.SkillPointTotal = mHeroInfo.SkillPointTotal + plusSP;

	//HANDLE                handle  = NULL;
	//CHARACTER_SKILLLEVEL* characterSkillLevel = (CHARACTER_SKILLLEVEL*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_SKILLLEVEL );
	//if ( characterSkillLevel == NULL )
	//{
	//	assert(NULL);
	//	NETWORK2->PostServerEvent("cPlayer[%d]::SkillPointPlus characterSkillLevel == NULL", mObject.index );
	//	return false;
	//}

	//characterSkillLevel->idx = mObject.index;
	//characterSkillLevel->skillLevel = mHeroInfo.SkillLevel;
	//characterSkillLevel->levelUpPoint = 0;
	//characterSkillLevel->skillPoint = mHeroInfo.SkillPointRemain;
	//characterSkillLevel->skillTotal = mHeroInfo.SkillPointTotal;
	//characterSkillLevel->skillUpPoint = plusSP;
	//characterSkillLevel->retvalue = -1;

	//NETWORK2->SendSQL( handle, sizeof(CHARACTER_SKILLLEVEL) );

	SendSkillPoint();

	return true;
}


void cPlayer::SetSkillReset( unsigned short skillPoint, TB_CHARACTER_SKILL* pSkill, long cnt )
{
	mHeroInfo.SkillPointRemain = skillPoint;

	HANDLE handle = NULL;
	MSG_SYN_PLAYER_SKILLRESET* sendMsg = (MSG_SYN_PLAYER_SKILLRESET*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, 
		NM_PLAYER, NM_PLAYER_SKILLRESET_SYN );
	sendMsg->ErrorCode = ERROR_SKILLRESET_SUCCESS;
	sendMsg->mRowCount = cnt;
	sendMsg->mSkillPoint = skillPoint;
	for( long i = 0 ; i < cnt ; ++i )
	{
		SKILLMANAGER->AddSkillReset( mObject.index, pSkill->mSkillIdx, pSkill->mSkillStep );
		sendMsg->mTable[i].mSkillIdx = pSkill->mSkillIdx;
		sendMsg->mTable[i].mSkillStep = pSkill->mSkillStep;
	}
	NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength() );

	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );
}


void cPlayer::SendSkillPoint()
{
	HANDLE                handle  = NULL;
	MSG_RES_PLAYER_SP* sendMsg = (MSG_RES_PLAYER_SP*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	if ( sendMsg != NULL )
	{
		unsigned long length = sizeof(MSG_RES_PLAYER_SP) - sizeof(sendMsg->usedSP);

		sendMsg->Category    = NM_PLAYER;
		sendMsg->Protocol    = NM_PLAYER_SP_RES;
		sendMsg->mSP		 = mHeroInfo.SkillPointRemain;
		sendMsg->mTotalSP	 = mHeroInfo.SkillPointTotal;

		unsigned short* list = sendMsg->usedSP;
		for( unsigned long i = 0; i < mJobUsedSpArr.GetSize(); ++i, ++list )
		{
			*list = mJobUsedSpArr[i];
			sendMsg->rowCount++;
		}

		length += (sendMsg->rowCount * sizeof(sendMsg->usedSP));
		NETWORK2->SendMsgRoot( handle, length );
	}
}


bool cPlayer::MapChange( unsigned long posIdx )
{
	/// 맵이동중 맵이동이 올수없음
	if( mMapChange == true )
		return false;

	/// 메세지 대기상태 체크
	if( IsRequestRejection() == true )
		return false;

	float tempRange = OBJECTMANAGER->ObjectSizeRange( this, NULL, 0 );
	// 이동 좌표를 얻어옴
	sTargetPos targetPos = STAGESCRIPT->GetStageChangePos( mObjectPos.x, mObjectPos.y, mMapNumber, tempRange, posIdx );

	/// 이동 좌표 오류
	if( targetPos.mMapNumber == 0 )
		return false;

	/// 맵이동 가능인지 현재 상태 체크
	if( IsChangeState( eOBJECT_STATE_MOVE ) == false )
		return false;

	mMapChange = true;

	/// 이동 지역 설정
	SetMapTargetPos( targetPos );

	InfluenceProcessStop();

	return true;

}


bool cPlayer::CheatMapChange( unsigned short mapNumber, float x, float y )
{
	/// 맵이동중 맵이동이 올수없음
	if( mMapChange == true )
		return false;

	/// 메세지 대기상태 체크
	if( IsRequestRejection() == true )
		return false;

	/// 이동 좌표 오류
	if( mapNumber < MAP_MIN || MAP_MAX < mapNumber )
		return false;

	/// 이동 위치 체크
	float mapMaxSize = AIMANAGER->GetPathMaxSideSize( mapNumber );
	if( x < 0 || x > mapMaxSize || y < 0 || y > mapMaxSize )
		return false;

	/// 맵이동 가능인지 현재 상태 체크
	if( IsChangeState( eOBJECT_STATE_MOVE ) == false )
		return false;

	// 이동 좌표를 설정
	sTargetPos targetPos;
	targetPos.mMapNumber = mapNumber;
	targetPos.mPosX = x;
	targetPos.mPosY = y;
	targetPos.mRotAngle = 0;

	/// 좌표값이 0( 입력없음)이면 첫번째 맵이동 포인트로 이동
	if( FloatToInt( x ) == 0 && FloatToInt( y ) == 0 )
	{
		tPointerArray<void*>* ary = (tPointerArray<void*>*)STAGESCRIPT->GetMapChangeDestArr( mapNumber );
		if( ary->GetSize() == 0 )
			return false;

		sStageChangePos* pScript = (sStageChangePos*)((*ary)[0]);
		if( pScript == NULL )
			return false;
			
		targetPos.mPosX = pScript->mPosX;
		targetPos.mPosY = pScript->mPosY;				
	}

	/// 이동 지역 설정
	SetMapTargetPos( targetPos );

	InfluenceProcessStop();
	
	mMapChange = true;

	return true;

}


bool cPlayer::PvPMapChange( unsigned short mapNumber, unsigned short mapDataNumber, unsigned char teamType, float x, float y )
{
	/// 맵이동중 맵이동이 올수없음
	if( mMapChange == true )
		return false;

	/// 메세지 대기상태 체크
	if( IsRequestRejection() == true )
		return false;

	/// 이동 좌표 오류
	if( mapNumber < MAP_MIN || MAP_MAX < mapNumber )
		return false;

	/// 이동 위치 체크
	float mapMaxSize = AIMANAGER->GetPathMaxSideSize( mapNumber );
	if( x < 0 || x > mapMaxSize || y < 0 || y > mapMaxSize )
		return false;

	/// 맵이동 가능인지 현재 상태 체크
	if( IsChangeState( eOBJECT_STATE_MOVE ) == false )
		return false;

	// 이동 좌표를 설정
	sTargetPos targetPos;
	targetPos.mMapNumber = mapNumber;
	targetPos.mPosX = x;
	targetPos.mPosY = y;
	targetPos.mRotAngle = 0;

	/// 이동 지역 설정
	SetMapTargetPos( targetPos );

	InfluenceProcessStop();

	mPvPDMIdx = mapNumber;
	mPlayerExrInfo.mIsPvPJoin = true;
	mPlayerExrInfo.mPvPDMTeamType = teamType;
	mPvPMapDataNumber = mapDataNumber;

	mPvPJoinBeforeMapIdx = mMapNumber;
	mPvPJoinBeforPos = mObjectPos;

	mMapChange = true;

	return true;
}

bool cPlayer::IsItemMapChange( unsigned long posIdx )
{
	/// 맵이동중 맵이동이 올수없음
	if( mMapChange == true )
	{
		NETWORK2->PostServerEvent("cPlayer::ItemMapChange[%d,%d] mMapChange == true", mObject.index, posIdx);
		return false;
	}

	/// 메세지 대기상태 체크
	if( IsRequestRejection() == true )
	{
		NETWORK2->PostServerEvent("cPlayer::ItemMapChange[%d,%d] IsRequestRejection == true", mObject.index, GetRequestRejection());
		return false;
	}

	sTargetPos targetPos = STAGESCRIPT->GetStageChangePos( posIdx );

	/// 이동 좌표 오류
	if( targetPos.mMapNumber == 0 )
	{
		NETWORK2->PostServerEvent("cPlayer::ItemMapChange[%d] targetPos.mMapNumber == 0", mObject.index );
		return false;
	}

	if( AIMANAGER->IsPassible( targetPos.mMapNumber, targetPos.mPosX, targetPos.mPosY, mObject ) == false )
	{
		return false;
	}

	/// 맵이동 가능인지 현재 상태 체크
	if( IsChangeState( eOBJECT_STATE_MOVE ) == false )
	{
		NETWORK2->PostServerEvent("cPlayer::ItemMapChange[%d] IsChangeState( eOBJECT_STATE_MOVE ) == false", mObject.index );
		return false;
	}

	return true;
}

bool cPlayer::QuestMapChange( unsigned long posIdx )
{
	/// 맵이동중 맵이동이 올수없음
	if( mMapChange == true )
	{
		NETWORK2->PostServerEvent("cPlayer::QuestMapChange[%d,%d] mMapChange == true", mObject.index, posIdx);
		return false;
	}

	/// 메세지 대기상태 체크
	if( IsRequestRejection() == true )
	{
		NETWORK2->PostServerEvent("cPlayer::QuestMapChange[%d,%d] IsRequestRejection == true", mObject.index, GetRequestRejection());
		return false;
	}

	sTargetPos targetPos = STAGESCRIPT->GetStageChangePos( posIdx );

	/// 이동 좌표 오류
	if( targetPos.mMapNumber == 0 )
	{
		NETWORK2->PostServerEvent("cPlayer::QuestMapChange[%d] targetPos.mMapNumber == 0", mObject.index );
		return false;
	}

	if( AIMANAGER->IsPassible( targetPos.mMapNumber, targetPos.mPosX, targetPos.mPosY, mObject ) == false )
	{
		return false;
	}

	/// 맵이동 가능인지 현재 상태 체크
	if( !(GetState() == eOBJECT_STATE_IDLE || GetState() == eOBJECT_STATE_MOVE || 
		( GetState() == eOBJECT_STATE_STOP && GetStateStop() == eSTOP_NPCSPEECH )) )
	{
		NETWORK2->PostServerEvent("cPlayer::QuestMapChange[%d] GetState(%d) GetStateStop(%d)", mObject.index, GetState(), GetStateStop() );
		return false;
	}

	/// NPC 대화상태일때 강제로 풀기
	if( GetState() == eOBJECT_STATE_STOP && GetStateStop() == eSTOP_NPCSPEECH )
	{
		/// 대화중인 npc인덱스를 제거
		SetNpcIdx( 0 );

		if( ChangeState( eOBJECT_STATE_IDLE ) == true )
		{
			SetStateStop( eSTOP_NONE );

			HANDLE handle  = NULL;
			MSGROOT* sendMsg = (MSGROOT*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
			if ( sendMsg != NULL )
			{
				sendMsg->Category = NM_NPC;
				sendMsg->Protocol = NM_NPC_CLOSEFORCE_RES;
				NETWORK2->SendMsgRoot( handle, sizeof(MSGROOT) );
			}		
		}
	}
	mMapChange = true;

	/// 이동 지역 설정
	SetMapTargetPos( targetPos );

	InfluenceProcessStop();
	return true;
}

bool cPlayer::ItemMapChange( unsigned long posIdx )
{
	/// 맵이동중 맵이동이 올수없음
	if( mMapChange == true )
	{
		NETWORK2->PostServerEvent("cPlayer::ItemMapChange[%d,%d] mMapChange == true", mObject.index, posIdx);
		return false;
	}

	/// 메세지 대기상태 체크
	if( IsRequestRejection() == true )
	{
		NETWORK2->PostServerEvent("cPlayer::ItemMapChange[%d,%d] IsRequestRejection == true", mObject.index, GetRequestRejection());
		return false;
	}

	sTargetPos targetPos = STAGESCRIPT->GetStageChangePos( posIdx );

	/// 이동 좌표 오류
	if( targetPos.mMapNumber == 0 )
	{
		NETWORK2->PostServerEvent("cPlayer::ItemMapChange[%d] targetPos.mMapNumber == 0", mObject.index );
		return false;
	}

	if( AIMANAGER->IsPassible( targetPos.mMapNumber, targetPos.mPosX, targetPos.mPosY, mObject ) == false )
	{
		return false;
	}

	/// 맵이동 가능인지 현재 상태 체크
	if( GetState() != eOBJECT_STATE_ATTACK )
	{
		if( GetState() != eOBJECT_STATE_DIE )
		NETWORK2->PostServerEvent("cPlayer[%d,%d,%d]::ItemMapChange[%d] ChangeState( eOBJECT_STATE_MOVE ) == false", 
		mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop, mIsRequestRejection, mObject.index );
		return false;
	}

	mMapChange = true;

	/// 이동 지역 설정
	SetMapTargetPos( targetPos );

	InfluenceProcessStop();

	return true;

}


void cPlayer::SendMoveSpeed()
{
	MSG_SYN_MOVESPEED Msg;
	Msg.Category     = NM_PLAYER;
	Msg.Protocol     = NM_PLAYER_MOVESPEED_SYN;
	Msg.mCharacterIdx = mObject.index;
	Msg.moveSpeed    = GetMoveSpeed();
	NETWORK2->QuickSend( this, (char*)&Msg, sizeof(Msg) );
}


void  cPlayer::SendAttackSpeed()
{
	MSG_SYN_ATTACKSPEED Msg;
	Msg.Category = NM_PLAYER;
	Msg.Protocol = NM_PLAYER_ATTACKSPEED_SYN;
	Msg.mCharacterIdx = mObject.index;
	Msg.mAttackSpeed = mStatus2.mAttackSpeed;
	NETWORK2->QuickSend( this, (char*)&Msg, sizeof(Msg) );
}


void cPlayer::SendPlayerSize()
{
	MSG_SYN_PLAYER_SIZE synMsg;
	synMsg.Category = NM_PLAYER;
	synMsg.Protocol = NM_PLAYER_SCALE_SYN;
	synMsg.mCharacterIdx = mObject.index;
	synMsg.mScale = GetFixedObjectSizePer();
	NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );

}

//void cPlayer::SendSynStatus()
//{
//	HANDLE                handle  = NULL;
//	MSG_SYN_PLAYER_STATUS* sendMsg = (MSG_SYN_PLAYER_STATUS*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
//	if ( sendMsg != NULL )
//	{
//		sendMsg->Category = NM_PLAYER;
//		sendMsg->Protocol = NM_PLAYER_STATUS_RES;
//		sendMsg->mCharacterIdx = mObject.index;
//		sendMsg->mScale = mPlayerExrInfo.mFixedObjectSizePer;
//		sendMsg->mMoveSpeed = GetMoveSpeed();
//		sendMsg->mAttackSpeed = mStatus2.mAttackSpeed;
//		sendMsg->mMonsterIdx = mPlayerExrInfo.mChgMonsterIdx;
//		sendMsg->mIsSleep = mIsSleep;
//		sendMsg->mIsStun = mIsStun;
//		sendMsg->mVehicleIdx = mPlayerExrInfo.mVehicleIdx;
//		NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_STATUS) );
//	}
//
//	if( sendMsg->mMonsterIdx != 0 )
//		SendCoolTimeChgMon();
//}

void cPlayer::SetGameIn()
{ 
	mGameIn = true; 
	mNextNatureRecoveryTime = NETWORK2->GetAccumTime(); 

	/// 일괄 저장 시간 설정
	mBatchSaveTime = NETWORK2->GetAccumTime() + BATCH_SAVE_TIME;
}


bool cPlayer::IsGameFinish()
{
	if( GetRequestRejection() != eREQREJCT_GAMEFINISH )
		return false;

	if( GetState( ) != eOBJECT_STATE_STOP )
		return false;

	if( GetStateStop( ) != eSTOP_GAMEFINISH )
		return false;

	return true;
}


void cPlayer::CalcNatureRecovery()
{
	/// 5초가 안됐으면 자연 회복 안함
	if( mNextNatureRecoveryTime > NETWORK2->GetAccumTime() )
		return;

	/// 다음 자연회복 시간 설정
	mNextNatureRecoveryTime = mNextNatureRecoveryTime + NATURE_RECOVERY_TIME;

	/// 죽었으면 회복 안함
	if( GetStateDie() == true )
		return;

	/// 게임 밖이면 자연회복 안함
	if( mGameIn == false )
		return;

	/// 스텟 계산 전이면 자연회복 안함
	if( GetMaxHP() == 0 )
		return;

	assert( mPlayerExrInfo.RestHP );

	/// 회복 배율
	unsigned long sitDown = 1;

	/// 081209 PKH 회복 2배 스텟 계산
	if( mSitDownStartTime != 0 && mSitDownStatusCalc == true && mPlayerExrInfo.mState == ePLAYER_STATE_SITDOWN &&
		mSitDownStartTime + NATURE_RECOVERY_TIME < NETWORK2->GetAccumTime() )
	{
		mSitDownStatusCalc = false;
		STATUSCALC->CalcPlayerExtensionGlobal( mObject );
	}

	/// 앉아 있는 경우 배율
	if( mSitDownStartTime != 0 && mPlayerExrInfo.mState == ePLAYER_STATE_SITDOWN && 
		mSitDownStartTime + NATURE_RECOVERY_TIME < NETWORK2->GetAccumTime() )
	{
		sitDown = (unsigned long)STATUSSCRIPT->GetDamageCalcNumericalInfo( 10 );
	}

	unsigned long recovHP = FloatToInt( mStatus2.mRecovHP * sitDown );
	unsigned long recovMP = FloatToInt( mStatus2.mRecovMP * sitDown );

	if( recovHP != 0 )
		HPHeal( recovHP, true, 0, 0, eTAKEDAMAGETYPE_NONE );

	if( recovMP != 0 )
		MPHeal( recovMP, true );
}


void cPlayer::ApplyInitRest()
{
	if( mInitRestHP != 0 && mInitRestMP != 0 )
	{
		STATUSCALC->CalcPlayerExtensionGlobal( mObject );

		if( mInitRestHP != mPlayerExrInfo.RestHP )
		{
			unsigned long hp = mInitRestHP - mPlayerExrInfo.RestHP;
			HPHeal( hp, true, 0, 0, eTAKEDAMAGETYPE_NONE );
			mInitRestHP = 0;
		}

		if( mInitRestMP != mPlayerExrInfo.RestMP )
		{
			unsigned long mp = mInitRestMP - mPlayerExrInfo.RestMP;
			MPHeal( mp, true );
			mInitRestMP = 0;
		}
	}
}


void cPlayer::SetInitRestHP()
{ 
	mInitRestHP = mPlayerExrInfo.RestHP;
	if( mPlayerExrInfo.RestHP > mStatus2.mMaxHP )
		mPlayerExrInfo.RestHP = FloatToInt(mStatus2.mMaxHP);
}


void cPlayer::SetInitRestMP()
{ 
	mInitRestMP = mPlayerExrInfo.RestMP;
	if( mPlayerExrInfo.RestMP > mStatus2.mMaxMP )
		mPlayerExrInfo.RestMP = FloatToInt(mStatus2.mMaxMP);
}


void cPlayer::SetCommunitySkill( unsigned long communitySkillIdx )
{
	/// 생활 스킬 스크립트에서 읽어온 사용 시간 반영
	mPlayerExrInfo.mCommunitySkillIdx = communitySkillIdx;
	float skillTime = COMMUNITYSCRIPT->GetEmotingMaintainTime( communitySkillIdx, mPlayerInfo.Race, mPlayerInfo.Gender );

	mCommunitySkillEndTime = NETWORK2->GetAccumTime() + skillTime;
}


unsigned char cPlayer::SelectFollowPos( unsigned char followPos )
{
	/// 요청한자가 이미 중심 위치면 리턴
	if( followPos == FOLLOW_POS_CENTER )
		return followPos;

	/// 현재 추적위치를 가지고 있는 몬스터가 (중심자리)요청을 한경우
	if( mFollowPos[FOLLOW_POS_CENTER] == false )
	{
		/// 자신이 사용중이던 자리 반환
		if( followPos != FOLLOW_POS_MAX )
			mFollowPos[followPos] = false;

		/// 센터 설정후 리턴
		mFollowPos[FOLLOW_POS_CENTER] = true;
		return FOLLOW_POS_CENTER;            
	}

	/// 기존 자리를 배당 받은 요청자는 센터만 체크하고 리턴
	if( followPos != FOLLOW_POS_MAX )
		return followPos;

	/// 위치가 지정되지 않은 몬스터들의 위치 선정
	///unsigned char selectPos[FOLLOW_POS_MAX] = { 4, 0, 8, 6, 2, 7, 1, 3, 5 };
	unsigned char selectPos[FOLLOW_POS_MAX] = { 4, 0, 2, 8, 6, 1, 5, 7, 3 };

	/// 전체 자리 검사
	for( unsigned char i = 0 ; i < FOLLOW_POS_MAX ; ++i )
	{
		/// 순서에 맞게 위치 빈자리 확인
		if( mFollowPos[selectPos[i]] == false )
		{
			/// 빈자리 설정
			mFollowPos[selectPos[i]] = true;
			return selectPos[i];
		}
	}

	/// 빈자리가 하나도 없는 경우 센터 자리 설정
	return FOLLOW_POS_MAX;
}


void cPlayer::UnSelectFollowPos( unsigned char followPos )
{
	/// 사용 플레그 끔
	if( followPos != FOLLOW_POS_MAX )
		mFollowPos[followPos] = false;
}


void cPlayer::DuelEnd()
{
	if( mDuelPlayerIdx == 0 )
		return;

	if( mIsDuelRequester == true )
		DUELMANAGER->DuelObjectEnd( mObject.index, mDuelPlayerIdx, mDuelIdx, true );
	else
		DUELMANAGER->DuelObjectEnd( mDuelPlayerIdx, mObject.index, mDuelIdx, false );
}


void cPlayer::DuelEndDebuffClear( unsigned long enemyIdx )
{

	cSkillInfluenceSet::cConstIterator start = mInfluenceSet.Begin();
	cSkillInfluenceSet::cConstIterator end = mInfluenceSet.End();

	unsigned long influenceIdx;
	cInfluenceObject* pInfluence;
	sInfluenceScript* pScript;
	while( start != end )
	{
		influenceIdx = *start++;
		pInfluence = SKILLMANAGER->GetInfluence( influenceIdx );
		if( pInfluence == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cPlayer::DuelEndDebuffClear pInfluence == NULL");
			//-/-//SKILLMANAGER->InsertDeleteInfluenceObject( pInfluence );	/// 객체 삭제 대기열에 넣는다.
			continue;
		}

		pScript = SKILLSCRIPT->GetInfluenceInfo( pInfluence->GetInfluenceClassIdx() );
		if( pScript == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cPlayer::DuelEndDebuffClear pScript == NULL");
			continue;
		}

		/// 디버프만 제거
		if( pScript->mType != eINFLUENCETYPE_DEBUF )
			continue;

		/// 결투 상대편이 걸어준 디버프만 제거
		if( pInfluence->GetAttacker().index != enemyIdx )
			continue;

		/// 스킬 메니저에서 객체 삭제
		SKILLMANAGER->DeleteInfluenceList( influenceIdx );
	}
}


bool cPlayer::CheckPvPJoin()
{
	/// pvp 진입 불가 효과를 가진 사람 체크

	cSkillInfluenceSet::cIterator infBegin = mInfluenceSet.Begin();
	cSkillInfluenceSet::cIterator infEnd = mInfluenceSet.End();

	while( infBegin != infEnd )
	{
		cInfluenceObject* pInf = SKILLMANAGER->GetInfluence( (*infBegin++) );
		if( pInf != NULL && pInf->GetInfluenceClassIdx() == PVP_DM_CANTPVP_INFLUENCE )
			return false;
	}

	return true;
}


void cPlayer::PvPOut()
{ 
	mPlayerExrInfo.mIsPvPJoin = false; 
	mPvPDMIdx = 0;

	/// 무적 효과 제거
	SKILLMANAGER->DeleteInfluenceClassIdx( mObject, PVP_DM_NODAMAGE_INFLUENCE );

	/// 수장 효과 제거
	SKILLMANAGER->DeleteInfluenceClassIdx( mObject, PVP_DM_LEADER_INFLUENCE );
	SKILLMANAGER->DeleteInfluenceClassIdx( mObject, PVP_DM_LEADER_INFLUENCE2 );

	HPHeal( GetMaxHP(), true, 0, 0, eTAKEDAMAGETYPE_NONE );
	MPHeal( GetMaxMP(), true );

	NiPoint2 pos = STAGESCRIPT->CalcNearTargetMapPos( mPvPJoinBeforeMapIdx, mPvPJoinBeforPos.x, mPvPJoinBeforPos.y );
}


void cPlayer::AutoPvPOut()
{
	PvPOut();

	/// 마을이동 알림
	HANDLE handle  = NULL;
	MSGROOT* sendMsg = (MSGROOT*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	if ( sendMsg != NULL )
	{
		sendMsg->Category = NM_PVP;
		sendMsg->Protocol = NM_PVP_DM_END_SYN;
		NETWORK2->SendMsgRoot( handle, sizeof(MSGROOT) );
	}		
}


bool cPlayer::SendPvPTeamChat( char* msg, int length )
{
	cDeathMatchObject* pDM = PVPMANAGER->GetPvPDMObject( mPvPDMIdx );
	if( pDM != NULL )
	{
		pDM->TeamQuickSend( (ePVPDM_TEAM_TYPE)mPlayerExrInfo.mPvPDMTeamType, msg, length );
		return true;
	}

	return false;
}


void cPlayer::AddTakeDamage( sObject attacker, unsigned long damage, long /*distressPoint*/, eTAKEDAMAGE_TYPE /*type*/ )
{
	if( attacker.type == eOBJECTTYPE_PLAYER )
	{
		cPlayer* pAttacker = GRIDMANAGER->GetPlayer( attacker.index );
		if( pAttacker == NULL )
			return;

		if( GetStateDie() == true && mPvPDMIdx != 0 && damage !=0 )
		{
			cDeathMatchObject* pDM = PVPMANAGER->GetPvPDMObject( mPvPDMIdx );
			pDM->ScoreUpdate( attacker.index, (ePVPDM_TEAM_TYPE)pAttacker->GetPvPDMTeam(), 
				mObject.index, (ePVPDM_TEAM_TYPE)mPlayerExrInfo.mPvPDMTeamType );
		}
	}
}

void cPlayer::StartRequestRejection( eREQUEST_REJECTION IsRequestRejection )
{ 
	if( mIsRequestRejection != eREQREJCT_NONE )
	{
		NETWORK2->PostServerEvent("cPlayer[%d,%d,%d]::StartRequestRejection member[%d] input[%d]", 
			mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop, mObject.index, mIsRequestRejection, IsRequestRejection);
	}

	mIsRequestRejection = IsRequestRejection; 
}


void cPlayer::EndRequestRejection( eREQUEST_REJECTION IsRequestRejection )
{ 
	if( mIsRequestRejection != IsRequestRejection )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer[%d,%d,%d]::EndRequestRejection member[%d] input[%d]", 
			mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop, mObject.index, mIsRequestRejection, IsRequestRejection );
	}

	/// 종료 취소 요청이 들어온경우 기본 상태로 변경
	if( mPlayerExrInfo.mState == eOBJECT_STATE_STOP && mPlayerExrInfo.mStateStop == eSTOP_GAMEFINISH && eREQREJCT_GAMEFINISH == IsRequestRejection )
	{
		ChangeState( eOBJECT_STATE_IDLE );
		SetStateStop( eSTOP_NONE );		
	}

	mIsRequestRejection = eREQREJCT_NONE;
}



unsigned long cPlayer::ExpDown( unsigned long minimum, unsigned long maximum )
{

	if( mPlayerInfo.Level < 10 )
		return 0;

	/// 현재 캐릭터 lv에서 다음 lv까지 얻어야 하는 총 경험치
	sExpTable* pExpTable = LEVELSCRIPT->GetExpTable( mPlayerInfo.Level );
	if( pExpTable == NULL )
	{
		NETWORK2->PostServerEvent("cPlayer[%d]::ExpDown[%d,%d] pExpTable[%d] == NULL", mObject.index, minimum, maximum, mPlayerInfo.Level );
		return 0;
	}

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

	/// 랜덤 감소 확률 선택 - 소수점 2자리 살림
	unsigned long downGap = ( maximum * 100 ) - ( minimum * 100 );
		
	/// 최소 최대 구간 사이의 랜덤값
	unsigned long downGapExp = (unsigned long)( pExpTable->mExp * ( ( randvalue % downGap ) * 0.01f ) * 0.01f );
	/// 최소 경험치 하락분
	unsigned long downMinExp = (unsigned long)( pExpTable->mExp * minimum * 0.01f );

	/// 하락할 경험치량
	unsigned long minusExp = downMinExp + downGapExp;
	
	/// 경험치 감소 가능 한 만큼 만 감소
	if( mHeroInfo.Exp < minusExp )
	{
		minusExp = mHeroInfo.Exp;		/// 실제 감소량 기록
		mHeroInfo.Exp = 0;				/// 경험치 감소
		return minusExp;					/// 감소량 리턴
	}
	else
	{	
		mHeroInfo.Exp = mHeroInfo.Exp - minusExp;	/// 경험치 감소
		return minusExp;									/// 감소량 리턴
	}
}


void cPlayer::DBUpdate( bool updateComplete )
{
	HANDLE            handle          = NULL;
	CHARACTER_UPDATE* characterUpdate = (CHARACTER_UPDATE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_UPDATE );

	characterUpdate->idx   = mPlayerInfo.CharacterIdx;

	characterUpdate->exp        = mHeroInfo.Exp;
	characterUpdate->skillExp   = mHeroInfo.SkillExp;
	characterUpdate->hp         = mPlayerExrInfo.RestHP;
	characterUpdate->mp         = mPlayerExrInfo.RestMP;
	characterUpdate->tarotPoint = mHeroInfo.TarotPoint;
	characterUpdate->titleIndex = mPlayerExrInfo.mTitleIndex;
	characterUpdate->guildIndex = mPlayerExrInfo.mGuildIndex;
	characterUpdate->guildPosition = mPlayerExrInfo.mGuildPosition;

	
	if( mPlayerExrInfo.mIsPvPJoin == true )
	{
		NiPoint2 pos = STAGESCRIPT->CalcNearTargetMapPos( mPvPJoinBeforeMapIdx, mPvPJoinBeforPos.x, mPvPJoinBeforPos.y );
		characterUpdate->mapNumber = mPvPJoinBeforeMapIdx;
		characterUpdate->xPos      = pos.x;
		characterUpdate->yPos      = pos.y;
	}
	else
	{
		characterUpdate->mapNumber = mMapNumber;
		characterUpdate->xPos      = mObjectPos.x;
		characterUpdate->yPos      = mObjectPos.y;
	}

	characterUpdate->retvalue = -1;

	NETWORK2->SendSQL( mConnectionIdx, handle, sizeof(CHARACTER_UPDATE), updateComplete ? COMMON_DB_CHARACTER_UPDATE : 0 );
}

/// 순간이동
void cPlayer::Blink()	
{
	if( mBlinkPos == NiPoint2::ZERO )
		return;

	SetMoveTargetPos( mBlinkPos.x, mBlinkPos.y );
	SetPos( mBlinkPos.x, mBlinkPos.y );		

	// 다른 플레이어들에게도 알림
	MSG_SYN_PLAYER_BLINK syn;
	syn.Category = NM_PLAYER;
	syn.Protocol = NM_PLAYER_BLINK;
	syn.mObjIndex = mObject.index;
	syn.mDestX = mBlinkPos.x;
	syn.mDestY = mBlinkPos.y;
	NETWORK2->QuickSend( this, (char*)&syn, sizeof(syn) );

	mBlinkPos.x = 0.0f;
	mBlinkPos.y = 0.0f;
}


bool cPlayer::IsChgMonCoolTimeEnd( eMONSTERATTACK_TYPE type )
{
	if( mChgMonCoolTime[type] > NETWORK2->GetAccumTime() )
		return false;

	return true;
}


void cPlayer::UpdateChgMonCoolTime( eMONSTERATTACK_TYPE type )
{
	if( type >= eMONSTERATTACK_MAX )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer[%d]::UpdateCoolTime type[%d] >= eMONSTERATTACK_MAX", mObject.index, type );
		return;
	}

	if( mPlayerExrInfo.mChgMonsterIdx == 0 )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer[%d]::UpdateCoolTime mPlayerExrInfo.mChgMonsterIdx[%d] != 0", mObject.index, mPlayerExrInfo.mChgMonsterIdx );
		return;
	}

	sMonsterSkillScript* pScript = SKILLSCRIPT->GetMonsterSkillInfo( mPlayerExrInfo.mChgMonsterIdx, type );
	if( pScript == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cPlayer[%d]::UpdateCoolTime pScript[%d,%d] == NULL", mObject.index, mPlayerExrInfo.mChgMonsterIdx, type );
		return;
	}

	/// 몬스터가 사용하는 쿨타임의 3배를 적용한다!
	if( !( type == eMONSTERATTACK_NORMAL1 || type == eMONSTERATTACK_NORMAL2 ) )
		mChgMonCoolTime[type] = NETWORK2->GetAccumTime() + ( pScript->mCoolTime * 3 );
}


void cPlayer::SendCoolTimeChgMon()
{
	if( mPlayerExrInfo.mChgMonsterIdx == 0 )
		return;

	unsigned long accumTime = NETWORK2->GetAccumTime();
	unsigned long skill[eMONSTERATTACK_MAX - eMONSTERATTACK_SKILL1] = {0,};

	for( int i = 0 ; i < eMONSTERATTACK_MAX - eMONSTERATTACK_SKILL1 ; ++i )
	{
		if( mChgMonCoolTime[ eMONSTERATTACK_SKILL1 + i ] > accumTime )
			skill[i] = mChgMonCoolTime[eMONSTERATTACK_SKILL1 + i] - accumTime;
		else
			skill[i] = 0;

		/// 보유 스킬 체크
		if( SKILLSCRIPT->GetMonsterSkillInfo( mPlayerExrInfo.mChgMonsterIdx, 
			eMONSTERATTACK_TYPE( eMONSTERATTACK_SKILL1 + i ) ) == NULL )
			skill[i] = ULONG_MAX;
	}

	HANDLE handle  = NULL;
	MSG_RES_SKILL_CHGMON_COOLTIME* sendMsg = (MSG_RES_SKILL_CHGMON_COOLTIME*)NETWORK2->GetMsgRoot( &handle, 
		mConnectionIdx, NM_SKILL, NM_SKILL_CHGMON_SKILLCOOLTIME );
	if ( sendMsg != NULL )
	{
		sendMsg->mSkill1CoolTime = skill[eMONSTERATTACK_SKILL1 - eMONSTERATTACK_SKILL1];
		sendMsg->mSkill2CoolTime = skill[eMONSTERATTACK_SKILL2 - eMONSTERATTACK_SKILL1];
		sendMsg->mSkill3CoolTime = skill[eMONSTERATTACK_SKILL3 - eMONSTERATTACK_SKILL1];
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_SKILL_CHGMON_COOLTIME) );
	}

}


void cPlayer::SetDuelIdx( unsigned long duelIdx )
{ 
	mDuelIdx = duelIdx; 

	mPlayerExrInfo.mIsDuelIdx = duelIdx == 0 ? false : true;
}


void cPlayer::StateOddity( long* pOddity )
{

	for( int i = 0 ; i < eODDITYTYPE_MAX ; ++i )
	{
		if( mODDITY[i] != pOddity[i] )
		{
			if( pOddity[i] != 0 )
				SendOddity( (eODDITYTYPE)i, true );
			else
				SendOddity( (eODDITYTYPE)i, false );

			mODDITY[i] = pOddity[i];
		}
	}
}


void cPlayer::SendOddity( eODDITYTYPE pos, bool apply )
{
	/// 상태 이상중 클라이언트에 메세지를 발송 해줘야 하는 부분 처리
	HANDLE handle  = NULL;
	switch(pos)
	{
	case eODDITYTYPE_CANTMOVE:
		{
			MoveStop();

			MSG_SYN_PLAYER_CANTMOVE synMsg;
			synMsg.Category = NM_PLAYER;
			synMsg.Protocol = NM_PLAYER_CANTMOVE_SYN;
			synMsg.mIsCantMove = apply;  
			synMsg.mCharaterIdx = mObject.index;
			synMsg.mCharacterPos.x = mObjectPos.x;
			synMsg.mCharacterPos.y = mObjectPos.y;

			NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
		}
		break;
	case eODDITYTYPE_CANTSKILL:
		{
			MSG_SYN_PLAYER_CANTSKILL* sendMsg = (MSG_SYN_PLAYER_CANTSKILL*)NETWORK2->GetMsgRoot( &handle, 
				mConnectionIdx, NM_PLAYER, NM_PLAYER_CANTSKILL_SYN );
			if ( sendMsg != NULL )
			{
				sendMsg->mIsCantSkill = apply;  
				NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_PLAYER_CANTSKILL) );
			}
		}
		break;
	case eODDITYTYPE_SLEEP:
		{
			MoveStop();

			MSG_SYN_PLAYER_SLEEP synMsg;
			synMsg.Category = NM_PLAYER;
			synMsg.Protocol = NM_PLAYER_SLEEP_SYN;
			synMsg.mIsSleep = apply;  
			synMsg.mCharaterIdx = mObject.index;
			synMsg.mCharacterPos.x = mObjectPos.x;
			synMsg.mCharacterPos.y = mObjectPos.y;

			NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
		}
		break;
	case eODDITYTYPE_STUN:
		{
			MoveStop();

			MSG_SYN_PLAYER_STUN synMsg;
			synMsg.Category = NM_PLAYER;
			synMsg.Protocol = NM_PLAYER_SLEEP_SYN;
			synMsg.mIsStun = apply;  
			synMsg.mCharaterIdx = mObject.index;
			synMsg.mCharacterPos.x = mObjectPos.x;
			synMsg.mCharacterPos.y = mObjectPos.y;

			NETWORK2->QuickSend( this, (char*)&synMsg, sizeof(synMsg) );
		}
		break;
	}
}

// IsAddMoney Method.
bool cPlayer::IsAddMoney(long money)
{
	if ( money > 0 )
	{
		unsigned long maxMoney = ULONG_MAX - 1UL;
		unsigned long newMoney = mMoney + money;

		// 최대 금액보다 커질겨우 또는 오버플로 일 경우
		return ( newMoney > maxMoney || newMoney < mMoney ) ? false : true;
	}
	else
		return ( (unsigned long)labs(money) <= mMoney ) ? true : false;
}

// AddMoney Method - #include <LIMITS.H> ULONG_MAX=[4,294,967,295].
long cPlayer::AddMoney(sObject object, long money, bool save)
{
	if ( money > 0 )
	{
		unsigned long maxMoney = ULONG_MAX - 1UL;
		unsigned long newMoney = mMoney + money;

		// 최대 금액보다 커질겨우 또는 오버플로 일 경우
		if ( newMoney > maxMoney || newMoney < mMoney )
		{
			money = maxMoney - mMoney;
		}

		// 변경된 금액 저장(DB).
		if ( save == true )
			SaveMoney( object, mMoney, mMoney+money, money );

		mMoney += money;

		// 변경된 금액 발송.
		SendMoney( object, money );
		return money;
	}
	else if ( (unsigned long)labs(money) <= mMoney )
	{
		// 변경된 금액 저장(DB).
		if ( save == true )
			SaveMoney( object, mMoney, mMoney+money, money );

		mMoney += money;

		// 변경된 금액 발송.
		SendMoney( object, money );
		return money;
	}
	else
		return 0;
}

// SaveMoney Method - 변경할 금액만 반영.
bool cPlayer::SaveMoney(sObject object, unsigned long befor, unsigned long after, long money)
{
	HANDLE           handle         = NULL;
	CHARACTER_MONEY* characterMoney = (CHARACTER_MONEY*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_MONEY );
	if ( characterMoney != NULL )
	{
		characterMoney->idx         = mObject.index;
		characterMoney->money       = (__int64)money;
		characterMoney->objectType  = object.type;
		characterMoney->objectIndex = object.index;
		characterMoney->befor       = befor;
		characterMoney->after       = after;

		switch ( object.type )
		{
		case eOBJECTTYPE_MONSTER:
			{
				cMonster* monster = OBJECTMANAGER->GetMonster( object.index );
				if ( monster != NULL )
					characterMoney->objectRaceGender = monster->GetRaceGender( );
			}
			break;
		case eOBJECTTYPE_NPC:
			{
				cNpc* npc = OBJECTMANAGER->GetNpc( object.index );
				if ( npc != NULL )
					characterMoney->objectRaceGender = npc->GetRaceGender( );
			}
			break;
		}

		return NETWORK2->SendSQL( handle, sizeof(CHARACTER_MONEY) );
	}
	return false;
}

// SendMoney Method.
bool cPlayer::SendMoney(sObject object, long addMoney)
{
	HANDLE                handle  = NULL;
	MSG_RES_PLAYER_MONEY* sendMsg = (MSG_RES_PLAYER_MONEY*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	if ( sendMsg != NULL )
	{
		sendMsg->Category    = NM_PLAYER;
		sendMsg->Protocol    = NM_PLAYER_MONEY_RES;
		sendMsg->mMoney      = mMoney;
		sendMsg->mObjectType = object.type;
		sendMsg->mAddMoney   = addMoney;
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_MONEY) );
	}
	return false;
}

// AddDeposit Method - #include <LIMITS.H> ULONG_MAX=[4,294,967,295].
long cPlayer::AddDeposit(sObject object, long deposit)
{
	if ( deposit > 0 )
	{
		unsigned long maxDeposit = ULONG_MAX - 1UL;
		unsigned long newDeposit = mDeposit + deposit;

		// 최대 금액보다 커질겨우 또는 오버플로 일 경우
		if ( newDeposit > maxDeposit || newDeposit < mDeposit )
		{
			deposit = maxDeposit - mDeposit;
		}

		// 변경된 금액 저장(DB).
		SaveDeposit( object, mDeposit, mDeposit+deposit, deposit );

		mDeposit += deposit;

		// 변경된 금액 발송.
		SendDeposit( object, deposit );
		return deposit;
	}
	else if ( (unsigned long)labs(deposit) <= mDeposit )
	{
		// 변경된 금액 저장(DB).
		SaveDeposit( object, mDeposit, mDeposit+deposit, deposit );

		mDeposit += deposit;

		// 변경된 금액 발송.
		SendDeposit( object, deposit );
		return deposit;
	}
	else
		return 0;
}

// SaveDeposit Method - 변경할 금액만 반영.
bool cPlayer::SaveDeposit(sObject object, unsigned long befor, unsigned long after, long deposit)
{
	HANDLE             handle           = NULL;
	CHARACTER_DEPOSIT* characterDeposit = (CHARACTER_DEPOSIT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_DEPOSIT );
	if ( characterDeposit != NULL )
	{
		characterDeposit->idx         = mObject.index;
		characterDeposit->money       = (__int64)deposit;
		characterDeposit->objectType  = object.type;
		characterDeposit->objectIndex = object.index;
		characterDeposit->befor       = befor;
		characterDeposit->after       = after;

		if ( object.type == eOBJECTTYPE_NPC )
		{
			cNpc* npc = OBJECTMANAGER->GetNpc( object.index );
			if ( npc != NULL )
			{
				characterDeposit->objectRaceGender = npc->GetRaceGender( );
			}
		}

		return NETWORK2->SendSQL( handle, sizeof(CHARACTER_DEPOSIT) );
	}
	return false;
}

// SendDeposit Method.
bool cPlayer::SendDeposit(sObject object, long addDeposit)
{
	HANDLE                  handle  = NULL;
	MSG_RES_PLAYER_DEPOSIT* sendMsg = (MSG_RES_PLAYER_DEPOSIT*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	if ( sendMsg != NULL )
	{
		sendMsg->Category    = NM_PLAYER;
		sendMsg->Protocol    = NM_PLAYER_DEPOSIT_RES;
		sendMsg->mDeposit    = mDeposit;
		sendMsg->mObjectType = object.type;
		sendMsg->mAddDeposit = addDeposit;
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_DEPOSIT) );
	}
	return false;
}

// NpcDeposit Method.
void cPlayer::NpcDeposit(MSG_REQ_NPC_DEPOSIT* msg)
{
	// npc 인증
	unsigned long retValue = OBJECTMANAGER->IsNpcTalk( msg->npcIdx, this, eNPCTALK_INVENTORY, 0 );
	if ( retValue != ERROR_NPC_OPEN_SUCCESS )
	{
		NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]cPlayer::NpcDeposit retValue != ERROR_NPC_OPEN_SUCCESS", 
			mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop, retValue );
		throw ERROR_NPC_DEPOSIT_NPC;
	}

	if ( msg->money > GetMoney( ) )
		throw ERROR_NPC_DEPOSIT_FAIL;

	sObject object  = { eOBJECTTYPE_NPC, msg->npcIdx };
	long    money   = -(long)msg->money;
	long    deposit = +(long)msg->money;
	long    result;

	result = AddMoney( object, money );

	if ( result != money )
		NETWORK2->PostServerEvent( "WARNING - NM_NPC_DEPOSIT_REQ - CharacterIdx(%u) Money(%u)", mObject.index, GetMoney( ) );

	result = AddDeposit( object, deposit );
	if ( result != deposit )
		NETWORK2->PostServerEvent( "WARNING - NM_NPC_DEPOSIT_REQ - CharacterIdx(%u) Deposit(%u)", mObject.index, GetDeposit( ) );

	throw ERROR_NPC_DEPOSIT_SUCCESS;
}

// NpcWithdraw Method.
void cPlayer::NpcWithdraw(MSG_REQ_NPC_WITHDRAW* msg)
{
	// npc 인증
	unsigned long retValue = OBJECTMANAGER->IsNpcTalk( msg->npcIdx, this, eNPCTALK_INVENTORY, 0 );
	if ( retValue != ERROR_NPC_OPEN_SUCCESS )
	{
		NETWORK2->PostServerEvent( "player[%d,%d,%D] return[%d]cPlayer::NpcWithdraw retValue != ERROR_NPC_OPEN_SUCCESS", 
			mObject.index, mPlayerExrInfo.mState, mPlayerExrInfo.mStateStop, retValue );
		throw ERROR_NPC_WITHDRAW_NPC;
	}

	if ( msg->money > GetDeposit( ) )
		throw ERROR_NPC_WITHDRAW_FAIL;

	sObject object   = { eOBJECTTYPE_NPC, msg->npcIdx };
	long    money    = +(long)msg->money;
	long    withdraw = -(long)msg->money;
	long    result;

	result = AddMoney( object, money );
	if ( result != money )
		NETWORK2->PostServerEvent( "WARNING - NM_NPC_WITHDRAW_REQ - CharacterIdx(%u) Money(%u)", mObject.index, GetMoney( ) );

	result = AddDeposit( object, withdraw );
	if ( result != withdraw )
		NETWORK2->PostServerEvent( "WARNING - NM_NPC_WITHDRAW_REQ - CharacterIdx(%u) Deposit(%u)", mObject.index, GetDeposit( ) );

	throw ERROR_NPC_WITHDRAW_SUCCESS;
}

// AddTarotPoint Method
long cPlayer::AddTarotPoint(long point)
{
	unsigned long& point_t = mHeroInfo.TarotPoint;
	if ( point > 0 )
	{
		unsigned long maxPoint = ULONG_MAX - 1UL;
		unsigned long newPoint = point_t + point;

		// 최대 금액보다 커질겨우 또는 오버플로 일 경우
		if ( newPoint > maxPoint || newPoint < point_t )
		{
			point = maxPoint - point_t;
		}

		// 변경된 금액 저장(DB).
		SaveTarotPoint( point );

		point_t += point;

		// 변경된 금액 발송.
		SendTarotPoint( point );
		return point;
	}
	else if ( (unsigned long)labs(point) <= point_t )
	{
		// 변경된 금액 저장(DB).
		SaveTarotPoint( point );

		point_t += point;

		// 변경된 금액 발송.
		SendTarotPoint( point );
		return point;
	}
	else
		return 0;
}

// SaveTarotPoint Method
bool cPlayer::SaveTarotPoint(long point)
{
	HANDLE                 handle     = NULL;
	CHARACTER_TAROT_POINT* tarotPoint = (CHARACTER_TAROT_POINT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_TAROT_POINT );
	if ( point != NULL )
	{
		tarotPoint->idx   = mObject.index;
		tarotPoint->point = point;
		return NETWORK2->SendSQL( handle, sizeof(CHARACTER_TAROT_POINT) );
	}
	return false;
}

// SendTarotPoint Method
bool cPlayer::SendTarotPoint(long point)
{
	HANDLE                      handle  = NULL;
	MSG_RES_PLAYER_TAROT_POINT* sendMsg = (MSG_RES_PLAYER_TAROT_POINT*)NETWORK2->GetMsgRoot( &handle
																							,mConnectionIdx
																							,NM_PLAYER
																							,NM_PLAYER_TAROT_POINT_RES );
	if ( sendMsg != NULL )
	{
		sendMsg->mPoint    = mHeroInfo.TarotPoint;
		sendMsg->mAddPoint = point;
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_TAROT_POINT) );
	}
	return false;
}

// AddPvPPoint Method
long cPlayer::AddPvPPoint(long point)
{
	unsigned long& point_t = mHeroInfo.mPvPPoint;
	if ( point > 0 )
	{
		unsigned long maxPoint = ULONG_MAX - 1UL;
		unsigned long newPoint = point_t + point;

		// 최대 금액보다 커질겨우 또는 오버플로 일 경우
		if ( newPoint > maxPoint || newPoint < point_t )
		{
			point = maxPoint - point_t;
		}

		// 변경된 금액 저장(DB).
		SaveTarotPoint( point );

		point_t += point;

		// 변경된 금액 발송.
		SendTarotPoint( point );
		return point;
	}
	else if ( (unsigned long)labs(point) <= point_t )
	{
		// 변경된 금액 저장(DB).
		SaveTarotPoint( point );

		point_t += point;

		// 변경된 금액 발송.
		SendTarotPoint( point );
		return point;
	}
	else
		return 0;
}

// SavePvPPoint Method
bool cPlayer::SavePvPPoint(long point)
{
	HANDLE               handle   = NULL;
	CHARACTER_PVP_POINT* pvpPoint = (CHARACTER_PVP_POINT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_PVP_POINT );
	if ( pvpPoint != NULL )
	{
		pvpPoint->idx   = mObject.index;
		pvpPoint->point = point;
		return NETWORK2->SendSQL( handle, sizeof(CHARACTER_PVP_POINT) );
	}
	return false;
}

// SendPvPPoint Method
bool cPlayer::SendPvPPoint(long point)
{
	HANDLE                    handle  = NULL;
	MSG_RES_PLAYER_PVP_POINT* sendMsg = (MSG_RES_PLAYER_PVP_POINT*)NETWORK2->GetMsgRoot( &handle
																						,mConnectionIdx
																						,NM_PLAYER
																						,NM_PLAYER_PVP_POINT_RES );
	if ( sendMsg != NULL )
	{
		sendMsg->mPoint    = mHeroInfo.mPvPPoint;
		sendMsg->mAddPoint = point;
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_PVP_POINT) );
	}
	return false;
}

/// InventoryMove Method
bool cPlayer::IsInventoryMove(unsigned short number1, unsigned short number2, unsigned short& except, int& depth)
{
	TB_INVENTORY* inventory1 = SelectInventory( number1 );
	TB_INVENTORY* inventory2 = SelectInventory( number2 );

	if ( IsInventory( inventory1 ) == false )
	{
		return false;
	}
	else if ( IsInventory( inventory2 ) && depth == 0 )
	{
		if ( IsInventoryMove( number2, number1, except, (--depth) ) == false )
			return false;
	}

	if ( IsInventoryEquip( number2 ) == false )
		return true;

	/*-- 아이템 착용 가능 확인
	*/
	TB_ITEM_DEFINE* tbItemDefine = ITEMMANAGER->GetItemDefine( inventory1->itemDefineIdx );
	if ( tbItemDefine == NULL )
		return false;

	/*-- Item Limit Table(TB_ITEM_LIMIT) 적용
	*/
	TB_ITEM_LIMIT* tbItemLimit = ITEMMANAGER->GetItemLimit( inventory1->itemDefineIdx );
	if ( tbItemLimit != NULL )
	{
		// 종족; TB_ITEM_LIMIT::charRace 와 sPlayerInfo::Race 는 정의는 다르나 같은 값을 사용.
		if ( tbItemLimit->charRace != ITEM_RACE_ALL )
		{
			if ( tbItemLimit->charRace != mPlayerInfo.Race )
				return false;
		}

		// 성별; TB_ITEM_LIMIT::charGender 와 sPlayerInfo::Gender 는 정의는 다르나 같은 값을 사용.
		if ( tbItemLimit->charGender != ITEM_GENDER_ALL )
		{
			if ( tbItemLimit->charGender != mPlayerInfo.Gender )
				return false;
		}

		// 직업; TB_ITEM_LIMIT::charJob 와 sPlayerInfo::Job 은 정의는 다르나 같은 값을 사용.
		if ( tbItemLimit->charJob != ITEM_JOB_ALL )
		{
			long charJob = mPlayerInfo.Job - (mPlayerInfo.Job % ITEM_JOB_FIGHTER);
			if ( tbItemLimit->charJob != charJob )
				return false;
		}

		// 레벨; 이하일 경우만 제어를 한다.
		if ( (BYTE)mPlayerInfo.Level < tbItemLimit->charLevel )
			return false;
	}

	switch ( tbItemDefine->type )
	{
	case ITEM_WEAPON:
		switch ( tbItemDefine->subType )
		{
		case ITEM_WEAPON_SWORD:
			switch ( number2 )
			{
			case INVENTORY_HAND_RIGHT1:
				{
					TB_INVENTORY* left1 = SelectInventory( INVENTORY_HAND_LEFT1 );
					if ( IsInventory( left1 ) )
					{
						if ( ITEMMANAGER->GetItemDefineSubType( left1->itemDefineIdx ) != ITEM_WEAPON_SHIELD )
							except = INVENTORY_HAND_LEFT1;
					}
				} return true;
			case INVENTORY_HAND_RIGHT2:
				{
					TB_INVENTORY* left2 = SelectInventory( INVENTORY_HAND_LEFT2 );
					if ( IsInventory( left2 ) )
					{
						if ( ITEMMANAGER->GetItemDefineSubType( left2->itemDefineIdx ) != ITEM_WEAPON_SHIELD )
							except = INVENTORY_HAND_LEFT2;
					}
				} return true;
			}
			break;		// tbItemDefine->subType

		case ITEM_WEAPON_BLADE:
		case ITEM_WEAPON_DUAL:
		case ITEM_WEAPON_CUTTER:
		case ITEM_WEAPON_GUN:
		case ITEM_WEAPON_STAFF:
			switch ( number2 )
			{
			case INVENTORY_HAND_LEFT1:
				if ( IsInventory( SelectInventory( INVENTORY_HAND_RIGHT1 ) ) )
					except = INVENTORY_HAND_RIGHT1;
				return true;
			case INVENTORY_HAND_RIGHT1:
				if ( IsInventory( SelectInventory( INVENTORY_HAND_LEFT1 ) ) )
					except = INVENTORY_HAND_LEFT1;
				return true;
			case INVENTORY_HAND_LEFT2:
				if ( IsInventory( SelectInventory( INVENTORY_HAND_RIGHT2 ) ) )
					except = INVENTORY_HAND_RIGHT2;
				return true;
			case INVENTORY_HAND_RIGHT2:
				if ( IsInventory( SelectInventory( INVENTORY_HAND_LEFT2 ) ) )
					except = INVENTORY_HAND_LEFT2;
				return true;
			}
			break;		// tbItemDefine->subType

		case ITEM_WEAPON_SHIELD:
			switch ( number2 )
			{
			case INVENTORY_HAND_LEFT1:
				{
					TB_INVENTORY* right1 = SelectInventory( INVENTORY_HAND_RIGHT1 );
					if ( IsInventory( right1 ) )
					{
						if ( ITEMMANAGER->GetItemDefineSubType( right1->itemDefineIdx ) != ITEM_WEAPON_SWORD )
							except = INVENTORY_HAND_RIGHT1;
					}
				} return true;
			case INVENTORY_HAND_LEFT2:
				{
					TB_INVENTORY* right2 = SelectInventory( INVENTORY_HAND_RIGHT2 );
					if ( IsInventory( right2 ) )
					{
						if ( ITEMMANAGER->GetItemDefineSubType( right2->itemDefineIdx ) != ITEM_WEAPON_SWORD )
							except = INVENTORY_HAND_RIGHT2;
					}
				} return true;
			}
			break;		// tbItemDefine->subType
		}
		return false;	// switch ( tbItemDefine->type )

	case ITEM_WEAR:
		switch ( tbItemDefine->subType )
		{
		case ITEM_WEAR_HEAD:
			return (number2 == INVENTORY_WEAR_HAT);
		case ITEM_WEAR_UPPER:
			return (number2 == INVENTORY_WEAR_BODY1);
		case ITEM_WEAR_LOWER:
			return (number2 == INVENTORY_WEAR_BODY2);
		case ITEM_WEAR_HANDS:
			return (number2 == INVENTORY_WEAR_HAND);
		case ITEM_WEAR_FEET:
			return (number2 == INVENTORY_WEAR_FOOT);
		case ITEM_WEAR_ONEPIECE:
			switch ( number2 )
			{
			case INVENTORY_WEAR_BODY1:
				if ( IsInventory( SelectInventory( INVENTORY_WEAR_BODY2 ) ) )
					except = INVENTORY_WEAR_BODY2;
				return true;
			case INVENTORY_WEAR_BODY2:
				if ( IsInventory( SelectInventory( INVENTORY_WEAR_BODY1 ) ) )
					except = INVENTORY_WEAR_BODY1;
				return true;
			}
			break;
		}
		return false;	// switch ( tbItemDefine->type )

	case ITEM_ACCESSORY:
		switch ( tbItemDefine->subType )
		{
		case ITEM_ACCESSORY_EARRING:
			return (number2 == INVENTORY_WEAR_EARRING);
		case ITEM_ACCESSORY_NECKLACE:
			return (number2 == INVENTORY_WEAR_NECKLACE);
		case ITEM_ACCESSORY_BROOCH:
			return (number2 == INVENTORY_WEAR_BROOCH);
		case ITEM_ACCESSORY_BRACELET:
			return (number2 == INVENTORY_WEAR_BRACELET);
		case ITEM_ACCESSORY_RING:
			return (number2 == INVENTORY_WEAR_RING);
		}
		return false;	// switch ( tbItemDefine->type )

	case ITEM_CARD:
		if ( tbItemDefine->subType == ITEM_CARD_EQUIP_C )
		{
			switch ( number2 )
			{
			case INVENTORY_WEAR_CARD1:
			case INVENTORY_WEAR_CARD2:
			case INVENTORY_WEAR_CARD3:
				return true;
			}
		}
		return false;	// switch ( tbItemDefine->type )

	default:
		return false;	// switch ( tbItemDefine->type )
	}
}

/// IsInventoryMerge Method
bool cPlayer::IsInventoryMerge(unsigned short number1, unsigned short number2)
{
	TB_INVENTORY* inventory1 = SelectInventory( number1 );
	TB_INVENTORY* inventory2 = SelectInventory( number2 );

	if ( IsInventory( inventory1 ) && IsInventory( inventory2 ) )
	{
		if ( inventory1->itemDefineIdx == inventory2->itemDefineIdx )
		{
			TB_ITEM_DEFINE* tbItemDefine = ITEMMANAGER->GetItemDefine( inventory1->itemDefineIdx );

			if ( tbItemDefine == NULL )
				return false;

			return tbItemDefine->capacity > 1 ? true : false;
		}
	}

	return false;
}

/// IsInventoryDivide Method
bool cPlayer::IsInventoryDivide(unsigned short number1, unsigned short number2, unsigned short divide)
{
	TB_INVENTORY* inventory1 = SelectInventory( number1 );
	TB_INVENTORY* inventory2 = SelectInventory( number2 );

	if ( IsInventory( inventory1 ) == false || IsInventory( inventory2 ) == true )
		return false;

	return (inventory1->count > divide) ? true : false;
}

// GetEmptyBagNumber Method
short cPlayer::GetEmptyBagNumber(short number)
{
	TB_INVENTORY* inventory = SelectInventory( number );
	for ( short i = number; i <= INVENTORY_BAG_END; i++, inventory++ )
	{
		if ( !IsInventory( inventory ) )
			return i;
	}
	return INVENTORY_BAG_NONE;
}

// GetEmptyQuestNumber Method
short cPlayer::GetEmptyQuestNumber(short number)
{
	TB_INVENTORY* inventory = SelectInventory( number );
	for ( short i = number; i <= INVENTORY_QUEST_END; i++, inventory++ )
	{
		if ( !IsInventory( inventory ) )
			return i;
	}
	return -1;
}

/// InventorySwap Method
void cPlayer::InventorySwap(unsigned short number1, unsigned short number2)
{
	TB_INVENTORY* inventory1 = SelectInventory( number1 );
	TB_INVENTORY* inventory2 = SelectInventory( number2 );

	XCopyInventory( inventory1, inventory2, XCOPY_INVENTORY_SWAP );
}

// InventoryMove Method
void cPlayer::InventoryMove(unsigned short number1, unsigned short number2)
{
	TB_INVENTORY* inventory1 = SelectInventory( number1 );
	TB_INVENTORY* inventory2 = SelectInventory( number2 );

	inventory1->number = number2;
	XCopyInventory( inventory2, inventory1, XCOPY_INVENTORY_ALL ); //(*inventory2) = (*inventory1);

	inventory1->number = number1;
	RemoveInventory( inventory1 ); // memset( inventory1, 0, sizeof(TB_INVENTORY) );
}

// InventoryExcept Method
void cPlayer::InventoryExcept(unsigned short number1, unsigned short number2)
{
	TB_INVENTORY* inventory1 = SelectInventory( number1 );
	TB_INVENTORY* inventory2 = SelectInventory( number2 );

	inventory1->number = number2;
	XCopyInventory( inventory2, inventory1, XCOPY_INVENTORY_ALL ); //(*inventory2) = (*inventory1);

	inventory1->number = number1;
	RemoveInventory( inventory1 ); // memset( inventory1, 0, sizeof(TB_INVENTORY) );
}

// GetItemCount Method.
short cPlayer::GetItemCount(long itemDefineIndex)
{
	PerItemCount* itemCount = ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, itemDefineIndex );
	if ( itemCount != NULL )
	{
		return (short)itemCount->bag;
	}
	return 0;
}

// IsItem Method.
bool cPlayer::IsItem(long itemDefineIndex)
{
	PerItemCount* itemCount = ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, itemDefineIndex );
	if ( itemCount != NULL )
	{
		return (itemCount->bag > 0) ? true : false;
	}
	return false;
}

// IsItemUse Method.
bool cPlayer::IsItemUse(long itemDefineIndex, unsigned short count)
{
	// 쿨타임 검사.
	if ( COOLTIMEPOOL->IsCooltime( &mCooltimeRoot, itemDefineIndex ) == true )
		return false;

	PerItemCount* itemCount = ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, itemDefineIndex );
	if ( itemCount != NULL )
	{
		return (itemCount->bag >= count) ? true : false;
	}
	return false;
}

// ItemUse Method.
bool cPlayer::ItemUse(long itemDefineIndex, unsigned short count)
{
	if ( IsItemUse( itemDefineIndex, count ) == true )
	{
		TB_INVENTORY* inventory   = SelectInventory( INVENTORY_BAG_BEGIN );
		short         remainCount = count;

		HANDLE    handle   = NULL;
		ITEM_USE* itemUse  = (ITEM_USE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_USE );
		long&     rowCount = itemUse->rowCount;

		itemUse->characterIdx = mObject.index;

		for ( int i = INVENTORY_BAG_BEGIN; (i <= INVENTORY_BAG_END) && (remainCount > 0); i++, inventory++ )
		{
			if ( IsInventory( inventory ) == true )
			{
				if ( itemDefineIndex == inventory->itemDefineIndex )
				{
					short shortCount = (remainCount < inventory->count) ? remainCount : inventory->count;

					itemUse->table[ rowCount ].inventoryIdx = inventory->idx;
					itemUse->table[ rowCount ].count        = shortCount;
					rowCount++;

					// 쿨타임 적용.
					COOLTIMEPOOL->ApplyCooltime( &mCooltimeRoot, inventory );

					// 가상처리; 차감을 완성 해 놓는다.
					UpdateInventory( inventory, (-shortCount) ); // inventory->count = inventory->count - shortCount;

					// 가상처리; 사용된 아이템 정보를 전송한다.
					SendItemUse( inventory );

					// 가상처리; 사용이 완료된 인벤토리 삭제.
					if ( inventory->count == 0 )
						RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );

					// 남은갯수 정산.
					remainCount = remainCount - shortCount;
				}
			}
		}

		CalcItemWeight( );

		return NETWORK2->SendSQL( handle, sizeof(ITEM_USE) );
	}
	else
		return false;
}

// IsItemUseSkill Method.
long cPlayer::IsItemMapChange(short number, long itemDefineIndex, long itemAbilityInfluence)
{
	TB_INVENTORY* inventory = SelectInventory( number );
	if ( IsInventory( inventory ) == true )
	{
		if ( inventory->itemDefineIndex == itemDefineIndex )
		{
			TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
			if ( itemDefine != NULL )
			{
				if ( itemDefine->type == ITEM_MATERIAL && itemDefine->subType == ITEM_MATERIAL_MAP )
				{
					TB_ITEM_ABILITY* itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
					if ( itemAbility != NULL )
					{
						if ( itemAbility->influence_idx == itemAbilityInfluence )
						{
							// 쿨타임 검사.
							if ( COOLTIMEPOOL->IsCooltime( &mCooltimeRoot, itemDefineIndex ) != true )
								return inventory->idx;
							else
								return 0;
						}
					}
					else
						NETWORK2->PostServerEvent( "ERROR - cPlayer::IsItemUseMapChange:TB_ITEM_ABILITY::IDX(=%d)", itemDefine->idxAbility );
				}
			}
			else
				NETWORK2->PostServerEvent( "ERROR - cPlayer::IsItemUseMapChange:TB_ITEM_DEFINE::IDX(=%d)", inventory->itemDefineIdx );
		}
	}
	return 0;
}

// IsItemUseSkill Method.
long cPlayer::IsItemVehicle(short number, long itemDefineIndex, long itemAbilityInfluence)
{
	TB_INVENTORY* inventory = SelectInventory( number );
	if ( IsInventory( inventory ) == true )
	{
		if ( inventory->itemDefineIndex == itemDefineIndex )
		{
			TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
			if ( itemDefine != NULL )
			{
				if ( itemDefine->type == ITEM_MATERIAL && itemDefine->subType == ITEM_MATERIAL_VEHICLE )
				{
					TB_ITEM_ABILITY* itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
					if ( itemAbility != NULL )
					{
						if ( itemAbility->influence_idx == itemAbilityInfluence )
						{
							// 쿨타임 검사.
							if ( COOLTIMEPOOL->IsCooltime( &mCooltimeRoot, itemDefineIndex ) != true )
								return inventory->itemDefineIndex;
							else
								return 0;
						}
					}
					else
						NETWORK2->PostServerEvent( "ERROR - cPlayer::IsItemUseMapChange:TB_ITEM_ABILITY::IDX(=%d)", itemDefine->idxAbility );
				}
			}
			else
				NETWORK2->PostServerEvent( "ERROR - cPlayer::IsItemUseMapChange:TB_ITEM_DEFINE::IDX(=%d)", inventory->itemDefineIdx );
		}
	}
	return 0;
}

// ItemUseMapChange Method.
bool cPlayer::ItemUseMapChange(long inventroyIdx)
{
	TB_INVENTORY* inventory = SelectInventory( INVENTORY_BAG_BEGIN );
	bool          apply     = false;

	HANDLE        handle   = NULL;
	ITEM_USE*     itemUse  = (ITEM_USE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_USE );
	long&         rowCount = itemUse->rowCount;

	itemUse->characterIdx = mObject.index;

	for ( int i = INVENTORY_BAG_BEGIN; (i <= INVENTORY_BAG_END) && (apply == false); i++, inventory++ )
	{
		if ( inventory->idx == inventroyIdx )
		{
			itemUse->table[ rowCount ].inventoryIdx = inventory->idx;
			itemUse->table[ rowCount ].count        = 1;
			rowCount++;

			// 쿨타임 적용.
			COOLTIMEPOOL->ApplyCooltime( &mCooltimeRoot, inventory );

			// 가상처리; 차감을 완성 해 놓는다.
			UpdateInventory( inventory, (-1) ); // inventory->count = inventory->count - 1;

			// 가상처리; 사용된 아이템 정보를 전송한다.
			SendItemUse( inventory );

			// 가상처리; 사용이 완료된 인벤토리 삭제.
			if ( inventory->count == 0 )
				RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );

			// 적용완료.
			apply = true;
		}
	}

	CalcItemWeight( );

	if ( apply == true )
	{
		return NETWORK2->SendSQL( handle, sizeof(ITEM_USE) );
	}
	else
	{
		NETWORK2->ReleaseSQL( handle, sizeof(ITEM_USE) );
		return false;
	}
}

// SendItemUse Method.
bool cPlayer::SendItemUse(TB_INVENTORY* inventory)
{
	HANDLE            handle  = NULL;
	MSG_RES_ITEM_USE* sendMsg = (MSG_RES_ITEM_USE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_ITEM, NM_ITEM_USE_RES );
	if ( sendMsg != NULL )
	{
		Inventory2sInventory( &sendMsg->inventory, inventory );
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_ITEM_USE) );
	}
	return false;
}

// IsItemReward Method - 분해될 아이템은 비어있는 인벤토리로 간주한다.
unsigned int cPlayer::IsItemReward(long inventoryIdx, eDisjointItem* items, int maxCount)
{
	TB_ITEM_DEFINE* itemDefine  = NULL;
	TB_INVENTORY*   inventory   = NULL;
	short           inventoryCount;

	int             emptyCount  = 0;
	int             applyCount  = 0;

	// 남은 인벤토리 수량 검토 - 분해될 아이템은 남은 인벤토리로 처리.
	inventory = SelectInventory( INVENTORY_BAG_BEGIN );
	for ( int i0 = INVENTORY_BAG_BEGIN; i0 <= INVENTORY_BAG_END && emptyCount < maxCount; i0++, inventory++ )
	{
		if ( (inventory->idx == 0) || (inventory->idx == inventoryIdx) )
			emptyCount++;
	}

	// 남은 인벤토리 수량이 보상받을 아이템 수량보다 적은 경우, 겹치기 가능 아이템 검토.
	if ( emptyCount < maxCount )
	{
		for ( int i0 = 0; i0 < maxCount; i0++, items++ )
		{
			itemDefine = ITEMMANAGER->GetItemDefineByIndex( items->itemDefineIndex );

			if ( itemDefine == NULL )
			{
				return applyCount;
			}
			else if ( (!items->inventoryCount) || (itemDefine->capacity < items->inventoryCount) )
			{
				return applyCount;
			}
			else if ( (emptyCount + applyCount) < maxCount )
			{
				inventory      = SelectInventory( INVENTORY_BAG_BEGIN );
				inventoryCount = 0;

				for ( int i1 = INVENTORY_BAG_BEGIN; i1 <= INVENTORY_BAG_END && inventoryCount < items->inventoryCount; i1++, inventory++ )
				{
					// 분해될 아이템은 무시
					if ( inventory->idx == inventoryIdx )
						continue;

					// 남은 수량 누적.
					if ( inventory->itemDefineIndex == (long)items->itemDefineIndex )
						inventoryCount += (itemDefine->capacity - inventory->count);
				}

				// 겹치기 확인후 적용 행수 증가.
				if ( inventoryCount >= items->inventoryCount )
				{
					applyCount++;
				}
				// 겹치기 실패시 남은 인벤토리 차감후 적용행수 증가.
				else if ( emptyCount > 0 )
				{
					emptyCount--;
					applyCount++;
				}
			}
			else
				applyCount++;
		}
	}
	// 남은 인벤토리 수량이 보상받을 아이템 수량보다 큰 경우, 아이템 정의 테이블만 검토.
	else
	{
		for ( int i0 = 0; i0 < maxCount; i0++, items++ )
		{
			itemDefine = ITEMMANAGER->GetItemDefineByIndex( items->itemDefineIndex );

			if ( itemDefine == NULL )
			{
				return applyCount;
			}
			else if ( (!items->inventoryCount) || (itemDefine->capacity < items->inventoryCount) )
			{
				return applyCount;
			}
			else
				applyCount++;
		}
	}

	return applyCount;
}

// IsItemReward Method.
unsigned int cPlayer::IsItemReward(sQuestItem* items, int maxCount)
{
	TB_ITEM_DEFINE* itemDefine  = NULL;
	TB_INVENTORY*   inventory   = NULL;
	unsigned int    inventoryCount;

	int             emptyCount  = 0;
	int             applyCount  = 0;

	// 남은 인벤토리 수량 검토.
	inventory = SelectInventory( INVENTORY_BAG_BEGIN );
	for ( int i0 = INVENTORY_BAG_BEGIN; i0 <= INVENTORY_BAG_END && emptyCount < maxCount; i0++, inventory++ )
	{
		if ( !inventory->idx )
			emptyCount++;
	}

	// 남은 인벤토리 수량이 보상받을 아이템 수량보다 적은 경우, 겹치기 가능 아이템 검토.
	if ( emptyCount < maxCount )
	{
		for ( int i0 = 0; i0 < maxCount; i0++, items++ )
		{
			itemDefine = ITEMMANAGER->GetItemDefineByIndex( items->itemIndex );

			if ( itemDefine == NULL )
			{
				return applyCount;
			}
			else if ( (!items->count) || (itemDefine->capacity < (short)items->count) )
			{
				return applyCount;
			}
			else if ( (emptyCount + applyCount) < maxCount )
			{
				inventory      = SelectInventory( INVENTORY_BAG_BEGIN );
				inventoryCount = 0;

				for ( int i1 = INVENTORY_BAG_BEGIN; i1 <= INVENTORY_BAG_END && inventoryCount < items->count; i1++, inventory++ )
				{
					if ( inventory->itemDefineIndex == (long)items->itemIndex )
					{
						inventoryCount += (itemDefine->capacity - inventory->count);
					}
				}

				// 겹치기 확인후 적용 행수 증가.
				if ( inventoryCount >= items->count )
				{
					applyCount++;
				}
				// 겹치기 실패시 남은 인벤토리 차감후 적용행수 증가.
				else if ( emptyCount > 0 )
				{
					emptyCount--;
					applyCount++;
				}
			}
			else
				applyCount++;
		}
	}
	// 남은 인벤토리 수량이 보상받을 아이템 수량보다 큰 경우, 아이템 정의 테이블만 검토.
	else
	{
		for ( int i0 = 0; i0 < maxCount; i0++, items++ )
		{
			itemDefine = ITEMMANAGER->GetItemDefineByIndex( items->itemIndex );

			if ( itemDefine == NULL )
			{
				return applyCount;
			}
			else if ( (!items->count) || (itemDefine->capacity < (short)items->count) )
			{
				return applyCount;
			}
			else
				applyCount++;
		}
	}

	return applyCount;
}

// IsItemTake Method.
unsigned int cPlayer::IsItemTake(sQuestItem* items, int maxCount)
{
	TB_INVENTORY* inventory  = NULL;
	int           remainCount;

	int           applyCount = 0;

	for ( int i0 = 0; i0 < maxCount; i0++, items++ )
	{
		inventory   = SelectInventory( INVENTORY_BAG0_BEGIN );
		remainCount = items->count;
		for ( int i1 = INVENTORY_BAG0_BEGIN; i1 <= INVENTORY_QUEST_END; i1++, inventory++ )
		{
			if ( inventory->itemDefineIndex == (long)items->itemIndex )
			{
				remainCount -= inventory->count;
				if ( !(remainCount > 0) )
				{
					applyCount++;
					break;
				}
			}
		}
	}

	return applyCount;
}

/// 해당 퀘스트 아이템 검사 
unsigned int cPlayer::GetQuestItemCount( long itemDefineIndex, int maxCount )
{
	TB_INVENTORY* inventory  = SelectInventory( INVENTORY_QUEST_BEGIN );
	int           totalCount = 0;

	for ( int i = INVENTORY_QUEST_BEGIN; i <= INVENTORY_QUEST_END; i++, inventory++ )
	{
		if ( inventory->idx > 0 &&
			 inventory->count > 0 &&
			 inventory->itemDefineIndex == itemDefineIndex )
		{
			totalCount += inventory->count;
			if ( totalCount >= maxCount )
				return maxCount;
		}
	}

	return totalCount;
}

// EquipItems Method.
void cPlayer::EquipItems( )
{
	// 활성 무기정보 갱신.
	short leftHand;
	short rightHand;

	if ( mItemActiveWeapon == ItemActiveFront )
	{
		leftHand  = INVENTORY_HAND_LEFT2;
		rightHand = INVENTORY_HAND_RIGHT2;
	}
	else
	{
		leftHand  = INVENTORY_HAND_LEFT1;
		rightHand = INVENTORY_HAND_RIGHT1;
	}

	// 캐릭터의 무기 상  태 정보 갱신.
	{
		int           weaponState = eWEAPON_STATE_NONE;
		TB_INVENTORY* inventory;

		inventory = SelectInventory( leftHand );
		for ( short i = leftHand; i <= rightHand; i++, inventory++ )
		{
			if ( IsInventory( inventory ) )
			{
				if ( ITEMMANAGER->GetItemDefineType( inventory->itemDefineIdx ) == ITEM_WEAPON )
				{
					switch ( ITEMMANAGER->GetItemDefineSubType( inventory->itemDefineIdx ) )
					{
					case ITEM_WEAPON_SWORD:  weaponState += eWEAPON_STATE_SWORD;       break;
					case ITEM_WEAPON_BLADE:  weaponState += eWEAPON_STATE_LONGSWORD;   break;
					case ITEM_WEAPON_DUAL:   weaponState += eWEAPON_STATE_DOUBLESWORD; break;
					case ITEM_WEAPON_CUTTER: weaponState += eWEAPON_STATE_SHORTSWORD;  break;
					case ITEM_WEAPON_GUN:    weaponState += eWEAPON_STATE_GUN;         break;
					case ITEM_WEAPON_STAFF:  weaponState += eWEAPON_STATE_STAFF;       break;
					case ITEM_WEAPON_SHIELD: weaponState += eWEAPON_STATE_SHEILD;      break;
					}
				}
			}
		}

		mPlayerWeaponState = (eWEAPON_STATE)weaponState;
	}
	// 캐릭터의 착용 아이템 정보 갱신.
	{
		// 방어구
		unsigned long wearHat   = SelectInventory( INVENTORY_WEAR_HAT   )->itemDefineIndex;
		unsigned long wearBody1 = SelectInventory( INVENTORY_WEAR_BODY1 )->itemDefineIndex;
		unsigned long wearBody2 = SelectInventory( INVENTORY_WEAR_BODY2 )->itemDefineIndex;
		unsigned long wearHand  = SelectInventory( INVENTORY_WEAR_HAND  )->itemDefineIndex;
		unsigned long wearFoot  = SelectInventory( INVENTORY_WEAR_FOOT  )->itemDefineIndex;

		SetWearInfoWithSendMsg( eWEAR_HAT,   wearHat   ); // 모자
		SetWearInfoWithSendMsg( eWEAR_BODY1, wearBody1 ); // 바디1 - 상의
		SetWearInfoWithSendMsg( eWEAR_BODY2, wearBody2 ); // 바디2 - 하의
		SetWearInfoWithSendMsg( eWEAR_HAND,  wearHand  ); // 장갑
		SetWearInfoWithSendMsg( eWEAR_FOOT,  wearFoot  ); // 신발

		// 액세서리
		mPlayerWearInfo.Earring  = SelectInventory( INVENTORY_WEAR_EARRING  )->itemDefineIndex;
		mPlayerWearInfo.Necklace = SelectInventory( INVENTORY_WEAR_NECKLACE )->itemDefineIndex;
		mPlayerWearInfo.Brooch   = SelectInventory( INVENTORY_WEAR_BROOCH   )->itemDefineIndex;
		mPlayerWearInfo.Bracelet = SelectInventory( INVENTORY_WEAR_BRACELET )->itemDefineIndex;
		mPlayerWearInfo.Ring     = SelectInventory( INVENTORY_WEAR_RING     )->itemDefineIndex;

		// 무기
		SetWeaponInfoWithSendMsg( SelectInventory( leftHand  ), SelectInventory( rightHand ) );
	}

	// 캐릭터의 방어 아이템 세트 갱신.
	ResetArmorSets( );

	// 캐릭터의 착용 아이템 속성 갱신.
	{
		TB_INVENTORY*     inventory;
		TB_ITEM_ABILITY*  itemAbility;
		TB_ITEM_ENHANCED* itemEnhanced;

		memset( &mEquipAbility, 0, sizeof(mEquipAbility) );

		// 아이템 누적 속성 - 착용 방어구 & 액세서리 & 카드.
		inventory = SelectInventory( INVENTORY_WEAR_BEGIN );
		for ( int i = INVENTORY_WEAR_BEGIN; i <= INVENTORY_WEAR_END; i++, inventory++ )
		{
			if ( IsInventory( inventory ) )
			{
				itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
				if ( itemAbility != NULL )
				{
					AddEquipAbility( itemAbility );
				}
				if ( !(inventory->enhanced < MIN_ITEM_ENHANCED || inventory->enhanced > MAX_ITEM_ENHANCED) )
				{
					itemEnhanced = ITEMMANAGER->GetItemEnhanced( inventory->itemDefineIdx );
					if ( itemEnhanced != NULL )
					{
						AddEquipEnhanced( itemEnhanced, inventory->enhanced );
					}
				}
				AddEquipCards( inventory );
			}
		}

		// 아이템 누적 속성 - 착용 무기.
		inventory = SelectInventory( leftHand );
		for ( int i = leftHand; i <= rightHand; i++, inventory++ )
		{
			if ( IsInventory( inventory ) )
			{
				itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
				if ( itemAbility != NULL )
				{
					AddEquipAbility( itemAbility );
				}
				if ( !(inventory->enhanced < MIN_ITEM_ENHANCED || inventory->enhanced > MAX_ITEM_ENHANCED) )
				{
					itemEnhanced = ITEMMANAGER->GetItemEnhanced( inventory->itemDefineIdx );
					if ( itemEnhanced != NULL )
					{
						AddEquipEnhanced( itemEnhanced, inventory->enhanced );
					}
				}
				AddEquipCards( inventory );
			}
		}
	}
}

// CalcItemWeight Method.
void cPlayer::CalcItemWeight( )
{
	TB_INVENTORY*   inventory;
	TB_ITEM_DEFINE* itemDefine;

	mItemWeight = 0;

	inventory = SelectInventory( INVENTORY_CHARACTER_BEGIN );
	for ( int i = INVENTORY_CHARACTER_BEGIN; i <= INVENTORY_CHARACTER_END; ++i, ++inventory )
	{
		itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
		if ( itemDefine != NULL )
		{
			mItemWeight = mItemWeight + (itemDefine->weight * inventory->count);
		}
	}

	/// 퀘스트 갱신
	UpdateDutyItem();
}

void cPlayer::AddEquipAbility(short opt, short value)
{
	#define Warning4244(V,A) V = V + A

	switch ( opt )
	{
	case ITEM_OPT_STR:	// 힘
		Warning4244( mEquipAbility.mOptStr, value );
		break;
	case ITEM_OPT_DEX:	// 민첩
		Warning4244( mEquipAbility.mOptDex, value );
		break;
	case ITEM_OPT_CON:	// 체력
		Warning4244( mEquipAbility.mOptCon, value );
		break;
	case ITEM_OPT_INT:	// 지능
		Warning4244( mEquipAbility.mOptInt, value );
		break;
	case ITEM_OPT_WIS:	// 지혜
		Warning4244( mEquipAbility.mOptWis, value );
		break;
	case ITEM_OPT_ALL:	// 모두(힘:민첩:체력:지능:지혜)
		Warning4244( mEquipAbility.mOptStr, value );
		Warning4244( mEquipAbility.mOptDex, value );
		Warning4244( mEquipAbility.mOptCon, value );
		Warning4244( mEquipAbility.mOptInt, value );
		Warning4244( mEquipAbility.mOptWis, value );
		break;

	// Option - Plus
	case ITEM_OPT_PLUS_PHYSIC_ATTACK:	// 물리공격력
		Warning4244( mEquipAbility.mOptPlusPhysicAttack, value );
		break;
	case ITEM_OPT_PLUS_MAGIC_ATTACK:	// 마법공격력
		Warning4244( mEquipAbility.mOptPlusMagicAttack, value );
		break;
	case ITEM_OPT_PLUS_PHYSIC_DEFENSE:	// 물리방어력
		Warning4244( mEquipAbility.mOptPlusPhysicDefense, value );
		break;
	case ITEM_OPT_PLUS_MAGIC_DEFENSE:	// 마법방어력
		Warning4244( mEquipAbility.mOptPlusMagicDefense, value );
		break;
	case ITEM_OPT_PLUS_PHYSIC_ATTACK_RATE:	// 물리명중률
		Warning4244( mEquipAbility.mOptPlusPhysicAttackRate, value );
		break;
	case ITEM_OPT_PLUS_MAGIC_ATTACK_RATE:	// 마법명중률
		Warning4244( mEquipAbility.mOptPlusMagicAttack, value );
		break;
	case ITEM_OPT_PLUS_PHYSIC_CRITICAL:	// 물리크리티컬
		Warning4244( mEquipAbility.mOptPlusPhysicCritical, value );
		break;
	case ITEM_OPT_PLUS_MAGIC_CRITICAL:	// 마법크리티컬
		Warning4244( mEquipAbility.mOptPlusMagicCritical, value );
		break;
	case ITEM_OPT_PLUS_DOD:	// 회피
		Warning4244( mEquipAbility.mOptPlusDod, value );
		break;
	case ITEM_OPT_PLUS_WEI:	// 무게
		Warning4244( mEquipAbility.mOptPlusWei, value );
		break;
	case ITEM_OPT_PLUS_MAX_HP:	// 최대량 HP
		Warning4244( mEquipAbility.mOptPlusMaxHp, value );
		break;
	case ITEM_OPT_PLUS_MAX_MP:	// 최대량 MP
		Warning4244( mEquipAbility.mOptPlusMaxMp, value );
		break;
	case ITEM_OPT_PLUS_MOV:	// 이동
		Warning4244( mEquipAbility.mOptPlusMov, value );
		break;
	case ITEM_OPT_PLUS_HP:	// HP 회복
		Warning4244( mEquipAbility.mOptPlusHp, value );
		break;
	case ITEM_OPT_PLUS_MP:	// MP 회복
		Warning4244( mEquipAbility.mOptPlusMp, value );
		break;

	// Option - Percent
	case ITEM_OPT_PERCENT_PHYSIC_ATTACK:	// 물리공격력
		Warning4244( mEquipAbility.mOptPercentPhysicAttack, value );
		break;
	case ITEM_OPT_PERCENT_MAGIC_ATTACK:	// 마법공격력
		Warning4244( mEquipAbility.mOptPercentMagicAttack, value );
		break;
	case ITEM_OPT_PERCENT_PHYSIC_DEFENSE:	// 물리방어력
		Warning4244( mEquipAbility.mOptPercentPhysicDefense, value );
		break;
	case ITEM_OPT_PERCENT_MAGIC_DEFENSE:	// 마법방어력
		Warning4244( mEquipAbility.mOptPercentMagicDefense, value );
		break;
	case ITEM_OPT_PERCENT_PHYSIC_ATTACK_RATE:	// 물리명중률
		Warning4244( mEquipAbility.mOptPercentPhysicAttackRate, value );
		break;
	case ITEM_OPT_PERCENT_MAGIC_ATTACK_RATE:	// 마법명중률
		Warning4244( mEquipAbility.mOptPercentMagicAttack, value );
		break;
	case ITEM_OPT_PERCENT_PHYSIC_CRITICAL:	// 물리크리티컬
		Warning4244( mEquipAbility.mOptPercentPhysicCritical, value );
		break;
	case ITEM_OPT_PERCENT_MAGIC_CRITICAL:	// 마법크리티컬
		Warning4244( mEquipAbility.mOptPercentMagicCritical, value );
		break;
	case ITEM_OPT_PERCENT_DOD:	// 회피
		Warning4244( mEquipAbility.mOptPercentDod, value );
		break;
	case ITEM_OPT_PERCENT_WEI:	// 무게
		Warning4244( mEquipAbility.mOptPercentWei, value );
		break;
	case ITEM_OPT_PERCENT_MAX_HP:	// 최대량 HP
		Warning4244( mEquipAbility.mOptPercentMaxHp, value );
		break;
	case ITEM_OPT_PERCENT_MAX_MP:	// 최대량 MP
		Warning4244( mEquipAbility.mOptPercentMaxMp, value );
		break;
	case ITEM_OPT_PERCENT_MOV:	// 이동
		Warning4244( mEquipAbility.mOptPercentMov, value );
		break;
	case ITEM_OPT_PERCENT_HP:	// HP 회복
		Warning4244( mEquipAbility.mOptPercentHp, value );
		break;
	case ITEM_OPT_PERCENT_MP:	// MP 회복
		Warning4244( mEquipAbility.mOptPercentMp, value );
		break;

	// Option - Etc
	case ITEM_OPT_PERCENT_DRAIN_HP:				// HP 흡수
		Warning4244( mEquipAbility.mOptPercentDrainHp, value );
		break;
	case ITEM_OPT_PERCENT_DRAIN_MP:				// MP 흡수
		Warning4244( mEquipAbility.mOptPercentDrainMp, value );
		break;
	case ITEM_OPT_PERCENT_SKILL_HEAL_INC:		// 스킬 힐 회복량 증가
		Warning4244( mEquipAbility.mOptPercentSkillHealInc, value );
		break;
	case ITEM_OPT_PERCENT_DAMAGE_DEC:			// 데미지 흡수/감소
		Warning4244( mEquipAbility.mOptPercentDamageDec, value );
		break;
	case ITEM_OPT_PERCENT_DAMAGE_REF:			// 데미지 반사
		Warning4244( mEquipAbility.mOptPercentDamageRef, value );
		break;
	}
}

// AddEquipAbility Method.
void cPlayer::AddEquipAbility(TB_ITEM_ABILITY* itemAbility)
{
	// 기본 값 적용.
	mEquipAbility.mPhysicDefense    = mEquipAbility.mPhysicDefense    + itemAbility->p_def;
	mEquipAbility.mMagicDefense     = mEquipAbility.mMagicDefense     + itemAbility->m_def;
	mEquipAbility.mAttackRange      = mEquipAbility.mAttackRange      + itemAbility->atk_range;
	mEquipAbility.mAttackSpeed      = mEquipAbility.mAttackSpeed      + itemAbility->atk_spd;

	mEquipAbility.mPhysicMinAttack  = mEquipAbility.mPhysicMinAttack  + itemAbility->min_p_atk;
	mEquipAbility.mPhysicMaxAttack  = mEquipAbility.mPhysicMaxAttack  + itemAbility->max_p_atk;
	mEquipAbility.mMagicMinAttack   = mEquipAbility.mMagicMinAttack   + itemAbility->min_m_atk;
	mEquipAbility.mMagicMaxAttack   = mEquipAbility.mMagicMaxAttack   + itemAbility->max_m_atk;

	// 옵션 값 적용.
	AddEquipAbility( itemAbility->opt01_index, itemAbility->opt01_value );
	AddEquipAbility( itemAbility->opt02_index, itemAbility->opt02_value );
	AddEquipAbility( itemAbility->opt03_index, itemAbility->opt03_value );
	AddEquipAbility( itemAbility->opt04_index, itemAbility->opt04_value );
	AddEquipAbility( itemAbility->opt05_index, itemAbility->opt05_value );
	AddEquipAbility( itemAbility->opt06_index, itemAbility->opt06_value );
	AddEquipAbility( itemAbility->opt07_index, itemAbility->opt07_value );
	AddEquipAbility( itemAbility->opt08_index, itemAbility->opt08_value );
	AddEquipAbility( itemAbility->opt09_index, itemAbility->opt09_value );
	AddEquipAbility( itemAbility->opt10_index, itemAbility->opt10_value );
}

// AddEquipEnhanced Method - 강화단계는 MIN_ITEM_ENHANCED 에서 MAX_ITEM_ENHANCED 까지.
void cPlayer::AddEquipEnhanced(TB_ITEM_ENHANCED* itemEnhanced, BYTE enhanced)
{
	short enhancedPlus = itemEnhanced->IndPlus[ (enhanced-MIN_ITEM_ENHANCED) ];

	if ( itemEnhanced->enhanced1 == ITEM_ENHANCED_PHYSIC_ATTACK )
	{
		mEquipAbility.mPhysicMinAttack = mEquipAbility.mPhysicMinAttack + enhancedPlus;
		mEquipAbility.mPhysicMaxAttack = mEquipAbility.mPhysicMaxAttack + enhancedPlus;
	}
	else if ( itemEnhanced->enhanced1 == ITEM_ENHANCED_MAGIC_ATTACK )
	{
		mEquipAbility.mMagicMinAttack = mEquipAbility.mMagicMinAttack + enhancedPlus;
		mEquipAbility.mMagicMaxAttack = mEquipAbility.mMagicMaxAttack + enhancedPlus;
	}
	else if ( itemEnhanced->enhanced1 == ITEM_ENHANCED_PHYSIC_DEFENSE )
	{
		mEquipAbility.mPhysicDefense = mEquipAbility.mPhysicDefense + enhancedPlus;
	}
	else if ( itemEnhanced->enhanced1 == ITEM_ENHANCED_MAGIC_DEFENSE )
	{
		mEquipAbility.mMagicDefense = mEquipAbility.mMagicDefense + enhancedPlus;
	}

	if ( itemEnhanced->enhanced2 == ITEM_ENHANCED_PHYSIC_ATTACK )
	{
		mEquipAbility.mPhysicMinAttack = mEquipAbility.mPhysicMinAttack + enhancedPlus;
		mEquipAbility.mPhysicMaxAttack = mEquipAbility.mPhysicMaxAttack + enhancedPlus;
	}
	else if ( itemEnhanced->enhanced2 == ITEM_ENHANCED_MAGIC_ATTACK )
	{
		mEquipAbility.mMagicMinAttack = mEquipAbility.mMagicMinAttack + enhancedPlus;
		mEquipAbility.mMagicMaxAttack = mEquipAbility.mMagicMaxAttack + enhancedPlus;
	}
	else if ( itemEnhanced->enhanced2 == ITEM_ENHANCED_PHYSIC_DEFENSE )
	{
		mEquipAbility.mPhysicDefense = mEquipAbility.mPhysicDefense + enhancedPlus;
	}
	else if ( itemEnhanced->enhanced2== ITEM_ENHANCED_MAGIC_DEFENSE )
	{
		mEquipAbility.mMagicDefense = mEquipAbility.mMagicDefense + enhancedPlus;
	}
}

// AddEquipCards Method.
void cPlayer::AddEquipCards(TB_INVENTORY* inventory)
{
	TB_ITEM_DEFINE*  itemDefine;
	TB_ITEM_ABILITY* itemAbility;

	itemDefine = ITEMMANAGER->GetItemDefineByIndex( inventory->cardSlot1 );
	if ( itemDefine != NULL )
	{
		itemAbility = ITEMMANAGER->GetItemAbility( itemDefine->idx );
		if ( itemAbility != NULL )
		{
			AddEquipAbility( itemAbility );
		}
	}
	itemDefine = ITEMMANAGER->GetItemDefineByIndex( inventory->cardSlot2 );
	if ( itemDefine != NULL )
	{
		itemAbility = ITEMMANAGER->GetItemAbility( itemDefine->idx );
		if ( itemAbility != NULL )
		{
			AddEquipAbility( itemAbility );
		}
	}
	itemDefine = ITEMMANAGER->GetItemDefineByIndex( inventory->cardSlot3 );
	if ( itemDefine != NULL )
	{
		itemAbility = ITEMMANAGER->GetItemAbility( itemDefine->idx );
		if ( itemAbility != NULL )
		{
			AddEquipAbility( itemAbility );
		}
	}
	itemDefine = ITEMMANAGER->GetItemDefineByIndex( inventory->cardSlot4 );
	if ( itemDefine != NULL )
	{
		itemAbility = ITEMMANAGER->GetItemAbility( itemDefine->idx );
		if ( itemAbility != NULL )
		{
			AddEquipAbility( itemAbility );
		}
	}
	itemDefine = ITEMMANAGER->GetItemDefineByIndex( inventory->cardSlot5 );
	if ( itemDefine != NULL )
	{
		itemAbility = ITEMMANAGER->GetItemAbility( itemDefine->idx );
		if ( itemAbility != NULL )
		{
			AddEquipAbility( itemAbility );
		}
	}
}

// ResetArmorSets Method.
void cPlayer::ResetArmorSets( )
{
	TB_ITEM_DEFINE*  itemDefine;
	TB_ITEM_ABILITY* itemAbility;
	TB_INVENTORY*    inventory;

	// CLEAR - ARMOR SETS
	mArmorSets = ARMOR_SETS_NONE;

	// ARMOR SETS - HAT
	inventory   = SelectInventory( INVENTORY_WEAR_HAT );
	itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
	if ( itemAbility != NULL )
	{
		switch ( itemAbility->def_class )
		{
		case 1:
			mArmorSets = mArmorSets | ROBES_HAT;
			break; // 천
		case 2:
			mArmorSets = mArmorSets | LIGHT_ARMOR_HAT;
			break; // 가죽
		case 3:
			mArmorSets = mArmorSets | HEAVY_ARMOR_HAT;
			break; // 금속
		}
	}

	// ARMOR SETS - UPPER(BODY1)
	inventory  = SelectInventory( INVENTORY_WEAR_BODY1 );
	itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
	if ( itemDefine != NULL )
	{
		if ( itemDefine->subType == ITEM_WEAR_ONEPIECE )
		{
			itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
			switch ( itemAbility->def_class )
			{
			case 1:
				mArmorSets = mArmorSets | ROBES_ONEPIECE;
				break; // 천
			case 2:
				mArmorSets = mArmorSets | LIGHT_ARMOR_ONEPIECE;
				break; // 가죽
			case 3:
				mArmorSets = mArmorSets | HEAVY_ARMOR_ONEPIECE;
				break; // 금속
			}
		}
		else
		{
			itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
			if ( itemAbility != NULL )
			{
				switch ( itemAbility->def_class )
				{
				case 1:
					mArmorSets = mArmorSets | ROBES_UPPER;
					break; // 천
				case 2:
					mArmorSets = mArmorSets | LIGHT_ARMOR_UPPER;
					break; // 가죽
				case 3:
					mArmorSets = mArmorSets | HEAVY_ARMOR_UPPER;
					break; // 금속
				}
			}
		}
	}

	// ARMOR SETS - LOWER(BODY2)
	inventory  = SelectInventory( INVENTORY_WEAR_BODY2 );
	itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
	if ( itemDefine != NULL )
	{
		if ( itemDefine->subType == ITEM_WEAR_ONEPIECE )
		{
			itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
			switch ( itemAbility->def_class )
			{
			case 1:
				mArmorSets = mArmorSets | ROBES_ONEPIECE;
				break; // 천
			case 2:
				mArmorSets = mArmorSets | LIGHT_ARMOR_ONEPIECE;
				break; // 가죽
			case 3:
				mArmorSets = mArmorSets | HEAVY_ARMOR_ONEPIECE;
				break; // 금속
			}
		}
		else
		{
			itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
			if ( itemAbility != NULL )
			{
				switch ( itemAbility->def_class )
				{
				case 1:
					mArmorSets = mArmorSets | ROBES_LOWER;
					break; // 천
				case 2:
					mArmorSets = mArmorSets | LIGHT_ARMOR_LOWER;
					break; // 가죽
				case 3:
					mArmorSets = mArmorSets | HEAVY_ARMOR_LOWER;
					break; // 금속
				}
			}
		}
	}

	// ARMOR SETS - HANDS(HAND)
	inventory   = SelectInventory( INVENTORY_WEAR_HAND );
	itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
	if ( itemAbility != NULL )
	{
		switch ( itemAbility->def_class )
		{
		case 1:
			mArmorSets = mArmorSets | ROBES_HANDS;
			break; // 천
		case 2:
			mArmorSets = mArmorSets | LIGHT_ARMOR_HANDS;
			break; // 가죽
		case 3:
			mArmorSets = mArmorSets | HEAVY_ARMOR_HANDS;
			break; // 금속
		}
	}

	// ARMOR SETS - FEET(HAND)
	inventory   = SelectInventory( INVENTORY_WEAR_FOOT );
	itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
	if ( itemAbility != NULL )
	{
		switch ( itemAbility->def_class )
		{
		case 1:
			mArmorSets = mArmorSets | ROBES_FEET;
			break; // 천
		case 2:
			mArmorSets = mArmorSets | LIGHT_ARMOR_FEET;
			break; // 가죽
		case 3:
			mArmorSets = mArmorSets | HEAVY_ARMOR_FEET;
			break; // 금속
		}
	}
}

// AddCooltime Method.
void cPlayer::AddCooltime(TB_INVENTORY_COOLTIME* table)
{
	PerCooltime* perCooltime = COOLTIMEPOOL->GetCooltime( &mCooltimeRoot, table->itemDefineIndex );

	if ( perCooltime != NULL )
	{
		time_t    ltime;
		struct tm validThru;

		time( &ltime );
		validThru = *localtime( &ltime );
		validThru.tm_sec += (table->cooltime/1000);

		perCooltime->cooltime1 = table->cooltime1;
		perCooltime->cooltime2 = table->cooltime2;
		perCooltime->validThru = mktime( &validThru );
	}
}

// CheckInventory Method.
bool cPlayer::CheckInventory(long itemDefineIndex, int itemCount)
{
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( itemDefineIndex );

	if ( itemDefine != NULL )
	{
		TB_INVENTORY* inventory   = SelectInventory( INVENTORY_BAG_BEGIN );
		int           remainCount = itemCount;

		for ( int i = INVENTORY_BAG_BEGIN; (i <= INVENTORY_BAG_END) && (remainCount > 0); i++, inventory++ )
		{
			if ( IsInventory( inventory ) == false )
			{
				remainCount -= itemDefine->capacity;
			}
			else if ( inventory->itemDefineIndex == itemDefineIndex )
			{
				remainCount -= (itemDefine->capacity - inventory->count);
			}
		}

		return (remainCount > 0) ? false : true;
	}

	return false;
}

// XCopyInventory Method.
TB_INVENTORY* cPlayer::XCopyInventory(TB_INVENTORY* destination, TB_INVENTORY* source, XCOPY_INVENTORY_OPT opt)
{
	size_t length = sizeof(TB_INVENTORY);
	switch ( opt )
	{
	case XCOPY_INVENTORY_ALL:
		ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, source->itemDefineIndex, source->number, (source->count - destination->count) );
		memcpy( destination, source, length );
		break;
	case XCOPY_INVENTORY_COUNT:
		ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, source->itemDefineIndex, source->number, (source->count - destination->count) );
		destination->count = source->count;
		break;
	case XCOPY_INVENTORY_SWAP:
		{
			TB_INVENTORY tempInv = (*destination);
			short        tempNum;

			ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, source->itemDefineIndex, destination->number, source->count );

				tempNum = destination->number;
				memcpy( destination, source, length );
				destination->number = tempNum;

			ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, source->itemDefineIndex, source->number, (-source->count) );


			ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, tempInv.itemDefineIndex, source->number, tempInv.count );

				tempNum = source->number;
				memcpy( source, &tempInv, length );
				source->number = tempNum;

			ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, tempInv.itemDefineIndex, tempInv.number, (-tempInv.count) );
		}
		break;
	default:
		return destination;
	}
	return destination;
}

// XCopyInventory Method.
TB_INVENTORY* cPlayer::XCopyInventory(TB_INVENTORY* source, XCOPY_INVENTORY_OPT opt)
{
	TB_INVENTORY* destination = SelectInventory( source->number );
	if ( destination != NULL )
	{
		return XCopyInventory( destination, source, opt );
	}
	return NULL;
}

// SelectInventoryIdx Method.
TB_INVENTORY* cPlayer::SelectInventoryIdx(long idx)
{
	TB_INVENTORY* inventory = SelectInventory( MIN_INVENTORY );
	for ( short i = MIN_INVENTORY; i <= MAX_INVENTORY; i++, inventory++ )
	{
		if ( inventory->idx == idx )
			return inventory;
	}
	return NULL;
}

// CreateInventory Method.
TB_INVENTORY* cPlayer::CreateInventory(TB_ITEM_DEFINE* itemDefine, short number, short count, short* pcbValue)
{
	TB_INVENTORY* inventory = SelectInventory( number );

	if ( inventory != NULL )
	{
		if ( IsInventory( inventory ) == false )
		{
			short         shortCount = (count > itemDefine->capacity) ? itemDefine->capacity : count;
			PerItemCount* itemCount  = ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, itemDefine->index, number, shortCount );

			inventory->idx             = LONG_MAX;
			inventory->itemDefineIdx   = itemDefine->idx;
			inventory->itemDefineIndex = itemDefine->index;
			inventory->number          = number;
			inventory->count           = shortCount;
			inventory->enhanced        = 0;
			inventory->seal            = 0;
			inventory->apply           = InventoryApplyNone;

			(*pcbValue) = shortCount;

			if ( itemCount != NULL )
			{
				// 인벤토리 생성.
				//UpdateDutyItem( itemCount->index, itemCount->bag );
			}
			return inventory;
		}
	}

	return NULL;
}
TB_INVENTORY* cPlayer::CreateInventory(long itemDefineIndex, short number, short count, short* pcbValue)
{
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( itemDefineIndex );
	return ( itemDefine != NULL ) ? CreateInventory( itemDefine, number, count, pcbValue ) : NULL;
}

// UpdateInventory Method.
short cPlayer::UpdateInventory(TB_INVENTORY* inventory, short count)
{
	short retvalue = 0;
	if ( inventory != NULL )
	{
		if ( inventory->count > 0 )
		{
			PerItemCount* itemCount = NULL;
			if ( count > 0 )
			{
				TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
				if ( itemDefine != NULL )
				{
					if ( (inventory->count + count) > itemDefine->capacity )
						retvalue = itemDefine->capacity - inventory->count;
					else
						retvalue = count;

					itemCount =ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, inventory->itemDefineIndex, inventory->number, retvalue );
					inventory->count = inventory->count + retvalue;
				}
			}
			else
			{
				if ( inventory->count <= abs( count ) )
					retvalue = (-inventory->count);
				else
					retvalue = count;

				itemCount =ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, inventory->itemDefineIndex, inventory->number, retvalue );
				inventory->count = inventory->count + retvalue;
			}
			if ( itemCount != NULL )
			{
				// 인벤토리 갱신
				//UpdateDutyItem( itemCount->index, itemCount->bag );
			}
			else
			{
				// 인벤토리 삭제
				//UpdateDutyItem( inventory->itemDefineIdx, 0 );
			}
		}
	}
	return retvalue;
}

// UpdateInventory Method.
short cPlayer::UpdateInventory(short number, short count)
{
	return UpdateInventory( SelectInventory( number ), count );
}

// RemoveInventory Method.
bool cPlayer::RemoveInventory(TB_INVENTORY* inventory)
{
	if ( inventory != NULL )
	{
		int inventoryBagCount = 0;

		if ( inventory->count > 0 )
		{
			PerItemCount* itemCount = ITEMCOUNTPOOL->UpdateItemCount( &mItemCountRoot, inventory->itemDefineIndex, inventory->number, (-inventory->count) );
			if ( itemCount != NULL )
			{
				inventoryBagCount = itemCount->bag;
				// 인벤토리 갱신.
				//UpdateDutyItem( itemCount->index, itemCount->bag );
			}
			else
			{
				// 인벤토리 삭제.
				//UpdateDutyItem( inventory->itemDefineIndex, 0 );
			}
		}

		TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
		if ( itemDefine != NULL )
		{
			if ( itemDefine->type == ITEM_MATERIAL && itemDefine->subType == ITEM_MATERIAL_VEHICLE )
			{
				TB_ITEM_ABILITY* ability = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
				if ( ability != NULL && !(inventoryBagCount > 0) )
				{
					EndVehicleItem( ability->influence_idx );
				}
			}
		}

		memset( inventory, 0, sizeof(TB_INVENTORY) );
		return true;
	}
	return false;
}

// RemoveInventory Method.
bool cPlayer::RemoveInventory(short number)
{
	return RemoveInventory( SelectInventory( number ) );
}

// ItemUseInventory Method.
void cPlayer::ItemUseInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_USE_INVENTORY* msg)
{
	/*-- 아이템 사용시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	if ( msg->number < INVENTORY_BAG_BEGIN || msg->number > INVENTORY_BAG_END )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	TB_INVENTORY* inventory = SelectInventory( msg->number );

	if ( IsInventory( inventory ) == false )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	if ( inventory->apply != InventoryApplyNone )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	/*-- Item Define Table(TB_ITEM_DEFINE) 적용.
	*/
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );

	if ( itemDefine == NULL )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	if ( !( (itemDefine->type == ITEM_ETC1) || (itemDefine->type == ITEM_CARD && itemDefine->subType == ITEM_CARD_COMMON) ) )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	if ( itemDefine->type == ITEM_ETC1 && itemDefine->subType == ITEM_ETC1_QUEST )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	if ( itemDefine->type == ITEM_CARD && itemDefine->subType == ITEM_CARD_COMMON )
	{
		unsigned int getState = GetState( );
		if ( !( getState == eOBJECT_STATE_IDLE || getState == ePLAYER_STATE_ITEMPICK ) )
			throw ERROR_ITEM_USE_INVENTORY_FAIL;
	}

	/*-- Item Limit Table(TB_ITEM_LIMIT) 적용
	*/
	TB_ITEM_LIMIT* itemLimit = ITEMMANAGER->GetItemLimit( inventory->itemDefineIdx );
	if ( itemLimit != NULL )
	{
		// 종족; TB_ITEM_LIMIT::charRace 와 sPlayerInfo::Race 는 정의는 다르나 같은 값을 사용.
		if ( itemLimit->charRace != ITEM_RACE_ALL )
		{
			if ( itemLimit->charRace != mPlayerInfo.Race )
				throw ERROR_ITEM_USE_INVENTORY_FAIL;
		}

		// 성별; TB_ITEM_LIMIT::charGender 와 sPlayerInfo::Gender 는 정의는 다르나 같은 값을 사용.
		if ( itemLimit->charGender != ITEM_GENDER_ALL )
		{
			if ( itemLimit->charGender != mPlayerInfo.Gender )
				throw ERROR_ITEM_USE_INVENTORY_FAIL;
		}

		// 직업; TB_ITEM_LIMIT::charJob 와 sPlayerInfo::Job 은 정의는 다르나 같은 값을 사용.
		if ( itemLimit->charJob != ITEM_JOB_ALL )
		{
			long charJob = mPlayerInfo.Job - (mPlayerInfo.Job % ITEM_JOB_FIGHTER);
			if ( itemLimit->charJob != charJob )
				throw ERROR_ITEM_USE_INVENTORY_FAIL;
		}

		// 레벨; 이하일 경우만 제어를 한다.
		if (  itemLimit->charLevel > (BYTE)mPlayerInfo.Level )
			throw ERROR_ITEM_USE_INVENTORY_FAIL;

		// 전용; 전장전용 아이템일경우만 사용가능.
		if ( itemLimit->pvpOnly && (NETWORK2->GetServerType( ) != _E_ST_ID_PVP_) )
			throw ERROR_ITEM_USE_INVENTORY_FAIL;
	}

	/*-- 사용할 효과 적용. --*/

	TB_ITEM_ABILITY* itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
	cTitleDefine*    titleDefine = TITLEMAN->GetTitleDefine( itemDefine->titleIndex );

	/*-- Item Ability 효과 적용가능 검사
	*/
	if ( itemAbility != NULL )
	{
		bool apply = false;
		if ( itemAbility->influence_idx > 0 )
		{
			// 우선 검사 조건.
			if ( itemAbility->target_type == ITEM_TARGET_MYSELF )
			{
				apply = SKILLMANAGER->ItemCheckAddInf( this, this, itemAbility->influence_idx );
			}
			else if ( itemAbility->target_type == ITEM_TARGET_MONSTER )
			{
				if ( msg->targetType == eOBJECTTYPE_MONSTER )
				{
					cMonster* monster = OBJECTMANAGER->GetMonster( msg->targetIndex );
					if ( monster != NULL )
					{
						NiPoint3 monsterPos( monster->GetXPos( ), monster->GetYPos( ), monster->Height( ) );
						NiPoint3 heroPos( mObjectPos.x, mObjectPos.y, Height() );
						if ( mMapNumber == monster->GetMapNumber( ) && (short)(heroPos - monsterPos).Length( ) < itemAbility->use_range )
						{
							apply = SKILLMANAGER->ItemCheckAddInf( this, monster, itemAbility->influence_idx );
						}
					}
				}
			}
			// 차선 검사 조건.
			else
			{
				if ( itemAbility->target_type & ITEM_TARGET_MYSELF )
				{
					if ( msg->targetType == eOBJECTTYPE_HERO || msg->targetIndex == mObject.index )
					{
						apply = SKILLMANAGER->ItemCheckAddInf( this, this, itemAbility->influence_idx );
					}
				}
				if ( itemAbility->target_type & ITEM_TARGET_PLAYER )
				{
					if ( msg->targetType == eOBJECTTYPE_PLAYER )
					{
						cPlayer* player = OBJECTMANAGER->GetPlayer( msg->targetIndex );
						if ( player != NULL )
						{
							NiPoint3 playerPos( player->GetXPos( ), player->GetYPos( ), player->Height( ) );
							NiPoint3 heroPos( mObjectPos.x, mObjectPos.y, Height() );
							if ( mMapNumber == player->GetMapNumber( ) && (short)(heroPos - playerPos).Length( ) < itemAbility->use_range )
							{
								apply = SKILLMANAGER->ItemCheckAddInf( this, player, itemAbility->influence_idx );
							}
						}
					}
				}
				if ( itemAbility->target_type & ITEM_TARGET_PARTY )
				{
					cParty* party = PARTYMAN->GetParty( mPlayerExrInfo.mPartyIndex );
					if ( party != NULL )
					{
						if ( party->IsUser( msg->targetIndex ) == true )
						{
							cPlayer* player = OBJECTMANAGER->GetPlayer( msg->targetIndex );
							if ( player != NULL )
							{
								NiPoint3 playerPos( player->GetXPos( ), player->GetYPos( ), player->Height( ) );
								NiPoint3 heroPos( mObjectPos.x, mObjectPos.y, Height() );
								if ( mMapNumber == player->GetMapNumber( ) && (short)(heroPos - playerPos).Length( ) < itemAbility->use_range )
								{
									apply = SKILLMANAGER->ItemCheckAddInf( this, player, itemAbility->influence_idx );
								}
							}
						}
					}
				}
			}
		}

		// 스킬의 효과 적용여부 확인.
		if ( apply == false )
		{
			throw ERROR_ITEM_USE_INVENTORY_FAIL;
		}
	}
	/* Title Table 효과 적용가능 검사
	*/
	else if ( titleDefine != NULL )
	{
		cHaveTitleSet::cIterator title = mHaveTitleSet.Find( itemDefine->titleIndex );
		if ( title != mHaveTitleSet.End( ) )
			throw ERROR_ITEM_USE_INVENTORY_TITLE;
	}
	else
		throw ERROR_ITEM_USE_INVENTORY_FAIL;

	/*-- Item Ability Table - 쿨타임 검사 및 적용.
	*/
	if ( COOLTIMEPOOL->IsCooltime( &mCooltimeRoot, inventory->itemDefineIndex ) == true )
		throw ERROR_ITEM_USE_INVENTORY_COOLTIME;
	else
		COOLTIMEPOOL->ApplyCooltime( &mCooltimeRoot, inventory );

	/*-- Item Ability Table - 효과 적용.
	*/
	if ( itemAbility != NULL )
	{
		bool apply = false;
		if ( itemAbility->influence_idx > 0 )
		{
			// 우선 검사 조건.
			if ( itemAbility->target_type == ITEM_TARGET_MYSELF )
			{
				apply = SKILLMANAGER->AddInfluence( mObject, mObject, itemAbility->influence_idx, 0, true );
			}
			else if ( itemAbility->target_type == ITEM_TARGET_MONSTER )
			{
				cMonster* monster = OBJECTMANAGER->GetMonster( msg->targetIndex );
				if ( monster != NULL )
					apply = SKILLMANAGER->AddInfluence( mObject, monster->GetObject( ), itemAbility->influence_idx, 0, true );
			}
			// 차선 검사 조건.
			else
			{
				if ( msg->targetType == eOBJECTTYPE_HERO || msg->targetIndex == mObject.index )
				{
					apply = SKILLMANAGER->AddInfluence( mObject, mObject, itemAbility->influence_idx, 0, true );
				}
				if ( itemAbility->target_type & ITEM_TARGET_PLAYER )
				{
					if ( msg->targetType == eOBJECTTYPE_PLAYER )
					{
						cPlayer* player = OBJECTMANAGER->GetPlayer( msg->targetIndex );
						if ( player != NULL )
							apply = SKILLMANAGER->AddInfluence( mObject, player->GetObject( ), itemAbility->influence_idx, 0, true );
					}
				}
				if ( itemAbility->target_type & ITEM_TARGET_PARTY )
				{
					cParty* party = PARTYMAN->GetParty( GetPartyIndex( ) );
					if ( party != NULL )
					{
						if ( party->IsUser( msg->targetIndex ) == true )
						{
							cPlayer* player = OBJECTMANAGER->GetPlayer( msg->targetIndex );
							if ( player != NULL )
								apply = SKILLMANAGER->AddInfluence( mObject, player->GetObject( ), itemAbility->influence_idx, 0, true );
						}
					}
				}
			}
		}
		if ( apply == false )
		{
			NETWORK2->PostServerEvent( "ERROR INVENTORY_USE, ItemIndex(=%d)", inventory->itemDefineIndex );
			throw ERROR_ITEM_USE_INVENTORY_FAIL;
		}
	}
	else if ( titleDefine != NULL )
	{
		HANDLE        handle      = NULL;
		TITLE_INSERT* titleInsert = (TITLE_INSERT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_TITLE_INSERT );

		titleInsert->characterIdx = mObject.index;
		titleInsert->titleIdx     = itemDefine->titleIndex;

		NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(TITLE_INSERT) );
	}

	// 아이템 사용 선처리.
	UpdateInventory( inventory, (-1) );

	// DATABASE - 인벤토리내 아이템 사용 요청.
	HANDLE         handle       = NULL;
	INVENTORY_USE* inventoryUse = (INVENTORY_USE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_USE );

	inventoryUse->idx = inventory->idx;

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(INVENTORY_USE) ) == false )
		throw ERROR_ITEM_USE_INVENTORY_FAIL;
}

// ItemDelInventory Method.
void cPlayer::ItemDelInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_DEL_INVENTORY* msg)
{
	/*-- 아이템 삭제시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_DEL_INVENTORY_FAIL;

	if ( msg->number < INVENTORY_BAG_BEGIN || msg->number > INVENTORY_BAG_END )
		throw ERROR_ITEM_DEL_INVENTORY_FAIL;

	TB_INVENTORY* inventory = SelectInventory( msg->number );

	if ( IsInventory( inventory ) == false )
		throw ERROR_ITEM_DEL_INVENTORY_FAIL;

	if ( inventory->apply != InventoryApplyNone )
		throw ERROR_ITEM_DEL_INVENTORY_FAIL;

	// DATABASE - 인벤토리내 아이템 삭제 요청.
	HANDLE            handle          = NULL;
	INVENTORY_REMOVE* inventoryRemove = (INVENTORY_REMOVE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_REMOVE );

	inventoryRemove->idx    = inventory->idx;
	inventoryRemove->number = inventory->number;

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(INVENTORY_REMOVE) ) == false )
		throw ERROR_ITEM_DEL_INVENTORY_FAIL;
}

// ItemSwiInventory Method.
void cPlayer::ItemSwiInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_SWI_INVENTORY* /*msg*/)
{
	/*-- 아이템 삭제시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_SWI_INVENTORY_FAIL;

	if ( GetState( ) == eOBJECT_STATE_ATTACK )
		throw ERROR_ITEM_SWI_INVENTORY_FAIL;

	int activeWeapon  = mItemActiveWeapon ^ ItemActiveSwitch;
	mItemActiveWeapon = (eItemActiveWeapon)activeWeapon;

	HANDLE                      handle  = NULL;
	MSG_RES_ITEM_SWI_INVENTORY* sendMsg = (MSG_RES_ITEM_SWI_INVENTORY*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)socketContext );

	sendMsg->Category     = NM_ITEM;
	sendMsg->Protocol     = NM_ITEM_SWI_INVENTORY_RES;
	sendMsg->ErrorCode    = ERROR_ITEM_SWI_INVENTORY_SUCCESS;
	sendMsg->activeWeapon = mItemActiveWeapon;

	NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_ITEM_SWI_INVENTORY) );
}

// ItemDivideInventory Method.
void cPlayer::ItemDivideInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_DIVIDE_INVENTORY* msg)
{
	/*-- 아이템 분활시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if ( msg->number1 == msg->number2 )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if ( !IsInventoryRange( msg->number1 ) || !IsInventoryRange( msg->number2 ) )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if ( IsInventoryEquip( msg->number1 ) || IsInventoryEquip( msg->number2 ) )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if ( IsInventoryQuest( msg->number1 ) || IsInventoryQuest( msg->number2 ) )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if ( IsInventoryMall( msg->number1 ) != IsInventoryMall( msg->number2 ) )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if ( IsInventoryWarehouse( msg->number1 ) != IsInventoryWarehouse( msg->number2 ) )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	TB_INVENTORY* inventory1 = SelectInventory( msg->number1 );
	TB_INVENTORY* inventory2 = SelectInventory( msg->number2 );

	if ( IsInventory( inventory1 ) == false || IsInventory( inventory2 ) == true )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if ( inventory1->apply != InventoryApplyNone )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	if  ( (msg->divide < 1) || (msg->divide >= inventory1->count) )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory1->itemDefineIdx );
	if ( itemDefine == NULL )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;

	// DATABASE - 인벤토리내 아이템 분활 요청.
	HANDLE            handle          = NULL;
	INVENTORY_DIVIDE* inventoryDivide = (INVENTORY_DIVIDE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_DIVIDE );

	inventoryDivide->characterIdx = mObject.index;

	// 인벤토리 분활.
	inventory1->idx = inventory1->idx;
	UpdateInventory( inventory1, (-msg->divide) ); // inventory1->count = inventory1->count - msg->divide;

	// 인벤토리 생성.
	short shortCount=0;
	CreateInventory( itemDefine, msg->number2, msg->divide, &shortCount );

	// DB 값 입력.
	inventoryDivide->rowCount = 2;
	inventoryDivide->table[ 0 ] = (*inventory1);	// 인벤토리 정보 복사.
	inventoryDivide->table[ 1 ] = (*inventory2);	// 인벤토리 정보 복사.
	inventoryDivide->table[ 1 ].idx = 0;			// 임시 생성번호 삭제.

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(INVENTORY_DIVIDE) ) == false )
		throw ERROR_ITEM_DIVIDE_INVENTORY_FAIL;
}

// ItemMov2Inventory Method.
void cPlayer::ItemMov2Inventory(ULONG_PTR socketContext, MSG_REQ_NPC_ITEM_MOV2_INVENTORY* msg)
{
	/*-- 아이템 이동시(창고 & 인벤) 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_MOV2_INVENTORY_FAIL;

	if ( !IsInventoryRange( msg->number1 ) || !IsInventoryRange( msg->number2 ) )
		throw ERROR_ITEM_MOV2_INVENTORY_FAIL;

	if ( !IsInventoryWarehouse( msg->number1 ) && !IsInventoryWarehouse( msg->number2 ) )
		throw ERROR_ITEM_MOV2_INVENTORY_FAIL;

	TB_INVENTORY* inventory1 = SelectInventory( msg->number1 );
	TB_INVENTORY* inventory2 = SelectInventory( msg->number2 );

	if ( !IsInventory( inventory1 ) || IsInventory( inventory2 ) )
		throw ERROR_ITEM_MOV2_INVENTORY_FAIL;

	if ( msg->count > inventory1->count )
		throw ERROR_ITEM_MOV2_INVENTORY_FAIL;

	if ( msg->count == 0 )
		msg->count = inventory1->count;

	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory1->itemDefineIdx );
	if ( itemDefine == NULL )
		throw ERROR_ITEM_MOV2_INVENTORY_FAIL;

	// DATABASE - 인벤토리내 아이템 이동(창고 & 인벤) 요청.
	HANDLE           handle         = NULL;
	INVENTORY_MOVE2* inventoryMove2 = (INVENTORY_MOVE2*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_MOVE2 );
	unsigned long    length         = sizeof(INVENTORY_MOVE2) - sizeof(inventoryMove2->table);

	inventoryMove2->characterIdx = mObject.index;

	if ( msg->count == inventory1->count )
	{
		InventoryMove( msg->number1, msg->number2 );

		inventoryMove2->rowCount = 1;
		inventoryMove2->table[ 0 ] = (*inventory2);
	}
	else
	{
		// 인벤토리 분활.
		inventory1->idx = inventory1->idx;
		UpdateInventory( inventory1, (-msg->count) ); // inventory1->count = inventory1->count - msg->count;

		// 인벤토리 생성.
		short shortCount=0;
		CreateInventory( itemDefine, msg->number2, msg->count, &shortCount );

		// DB 값 입력.
		inventoryMove2->rowCount = 2;
		inventoryMove2->table[ 0 ] = (*inventory1);	// 인벤토리 정보 복사.
		inventoryMove2->table[ 1 ] = (*inventory2);	// 인벤토리 정보 복사.
		inventoryMove2->table[ 1 ].idx = 0;			// 임시 생성번호 삭제.
	}

	length += (inventoryMove2->rowCount * sizeof(inventoryMove2->table));
	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) == false )
		throw ERROR_ITEM_MOV2_INVENTORY_FAIL;
}

// ItemEnhancedInventory Method.
void cPlayer::ItemEnhancedInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_ENHANCED_END* msg)
{
	/*-- 아이템 강화시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	if ( msg->number1 < INVENTORY_BAG_BEGIN || msg->number1 > INVENTORY_BAG_END )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	if ( msg->number2 < INVENTORY_BAG_BEGIN || msg->number2 > INVENTORY_BAG_END )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	// 강화 장비 검사.
	TB_ITEM_DEFINE* itemDefine1 = NULL;
	TB_INVENTORY*   inventory1  = NULL;

	inventory1 = SelectInventory( msg->number1 );
	if ( IsInventory( inventory1 ) == false )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	if ( inventory1->apply != InventoryApplyNone )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	itemDefine1 = ITEMMANAGER->GetItemDefine( inventory1->itemDefineIdx );
	if ( itemDefine1 == NULL )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	if ( !(itemDefine1->type != ITEM_WEAPON || itemDefine1->type != ITEM_WEAR) )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	if ( !(inventory1->enhanced < MAX_ITEM_ENHANCED) )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	// 강화 카드 검사.
	TB_ITEM_DEFINE* itemDefine2 = NULL;
	TB_INVENTORY*   inventory2  = NULL;

	inventory2 = SelectInventory( msg->number2 );
	if ( IsInventory( inventory2 ) == false )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	if ( inventory2->apply != InventoryApplyNone )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	itemDefine2 = ITEMMANAGER->GetItemDefine( inventory2->itemDefineIdx );
	if ( itemDefine2 == NULL )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	if ( itemDefine2->type != ITEM_CARD )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	// 강화장비 & 강화카드 조합검사.
	if ( inventory1->enhanced < ITEM_ENHANCED_LV1_END )
	{
		if ( !(itemDefine1->type == ITEM_WEAPON && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAPON_LV1) &&
			 !(itemDefine1->type == ITEM_WEAR && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAR_LV1) )
			 throw ERROR_ITEM_ENHANCED_END_FAIL;
	}
	else if ( inventory1->enhanced < ITEM_ENHANCED_LV2_END )
	{
		if ( !(itemDefine1->type == ITEM_WEAPON && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAPON_LV2) &&
			 !(itemDefine1->type == ITEM_WEAR && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAR_LV2) )
			 throw ERROR_ITEM_ENHANCED_END_FAIL;
	}
	else if ( inventory1->enhanced < ITEM_ENHANCED_LV3_END )
	{
		if ( !(itemDefine1->type == ITEM_WEAPON && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAPON_LV3) &&
			 !(itemDefine1->type == ITEM_WEAR && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAR_LV3) )
			 throw ERROR_ITEM_ENHANCED_END_FAIL;
	}
	else if ( inventory1->enhanced < ITEM_ENHANCED_LV4_END )
	{
		if ( !(itemDefine1->type == ITEM_WEAPON && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAPON_LV4) &&
			 !(itemDefine1->type == ITEM_WEAR && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAR_LV4) )
			 throw ERROR_ITEM_ENHANCED_END_FAIL;
	}
	else if ( inventory1->enhanced < ITEM_ENHANCED_LV5_END )
	{
		if ( !(itemDefine1->type == ITEM_WEAPON && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAPON_LV5) &&
			 !(itemDefine1->type == ITEM_WEAR && itemDefine2->subType == ITEM_CARD_ENHANCE_WEAR_LV5) )
			 throw ERROR_ITEM_ENHANCED_END_FAIL;
	}
	else
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	/*-- 강화확률 적용 방법.
	     0                                  1 = 0 ~ 1 사이의 확률 (최대 1/1000000)
		 | SUCCESS | DOWN | DESTRUCT | FAIL | = 성공 < 다운 < 파괴 < 변화없음 순으로 적용.
	*/
	TB_ITEM_ENHANCED_RATE* itemEnhancedRate = ITEMMANAGER->GetItemEnhancedRate( inventory1->itemDefineIdx );
	if ( itemEnhancedRate == NULL )
		throw ERROR_ITEM_ENHANCED_END_FAIL;

	double success  = *(itemEnhancedRate->ind.success  + inventory1->enhanced);
	double down     = *(itemEnhancedRate->ind.down     + inventory1->enhanced) + success;
	double destruct = *(itemEnhancedRate->ind.destruct + inventory1->enhanced) + down;

	// 아이템 강화확률 - 고유확률 적용.
	double randomValue = RANDOMTABLE->Get( mItemEnhancedSeed );

	// DATABASE - 인벤토리내 아이템 이동(창고 & 인벤) 요청.
	HANDLE              handle            = NULL;
	INVENTORY_ENHANCED* inventoryEnhanced = (INVENTORY_ENHANCED*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_ENHANCED );

	inventoryEnhanced->idx1 = inventory1->idx;
	inventoryEnhanced->idx2 = inventory2->idx;

	if ( randomValue <= success )
	{
		inventoryEnhanced->result = RESULT_INVENTORY_ENHANCED_SUCCESS;
	}
	else if ( randomValue <= down && inventory1->enhanced > 0 )
	{
		inventoryEnhanced->result = RESULT_INVENTORY_ENHANCED_DOWN;
	}
	else if ( randomValue <= destruct )
	{
		inventoryEnhanced->result = RESULT_INVENTORY_ENHANCED_DESTRUCT;
	}
	else
	{
		inventoryEnhanced->result = RESULT_INVENTORY_ENHANCED_FAIL;
	}

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(INVENTORY_ENHANCED) ) == false )
		throw ERROR_ITEM_ENHANCED_END_FAIL;
}

// ItemDisjointInventory Method.
void cPlayer::ItemDisjointInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_DISJOINT_INVENTORY* msg)
{
	/*-- 아이템 분해시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;

	if ( msg->number < INVENTORY_BAG_BEGIN || msg->number > INVENTORY_BAG_END )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;

	TB_INVENTORY* inventory = SelectInventory( msg->number );
	if ( IsInventory( inventory ) == false )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;

	if ( inventory->apply != InventoryApplyNone )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;

	TB_ITEM_DISJOINT* itemDisjoint = ITEMMANAGER->GetItemDisjoint( inventory->itemDefineIdx );
	if ( itemDisjoint == NULL )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;

	// 비용확인.
	if ( IsAddMoney( -(itemDisjoint->price) ) == false )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;

	// 보상아이템 목록.
	eDisjointItem disjointItems[ MAX_DISJOINT_ITEM ];
	unsigned int  disjointItemsCount = 0;

	if ( itemDisjoint->itemDefineIndex1 > 0 )
	{
		disjointItems[ disjointItemsCount ].itemDefineIndex = itemDisjoint->itemDefineIndex1;
		disjointItems[ disjointItemsCount ].inventoryCount  = itemDisjoint->inventoryCount1;
		disjointItemsCount++;
	}
	if ( itemDisjoint->itemDefineIndex2 > 0 )
	{
		disjointItems[ disjointItemsCount ].itemDefineIndex = itemDisjoint->itemDefineIndex2;
		disjointItems[ disjointItemsCount ].inventoryCount  = itemDisjoint->inventoryCount2;
		disjointItemsCount++;
	}
	if ( itemDisjoint->itemDefineIndex3 > 0 )
	{
		disjointItems[ disjointItemsCount ].itemDefineIndex = itemDisjoint->itemDefineIndex3;
		disjointItems[ disjointItemsCount ].inventoryCount  = itemDisjoint->inventoryCount3;
		disjointItemsCount++;
	}
	if ( itemDisjoint->itemDefineIndex4 > 0 )
	{
		disjointItems[ disjointItemsCount ].itemDefineIndex = itemDisjoint->itemDefineIndex4;
		disjointItems[ disjointItemsCount ].inventoryCount  = itemDisjoint->inventoryCount4;
		disjointItemsCount++;
	}
	if ( itemDisjoint->itemDefineIndex5 > 0 )
	{
		disjointItems[ disjointItemsCount ].itemDefineIndex = itemDisjoint->itemDefineIndex5;
		disjointItems[ disjointItemsCount ].inventoryCount  = itemDisjoint->inventoryCount5;
		disjointItemsCount++;
	}
	if ( itemDisjoint->itemDefineIndex6 > 0 )
	{
		disjointItems[ disjointItemsCount ].itemDefineIndex = itemDisjoint->itemDefineIndex6;
		disjointItems[ disjointItemsCount ].inventoryCount  = itemDisjoint->inventoryCount6;
		disjointItemsCount++;
	}

	// 보상아이템 검사.
	if ( IsItemReward( inventory->idx, disjointItems, disjointItemsCount ) < disjointItemsCount )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;

	HANDLE              handle            = NULL;
	INVENTORY_DISJOINT* inventoryDisjoint = (INVENTORY_DISJOINT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_DISJOINT );
	unsigned long       length            = sizeof(INVENTORY_DISJOINT) - sizeof(inventoryDisjoint->table);
	long&               rowCount          = inventoryDisjoint->rowCount;

	/*-- 캐릭터 고유번호
	*/
	inventoryDisjoint->characterIdx = mObject.index;

	/*-- 분해 비용
	*/
	inventoryDisjoint->characterMoney = -(itemDisjoint->price);

	/*-- 분해 아이템
	*/
	inventoryDisjoint->table[ rowCount ].idx             = inventory->idx;
	inventoryDisjoint->table[ rowCount ].itemDefineIndex = inventory->itemDefineIndex;
	inventoryDisjoint->table[ rowCount ].number          = inventory->number;
	inventoryDisjoint->table[ rowCount ].count           = (-inventory->count);
	rowCount++;

	RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );

	/*-- 보상 아이템
	*/
	eDisjointItem* items = disjointItems;

	for ( unsigned int i0 = 0; i0 < disjointItemsCount; i0++, items++ )
	{
		TB_ITEM_DEFINE* itemDefine  = ITEMMANAGER->GetItemDefineByIndex( items->itemDefineIndex );
		u_short         remainCount = (u_short)items->inventoryCount;

		if ( itemDefine != NULL && remainCount > 0 )
		{
			TB_INVENTORY* inventory;
			short         shortCount;

			// 보상 아이템1. 겹치기 검사 및 처리.
			inventory = SelectInventory( INVENTORY_BAG_BEGIN );
			for ( short i1 = INVENTORY_BAG_BEGIN; i1 <= INVENTORY_BAG_END && remainCount > 0; i1++, inventory++ )
			{
				if ( inventory->itemDefineIndex == items->itemDefineIndex )
				{
					if ( inventory->count < itemDefine->capacity )
					{
						shortCount = UpdateInventory( inventory, remainCount );

						// INVENTORY_IDX, ITEM_DEFINE_INDEX, INVENTORY_NUMBER, INVENTORY_COUNT...
						inventoryDisjoint->table[ rowCount ].idx             = inventory->idx;
						inventoryDisjoint->table[ rowCount ].itemDefineIndex = inventory->itemDefineIndex;
						inventoryDisjoint->table[ rowCount ].number          = inventory->number;
						inventoryDisjoint->table[ rowCount ].count           = shortCount;
						rowCount++;

						remainCount = remainCount - shortCount;
					}
				}
			}
			// 보상 아이템2. 생성 검사 및 등록.
			if ( remainCount > 0 )
			{
				short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );
				while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
				{
					CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

					// INVENTORY_IDX, ITEM_DEFINE_INDEX, INVENTORY_NUMBER, INVENTORY_COUNT...
					inventoryDisjoint->table[ rowCount ].idx             = 0;
					inventoryDisjoint->table[ rowCount ].itemDefineIndex = items->itemDefineIndex;
					inventoryDisjoint->table[ rowCount ].number          = number;
					inventoryDisjoint->table[ rowCount ].count           = shortCount;
					rowCount++;

					remainCount = remainCount - shortCount;
					number      = GetEmptyBagNumber( (++number) );
				}
			}
		}
	}

	length += (inventoryDisjoint->rowCount * sizeof(inventoryDisjoint->table));
	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) == false )
		throw ERROR_ITEM_DISJOINT_INVENTORY_FAIL;
}

// ItemPutCardInventory Method.
void cPlayer::ItemPutCardInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_PUT_CARD_INVENTORY* msg)
{
	/*-- 아이템 카드장착시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	if ( msg->number1 < INVENTORY_BAG_BEGIN || msg->number1 > INVENTORY_BAG_END
		|| msg->number2 < INVENTORY_BAG_BEGIN || msg->number2 > INVENTORY_BAG_END )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	TB_INVENTORY* inventory1 = SelectInventory( msg->number1 );
	TB_INVENTORY* inventory2 = SelectInventory( msg->number2 );

	if ( IsInventory( inventory1 ) == false || IsInventory( inventory2 ) == false )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	if ( inventory1->apply != InventoryApplyNone || inventory2->apply != InventoryApplyNone )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	if ( msg->cardSlot < MIN_CARD_SLOT || msg->cardSlot > MAX_CARD_SLOT )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	// 장착슬롯 검사.
	TB_ITEM_CARD_SLOT* itemCardSlot = ITEMMANAGER->GetItemCardSlot( inventory1->itemDefineIdx );
	if ( itemCardSlot == NULL )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	// 장착카드 검사.
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory2->itemDefineIdx );
	if ( itemDefine == NULL )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	if ( itemDefine->type != ITEM_CARD )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;

	switch ( msg->cardSlot )
	{
	case ITEM_CARD_SLOT_1:
		if ( !(ItemSlotType2Mtr( itemCardSlot->slot1 ) & ItemCardType2Mtr( itemDefine->subType )) )
			throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;
		break;
	case ITEM_CARD_SLOT_2:
		if ( !(ItemSlotType2Mtr( itemCardSlot->slot2 ) & ItemCardType2Mtr( itemDefine->subType )) )
			throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;
		break;
	case ITEM_CARD_SLOT_3:
		if ( !(ItemSlotType2Mtr( itemCardSlot->slot3 ) & ItemCardType2Mtr( itemDefine->subType )) )
			throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;
		break;
	case ITEM_CARD_SLOT_4:
		if ( !(ItemSlotType2Mtr( itemCardSlot->slot4 ) & ItemCardType2Mtr( itemDefine->subType )) )
			throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;
		break;
	case ITEM_CARD_SLOT_5:
		if ( !(ItemSlotType2Mtr( itemCardSlot->slot5 ) & ItemCardType2Mtr( itemDefine->subType )) )
			throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;
		break;
	default:
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;
	}

	HANDLE              handle           = NULL;
	INVENTORY_PUT_CARD* inventoryPutCard = (INVENTORY_PUT_CARD*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_PUT_CARD );

	inventoryPutCard->idx1            = inventory1->idx;
	inventoryPutCard->cardSlot        = msg->cardSlot;
	inventoryPutCard->idx2            = inventory2->idx;
	inventoryPutCard->itemDefineIndex = inventory2->itemDefineIndex;

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(INVENTORY_PUT_CARD) ) == false )
		throw ERROR_ITEM_PUT_CARD_INVENTORY_FAIL;
}

// ItemChangeInventory Method.
void cPlayer::ItemChangeInventory(ULONG_PTR socketContext, MSG_REQ_ITEM_CHANGE_INVENTORY* msg)
{
	/*-- 아이템 체인지 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_CHANGE_INVENTORY_FAIL;

	if ( msg->number < INVENTORY_BAG_BEGIN || msg->number > INVENTORY_BAG_END )
		throw ERROR_ITEM_CHANGE_INVENTORY_FAIL;

	TB_INVENTORY* inventory = SelectInventory( msg->number );
	if ( IsInventory( inventory ) == false )
		throw ERROR_ITEM_CHANGE_INVENTORY_FAIL;

	if ( inventory->apply != InventoryApplyNone )
		throw ERROR_ITEM_CHANGE_INVENTORY_FAIL;

	TB_ITEM_CHANGE* itemChange = ITEMMANAGER->GetItemChange( inventory->itemDefineIdx );
	if ( itemChange == NULL )
		throw ERROR_ITEM_CHANGE_INVENTORY_FAIL;

	long   money       = itemChange->maxMoney - itemChange->minMoney;
	double randomValue = RANDOMTABLE->Get( mItemChangeSeed );

	if ( money > 0 )
		money = itemChange->minMoney + (rand( ) % money) + 1;
	else
		money = itemChange->minMoney;

	float* itemChangeRate  = itemChange->ind.rate;
	int    itemChangeItems = 0;
	for ( int i = 0; i < MAX_ITEM_CHANGE_RATE && (*itemChangeRate) != 0; i++, itemChangeRate++ )
	{
		if ( randomValue <= (*itemChangeRate) )
			itemChangeItems++;
	}

	long*  itemChangeIndex       = itemChange->ind.index;
	short* itemChangeCount       = itemChange->ind.count;
	int    itemChangeIndexOffset = 0;

	int    itemChnageData[ MAX_ITEM_CHANGE_DATA ];
	int*   itemChangePtr         = itemChnageData;

	for ( int i = 0; i < MAX_ITEM_CHANGE_DATA; i++, itemChangeIndex++, itemChangeCount++ )
	{
		if ( (*itemChangeIndex) != 0 && (*itemChangeCount) != 0 )
		{
			(*itemChangePtr) = itemChangeIndexOffset;

			itemChangeIndexOffset++;
			itemChangePtr++;
		}
		else
			break;
	}
	if ( itemChangeItems > 0 && itemChangeIndexOffset > 0 )
	{
		for ( int i = 0, s, t; i < itemChangeItems; i++ )
		{
			s = (int)(rand( ) % itemChangeIndexOffset);

			t                   = itemChnageData[ i ];
			itemChnageData[ i ] = itemChnageData[ s ];
			itemChnageData[ s ] = t;
		}
	}

	HANDLE            handle          = NULL;
	INVENTORY_CHANGE* inventoryChange = (INVENTORY_CHANGE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_CHANGE );
	long&             rowCount        = inventoryChange->rowCount;
	TB_INVENTORY*     table           = inventoryChange->table;
	unsigned long     length          = sizeof(INVENTORY_CHANGE) - sizeof(inventoryChange->table);

	/*-- 캐릭터 고유번호
	*/
	inventoryChange->characterIdx = mObject.index;

	/*-- 획득 비용
	*/
	inventoryChange->characterMoney = money;

	/*-- 체인지 아이템
	*/
	table->idx             = inventory->idx;
	table->itemDefineIdx   = inventory->itemDefineIdx;
	table->itemDefineIndex = inventory->itemDefineIndex;
	table->number          = inventory->number;
	table->count           = (-inventory->count);

	table++; rowCount++;

	RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );

	/*-- 보상 아이템
	*/
	for ( int i0 = 0; i0 < itemChangeItems; i0++ )
	{
		long  itemDefineIndex = *(itemChange->ind.index + itemChnageData[ i0 ]);
		short remainCount     = *(itemChange->ind.count + itemChnageData[ i0 ]);

		TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( itemDefineIndex );

		if ( itemDefine != NULL && remainCount > 0 )
		{
			TB_INVENTORY* inventory;
			short         shortCount;

			// 보상 아이템1. 겹치기 검사 및 처리.
			inventory = SelectInventory( INVENTORY_BAG_BEGIN );
			for ( short i1 = INVENTORY_BAG_BEGIN; i1 <= INVENTORY_BAG_END && remainCount > 0; i1++, inventory++ )
			{
				if ( inventory->itemDefineIndex == itemDefineIndex )
				{
					if ( inventory->count < itemDefine->capacity )
					{
						shortCount = UpdateInventory( inventory, remainCount );

						// INVENTORY_IDX, ITEM_DEFINE_INDEX, INVENTORY_NUMBER, INVENTORY_COUNT...
						table->idx             = inventory->idx;
						table->itemDefineIdx   = inventory->itemDefineIdx;
						table->itemDefineIndex = inventory->itemDefineIndex;
						table->number          = inventory->number;
						table->count           = shortCount;

						table++; rowCount++;

						remainCount = remainCount - shortCount;
					}
				}
			}
			// 보상 아이템2. 생성 검사 및 등록.
			if ( remainCount > 0 )
			{
				short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );
				while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
				{
					CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

					// INVENTORY_IDX, ITEM_DEFINE_INDEX, INVENTORY_NUMBER, INVENTORY_COUNT...
					table->idx             = 0;
					table->itemDefineIdx   = itemDefine->idx;
					table->itemDefineIndex = itemDefine->index;
					table->number          = number;
					table->count           = shortCount;

					table++; rowCount++;

					remainCount = remainCount - shortCount;
					number      = GetEmptyBagNumber( (++number) );
				}
			}
		}
	}

	length += (sizeof(inventoryChange->table) * rowCount);
	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) == false )
		throw ERROR_ITEM_CHANGE_INVENTORY_FAIL;
}

// ItemSell Method.
void cPlayer::ItemSell(ULONG_PTR socketContext, MSG_REQ_NPC_ITEM_SELL* msg)
{
	/*-- 아이템 판매시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_SELL_FAIL;

	if ( msg->inventoryNumber < INVENTORY_BAG_BEGIN || msg->inventoryNumber > INVENTORY_BAG_END )
		throw ERROR_ITEM_SELL_FAIL;

	// 인벤토리 내용 확인.
	TB_INVENTORY* inventory = SelectInventory( msg->inventoryNumber );

	if ( IsInventory( inventory ) == false )
		throw ERROR_ITEM_SELL_FAIL;

	if ( msg->count < 1 || msg->count > inventory->count )
		throw ERROR_ITEM_SELL_FAIL;

	// 아이템 정의 확인.
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
	long            sellType   = ItemSellNone;
	long            sellPrice  = 0;

	if ( itemDefine == NULL )
		throw ERROR_ITEM_SELL_FAIL;

	if ( !(itemDefine->sellPrice > 0) )
		throw ERROR_ITEM_SELL_FAIL;

	switch ( itemDefine->sellType )
	{
	case ItemSellNone:
		throw ERROR_ITEM_SELL_FAIL;
	case ItemSellMoney:
	case ItemSellPvp:
	case ItemSellTarot:
		sellPrice = itemDefine->sellPrice * msg->count;
		break;
	default:
		sellType = itemDefine->sellType;
		break;
	}

	// 아이템 - 코인 습득검사.
	if ( sellType != ItemSellNone )
	{
		if ( CheckInventory( sellType, (itemDefine->sellPrice * msg->count) ) == false )
			throw ERROR_ITEM_SELL_FAIL;	// 판매가능 수량 오류일경우.
	}

	// DATABASE - 인벤토리내 아이템 판매 요청.
	HANDLE        handle   = NULL;
	ITEM_SELL*    itemSell = (ITEM_SELL*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_SELL );
	long&         rowCount = itemSell->rowCount;
	TB_INVENTORY* table    = itemSell->table;
	long          length   = sizeof(ITEM_SELL) - sizeof(itemSell->table);

	itemSell->characterIdx = mObject.index;
	itemSell->npcIdx       = msg->npcIdx;
	itemSell->sellType     = itemDefine->sellType;
	itemSell->sellPrice    = (+sellPrice);
	rowCount = 1;

	table->idx   = inventory->idx;
	table->count = (-msg->count);
	table++;

	if ( inventory->count == msg->count )
		RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );

	// 아이템 - 습득처리.
	if ( sellType != ItemSellNone )
	{
		TB_INVENTORY* tempInv     = SelectInventory( INVENTORY_BAG_BEGIN );
		int           remainCount = (itemDefine->sellPrice * msg->count);

		for ( short i = INVENTORY_BAG_BEGIN; (i <= INVENTORY_BAG_END) && (remainCount > 0); i++, tempInv++ )
		{
			if ( sellType == tempInv->itemDefineIndex )
			{
				TB_ITEM_DEFINE* tempDfn = ITEMMANAGER->GetItemDefine( tempInv->itemDefineIdx );
				if ( tempDfn != NULL )
				{
					if ( tempInv->count < tempDfn->capacity )
					{
						short shortCount;

						shortCount  = UpdateInventory( tempInv, (short)remainCount );
						remainCount = remainCount - shortCount;

						table->idx   = tempInv->idx;
						table->count = (+shortCount);
						table++;

						rowCount++;
					}
				}
			}
		}

		short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );

		while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
		{
			short         shortCount;
			CreateInventory( itemDefine, number, (short)remainCount, &(shortCount=0) );

			table->idx             = 0;
			table->itemDefineIndex = itemDefine->sellType;
			table->count           = (+shortCount);
			table->number          = number;
			table++;

			rowCount++;

			remainCount = remainCount - shortCount;
			number      = GetEmptyBagNumber( number+1 );
		}
	}

	length += (rowCount * sizeof(itemSell->table));
	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) == false )
		throw ERROR_ITEM_SELL_FAIL;
}

// ItemBuy Method.
void cPlayer::ItemBuy(ULONG_PTR socketContext, MSG_REQ_NPC_ITEM_BUY* msg)
{
	/*-- 아이템 구입시 검사할 내용들.
	*/
	// 캐릭터 상태 검사.
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_BUY_FAIL;

	// 희망하는 구매수량 검사.
	if ( !(msg->count > 0) )
		throw ERROR_ITEM_BUY_FAIL;

	// 아이템 정의 테이블 검사.
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( msg->itemDefineIndex );
	long            buyType    = ItemBuyNone;
	long            buyPrice   = 0;

	if ( itemDefine == NULL )
		throw ERROR_ITEM_BUY_FAIL;

	if ( !(itemDefine->buyPrice > 0) )
		throw ERROR_ITEM_BUY_MONEY;

	buyPrice = itemDefine->buyPrice * msg->count;

	switch ( itemDefine->buyType )
	{
	case ItemBuyNone:
		throw ERROR_ITEM_BUY_FAIL;
	case ItemBuyMoney:
		if ( (ULONG)buyPrice > mMoney )
			throw ERROR_ITEM_BUY_MONEY;
		break;
	case ItemBuyPvp:
		if ( (ULONG)buyPrice > mHeroInfo.mPvPPoint )
			throw ERROR_ITEM_BUY_PVP_POINT;
		break;
	case ItemBuyTarot:
		if ( (ULONG)buyPrice > mMoney )
			throw ERROR_ITEM_BUY_TAROT_POINT;
		break;
	default:
		buyType = itemDefine->buyType;
		break;
	}

	if ( buyType != ItemBuyNone )
	{
		PerItemCount* itemCount = ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, buyType );

		if ( itemCount != NULL )
		{
			if ( itemCount->bag < buyPrice )
				throw ERROR_ITEM_BUY_FAIL;
		}
		else
			throw ERROR_ITEM_BUY_FAIL;
	}

	// 구입가능 검사.
	if ( CheckInventory( msg->itemDefineIndex, msg->count ) == false )
		throw ERROR_ITEM_BUY_FAIL;

	// DATABASE - 아이템 구매 요청.
	HANDLE        handle      = NULL;
	ITEM_BUY*     itemBuy     = (ITEM_BUY*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_BUY );
	TB_INVENTORY* table       = itemBuy->table;
	u_long        length      = sizeof(ITEM_BUY) - sizeof(itemBuy->table);
	BYTE&         rowCount    = (BYTE&)itemBuy->rowCount;
	u_short       remainCount = msg->count;

	itemBuy->characterIdx = mObject.index;
	itemBuy->npcIdx       = msg->npcIdx;
	itemBuy->buyType      = itemDefine->buyType;
	itemBuy->buyPrice     = (-buyPrice);

	// 구매 아이템1. 겹치기 검사 및 처리.
	TB_INVENTORY* inventory = SelectInventory( INVENTORY_BAG_BEGIN );
	for ( short i = INVENTORY_BAG_BEGIN; i <= INVENTORY_BAG_END && remainCount > 0; i++, inventory++ )
	{
		if ( (inventory->idx > 0) && (inventory->itemDefineIndex == (long)msg->itemDefineIndex) )
		{
			if ( inventory->count < itemDefine->capacity )
			{
				short shortCount;

				shortCount  = UpdateInventory( inventory, remainCount );
				remainCount = remainCount - shortCount;
				
				table->idx   = inventory->idx;
				table->count = (+shortCount);
				table++;

				rowCount++;
			}
		}
	}

	// 구매 아이템2. 생성 검사 및 등록.
	short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );
	short shortCount;

	while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
	{
		CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

		table->idx             = 0;
		table->itemDefineIndex = itemDefine->index;
		table->count           = (+shortCount);
		table->number          = number;
		table++;

		rowCount++;

		remainCount = remainCount - shortCount;
		number      = GetEmptyBagNumber( number+1 );
	}

	if ( buyType != ItemSellNone )
	{
		TB_INVENTORY* tempInv;
		short         remainCount;

		tempInv     = SelectInventory( INVENTORY_BAG_BEGIN );
		remainCount = (short)(itemDefine->buyPrice * msg->count);

		for ( short i = INVENTORY_BAG_BEGIN; (i <= INVENTORY_BAG_END) && (remainCount > 0); i++, tempInv++ )
		{
			if ( buyType == tempInv->itemDefineIndex )
			{
				short shortCount;

				shortCount  = UpdateInventory( tempInv, (-remainCount) );
				remainCount = remainCount + shortCount;

				table->idx   = tempInv->idx;
				table->count = shortCount;
				table++;

				rowCount++;
			}
		}
	}

	if ( itemBuy->rowCount > 0 )
	{
		length += (itemBuy->rowCount * sizeof(itemBuy->table));
		NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length );
	}
	else
	{
		NETWORK2->ReleaseSQL( handle, length );
		throw ERROR_ITEM_BUY_FAIL;
	}
}

// ItemCollect Method.
void cPlayer::ItemCollect(ULONG_PTR socketContext, MSG_REQ_ITEM_COLLECT* msg)
{
	/*-- 아이템 판매시 검사할 내용들.
	*/
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_COLLECT_FAIL;

	if ( msg->number1 < INVENTORY_BAG_BEGIN || msg->number1 > INVENTORY_BAG_END )
		throw ERROR_ITEM_COLLECT_FAIL;

	if ( msg->number2 < INVENTORY_COLLECTION_BEGIN || msg->number2 > INVENTORY_COLLECTION_END )
		throw ERROR_ITEM_COLLECT_FAIL;

	// 인벤토리 내용 확인.
	TB_INVENTORY* inventory1 = SelectInventory( msg->number1 );
	TB_INVENTORY* inventory2 = SelectInventory( msg->number2 );

	if ( IsInventory( inventory1 ) == false )
		throw ERROR_ITEM_COLLECT_FAIL;

	// 아이템 테이블 확인.
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory1->itemDefineIdx );

	if ( itemDefine == NULL )
		throw ERROR_ITEM_COLLECT_FAIL;

	if ( itemDefine->type == ITEM_CARD )
	{
		if ( itemDefine->subType != ITEM_CARD_TAROT )
			throw ERROR_ITEM_COLLECT_FAIL;

		TB_ITEM_TAROT* itemTarot = ITEMMANAGER->GetItemTarot( inventory1->itemDefineIdx );
		if ( itemTarot == NULL )
			throw ERROR_ITEM_COLLECT_FAIL;

		if ( (itemTarot->number + INVENTORY_TAROT_BEGIN) != msg->number2 )
			throw ERROR_ITEM_COLLECT_FAIL;
	}
	else if ( itemDefine->type == ITEM_SPREAD )
	{
		if ( msg->number2 < INVENTORY_SPREAD_BEGIN || msg->number2 > INVENTORY_SPREAD_END )
			throw ERROR_ITEM_COLLECT_FAIL;
	}
	else
		throw ERROR_ITEM_COLLECT_FAIL;

	HANDLE        handle      = NULL;
	ITEM_COLLECT* itemCollect = (ITEM_COLLECT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_COLLECT );

	itemCollect->update.inventoryIdx = inventory1->idx;
	itemCollect->remove.inventoryIdx = inventory2->idx;
	itemCollect->inventoryNumber1 = msg->number1;
	itemCollect->inventoryNumber2 = msg->number2;

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(ITEM_COLLECT) ) == false )
		throw ERROR_ITEM_COLLECT_FAIL;
}

// SendInventory Method.
bool cPlayer::SendInventory(ULONG_PTR socketContext)
{
	HANDLE             handle     = NULL;
	MSG_RES_INVENTORY* sendMsg    = (MSG_RES_INVENTORY*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)socketContext, NM_ITEM, NM_ITEM_INVENTORY_RES );
	sInventory*        sinventory = sendMsg->Inventory;

	TB_INVENTORY*      inventory  = SelectInventory( );
	unsigned long      length     = sizeof(MSG_RES_INVENTORY) - sizeof(sendMsg->Inventory);
	int                index      = MIN_INVENTORY;

	sendMsg->activeWeapon = mItemActiveWeapon;
	sendMsg->RowCount     = 0;

	while ( index < MAX_INVENTORY )
	{
		if ( IsInventory( inventory ) )
		{
			Inventory2sInventory( sinventory, inventory );
			sinventory++;

			sendMsg->RowCount++;
		}
		inventory++;
		index++;
	}

	length += (sendMsg->RowCount * sizeof(sendMsg->Inventory));
	return NETWORK2->SendMsgRoot( handle, length );
}

// SendInventoryCooltime Method.
bool cPlayer::SendInventoryCooltime(ULONG_PTR socketContext)
{
	HANDLE                      handle  = NULL;
	MSG_RES_INVENTORY_COOLTIME* sendMsg = (MSG_RES_INVENTORY_COOLTIME*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)socketContext, NM_ITEM, NM_ITEM_INVENTORY_COOLTIME_RES );
	unsigned long               length  = sizeof(MSG_RES_INVENTORY_COOLTIME) - sizeof(sendMsg->InventoryCooltime);

	PerCooltime*        perCooltime       = (PerCooltime*)mCooltimeRoot.pool;
	sInventoryCooltime* inventoryCooltime = sendMsg->InventoryCooltime;
	long                remainCooltime;
	time_t              ltime;

	time( &ltime );

	while ( perCooltime != NULL )
	{
		remainCooltime = (long)difftime( perCooltime->validThru, ltime );
		if ( remainCooltime > 0 )
		{
			inventoryCooltime->ItemIndex = perCooltime->index;
			inventoryCooltime->cooltime2 = perCooltime->cooltime2;
			inventoryCooltime->cooltime  = remainCooltime*1000;
			inventoryCooltime->cooltime1 = perCooltime->cooltime1;
			inventoryCooltime++;

			sendMsg->RowCount++;
		}
		perCooltime = (PerCooltime*)perCooltime->next;
	}

	length += (sendMsg->RowCount * sizeof(sendMsg->InventoryCooltime));
	return NETWORK2->SendMsgRoot( handle, length );
}

// SetWearInfoWithSendMsg Method - Player의 착용정보(방어구) 설정과 관련 메시지 처리.
bool cPlayer::SetWearInfoWithSendMsg(eWEAR_TYPE type, unsigned long itemDefineIndex)
{
	if ( type < eWEAR_MAX && mPlayerWearInfo.WearIdx[ type ] != itemDefineIndex )
	{
		// 착용정보 백업.
		mPlayerWearInfo.WearIdx[ type ] = itemDefineIndex;

		// 착용정보 보내기-RES.
		HANDLE                     handle = NULL;
		MSG_RES_PLAYER_EQUIP_WEAR* msgRes = (MSG_RES_PLAYER_EQUIP_WEAR*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );

		if ( msgRes != NULL )
		{
			msgRes->Category  = NM_PLAYER;
			msgRes->Protocol  = NM_PLAYER_EQUIP_WEAR_RES;
			msgRes->mType     = (unsigned char)type;
			msgRes->mWearInfo = itemDefineIndex;
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_EQUIP_WEAR) );
		}

		// 착용정보 보내기-SYN.
		MSG_SYN_PLAYER_EQUIP_WEAR msgSyn;
		memset( &msgSyn, 0, sizeof(MSG_SYN_PLAYER_EQUIP_WEAR) );

		msgSyn.Category      = NM_PLAYER;
		msgSyn.Protocol      = NM_PLAYER_EQUIP_WEAR_SYN;
		msgSyn.mCharacterIdx = mObject.index;			// mPlayerInfo.CharacterIdx 와 같음.
		msgSyn.mType         = (unsigned char)type;
		msgSyn.mWearInfo     = itemDefineIndex;
		NETWORK2->QuickSendExcept( this, (char*)&msgSyn, sizeof(msgSyn) );
		return true;
	}
	return false;
}

// SetWeaponInfoWithSendMsg Method - Player의 착용정보(무기) 설정과 관련 메시지 처리.
void cPlayer::SetWeaponInfoWithSendMsg(TB_INVENTORY* left, TB_INVENTORY* right)
{
	// 착용정보 백업.
	mPlayerWeaponInfo.WeaponIdx[ eHAND_LEFT  ] = left->itemDefineIndex;
	mPlayerWeaponInfo.WeaponIdx[ eHAND_RIGHT ] = right->itemDefineIndex;

	mPlayerWeaponInfo.WeaponEnhanced[ eHAND_LEFT  ] = left->enhanced;
	mPlayerWeaponInfo.WeaponEnhanced[ eHAND_RIGHT ] = right->enhanced;

	// 착용정보 보내기-RES.
	HANDLE                       handle = NULL;
	MSG_RES_PLAYER_EQUIP_WEAPON* msgRes = (MSG_RES_PLAYER_EQUIP_WEAPON*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );

	if ( msgRes != NULL )
	{
		msgRes->Category     = NM_PLAYER;
		msgRes->Protocol     = NM_PLAYER_EQUIP_WEAPON_RES;
		msgRes->mWeaponState = (unsigned char)mPlayerWeaponState;
		msgRes->mWeaponInfo  = mPlayerWeaponInfo;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_EQUIP_WEAPON) );
	}

	// 착용정보 보내기-SYN.
	MSG_SYN_PLAYER_EQUIP_WEAPON msgSyn;
		memset( &msgSyn, 0, sizeof(MSG_SYN_PLAYER_EQUIP_WEAPON) );
		msgSyn.Category      = NM_PLAYER;
		msgSyn.Protocol      = NM_PLAYER_EQUIP_WEAPON_SYN;
		msgSyn.mCharacterIdx = mObject.index;
		msgSyn.mWeaponState  = (unsigned char)mPlayerWeaponState;
		msgSyn.mWeaponInfo   = mPlayerWeaponInfo;
	NETWORK2->QuickSendExcept( this, (char*)&msgSyn, sizeof(msgSyn) );
}

// ItemGetOpen Method.
void cPlayer::ItemGetOpen(MSG_REQ_ITEM_GET_OPEN* msg)
{
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_GET_OPEN_FAIL;

	if ( IsChangeState( ePLAYER_STATE_ITEMPICK ) == false )
		throw ERROR_ITEM_GET_OPEN_FAIL;

	if ( mItemGetOpen == true )
	{
		cItem* item = OBJECTMANAGER->GetItem( mItemGetIdx );
		if ( item != NULL )
		{
			if ( item->IsOpen( this ) )
				item->Close( );
		}
		ItemGetClose( );
	}

	cItem* item = OBJECTMANAGER->GetItem( msg->idx );

	if ( item == NULL )
		throw ERROR_ITEM_GET_OPEN_FAIL;

	item->Open( this );

	mItemGetOpen = true;
	mItemGetIdx  = item->GetObjectID( );

	if ( ChangeState( ePLAYER_STATE_ITEMPICK ) == false )
		throw ERROR_ITEM_GET_OPEN_FAIL;

	SendItemGetOpen( item );
}

// ItemGetClose Method.
void cPlayer::ItemGetClose(MSG_REQ_ITEM_GET_CLOSE* /*msg*/)
{
	if ( mItemGetOpen == false )
		throw ERROR_ITEM_GET_CLOSE_FAIL;

	cItem* item = OBJECTMANAGER->GetItem( mItemGetIdx );

	if ( item == NULL )
		throw ERROR_ITEM_GET_CLOSE_FAIL;

	if ( !item->IsOpen( this ) )
		throw ERROR_ITEM_GET_CLOSE_FAIL;

	if ( !item->Close( ) )
		throw ERROR_ITEM_GET_CLOSE_FAIL;

	ItemGetClose( );
}
void cPlayer::ItemGetClose( )
{
	mItemGetIdx  = 0;
	mItemGetOpen = false;

	if ( GetState( ) == ePLAYER_STATE_ITEMPICK )
		ChangeState( eOBJECT_STATE_IDLE );

	SendItemGetClose( );
}

// ItemGet Method.
void cPlayer::ItemGet(MSG_REQ_ITEM_GET* msg)
{
	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_GET_FAIL;

	if ( mItemGetOpen == false )
		throw ERROR_ITEM_GET_FAIL;

	cItem* item = OBJECTMANAGER->GetItem( mItemGetIdx );

	if ( item == NULL )
		throw ERROR_ITEM_GET_FAIL;

	if ( !item->IsOpen( this ) )
		throw ERROR_ITEM_GET_FAIL;

	BYTE       rowCount = 0;
	sItemData* itemData = item->GetItemData( rowCount );
	if ( rowCount > 0 && itemData != NULL )
	{
		HANDLE    handle  = NULL;
		ITEM_GET* itemGet = (ITEM_GET*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_GET );
		u_long    length  = sizeof(ITEM_GET) - sizeof(itemGet->table);

		BYTE&      count    = (BYTE&)itemGet->rowCount;

		itemGet->characterIdx = mObject.index;
		for ( int i = 0; (i < MAX_ITEMS) && (rowCount > 0); i++, itemData++ )
		{
			if ( itemData->itemDefineIndex > 0 && itemData->count > 0 )
			{
				bool getBegin = false;
				bool getEnd   = false;
				if ( msg->itemIndexOrInd == ITEM_GET_ALL )
				{
					getBegin = true;
				}
				else if ( msg->itemIndexOrInd == itemData->itemDefineIndex )
				{
					getBegin = true;
					getEnd   = true;
				}
				if ( getBegin == true )
				{
					TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( itemData->itemDefineIndex );
					TB_INVENTORY*   inventory;

					short           remainCount = itemData->count;
					short           shortCount;

					// 줍기 아이템1. 겹치기 검사 및 처리.
					inventory = SelectInventory( INVENTORY_BAG_BEGIN );
					for ( short i = INVENTORY_BAG_BEGIN; i <= INVENTORY_BAG_END && remainCount > 0; i++, inventory++ )
					{
						if ( (inventory->idx > 0) && (inventory->itemDefineIndex == (long)itemData->itemDefineIndex) )
						{
							if ( inventory->count < itemDefine->capacity )
							{
								shortCount = UpdateInventory( inventory, remainCount );

								itemGet->table[ count ].newOrUpdate     = true;
								itemGet->table[ count ].itemDefineIndex = itemDefine->index;
								itemGet->table[ count ].inventoryNumber = i;
								itemGet->table[ count ].count           = shortCount;
								count++;

								item->PopItem( itemDefine->index, shortCount );	// 아이템 차감.
								remainCount = remainCount - shortCount;
							}
						}
					}
					// 줍기 아이템2. 생성 검사 및 등록.
					short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );
					while ( remainCount > 0 && (number != INVENTORY_BAG_NONE) )
					{
						inventory = CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

						itemGet->table[ count ].newOrUpdate     = false;
						itemGet->table[ count ].itemDefineIndex = itemDefine->index;
						itemGet->table[ count ].inventoryNumber = number;
						itemGet->table[ count ].count           = shortCount;
						count++;

						// 아이템 차감.
						item->PopItem( itemDefine->index, shortCount );
						remainCount = remainCount - shortCount;

						// 인벤토리 빈공간 확인.
						number = GetEmptyBagNumber( (++number) );
					}
					rowCount--;
				}
				if ( getEnd == true )
					break; // for ( ... )
			}
		}
		if ( itemGet->rowCount > 0 )
		{
			length += (itemGet->rowCount * sizeof(itemGet->table));
			NETWORK2->SendSQL( mConnectionIdx, handle, length );

			// 습득후 남은 아이템 동기화.
			SendItemGetLeave( item );
		}
		else
		{
			NETWORK2->ReleaseSQL( handle, length );
			throw ERROR_ITEM_GET_FAIL;
		}
	}
	else
		throw ERROR_ITEM_GET_FAIL;
}

// ItemGetAutoPush Method.
unsigned short cPlayer::ItemGetAutoPush(unsigned long itemDefineIndex, unsigned short count)
{
	if ( mItemGetCount < MAX_ITEMS )
	{
		TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( itemDefineIndex );
		if ( itemDefine != NULL )
		{
			if ( count > itemDefine->capacity )
				count = itemDefine->capacity;

			mItemGetData[ mItemGetCount ].itemDefineIndex = itemDefineIndex;
			mItemGetData[ mItemGetCount ].count           = count;
			mItemGetCount +=1;
			return count;
		}
	}
	return 0;
}

// ItemGetAutoEnd Method.
bool cPlayer::ItemGetAutoEnd(bool apply)
{
	sItemData* itemGetData  = mItemGetData;
	int        itemGetCount = mItemGetCount;

	// 자동습득 수량 정리.
	mItemGetCount = 0;

	if ( apply == true )
	{
		if ( mItemGetOpen == true )
		{
			cItem* item = OBJECTMANAGER->GetItem( mItemGetIdx );
			if ( item != NULL )
			{
				if ( item->IsOpen( this ) )
					item->Close( );
			}
			ItemGetClose( );
		}

		unsigned itemIdx = ITEMMANAGER->MakeItemIdx( );
		cItem*   item    = OBJECTMANAGER->AddItem( itemIdx, this, true );

		if ( item != NULL )
		{
			item->PushOwner( mObject.index );

			for ( int i = 0; i < itemGetCount; ++i, ++itemGetData )
			{
				unsigned short remainCount = itemGetData->count;
				unsigned short shortCount  = 0;

				while ( remainCount > 0 )
				{
					shortCount  = item->PushItem( itemGetData->itemDefineIndex, remainCount );
					remainCount = ( shortCount > 0 ) ? (remainCount - shortCount) : 0;
				}
			}

			item->Open( this );

			mItemGetOpen = true;
			mItemGetIdx  = itemIdx;

			if ( ChangeState( ePLAYER_STATE_ITEMPICK ) == false )
			{
				OBJECTMANAGER->RemoveItem( itemIdx );
				return false;
			}

			return SendItemGatheringOpen( item );
		}
	}
	return false;
}

// ItemGetQuest Method.
bool cPlayer::ItemGetQuest(unsigned long itemDefineIndex, unsigned short count)
{
	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( itemDefineIndex );
	TB_INVENTORY*   inventory  = NULL;
	unsigned short  remainCount;
	short           shortCount;

	if ( itemDefine != NULL && count > 0 )
	{
		HANDLE          handle       = NULL;
		ITEM_GET_QUEST* itemGetQuest = (ITEM_GET_QUEST*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_GET_QUEST );

		itemGetQuest->characterIdx = mObject.index;

		// 1. 겹치기 검사 및 처리.
		inventory   = SelectInventory( INVENTORY_QUEST_BEGIN );
		remainCount = count;
		for ( short i = INVENTORY_QUEST_BEGIN; i <= INVENTORY_QUEST_END && remainCount > 0; i++, inventory++ )
		{
			if ( inventory->itemDefineIndex == (long)itemDefineIndex )
			{
				shortCount = UpdateInventory( inventory, remainCount );

				itemGetQuest->newOrUpdate     = true;
				itemGetQuest->itemDefineIndex = itemDefine->index;
				itemGetQuest->inventoryNumber = i;
				itemGetQuest->count           = shortCount;

				// 겹치기 오류.
				if ( remainCount != shortCount )
				{
					NETWORK2->PostServerEvent( "Error - cPlayer::ItemGetQuest(=%d, =%d, =%d )", itemDefineIndex, remainCount, shortCount );
				}
				remainCount = 0;
			}
		}
		// 2. 생성 검사 및 등록.
		if ( remainCount > 0 )
		{
			short number = GetEmptyQuestNumber( );
			if ( number > 0 )
			{
				CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

				itemGetQuest->newOrUpdate     = false;
				itemGetQuest->itemDefineIndex = itemDefine->index;
				itemGetQuest->inventoryNumber = number;
				itemGetQuest->count           = shortCount;

				// 겹치기 오류.
				if ( remainCount != shortCount )
				{
					NETWORK2->PostServerEvent( "Error - cPlayer::ItemGetQuest(=%d, =%d, =%d )", itemDefineIndex, remainCount, shortCount );
				}
				remainCount = 0;
			}
		}

		if ( remainCount == 0 )
		{
			return NETWORK2->SendSQL( mConnectionIdx, handle, sizeof(ITEM_GET_QUEST) );
		}
		else
		{
			NETWORK2->ReleaseSQL( handle, sizeof(ITEM_GET_QUEST) );
			NETWORK2->PostServerEvent( "Error - cPlayer::ItemGetQuest(=%d, =%d)", itemDefineIndex, remainCount );
		}
	}

	return false;
}

// SendItemGetOpen Method.
bool cPlayer::SendItemGetOpen(cBaseObject* baseObject)
{
	HANDLE                 handle   = NULL;
	MSG_RES_ITEM_GET_OPEN* sendMsg  = (MSG_RES_ITEM_GET_OPEN*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	bool                   retvalue = false;
	if ( sendMsg != NULL )
	{
		cItem*     item     = (cItem*)baseObject;
		sItemData* itemData = sendMsg->itemData;

		sendMsg->Category  = NM_ITEM;
		sendMsg->Protocol  = NM_ITEM_GET_OPEN_RES;
		sendMsg->ErrorCode = ERROR_ITEM_GET_OPEN_SUCCESS;
		sendMsg->rowCount  = item->GetItemData( itemData );

		retvalue = NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
	}

	MSG_SYN_ITEM_GET_OPEN sendSyn;

	sendSyn.Category     = NM_ITEM;
	sendSyn.Protocol     = NM_ITEM_GET_OPEN_SYN;
	sendSyn.characterIdx = mObject.index;

	NETWORK2->QuickSendExcept( this, (char*)&sendSyn, sizeof(MSG_SYN_ITEM_GET_OPEN) );
	return retvalue;
}

// SendItemGetClose Method.
bool cPlayer::SendItemGetClose( )
{
	HANDLE                  handle   = NULL;
	MSG_RES_ITEM_GET_CLOSE* sendMsg  = (MSG_RES_ITEM_GET_CLOSE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	bool                    retvalue = false;
	if ( sendMsg != NULL )
	{
		sendMsg->Category  = NM_ITEM;
		sendMsg->Protocol  = NM_ITEM_GET_CLOSE_RES;
		sendMsg->ErrorCode = ERROR_ITEM_GET_CLOSE_SUCCESS;

		retvalue = NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_ITEM_GET_CLOSE) );
	}

	MSG_SYN_ITEM_GET_CLOSE sendSyn;

	sendSyn.Category     = NM_ITEM;
	sendSyn.Protocol     = NM_ITEM_GET_CLOSE_SYN;
	sendSyn.characterIdx = mObject.index;

	NETWORK2->QuickSendExcept( this, (char*)&sendSyn, sizeof(MSG_SYN_ITEM_GET_CLOSE) );
	return retvalue;
}

// SendItemGetLeave Method.
bool cPlayer::SendItemGetLeave(cBaseObject* baseObject)
{
	HANDLE                  handle   = NULL;
	MSG_RES_ITEM_GET_LEAVE* sendMsg  = (MSG_RES_ITEM_GET_LEAVE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	if ( sendMsg != NULL )
	{
		cItem*     item     = (cItem*)baseObject;
		sItemData* itemData = sendMsg->itemData;

		sendMsg->Category  = NM_ITEM;
		sendMsg->Protocol  = NM_ITEM_GET_LEAVE_RES;
		sendMsg->rowCount  = item->GetItemData( itemData );

		return NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
	}
	return false;
}

// SendItemGatheringOpen Method.
bool cPlayer::SendItemGatheringOpen(cBaseObject* baseObject)
{
	HANDLE                       handle   = NULL;
	MSG_RES_ITEM_GATHERING_OPEN* sendMsg  = (MSG_RES_ITEM_GATHERING_OPEN*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	if ( sendMsg != NULL )
	{
		cItem*     item     = (cItem*)baseObject;
		sItemData* itemData = sendMsg->itemData;

		sendMsg->Category  = NM_ITEM;
		sendMsg->Protocol  = NM_ITEM_GATHERING_OPEN_RES;
		sendMsg->ErrorCode = ERROR_ITEM_GET_OPEN_SUCCESS;
		sendMsg->rowCount  = item->GetItemData( itemData );

		return NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
	}
	return false;
}

// ExchangeDie Method.
void cPlayer::ExchangeDie( )
{
	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );

	if ( player != NULL )
	{
		player->SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_CANCEL_RES, ERROR_ITEM_EXCHANGE_CANCEL_PLAYER );
		player->ExchangeCancel( );

		player->ChangeState( eOBJECT_STATE_IDLE );
		player->SetStateStop( eSTOP_NONE );
	}

	ExchangeCancel( );
}

// ExchangeShutdown Method.
void cPlayer::ExchangeShutdown(bool /*shutdown*/)
{
	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );

	if ( player != NULL )
	{
		player->SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_CANCEL_RES, ERROR_ITEM_EXCHANGE_CANCEL_PLAYER );
		player->ExchangeCancel( );

		player->ChangeState( eOBJECT_STATE_IDLE );
		player->SetStateStop( eSTOP_NONE );
	}

	ExchangeCancel( );

	ChangeState( eOBJECT_STATE_STOP );
	SetStateStop( eSTOP_GAMEFINISH );
}

// ExchangeAsk Method.
void cPlayer::ExchangeAsk(MSG_REQ_ITEM_EXCHANGE_ASK* msg)
{
	if ( IsChangeState( eOBJECT_STATE_STOP ) == false )
		throw ERROR_ITEM_EXCHANGE_ASK_FAIL;					// 캐릭터 상태 오류 - 변경불가.

	// 이미 거래중 확인.
	if ( mExchangeStatus != ItemExchangeNone )
		throw ERROR_ITEM_EXCHANGE_ASK_EXIST;

	if ( msg->mTarget.type != eOBJECTTYPE_PLAYER )
		throw ERROR_ITEM_EXCHANGE_ASK_FAIL;

	cPlayer* player = GRIDMANAGER->GetPlayer( msg->mTarget.index );
	if ( player == NULL )
		throw ERROR_ITEM_EXCHANGE_ASK_FAIL;

	sGameOption* toOption1 = player->GetOptionData();
	if( toOption1->option1.rejectionTrade == true )
		throw ERROR_ITEM_EXCHANGE_ASK_OPTIONREFUSE;

	// 맵 위치 검사.
	if ( mMapNumber != player->GetMapNumber( ) )
		throw ERROR_ITEM_EXCHANGE_ASK_FAIL;

	// 거리 검사.
	float tempRange = OBJECTMANAGER->ObjectSizeRange( this, player, ITEM_VALID_DISTANCE + SYNC_MOVE_RANGE );
	if ( (mObjectPos - player->GetPos( )).Length( ) > tempRange )
		throw ERROR_ITEM_EXCHANGE_ASK_DISTANCE;

	// 거래 응답 요구.
	player->ExchangeAsk( mObject );

	// 상태변경.
	ChangeState( eOBJECT_STATE_STOP );
	SetStateStop( eSTOP_EXCHANGE );							// 캐릭터 정지상태 중 거래중.

	// 거래중 처리와 대상 저장.
	mExchangeStatus = ItemExchangeAsk;
	mExchangeTarget = player->GetObject( );
	mExchangeItems  = 0;
	mExchangeMoney  = 0;

	SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_ASK_RES, ERROR_ITEM_EXCHANGE_ASK_SUCCESS );
}
void cPlayer::ExchangeAsk(sObject object)
{
	if ( IsRequestRejection( ) == true )
		throw ERROR_ITEM_EXCHANGE_ASK_FAIL;					// 캐릭터 상태오류.

	if ( GetStateDie( ) == true )
		throw ERROR_ITEM_EXCHANGE_ASK_FAIL;					// 캐릭터 상태오류.

	if ( GetState( ) == eOBJECT_STATE_STOP )
		throw ERROR_ITEM_EXCHANGE_ASK_FAIL;					// 캐릭터 상태오류.

	// 이미 거래중 확인.
	if ( mExchangeStatus != ItemExchangeNone )
		throw ERROR_ITEM_EXCHANGE_ASK_EXIST;

	StartRequestRejection( eREQREJCT_REQWAIT );				// 캐릭터 상태변경.

	// 거래중 처리와 대상 저장.
	mExchangeStatus = ItemExchangeAsk;
	mExchangeTarget = object;
	mExchangeItems  = 0;
	mExchangeMoney  = 0;

	HANDLE handle = NULL;
	MSG_RES_ITEM_EXCHANGE_ASK* sendMsg = (MSG_RES_ITEM_EXCHANGE_ASK*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_ITEM, NM_ITEM_EXCHANGE_ASK_RES );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode    = ERROR_ITEM_EXCHANGE_ASK_REPLAY;
		sendMsg->characterIdx = object.index;

		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_ITEM_EXCHANGE_ASK) );
	}
}

// ExchangeReply Method - 승락이 아닐경우, 모두 거절처리.
void cPlayer::ExchangeReply(MSG_REQ_ITEM_EXCHANGE_REP* msg)
{
	try
	{
		if ( IsRequestRejection( ) != true )
			throw ERROR_ITEM_EXCHANGE_REP_FAIL;					// 캐릭터 상태 오류.
		else
			EndRequestRejection( eREQREJCT_REQWAIT );			// 캐릭터 상태 변경.

		if ( IsChangeState( eOBJECT_STATE_STOP ) == false )
			throw ERROR_ITEM_EXCHANGE_REP_FAIL;					// 캐릭터 상태 오류 - 변경불가.

		if ( GetStateStop( ) != eSTOP_NONE )
			throw ERROR_ITEM_EXCHANGE_REP_FAIL;					// 캐릭터 상태 오류.
	}
	catch ( int error )
	{
		cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );
		if ( player != NULL )
		{
			if ( player->GetStateStop( ) == eSTOP_EXCHANGE )
			{
				player->ChangeState( eOBJECT_STATE_IDLE );
				player->SetStateStop( eSTOP_NONE );

				player->ExchangeReply( TYPE_ITEM_EXCHANGE_REP_REJECT );
			}
		}
		if ( mExchangeStatus == ItemExchangeAsk )
			mExchangeStatus = ItemExchangeNone;
		throw error;
	}

	// 이미 거래중 확인.
	if ( mExchangeStatus != ItemExchangeAsk )
		throw ERROR_ITEM_EXCHANGE_REP_FAIL;

	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );

	if ( player == NULL )
		throw ERROR_ITEM_EXCHANGE_REP_NOTEXIST;

	if ( player->GetStateStop( ) != eSTOP_EXCHANGE )
		throw ERROR_ITEM_EXCHANGE_REP_NOTEXIST;

	if ( msg->type == TYPE_ITEM_EXCHANGE_REP_ACCEPT )
	{
		ChangeState( eOBJECT_STATE_STOP );
		SetStateStop( eSTOP_EXCHANGE );						// 캐릭터 정지상태 중 거래.

		player->ExchangeReply( msg->type );
	}
	else /*if ( msg->type == TYPE_ITEM_EXCHANGE_REP_REJECT )*/
	{
		player->ChangeState( eOBJECT_STATE_IDLE );
		player->SetStateStop( eSTOP_NONE );

		player->ExchangeReply( msg->type );
	}

	ExchangeReply( msg->type );
}
void cPlayer::ExchangeReply(BYTE type)
{
	if ( mExchangeStatus != ItemExchangeAsk )
		throw ERROR_ITEM_EXCHANGE_REP_FAIL;

	if ( type == TYPE_ITEM_EXCHANGE_REP_ACCEPT )
	{
		mExchangeStatus = ItemExchangeBegin;
		SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_REP_RES, ERROR_ITEM_EXCHANGE_REP_SUCCESS );

	}
	else
	{
		mExchangeStatus = ItemExchangeNone;
		SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_REP_RES, ERROR_ITEM_EXCHANGE_REP_FAIL );
	}
}

// ExchangeAdd Method.
void cPlayer::ExchangeAdd(MSG_REQ_ITEM_EXCHANGE_ADD* msg)
{
	if ( mExchangeStatus != ItemExchangeBegin )
		throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );

	// 심각한 오류이므로, 반드시 오류처리 해야됨.
	if ( player == NULL )
		throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

	switch ( msg->type )
	{
	case TYPE_ITEM_EXCHANGE_ADD_ITEM:
		{
			if ( !(mExchangeItems < MAX_ITEM_EXCHANGE) )
				throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

			TB_INVENTORY* inventory = SelectInventory( msg->number );

			if ( IsInventory( inventory ) == false )
				throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

			if ( inventory->apply != InventoryApplyNone )
				throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

			TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );

			if ( itemDefine == NULL )
				throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

			// exchange 값이 0일경우 거래가능.
			if ( itemDefine->exchange != 0 )
				throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

			inventory->apply = InventoryApplyExchange;
			mExchangeItems++;

			player->ExchangeAdd( inventory );

			SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_ADD_RES, ERROR_ITEM_EXCHANGE_ADD_SUCCESS );
		}
		break;
	case TYPE_ITEM_EXCHANGE_ADD_MONEY:
		{
			unsigned long maxMoney = ULONG_MAX - 1UL;

			if ( msg->money == 0 || msg->money > mMoney || msg->money > maxMoney )
				throw ERROR_ITEM_EXCHANGE_ADD_FAIL;

			mExchangeMoney = msg->money;

			player->ExchangeAdd( mExchangeMoney );

			SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_ADD_RES, ERROR_ITEM_EXCHANGE_ADD_SUCCESS );
		}
		break;
	default:
		throw ERROR_ITEM_EXCHANGE_ADD_FAIL;
	}
}
void cPlayer::ExchangeAdd(TB_INVENTORY* inventory)
{
	HANDLE                     handle     = NULL;
	MSG_SYN_ITEM_EXCHANGE_ADD* sendMsg    = (MSG_SYN_ITEM_EXCHANGE_ADD*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_ITEM, NM_ITEM_EXCHANGE_ADD_SYN );

	sendMsg->type = TYPE_ITEM_EXCHANGE_ADD_ITEM;
	Inventory2sInventory( &sendMsg->inventory, inventory );

	NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_ITEM_EXCHANGE_ADD) );
}
void cPlayer::ExchangeAdd(unsigned long money)
{
	HANDLE                     handle  = NULL;
	MSG_SYN_ITEM_EXCHANGE_ADD* sendMsg = (MSG_SYN_ITEM_EXCHANGE_ADD*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );

	sendMsg->Category  = NM_ITEM;
	sendMsg->Protocol  = NM_ITEM_EXCHANGE_ADD_SYN;
	sendMsg->type      = TYPE_ITEM_EXCHANGE_ADD_MONEY;
	sendMsg->money     = money;

	NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_ITEM_EXCHANGE_ADD) );
}

// ExchangeDel Method.
void cPlayer::ExchangeDel(MSG_REQ_ITEM_EXCHANGE_DEL* msg)
{
	if ( mExchangeStatus != ItemExchangeBegin )
		throw ERROR_ITEM_EXCHANGE_DEL_FAIL;

	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );

	// 심각한 오류이므로, 반드시 오류처리 해야됨.
	if ( player == NULL )
		throw ERROR_ITEM_EXCHANGE_DEL_FAIL;

	if ( !(mExchangeItems > 0) )
		throw ERROR_ITEM_EXCHANGE_DEL_FAIL;

	TB_INVENTORY* inventory = SelectInventory( msg->number );

	if ( IsInventory( inventory ) == false )
		throw ERROR_ITEM_EXCHANGE_DEL_FAIL;

	if ( inventory->apply != InventoryApplyExchange )
		throw ERROR_ITEM_EXCHANGE_DEL_FAIL;

	inventory->apply = InventoryApplyNone;
	mExchangeItems--;

	player->ExchangeDel( inventory );

	SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_DEL_RES, ERROR_ITEM_EXCHANGE_DEL_SUCCESS );
}
void cPlayer::ExchangeDel(TB_INVENTORY* inventory)
{
	HANDLE                     handle  = NULL;
	MSG_SYN_ITEM_EXCHANGE_DEL* sendMsg = (MSG_SYN_ITEM_EXCHANGE_DEL*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_ITEM, NM_ITEM_EXCHANGE_DEL_SYN );

	Inventory2sInventory( &sendMsg->inventory, inventory );

	NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_ITEM_EXCHANGE_DEL) );
}

// ExchangeOk Method.
void cPlayer::ExchangeOk(MSG_REQ_ITEM_EXCHANGE_OK* /*msg*/)
{
	if ( mExchangeStatus != ItemExchangeBegin )
		throw ERROR_ITEM_EXCHANGE_OK_FAIL;

	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );
	if ( player == NULL )
		throw ERROR_ITEM_EXCHANGE_OK_FAIL;

	mExchangeStatus = ItemExchangeOk;

	player->SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_OK_RES, ERROR_ITEM_EXCHANGE_OK_PLAYER );

	SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_OK_RES, ERROR_ITEM_EXCHANGE_OK_SUCCESS );
}

// ExchangeRetry Method.
void cPlayer::ExchangeRetry(MSG_REQ_ITEM_EXCHANGE_RETRY* /*msg*/)
{
	if ( mExchangeStatus != ItemExchangeOk )
		throw ERROR_ITEM_EXCHANGE_RETRY_FAIL;

	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );
	if ( player == NULL )
		throw ERROR_ITEM_EXCHANGE_RETRY_FAIL;

	mExchangeStatus = ItemExchangeBegin;

	player->ExchangeRetry(  );
	SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_RETRY_RES, ERROR_ITEM_EXCHANGE_RETRY_SUCCESS );
}

// ExchangeRetry Method.
void cPlayer::ExchangeRetry( )
{
	if ( mExchangeStatus == ItemExchangeEnd )
		mExchangeStatus = ItemExchangeOk;

	SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_RETRY_RES, ERROR_ITEM_EXCHANGE_RETRY_PLAYER );
}

// ExchangeCancel Method.
void cPlayer::ExchangeCancel(MSG_REQ_ITEM_EXCHANGE_CANCEL* /*msg*/)
{
	if ( mExchangeStatus == ItemExchangeNone )
		throw ERROR_ITEM_EXCHANGE_CANCEL_FAIL;

	ExchangeCancel( );
	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );

	SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_CANCEL_RES, ERROR_ITEM_EXCHANGE_CANCEL_SUCCESS );

	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );
	if ( player != NULL )
	{
		player->SendMsgResExchange( NM_ITEM, NM_ITEM_EXCHANGE_CANCEL_RES, ERROR_ITEM_EXCHANGE_CANCEL_PLAYER );
		player->ExchangeCancel( );
		player->ChangeState( eOBJECT_STATE_IDLE );
		player->SetStateStop( eSTOP_NONE );
	}
}
void cPlayer::ExchangeCancel( )
{
	// 거래 상  태 초기화.
	mExchangeStatus = ItemExchangeNone;

	// 거래 아이템 초기화.
	TB_INVENTORY* inventory = SelectInventory( INVENTORY_BAG_BEGIN );
	for ( int i = INVENTORY_BAG_BEGIN; i <= INVENTORY_BAG_END && mExchangeItems > 0; i++, inventory++ )
	{
		if ( inventory->apply == InventoryApplyExchange )
		{
			inventory->apply = InventoryApplyNone;
			mExchangeItems--;
		}
	}

	// 거래 금  액 초기화.
	mExchangeMoney = 0;
}

// ExchangeEnd Method.
void cPlayer::ExchangeEnd(MSG_REQ_ITEM_EXCHANGE_END* /*msg*/)
{
	if ( mExchangeStatus != ItemExchangeOk )
		throw ERROR_ITEM_EXCHANGE_END_FAIL;

	cPlayer* player = GRIDMANAGER->GetPlayer( mExchangeTarget.index );
	if ( player == NULL )
		throw ERROR_ITEM_EXCHANGE_END_FAIL;

	if ( player->GetExchangeStatus( ) < ItemExchangeOk )
		throw ERROR_ITEM_EXCHANGE_END_FAIL;

	mExchangeStatus = ItemExchangeEnd;

	if ( player->GetExchangeStatus( ) != ItemExchangeEnd )
		throw ERROR_ITEM_EXCHANGE_END_WAIT;

	// Database 처리요청.
	HANDLE         handle       = NULL;
	ITEM_EXCHANGE* itemExchange = (ITEM_EXCHANGE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_EXCHANGE );
	TB_INVENTORY*  inventory;
	TB_INVENTORY*  table;

	// 적용 행 수
	itemExchange->rowCount = 2;

	// Player1 - 인벤토리 이동(캐릭터 고유번호).
	itemExchange->table[ 0 ].characterIdx   = mObject.index;
	itemExchange->table[ 0 ].characterMoney = player->GetExchangeMoney( ) - GetExchangeMoney( );
	itemExchange->table[ 0 ].characterFrom  = player->GetObjectID( );
	itemExchange->table[ 0 ].characterBefor = mMoney;
	itemExchange->table[ 0 ].characterAfter = mMoney + itemExchange->table[ 0 ].characterMoney;

	inventory = SelectInventory( INVENTORY_BAG0_BEGIN );
	table     = itemExchange->table[ 1 ].table;
	for ( int i = INVENTORY_BAG_BEGIN; i <= INVENTORY_BAG_END && itemExchange->table[ 0 ].rowCount < MAX_ITEM_EXCHANGE; i++, inventory++ )
	{
		if ( inventory->apply == InventoryApplyExchange )
		{
			(*table) = (*inventory);
			RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );

			itemExchange->table[ 1 ].rowCount++;
			table++;
		}
	}

	// Player2 - 인벤토리 이동(캐릭터 고유번호).
	itemExchange->table[ 1 ].characterIdx   = player->GetObjectID( );
	itemExchange->table[ 1 ].characterMoney = GetExchangeMoney( ) - player->GetExchangeMoney( );
	itemExchange->table[ 1 ].characterFrom  = mObject.index;
	itemExchange->table[ 1 ].characterBefor = player->GetMoney( );
	itemExchange->table[ 1 ].characterAfter = player->GetMoney( ) + itemExchange->table[ 1 ].characterMoney;

	inventory = player->SelectInventory( INVENTORY_BAG_BEGIN );
	table     = itemExchange->table[ 0 ].table;
	for ( int i = INVENTORY_BAG_BEGIN; i <= INVENTORY_BAG_END && itemExchange->table[ 0 ].rowCount < MAX_ITEM_EXCHANGE; i++, inventory++ )
	{
		if ( inventory->apply == InventoryApplyExchange )
		{
			(*table) = (*inventory);
			player->RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );

			itemExchange->table[ 0 ].rowCount++;
			table++;
		}
	}

	// Player1 - 인벤토리 번호정리(슬롯).
	table     = itemExchange->table[ 0 ].table;
	inventory = SelectInventory( INVENTORY_BAG0_BEGIN );
	for ( short i = INVENTORY_BAG_BEGIN, rowCount = 0; i <= INVENTORY_BAG_END && rowCount < itemExchange->table[ 0 ].rowCount; i++, inventory++ )
	{
		if ( inventory->idx == 0 )
		{
			table->number = i;
			XCopyInventory( table ); // (*inventory) = (*table);
			table++;

			rowCount++;
		}
	}
	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );
	SendMsgResExchangeEnd( itemExchange->table[ 0 ].table, itemExchange->table[ 0 ].rowCount );
	if ( GetExchangeMoney( ) > 0 )
	{
		long exchangeMoney = -GetExchangeMoney( );
		long result = AddMoney( player->GetObject( ), exchangeMoney, false );
		if ( result != exchangeMoney )
		{
			NETWORK2->PostServerEvent( "Error - SQL_GAME_PROCESS_ITEM_EXCHANGE From(=%d) To(=%d) AddMoney(=%d) != ResultMoney(=%d).", GetObjectID( ), GetObjectID( ), exchangeMoney, result );
		}
	}
	if ( player->GetExchangeMoney( ) > 0 )
	{
		long exchangeMoney = +player->GetExchangeMoney( );
		long result = AddMoney( player->GetObject( ), exchangeMoney, false );
		if ( result != exchangeMoney )
		{
			NETWORK2->PostServerEvent( "Error - SQL_GAME_PROCESS_ITEM_EXCHANGE From(=%d) To(=%d) AddMoney(=%d) != ResultMoney(=%d).", player->GetObjectID( ), GetObjectID( ), exchangeMoney, result );
		}
	}

	// Player2 - 인벤토리 번호정리(슬롯).
	table     = itemExchange->table[ 1 ].table;
	inventory = player->SelectInventory( INVENTORY_BAG0_BEGIN );
	for ( short i = INVENTORY_BAG_BEGIN, rowCount = 0; i <= INVENTORY_BAG_END && rowCount < itemExchange->table[ 1 ].rowCount; i++, inventory++ )
	{
		if ( inventory->idx == 0 )
		{
			table->number = i;
			player->XCopyInventory( table ); // (*inventory) = (*table);
			table++;

			rowCount++;
		}
	}
	player->ChangeState( eOBJECT_STATE_IDLE );
	player->SetStateStop( eSTOP_NONE );
	player->SendMsgResExchangeEnd( itemExchange->table[ 1 ].table, itemExchange->table[ 1 ].rowCount );
	if ( player->GetExchangeMoney( ) > 0 )
	{
		long exchangeMoney = -player->GetExchangeMoney( );
		long result = player->AddMoney( GetObject( ), exchangeMoney, false );
		if ( result != exchangeMoney )
		{
			NETWORK2->PostServerEvent( "Error - SQL_GAME_PROCESS_ITEM_EXCHANGE From(=%d) To(=%d) AddMoney(=%d) != ResultMoney(=%d).", player->GetObjectID( ), player->GetObjectID( ), exchangeMoney, result );
		}
	}
	if ( GetExchangeMoney( ) > 0 )
	{
		long exchangeMoney = +GetExchangeMoney( );
		long result = player->AddMoney( GetObject( ), exchangeMoney, false );
		if ( result != exchangeMoney )
		{
			NETWORK2->PostServerEvent( "Error - SQL_GAME_PROCESS_ITEM_EXCHANGE From(=%d) To(=%d) AddMoney(=%d) != ResultMoney(=%d).", GetObjectID( ), player->GetObjectID( ), exchangeMoney, result );
		}
	}

	if ( NETWORK2->SendSQL( handle, sizeof(ITEM_EXCHANGE) ) == false )
		throw ERROR_ITEM_EXCHANGE_END_FAIL;
}
void cPlayer::ExchangeEnd(TB_INVENTORY* table, unsigned long rowCount)
{
	TB_INVENTORY* inventory;

	while ( rowCount > 0 )
	{
		inventory = SelectInventory( table->number );

		if ( inventory->idx == table->idx )
			XCopyInventory( inventory, table ); // (*inventory) = (*table);
		else
			NETWORK2->PostServerEvent( "In cPlayer::ExchangeEnd Method. Error 'inventory_idx(=%d) and table_idx(=%d)'.", inventory->idx, table->idx );

		table++;
		rowCount--;
	}

	// 거래종료 & 초기화.
	mExchangeStatus = ItemExchangeNone;
	mExchangeItems  = 0;
	mExchangeMoney  = 0;
}

// SendMsgResExchange Method.
bool cPlayer::SendMsgResExchange(char category, char protocol, int error)
{
	HANDLE     handle  = NULL;
	MSG_ERROR* sendMsg = (MSG_ERROR*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, category, protocol );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = error;
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_ERROR) );
	}
	return false;
}

// SendMsgResExchangeEnd Method.
bool cPlayer::SendMsgResExchangeEnd(TB_INVENTORY* table, unsigned long rowCount)
{
	HANDLE                     handle     = NULL;
	MSG_RES_ITEM_EXCHANGE_END* sendMsg    = (MSG_RES_ITEM_EXCHANGE_END*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_ITEM, NM_ITEM_EXCHANGE_END_RES );
	sInventory*                sinventory = sendMsg->Inventory;

	// 거래아이템
	while ( sendMsg->RowCount < rowCount )
	{
		Inventory2sInventory( sinventory, table );

		sinventory++;
		table++;

		sendMsg->RowCount++;
	}

	sendMsg->ErrorCode = ERROR_ITEM_EXCHANGE_END_SUCCESS;
	return NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
}

// StallSellSearch Method.
void cPlayer::StallSellSearch(ULONG_PTR socketContext, MSG_REQ_ITEM_STALL_SELL_SEARCH* msg)
{
	if ( ITEMMANAGER->GetItemDefineByIndex( msg->itemDefineIndex ) == NULL || msg->enhanced > MAX_ITEM_ENHANCED )
		throw ERROR_ITEM_STALL_SELL_SEARCH_FAIL;

	HANDLE             handle          = NULL;
	STALL_SELL_SEARCH* stallSellSearch = (STALL_SELL_SEARCH*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_STALL_SELL_SEARCH );
	unsigned long      length          = sizeof(STALL_SELL_SEARCH) - sizeof(stallSellSearch->table);

	stallSellSearch->channelNum      = NETWORK2->GetChannelNum( );
	stallSellSearch->itemDefineIndex = msg->itemDefineIndex;
	stallSellSearch->enhanced        = msg->enhanced;

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length, COMMON_DB_STALL_SELL_SEARCH ) == false )
		throw ERROR_ITEM_STALL_SELL_SEARCH_FAIL;
}

// StallSellOpen Method.
void cPlayer::StallSellOpen(ULONG_PTR /*socketContext*/, MSG_REQ_ITEM_STALL_SELL_OPEN* msg)
{
	// 1-1. 캐릭터 상태 검사.
	if ( IsChangeState( eOBJECT_STATE_STOP ) == false )
		throw ERROR_ITEM_STALL_SELL_OPEN_FAIL;			// 캐릭터 상태 오류 - 변경불가.

	if ( GetStateStop( ) != eSTOP_NONE )
		throw ERROR_ITEM_STALL_SELL_OPEN_FAIL;			// 캐릭터 상태 오류.

	if ( IsRequestRejection( ) == true )
		throw ERROR_ITEM_STALL_SELL_OPEN_FAIL;			// 캐릭터 상태오류.

	// 1-2. 노점 상태 검사.
	if ( mStallSell == true )
		throw ERROR_ITEM_STALL_SELL_OPEN_EXIST;			// 노  점 상태 오류 - 이미 오픈중.

	// 2-1. 캐릭터 상태 변경.
	ChangeState( eOBJECT_STATE_STOP );					// 캐릭터 상태 - 정지.
	SetStateStop( eSTOP_OPENSTALL );					// 정지중 상태 - 노점오픈.

	// 2-2. 노점 판매자 열기.
	mStallSell = true;									// 노  점 상태 - 오픈.

	// 2-3. 노점명 등록.
	msg->title[ STALL_TITLE_LEN ] = 0;
	wcscpy( mStallSellTitle, msg->title );

	// 3-1. 노점열기 성공.
	SendMsgResStallSell( NM_ITEM, NM_ITEM_STALL_SELL_OPEN_RES, ERROR_ITEM_STALL_SELL_OPEN_SUCCESS );

	// 3-2. 노점열기 동기화.
	MSG_SYN_ITEM_STALL_SELL_OPEN sendMsg;
	memset( &sendMsg, 0, sizeof(MSG_SYN_ITEM_STALL_SELL_OPEN) );

	sendMsg.Category     = NM_ITEM;
	sendMsg.Protocol     = NM_ITEM_STALL_SELL_OPEN_SYN;
	sendMsg.characterIdx = mObject.index;
	wcscpy( sendMsg.title, mStallSellTitle );

	NETWORK2->QuickSendExcept( this, (char*)&sendMsg, sizeof(MSG_SYN_ITEM_STALL_SELL_OPEN) );
}

// StallSellClose Method.
void cPlayer::StallSellClose(ULONG_PTR socketContext, MSG_REQ_ITEM_STALL_SELL_CLOSE* /*msg*/)
{
	// 1-1. 캐릭터 상태 검사.
	if ( IsChangeState( eOBJECT_STATE_IDLE ) == false )
		throw ERROR_ITEM_STALL_SELL_CLOSE_FAIL;			// 캐릭터 상태 오류 - 변경불가.

	if ( GetStateStop( ) != eSTOP_OPENSTALL )
		throw ERROR_ITEM_STALL_SELL_CLOSE_FAIL;			// 캐릭터 상태 오류.

	// 1-2. 노점 상태 검사.
	if ( mStallSell != true )
		throw ERROR_ITEM_STALL_SELL_CLOSE_FAIL;

	// 2-1. 캐릭터 상태 변경.
	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );

	// 2-2. 노점 판매자 닫기.
	ReleaseStallSellAllGuests( );						// 게스트(GUEST)닫기.
	ClearStallSellItem( );								// 노  점 아이템 정리.

	// 3-1. 노점닫기 성공.
	SendMsgResStallSell( NM_ITEM, NM_ITEM_STALL_SELL_CLOSE_RES, ERROR_ITEM_STALL_SELL_CLOSE_SUCCESS );

	// 3-2. 노점닫기 동기화.
	MSG_SYN_ITEM_STALL_SELL_CLOSE sendMsg;
	memset( &sendMsg, 0, sizeof(MSG_SYN_ITEM_STALL_SELL_CLOSE) );

	sendMsg.Category     = NM_ITEM;
	sendMsg.Protocol     = NM_ITEM_STALL_SELL_CLOSE_SYN;
	sendMsg.characterIdx = mObject.index;

	NETWORK2->QuickSendExcept( this, (char*)&sendMsg, sizeof(MSG_SYN_ITEM_STALL_SELL_CLOSE) );

	// 4. Database 정리 - 비동기 처리 (SQL 관련한 오류는 무시함).
	HANDLE            handle         = NULL;
	STALL_SELL_CLEAR* stallSellClear = (STALL_SELL_CLEAR*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_STALL_SELL_CLEAR );
	stallSellClear->characterIdx = mObject.index;
	NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(STALL_SELL_CLEAR) );
}

// StallSellOpenDie Method.
void cPlayer::StallSellOpenDie( )
{
	ReleaseStallSellAllGuests( );   // 게스트 정리.
	ClearStallSellItem( );          // 아이템 정리.
}

// StallSellOpenShutdown Method.
void cPlayer::StallSellOpenShutdown(bool /*shutdown*/)
{
	ReleaseStallSellAllGuests( );   // 게스트 정리.
	ClearStallSellItem( );		    // 아이템 정리.

	ChangeState( eOBJECT_STATE_STOP );
	SetStateStop( eSTOP_GAMEFINISH );
}

// StallSellUseDie Method.
void cPlayer::StallSellUseDie( )
{
	cPlayer* player = GRIDMANAGER->GetPlayer( mStallSellOwner.index );
	if ( player != NULL )
		player->ReleaseStallSellGuest( mObject.index );
}

// StallSellUseShutdown Method.
void cPlayer::StallSellUseShutdown(bool /*shutdown*/)
{
	cPlayer* player = GRIDMANAGER->GetPlayer( mStallSellOwner.index );

	if ( player != NULL )
		player->ReleaseStallSellGuest( mObject.index );

	ChangeState( eOBJECT_STATE_STOP );
	SetStateStop( eSTOP_GAMEFINISH );
}

// GetStallSellItem Method - 생성.
StallSellItem* cPlayer::GetStallSellItem(TB_INVENTORY* inventory, long price)
{
	if ( !(mStallSellItemOffset < MAX_STALL_ITEM) )
		return NULL;

	StallSellItem* stallSellItem = mStallSellItems;
	for ( int i = 0; i < MAX_STALL_ITEM; i++, stallSellItem++ )
	{
		if ( stallSellItem->status == StallSellItemNone )
		{
			stallSellItem->status    = StallSellItemStart;	// 노점 아이템 판매시작.
			stallSellItem->inventory = inventory;			// 인벤토리 포인터 참조.
			stallSellItem->price     = price;				// 노점 아이템가격 설정.

			mStallSellItemOffset++;
			return stallSellItem;
		}
	}

	return NULL;
}

// GetStallSellItem Method - 검색.
StallSellItem* cPlayer::GetStallSellItem(TB_INVENTORY* inventory)
{
	StallSellItem* stallSellItem = mStallSellItems;
	for ( int i = 0; i < MAX_STALL_ITEM; i++, stallSellItem++ )
	{
		if ( stallSellItem->inventory == inventory )
			return stallSellItem;
	}
	return NULL;
}

// ReleaseStallSellItem Method - 해제.
bool cPlayer::ReleaseStallSellItem(StallSellItem* stallSellItem)
{
	if ( !(mStallSellItemOffset > 0) )
		return false;

	stallSellItem->status    = StallSellItemNone;	// 노점 아이템 판매중지.
	stallSellItem->inventory = NULL;				// 인벤토리 포인터 초기.
	stallSellItem->price     = 0;					// 노점 아이템 가격초기.

	mStallSellItemOffset--;

	// 노점 아이템 정렬 - 등록 기준으로 내림차순.
	qsort( (void*)mStallSellItems, MAX_STALL_ITEM, sizeof(StallSellItem), StallSellItemCompare );
	return true;
}

// ClearStallSellItem Method.
void cPlayer::ClearStallSellItem( )
{
	StallSellItem* stallSellItem = mStallSellItems;
	for ( int i = 0; i < MAX_STALL_ITEM && mStallSellItemOffset > 0; i++, stallSellItem++ )
	{
		if ( stallSellItem->status != StallSellItemNone )
		{
			stallSellItem->inventory->apply = InventoryApplyNone;

			stallSellItem->status    = StallSellItemNone;
			stallSellItem->inventory = NULL;
			stallSellItem->price     = 0;

			mStallSellItemOffset--;
		}
	}
	mStallSell = false;	// 노점종료.
}

// GetStallSellGuest Method
bool cPlayer::GetStallSellGuest(unsigned long characterIdx)
{
	if ( !(mStallSellGuestOffset < MAX_STALL_GUEST) )
		return false;

	unsigned long* stallSellGuest = mStallSellGuests;
	for ( int i = 0; i < MAX_STALL_GUEST; i++, stallSellGuest++ )
	{
		if ( (*stallSellGuest) == 0 )
		{
			(*stallSellGuest) = characterIdx;
			mStallSellGuestOffset++;
			return true;
		}
	}

	return false;
}

// ReleaseStallSellGuest Method
bool cPlayer::ReleaseStallSellGuest(unsigned long characterIdx)
{
	unsigned long* stallSellGuest = mStallSellGuests;
	for ( int i = 0; i < MAX_STALL_GUEST && mStallSellGuestOffset > 0; i++, stallSellGuest++ )
	{
		if ( (*stallSellGuest) == characterIdx )
		{
			(*stallSellGuest) = 0;
			mStallSellGuestOffset--;
			return true;
		}
	}
	return false;
}

// ReleaseStallSellAllGuests Method - 게스트(GUEST) 닫기.
void cPlayer::ReleaseStallSellAllGuests( )
{
	unsigned long* stallSellGuest = mStallSellGuests;
	for ( int i = 0; i < MAX_STALL_GUEST && mStallSellGuestOffset > 0; i++, stallSellGuest++ )
	{
		if ( (*stallSellGuest) != 0 )
		{
			cPlayer* player = OBJECTMANAGER->GetPlayer( (*stallSellGuest) );

			if ( player != NULL )
			{
				if ( player->GetState( ) == eOBJECT_STATE_STOP && player->GetStateStop( ) == eSTOP_USESTALL )
				{
					player->ChangeState( eOBJECT_STATE_IDLE );
					player->SetStateStop( eSTOP_NONE );

					player->StallSellLeave( ERROR_ITEM_STALL_SELL_LEAVE_CLOSED );
				}
			}

			(*stallSellGuest) = 0;
			mStallSellGuestOffset--;
		}
	}
}

// StallSellRename Method.
void cPlayer::StallSellRename(ULONG_PTR /*socketContext*/, MSG_REQ_ITEM_STALL_SELL_RENAME* msg)
{
	// 이미 오픈중 검사
	if ( mStallSell == false )
		throw ERROR_ITEM_STALL_SELL_RENAME_FAIL;

	// 노점명 변경.
	msg->title[ STALL_TITLE_LEN ] = 0;
	wcscpy( mStallSellTitle, msg->title );

	SendMsgResStallSell( NM_ITEM, NM_ITEM_STALL_SELL_RENAME_RES, ERROR_ITEM_STALL_SELL_RENAME_SUCCESS );

	MSG_SYN_ITEM_STALL_SELL_RENAME sendMsg;
	memset( &sendMsg, 0, sizeof(MSG_SYN_ITEM_STALL_SELL_RENAME) );

	sendMsg.Category     = NM_ITEM;
	sendMsg.Protocol     = NM_ITEM_STALL_SELL_RENAME_SYN;
	sendMsg.characterIdx = mObject.index;
	wcscpy( sendMsg.title, mStallSellTitle );

	NETWORK2->QuickSendExcept( this, (char*)&sendMsg, sizeof(MSG_SYN_ITEM_STALL_SELL_RENAME) );
}

// StallSellAdd Method - 목록에 아이템 등록후 DB 동기화 요청 - 데이터 처리후 메시지 전송.
void cPlayer::StallSellAdd(ULONG_PTR socketContext, MSG_REQ_ITEM_STALL_SELL_ADD* msg)
{
	// 노점 상태검사.
	if ( mStallSell == false )
		throw ERROR_ITEM_STALL_SELL_ADD_FAIL;

	StallSellItem* stallSellItem = NULL;
	TB_INVENTORY*  inventory     = SelectInventory( msg->number );

	if ( IsInventory( inventory ) == false || msg->price > LONG_MAX )
		throw ERROR_ITEM_STALL_SELL_ADD_FAIL;

	// 데이터 처리.
	if ( inventory->apply == InventoryApplyNone )
	{
		stallSellItem = GetStallSellItem( inventory, msg->price );	// 아이템 등록.

		if ( stallSellItem == NULL )
			throw ERROR_ITEM_STALL_SELL_ADD_FAIL;

		inventory->apply = InventoryApplyStallSell;					// 인벤토리 APPLY 상태변경.
	}
	else if ( inventory->apply == InventoryApplyStallSell )
	{
		stallSellItem = GetStallSellItem( inventory );				// 아이템 갱신.

		if ( stallSellItem == NULL )
			throw ERROR_ITEM_STALL_SELL_ADD_FAIL;

		if ( stallSellItem->status != StallSellItemPause )
			throw ERROR_ITEM_STALL_SELL_ADD_FAIL;

		stallSellItem->status = StallSellItemStart;
		stallSellItem->price  = msg->price;
	}
	else
		throw ERROR_ITEM_STALL_SELL_ADD_FAIL;

	// 메시지 전송.
	SendMsgResStallSell( NM_ITEM, NM_ITEM_STALL_SELL_ADD_RES, ERROR_ITEM_STALL_SELL_ADD_SUCCESS );
	SendMsgSynStallSell( NM_ITEM, NM_ITEM_STALL_SELL_ADD_SYN, stallSellItem->inventory, stallSellItem->price );

	// Database 등록 - 비동기 처리 (SQL 관련한 오류는 무시함).
	HANDLE             handle          = NULL;
	STALL_SELL_INSERT* stallSellInsert = (STALL_SELL_INSERT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_STALL_SELL_INSERT );

	stallSellInsert->channelNum      = (long)NETWORK2->GetChannelNum( );
	stallSellInsert->characterIdx    = mObject.index;
	stallSellInsert->inventoryIdx    = inventory->idx;
	stallSellInsert->itemDefineIndex = inventory->itemDefineIndex;
	stallSellInsert->enhanced        = inventory->enhanced;
	stallSellInsert->price           = msg->price;

	NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(STALL_SELL_INSERT) );
}

// StallSellDel Method - 목록중 아이템 삭제후 DB동기화 요청 - 메시지 전송후 데이터 처리.
void cPlayer::StallSellDel(ULONG_PTR socketContext, MSG_REQ_ITEM_STALL_SELL_DEL* msg)
{
	// 노점 상태검사.
	if ( mStallSell == false )
		throw ERROR_ITEM_STALL_SELL_DEL_FAIL;

	TB_INVENTORY* inventory = SelectInventory( msg->number );

	if ( IsInventory( inventory ) == false || inventory->apply != InventoryApplyStallSell )
		throw ERROR_ITEM_STALL_SELL_DEL_FAIL;

	// 노점 아이템 검색.
	StallSellItem* stallSellItem = GetStallSellItem( inventory );

	if ( stallSellItem == NULL )
		throw ERROR_ITEM_STALL_SELL_DEL_FAIL;

	if ( stallSellItem->status == StallSellItemNone )
		throw ERROR_ITEM_STALL_SELL_DEL_FAIL;

	// 메시지 전송.
	SendMsgResStallSell( NM_ITEM, NM_ITEM_STALL_SELL_DEL_RES, ERROR_ITEM_STALL_SELL_DEL_SUCCESS );
	SendMsgSynStallSell( NM_ITEM, NM_ITEM_STALL_SELL_DEL_SYN, stallSellItem->inventory, stallSellItem->price );

	// 데이터 처리.
	inventory->apply = InventoryApplyNone;			// 인벤토리 APPLY 상태변경.

	// 노점 아이템 삭제
	ReleaseStallSellItem( stallSellItem );

	// Database 삭제 - 비동기 처리 (SQL 관련한 오류는 무시함).
	HANDLE             handle          = NULL;
	STALL_SELL_REMOVE* stallSellRemove = (STALL_SELL_REMOVE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_STALL_SELL_REMOVE );

	stallSellRemove->inventoryIdx = inventory->idx;

	NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(STALL_SELL_INSERT) );
}

// StallSellMod Method - 상태만 변경.
void cPlayer::StallSellMod(ULONG_PTR /*socketContext*/, MSG_REQ_ITEM_STALL_SELL_MOD* msg)
{
	// 노점 상태검사.
	if ( mStallSell == false )
		throw ERROR_ITEM_STALL_SELL_MOD_FAIL;

	TB_INVENTORY* inventory = SelectInventory( msg->number );

	if ( IsInventory( inventory ) == false || inventory->apply != InventoryApplyStallSell )
		throw ERROR_ITEM_STALL_SELL_MOD_FAIL;

	if ( !(mStallSellItemOffset > 0) )
		throw ERROR_ITEM_STALL_SELL_MOD_FAIL;

	// 노점 아이템 검색.
	StallSellItem* stallSellItem = GetStallSellItem( inventory );

	if ( stallSellItem == NULL )
		throw ERROR_ITEM_STALL_SELL_MOD_FAIL;

	if ( stallSellItem->status != StallSellItemStart )
		throw ERROR_ITEM_STALL_SELL_MOD_FAIL;

	stallSellItem->status = StallSellItemPause;	// 노점 아이템 판매 일시중지.

	SendMsgResStallSell( NM_ITEM, NM_ITEM_STALL_SELL_MOD_RES, ERROR_ITEM_STALL_SELL_MOD_SUCCESS );
	SendMsgSynStallSell( NM_ITEM, NM_ITEM_STALL_SELL_MOD_SYN, stallSellItem->inventory, stallSellItem->price );
}

// StallSellJoin Method.
void cPlayer::StallSellJoin(ULONG_PTR /*socketContext*/, MSG_REQ_ITEM_STALL_SELL_JOIN* msg)
{
	if ( IsChangeState( eOBJECT_STATE_STOP ) == false )
		throw ERROR_ITEM_STALL_SELL_JOIN_FAIL;			// 캐릭터 상태 오류 - 변경불가.

	if ( GetStateStop( ) != eSTOP_NONE )
		throw ERROR_ITEM_STALL_SELL_JOIN_FAIL;			// 캐릭터 상태 오류.

	cPlayer* player = GRIDMANAGER->GetPlayer( msg->mTarget.index );

	if ( player == NULL )
		throw ERROR_ITEM_STALL_SELL_JOIN_FAIL;

	player->StallSellJoin( this );

	mStallSellOwner = player->GetObject( );

	ChangeState( eOBJECT_STATE_STOP );
	SetStateStop( eSTOP_USESTALL );
}
void cPlayer::StallSellJoin(cPlayer* player)
{
	// 노점 상태검사.
	if ( mStallSell == false )
		throw ERROR_ITEM_STALL_SELL_JOIN_FAIL;

	if ( GetStallSellGuest( player->GetObjectID( ) ) == false )
		throw ERROR_ITEM_STALL_SELL_JOIN_OVER;

	// 노점 아이템목록 전송.
	HANDLE handle = NULL;
	MSG_RES_ITEM_STALL_SELL_JOIN* sendMsg = (MSG_RES_ITEM_STALL_SELL_JOIN*)NETWORK2->GetMsgRoot( &handle, player->GetConnectionIdx( ), NM_ITEM, NM_ITEM_STALL_SELL_JOIN_RES );
	if ( sendMsg != NULL )
	{
		StallSellItem*  stallSellItem = mStallSellItems;
		sStallSellData* stallSellData = sendMsg->stallSellData;

		sendMsg->ErrorCode = ERROR_ITEM_STALL_SELL_JOIN_SUCCESS;	// 성공.
		wcscpy( sendMsg->title, mStallSellTitle );					// 타이틀 복사.

		for ( unsigned long i = 0; i < mStallSellItemOffset; i++, stallSellData++, stallSellItem++ )
		{
			stallSellData->status = stallSellItem->status;
			Inventory2sInventory( &stallSellData->inventory, stallSellItem->inventory );
			stallSellData->price  = stallSellItem->price;
			sendMsg->rowCount++;
		}

		NETWORK2->SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
	}
}

// StallSellLeave Method.
void cPlayer::StallSellLeave(ULONG_PTR /*socketContext*/, MSG_REQ_ITEM_STALL_SELL_LEAVE* /*msg*/)
{
	if ( GetState( ) != eOBJECT_STATE_STOP || GetStateStop( ) != eSTOP_USESTALL )
		throw ERROR_ITEM_STALL_SELL_LEAVE_FAIL;			// 캐릭터 상태 오류.

	if ( IsChangeState( eOBJECT_STATE_IDLE ) == false )
		throw ERROR_ITEM_STALL_SELL_LEAVE_FAIL;			// 캐릭터 상태 오류 - 변경불가.

	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );

	cPlayer* player = GRIDMANAGER->GetPlayer( mStallSellOwner.index );

	if ( player == NULL )
		throw ERROR_ITEM_STALL_SELL_LEAVE_FAIL;

	if ( player->StallSellLeave( this ) == false )
		throw ERROR_ITEM_STALL_SELL_LEAVE_FAIL;

	StallSellLeave( ERROR_ITEM_STALL_SELL_LEAVE_SUCCESS );
}
bool cPlayer::StallSellLeave(cPlayer* player)
{
	return ( mStallSell == true ) ? ReleaseStallSellGuest( player->GetObjectID( ) ) : false;
}
void cPlayer::StallSellLeave(int error)
{
	SendMsgResStallSell( NM_ITEM, NM_ITEM_STALL_SELL_LEAVE_RES, error );
}

// StallSellGet Method.
void cPlayer::StallSellGet(ULONG_PTR socketContext, MSG_REQ_ITEM_STALL_SELL_GET* msg)
{
	if ( GetState( ) != eOBJECT_STATE_STOP || GetStateStop( ) != eSTOP_USESTALL )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;			// 캐릭터 상태 오류.

	cPlayer* player = OBJECTMANAGER->GetPlayer( mStallSellOwner.index );

	if ( player == NULL )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	if ( player->IsStallSellOpen( ) == false )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	TB_INVENTORY* inventory = player->SelectInventory( msg->number );

	if ( IsInventory( inventory ) == false )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	if ( inventory->apply != InventoryApplyStallSell )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	if ( msg->count > inventory->count )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	StallSellItem* stallSellItem = player->GetStallSellItem( inventory );

	if ( stallSellItem == NULL )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	if ( stallSellItem->status != StallSellItemStart )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	if ( mMoney < (stallSellItem->price * msg->count) )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	short inventoryNumber = GetEmptyBagNumber( );

	if ( inventoryNumber == INVENTORY_BAG_NONE )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );

	if ( itemDefine == NULL )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	long totalPrice = (long)(stallSellItem->price * msg->count);

	// 구매시작 - 구매자 후 응답.
	HANDLE          handle       = NULL;
	STALL_SELL_GET* stallSellGet = (STALL_SELL_GET*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_STALL_SELL_GET );

	stallSellGet->fCharacterIdx    = player->GetObjectID( );
	stallSellGet->fObject          = player->GetObject( );
	stallSellGet->fCharacterMoney  = (+totalPrice);
	stallSellGet->fInventoryIdx    = inventory->idx;

	stallSellGet->tCharacterIdx    = GetObjectID( );
	stallSellGet->tCharacterMoney  = (-totalPrice);
	stallSellGet->tInventoryNumber = inventoryNumber;
	stallSellGet->tInventoryCount  = msg->count;

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(STALL_SELL_GET) ) == false )
		throw ERROR_ITEM_STALL_SELL_GET_FAIL;

	// 구매시작 - 판매자 선 응답.
	{
		// 판매자 선 응답 - 판매금액.
		unsigned long befor  = player->GetMoney( );
		long          result = player->AddMoney( GetObject( ), totalPrice, false );
		unsigned long after  = player->GetMoney( );

		NETWORK2->PostMoneyEvent( player->GetObjectID( ), befor, after, "STALL_SELL_GET(SELL)::Character(=%d):Money(=%d)", GetObjectID( ), totalPrice );

		if ( result != totalPrice )
			NETWORK2->PostServerEvent( "Error - SQL_GAME_PROCESS_STALL_SELL_GET CharacterIdx(=%d), AddMoney(=%d) != ResultMoney(=%d).", player->GetObjectID( ), totalPrice, result );

		// 판매자 선 응답 - 아이템.
		player->UpdateInventory( inventory, (-msg->count) ); // inventory->count = inventory->count - msg->count;

		HANDLE                       handle  = NULL;
		MSG_RES_ITEM_STALL_SELL_COM* sendMsg = (MSG_RES_ITEM_STALL_SELL_COM*)NETWORK2->GetMsgRoot( &handle, player->GetConnectionIdx( ), NM_ITEM, NM_ITEM_STALL_SELL_COM_RES );
		if ( sendMsg != NULL )
		{
			wcscpy( sendMsg->characterName, GetName() );
			Inventory2sInventory( &sendMsg->inventory, inventory );
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_ITEM_STALL_SELL_COM) );
		}

		if ( stallSellItem->inventory->count > 0 )
		{
			NETWORK2->PostInventoryEvent( EVENT_INVENTORY_UPDATE, player->GetObjectID( ), stallSellItem->inventory, "STALL SELL - SELL" );

			// 판매후 갱신.
			stallSellItem->status = StallSellItemStart;
			player->SendMsgSynStallSell( NM_ITEM, NM_ITEM_STALL_SELL_ADD_SYN, stallSellItem->inventory, stallSellItem->price );
		}
		else
		{
			NETWORK2->PostInventoryEvent( EVENT_INVENTORY_DELETE, player->GetObjectID( ), stallSellItem->inventory, "STALL SELL - SELL" );

			// 판매후 삭제.
			player->SendMsgSynStallSell( NM_ITEM, NM_ITEM_STALL_SELL_DEL_SYN, stallSellItem->inventory, stallSellItem->price );

			player->RemoveInventory( stallSellItem->inventory->number );
			player->ReleaseStallSellItem( stallSellItem );
		}
		player->CalcItemWeight( );
	}
}

// SendMsgResStallSell Method.
bool cPlayer::SendMsgResStallSell(char category, char protocol, int error)
{
	HANDLE     handle  = NULL;
	MSG_ERROR* sendMsg = (MSG_ERROR*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, category, protocol );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = error;
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_ERROR) );
	}
	return false;
}

// SendMsgSynStallSell Method.
void cPlayer::SendMsgSynStallSell(char category, char protocol, TB_INVENTORY* inventory, long price)
{
	unsigned long* stallSellGuest = mStallSellGuests;

	for ( int i = 0; i < MAX_STALL_GUEST; i++, stallSellGuest++ )
	{
		if ( (*stallSellGuest) != 0 )
		{
			cPlayer* player = OBJECTMANAGER->GetPlayer( (*stallSellGuest) );
			if ( player != NULL )
			{
				HANDLE                       handle = NULL;
				MSG_SYN_ITEM_STALL_SELL_DAT* synMsg = (MSG_SYN_ITEM_STALL_SELL_DAT*)NETWORK2->GetMsgRoot( &handle, player->GetConnectionIdx( ), category, protocol );
				if ( synMsg != NULL )
				{
					Inventory2sInventory( &synMsg->inventory, inventory );
					synMsg->price = price;

					NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_ITEM_STALL_SELL_DAT) );
				}
			}
			else
			{
				ReleaseStallSellGuest( (*stallSellGuest) );
			}
		}
	}
}

// IsTarotReaderClose Method.
bool cPlayer::IsTarotReaderClose( )
{
	unsigned long* tarotGuest       = mTarotGuests;
	int            tarotGuestOffset = mTarotGuestOffset;

	for ( int i = 0; i < MAX_TAROT_GUEST && tarotGuestOffset > 0; i++, tarotGuest++ )
	{
		if ( (*tarotGuest) > 0 )
		{
			cPlayer* player = OBJECTMANAGER->GetPlayer( (*tarotGuest) );

			if ( player != NULL )
			{
				if ( player->GetStateStop( ) == eSTOP_USETAROT )
					return false;
			}

			tarotGuestOffset--;
		}
	}
	return true;
}

// TarotReaderReady Method.
void cPlayer::TarotReaderReady(ULONG_PTR /*socketContext*/, MSG_REQ_TAROT_READER_READY* /*msg*/)
{
	// 2차상태 체크
	if ( IsRequestRejection() == true )
		throw ERROR_TAROT_READER_READY_FAIL;			// 캐릭터 상태2오류.

	if ( IsChangeState( eOBJECT_STATE_STOP ) == false )
		throw ERROR_TAROT_READER_READY_FAIL;			// 캐릭터 상태 오류 - 상태변경 불가.

	if ( GetStateStop( ) != eSTOP_NONE )
		throw ERROR_TAROT_READER_READY_FAIL;			// 캐릭터 상태 오류.

	/*-- 캐릭터 상태 변경.
	*/
	if ( ChangeState( eOBJECT_STATE_STOP ) == false )
		throw ERROR_TAROT_READER_READY_FAIL;			// 캐릭터 상태 오류 - 변경불가.

	SetStateStop( eSTOP_READYTAROT );					// 캐릭터 정지상태 중 타로준비.

	/*-- 타로 개설정보 보내기 [RES/SYN] 메시지 발송.
	*/
	SendMsgResTarot( NM_TAROT, NM_TAROT_READER_READY_RES, ERROR_TAROT_READER_READY_SUCCESS );

	MSG_SYN_TAROT_READER_READY msgSyn;
	memset( &msgSyn, 0, sizeof(MSG_SYN_TAROT_READER_READY) );

	msgSyn.Category      = NM_TAROT;
	msgSyn.Protocol      = NM_TAROT_READER_READY_SYN;
	msgSyn.mCharacterIdx = mObject.index;
	NETWORK2->QuickSendExcept( this, (char*)&msgSyn, sizeof(msgSyn) );
}

// TarotReaderOpen Method.
void cPlayer::TarotReaderOpen(ULONG_PTR /*socketContext*/, MSG_REQ_TAROT_READER_OPEN* msg)
{
	if ( GetState( ) != eOBJECT_STATE_STOP )
		throw ERROR_TAROT_READER_OPEN_FAIL;				// 캐릭터 상태 오류.

	if ( GetStateStop( ) != eSTOP_READYTAROT )
		throw ERROR_TAROT_READER_OPEN_FAIL;				// 캐릭터 상태 오류.

	/*-- 타로카드 검사 - 22장(1벌)을 모두 가지고 있는지 확인과 고유번호 검사.
	*/
	TB_INVENTORY*  tarotCard   = SelectInventory( INVENTORY_TAROT_BEGIN );
	TB_ITEM_TAROT* itemTarot   = NULL;
	long           tarotNumber = 0;

	for ( int i = INVENTORY_TAROT_BEGIN; i <= INVENTORY_TAROT_END; i++, tarotCard++ )
	{
		itemTarot = ITEMMANAGER->GetItemTarot( tarotCard->itemDefineIdx );
		if ( itemTarot != NULL )
		{
			if ( itemTarot->number != tarotNumber )		// 타로카드 고유번호 검사.
				throw ERROR_TAROT_READER_OPEN_CARD;		// 타로카드 고유번호 오류.
		}
		else
			throw ERROR_TAROT_READER_OPEN_CARD;			// 타로카드 오류.

		tarotNumber++;
	}

	/*-- 스프레드 검사.
	*/
	TB_INVENTORY* spread      = SelectInventory( INVENTORY_SPREAD_BEGIN );
	long          spreadCount = 0;

	for ( long i = INVENTORY_SPREAD_BEGIN; i <= INVENTORY_SPREAD_END; i++, spread++ )
	{
		if ( ITEMMANAGER->IsItemDefineType( spread->itemDefineIdx, ITEM_SPREAD ) )
			spreadCount++;
	}

	if ( !(spreadCount > 0) )
		throw ERROR_TAROT_READER_OPEN_SPREAD;			// 스프레드 오류.

	// 재료 아이템 수량 확인.
	mTarotItemCount = GetItemCount( TAROT_MATERIAL_ITEM );
	if ( !(mTarotItemCount > 0) )
		throw ERROR_TAROT_READER_OPEN_ITEM;				// 재료아이템 수량 오류.

	// 타로상점 개설.
	SetStateStop( eSTOP_OPENTAROT );					// 캐릭터 정지상태 중 타로개설.

	// 타로상점 개설 정보 백업.
	wcscpy( mTarotTitle, msg->strTitle );				// 개설명
	mTarotPrice         = msg->Price;					// 가격
	mTarotUserCount     = 0;							// 사용 캐릭터 수 - 초기화.
	mTarotCloseReserved = false;						// 닫기예약 - 초기화.

	/*-- 타로 개설정보 보내기 [RES/SYN] 메시지 발송.
	*/
	HANDLE                     handle = NULL;
	MSG_RES_TAROT_READER_OPEN* msgRes = (MSG_RES_TAROT_READER_OPEN*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_TAROT, NM_TAROT_READER_OPEN_RES );
	if ( msgRes != NULL )
	{
		msgRes->ErrorCode = ERROR_TAROT_READER_OPEN_SUCCESS;
		wcscpy( msgRes->strTitle, mTarotTitle );
		msgRes->Price       = mTarotPrice;
		msgRes->UserCount   = mTarotUserCount;
		msgRes->Point       = (unsigned short)mHeroInfo.TarotPoint;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_TAROT_READER_OPEN) );
	}

	MSG_SYN_TAROT_READER_OPEN msgSyn;
	memset( &msgSyn, 0, sizeof(MSG_SYN_TAROT_READER_OPEN) );

	msgSyn.Category      = NM_TAROT;
	msgSyn.Protocol      = NM_TAROT_READER_OPEN_SYN;
	msgSyn.mCharacterIdx = mObject.index;
	wcscpy( msgSyn.mTitle, mTarotTitle );
	NETWORK2->QuickSendExcept( this, (char*)&msgSyn, sizeof(msgSyn) );
}

// TarotReaderClose Method
void cPlayer::TarotReaderClose(ULONG_PTR /*socketContext*/, MSG_REQ_TAROT_READER_CLOSE* /*msg*/)
{
	eSTOPFLAG stopFlag = (eSTOPFLAG)GetStateStop( );
	if ( !(stopFlag == eSTOP_READYTAROT || stopFlag == eSTOP_OPENTAROT) )
		throw ERROR_TAROT_READER_CLOSE_FAIL;			// 캐릭터 상태 오류.

	if ( IsChangeState( eOBJECT_STATE_IDLE ) == false )
		throw ERROR_TAROT_READER_CLOSE_FAIL;			// 캐릭터 상태 오류 - 상태변경 불가.

	if ( IsTarotReaderClose( ) == false )
		throw ERROR_TAROT_READER_CLOSE_FAIL;			// 타로상점 닫기 불가.

	TarotReaderClose( ERROR_TAROT_READER_CLOSE_SUCCESS );
}
void cPlayer::TarotReaderClose(int error)
{
	// 상태 변경
	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );							// 캐릭터 정지 상태 - 세부설정 없음.

	// 모든 게스트 종료.
	ReleaseTarotAllGuests( );

	/*-- 타로종료 정보 보내기 [RES/SYN] 메시지 발송.
	*/
	SendMsgResTarot( NM_TAROT, NM_TAROT_READER_CLOSE_RES, error );

	MSG_SYN_TAROT_READER_CLOSE msgSyn;
	memset( &msgSyn, 0, sizeof(MSG_SYN_TAROT_READER_CLOSE) );

	msgSyn.Category = NM_TAROT;
	msgSyn.Protocol = NM_TAROT_READER_CLOSE_SYN;
	msgSyn.mCharacterIdx = mObject.index;
	NETWORK2->QuickSendExcept( this, (char*)&msgSyn, sizeof(msgSyn) );
}

// GetTarotGuest Method.
bool cPlayer::GetTarotGuest(unsigned long characterIdx)
{
	if ( mTarotGuestOffset < MAX_TAROT_GUEST )
	{
		unsigned long* tarotGuest = mTarotGuests;
		for ( int i = 0; i < MAX_STALL_GUEST; i++, tarotGuest++ )
		{
			if ( (*tarotGuest) == 0 )
			{
				(*tarotGuest) = characterIdx;
				mTarotGuestOffset++;
				return true;
			}
		}
	}
	return false;
}

// ReleaseTarotGuest Method.
bool cPlayer::ReleaseTarotGuest(unsigned long characterIdx)
{
	if ( mTarotGuestOffset > 0 )
	{
		unsigned long* tarotGuest = mTarotGuests;
		for ( int i = 0; i < MAX_TAROT_GUEST; i++, tarotGuest++ )
		{
			if ( (*tarotGuest) == characterIdx )
			{
				(*tarotGuest) = 0;
				mTarotGuestOffset--;
				return true;
			}
		}
	}
	return false;
}

// ReleaseTarotAllGuests Method.
void cPlayer::ReleaseTarotAllGuests( )
{
	unsigned long* tarotGuest = mTarotGuests;

	for ( int i = 0; i < MAX_TAROT_GUEST && mTarotGuestOffset > 0; i++, tarotGuest++ )
	{
		if ( (*tarotGuest) > 0 )
		{
			cPlayer* player = OBJECTMANAGER->GetPlayer( (*tarotGuest) );

			if ( player != NULL )
			{
				player->ChangeState( eOBJECT_STATE_IDLE );
				player->SetStateStop( eSTOP_NONE );
				player->SendMsgResTarot( NM_TAROT, NM_TAROT_SEEKER_CLOSE_RES, ERROR_TAROT_SEEKER_CLOSE_READER );
			}

			(*tarotGuest) = 0;
			--mTarotGuestOffset;
		}
	}
}

// GetTarotCards Method - 타로카드 인벤토리 복사.
bool cPlayer::GetTarotCards(TB_INVENTORY* inventory[])
{
	TB_INVENTORY*  tarotCard   = SelectInventory( INVENTORY_TAROT_BEGIN );
	TB_ITEM_TAROT* itemTarot   = NULL;

	for ( int i = 0; i < MAX_TAROT_CARDS; i++, tarotCard++ )
	{
		itemTarot = ITEMMANAGER->GetItemTarot( tarotCard->itemDefineIdx );
		if ( itemTarot != NULL )
		{
			if ( itemTarot->number == i )	// 타로카드 고유번호 검사.
			{
				inventory[ i ] = tarotCard;
			}
			else
				return false;
		}
		else
			return false;
	}

	return true;
}

// GetSpreads Method - 스프레드 인벤토리 복사.
void cPlayer::GetSpreads(TB_INVENTORY* inventory[], int& offset)
{
	TB_INVENTORY* spread = SelectInventory( INVENTORY_SPREAD_BEGIN );

	for ( long i = 0; i <= MAX_SPREAD; i++, spread++ )
	{
		if ( ITEMMANAGER->IsItemDefineType( spread->itemDefineIdx, ITEM_SPREAD ) )
		{
			inventory[ offset ] = spread;
			offset++;
		}
	}
}

// UseTarotCard Method.
void cPlayer::UseTarotCard(sObject object, long price, long itemDefineIndex)
{
	AddMoney( object, price );
	ItemUse( itemDefineIndex );

	mTarotItemCount = GetItemCount( TAROT_MATERIAL_ITEM );
}

// TarotSeekerOpen Method - 순서에 주의!.
void cPlayer::TarotSeekerOpen(ULONG_PTR /*socketContext*/, MSG_REQ_TAROT_SEEKER_OPEN* msg)
{
	// 2차상태 체크
	if ( IsRequestRejection() == true )
		throw ERROR_TAROT_SEEKER_OPEN_FAIL;			// 캐릭터 상태2오류.

	// 상태검사.
	if ( IsChangeState( eOBJECT_STATE_STOP ) == false || GetStateStop( ) != eSTOP_NONE )
		throw ERROR_TAROT_SEEKER_OPEN_FAIL;			// 캐릭터 상태오류.

	// 타로상점 - 리더검색.
	cPlayer* reader = OBJECTMANAGER->GetPlayer( msg->CharacterIdx );

	if ( reader == NULL )
		throw ERROR_TAROT_SEEKER_OPEN_READER;		// 타로상점 오류 - 캐릭터 검색 오류.

	if ( reader->GetStateStop( ) != eSTOP_OPENTAROT && reader->GetTarotCloseReserved( ) == false )
		throw ERROR_TAROT_SEEKER_OPEN_READER;		// 타로상점 오류 - 타로상점 개설 오류.

	// 타로상점 - 게스트 등록.
	if ( reader->GetTarotGuest( mObject.index ) == false )
		throw ERROR_TAROT_SEEKER_OPEN_OVER;			// 타로상점 오류 - 최대 게스트 오버.

	if ( reader->GetTarotCards( mTarotCards ) == false )
		throw ERROR_TAROT_SEEKER_OPEN_FAIL;			// 타로상점 오류 - 타로카드 복사 실패.

	reader->GetSpreads( mSpread, (mSpreadOffset=0) );
	if ( !(mSpreadOffset > 0) )
		throw ERROR_TAROT_SEEKER_OPEN_FAIL;			// 타로상점 오류 - 스프레드 복사 실패.

	// 타로상점, 리더의 캐릭터 고유번호 복사.
	mTarotReaderIdx = reader->GetObjectID( );		// 타로상점 백업 - 리더의 캐릭터 고유번호.

	// 상태변경.
	ChangeState( eOBJECT_STATE_STOP );				// 캐릭터 상태변경.
	SetStateStop( eSTOP_ENTERTAROT );				// 캐릭터 상태변경 - 세부항복.

	// 타로 개설정보 발송.
	SendTarotStoreOpen( reader->GetName( ), reader->GetTarotTitle( ), reader->GetTarotPrice( ) );
}

// TarotSeekerClose Method - 순서에 주의!.
void cPlayer::TarotSeekerClose(ULONG_PTR /*socketContext*/, MSG_REQ_TAROT_SEEKER_CLOSE* /*msg*/)
{
	// 상태검사.
	eSTOPFLAG stopFlag = (eSTOPFLAG)GetStateStop( );

	if ( !(stopFlag == eSTOP_ENTERTAROT || stopFlag == eSTOP_USETAROT) )
		throw ERROR_TAROT_SEEKER_CLOSE_FAIL;		// 캐릭터 상태오류.

	if ( IsChangeState( eOBJECT_STATE_IDLE ) == false )
		throw ERROR_TAROT_SEEKER_CLOSE_FAIL;		// 캐릭터 상태오류 - 상태변경 불가.

	// 타로상점 - 리더처리.
	cPlayer* reader = OBJECTMANAGER->GetPlayer( mTarotReaderIdx );

	if ( reader != NULL )
	{
		if ( reader->GetStateStop( ) == eSTOP_OPENTAROT )
		{
			reader->ReleaseTarotGuest( mObject.index );	// 타로 게스트 종료.

			if ( stopFlag == eSTOP_USETAROT )
				reader->SendTarotReaderLeave( mPlayerInfo.strName );
		}
	}

	// 타로상점 백업 - 클리어.
	mTarotReaderIdx = 0;

	// 상태변경.
	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );

	// 타로상점 참여종료.
	SendMsgResTarot( NM_TAROT, NM_TAROT_SEEKER_CLOSE_RES, ERROR_TAROT_SEEKER_CLOSE_SUCCESS );
}

// SendTarotStoreJoin Method.
void cPlayer::TarotSeekerJoin(ULONG_PTR /*socketContext*/, MSG_REQ_TAROT_SEEKER_JOIN* /*msg*/)
{
	// 상태검사.
	if ( GetState( ) != eOBJECT_STATE_STOP || GetStateStop( ) != eSTOP_ENTERTAROT )
		throw ERROR_TAROT_SEEKER_JOIN_FAIL;		// 캐릭터 상태오류.

	// 리더검색
	cPlayer* reader = OBJECTMANAGER->GetPlayer( mTarotReaderIdx );

	if ( reader == NULL )
		throw ERROR_TAROT_SEEKER_JOIN_READER;		// 리더검색 오류 - 자동으로 타로상점 닫기 추가 해야됨.

	if ( reader->GetStateStop( ) != eSTOP_OPENTAROT )
		throw ERROR_TAROT_SEEKER_JOIN_READER;		// 리더검색 오류 - 리더아님.

	if ( !(reader->GetItemCount( TAROT_MATERIAL_ITEM ) > 0) )
		throw ERROR_TAROT_SEEKER_JOIN_ITEM;			// 리더검색 오류 - 재료부족.

	// 타로상점 가격 얻어오기.
	long tarotPrice = (long)reader->GetTarotPrice( );

	if ( mMoney < (unsigned long)tarotPrice )
		throw ERROR_TAROT_SEEKER_JOIN_FAIL;		// 소지금액 부족.

	// 타로상점 이용금액 -.
	AddMoney( reader->GetObject( ), -tarotPrice );

	// 타로상점 이용금액 + & 아이템 사용.
	reader->UseTarotCard( mObject, +tarotPrice, TAROT_MATERIAL_ITEM );

	// 상태변경.
	SetStateStop( eSTOP_USETAROT );

	// 참여 씨커네임 전송.
	reader->SendTarotReaderJoin( mPlayerInfo.strName );

	// 참여 정보 전송.
	SendTarotStoreJoin( reader->GetTarotTitle( ), reader->GetTarotPrice( ) );
}

// TarotSeekerResult Method
void cPlayer::TarotSeekerResult(ULONG_PTR socketContext, MSG_REQ_TAROT_SEEKER_RESULT* msg)
{
	// 상태검사.
	if ( GetState( ) != eOBJECT_STATE_STOP || GetStateStop( ) != eSTOP_USETAROT )
		throw ERROR_TAROT_SEEKER_RESULT_FAIL;		// 캐릭터 상태오류.

	// 리더검색
	cPlayer* reader = OBJECTMANAGER->GetPlayer( mTarotReaderIdx );

	if ( reader == NULL )
		throw ERROR_TAROT_SEEKER_RESULT_READER;		// 리더검색 오류.

	if ( reader->GetStateStop( ) != eSTOP_OPENTAROT )
		throw ERROR_TAROT_SEEKER_RESULT_READER;		// 리더검색 오류 - 리더아님.

	// 스프레드 인덱스 검색.
	long spreadIndex = 0;
	bool exists      = false;

	// Spread 인덱스 확인.
	for ( long i = 0; i < mSpreadOffset; i++ )
	{
		if ( mSpread[ i ]->itemDefineIndex == (long)msg->SpreadIndex )
		{
			TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( mSpread[ i ]->itemDefineIdx );
			if ( itemDefine != NULL )
			{
				spreadIndex = itemDefine->spreadIndex;
				exists      = true;
			}
		}
	}

	if ( exists == false )
		throw ERROR_TAROT_SEEKER_RESULT_FAIL;		// 스프레드 인덱스 오류.

	// Database 처리 요청.
	HANDLE             handle          = NULL;
	ITEM_SPREAD_VALUE* itemSpreadValue = (ITEM_SPREAD_VALUE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_SPREAD_VALUE );

	itemSpreadValue->itemDefineIndex = msg->SpreadIndex;
	itemSpreadValue->spreadIndex     = spreadIndex;

	for ( long i = 0; i < MIN_TAROT_CARDS; i++ )
	{
		TB_ITEM_TAROT* tbItemTarot   = ITEMMANAGER->GetItemTarot( mTarotCards[ i ]->itemDefineIdx );
		int            dir           = (rand( ) % 2);

		itemSpreadValue->values[ i ].itemDefineIndex = mTarotCards[ i ]->itemDefineIndex;
		itemSpreadValue->values[ i ].dir             = (BYTE)(dir + 1);
		itemSpreadValue->values[ i ].tarot           = (dir == 0) ? tbItemTarot->strValue : tbItemTarot->invValue;
	}

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(ITEM_SPREAD_VALUE) ) == false )
		throw ERROR_TAROT_SEEKER_RESULT_FAIL;		// SQL 오류.

	mTarotResultIndex = msg->SpreadIndex;
}

// TarotSeekerAccept Method
void cPlayer::TarotSeekerAccept(ULONG_PTR socketContext, MSG_REQ_TAROT_SEEKER_ACCEPT* /*msg*/)
{
	// 상태검사.
	if ( GetState( ) != eOBJECT_STATE_STOP || GetStateStop( ) != eSTOP_USETAROT )
		throw ERROR_TAROT_SEEKER_ACCEPT_FAIL;		// 캐릭터 상태오류.

	else if ( IsChangeState( eOBJECT_STATE_IDLE ) == false )
		throw ERROR_TAROT_SEEKER_ACCEPT_FAIL;		// 캐릭터 상태오류 - 상태변경 불가.

	// 상태변경.
	ChangeState( eOBJECT_STATE_IDLE );
	SetStateStop( eSTOP_NONE );

	// 리더검색.
	cPlayer* reader = OBJECTMANAGER->GetPlayer( mTarotReaderIdx );

	if ( reader == NULL )
		throw ERROR_TAROT_SEEKER_ACCEPT_READER;		// 리더검색 오류.

	if ( reader->GetStateStop( ) != eSTOP_OPENTAROT )
		throw ERROR_TAROT_SEEKER_ACCEPT_READER;		// 리더검색 오류 - 리더아님.

	// 타로상점 - 게스트 종료.
	reader->ReleaseTarotGuest( mObject.index );
	reader->SendTarotReaderLeave( mPlayerInfo.strName );

	SendMsgResTarot( NM_TAROT, NM_TAROT_SEEKER_CLOSE_RES, ERROR_TAROT_SEEKER_CLOSE_SUCCESS );

	// Database 처리요청
	HANDLE             handle          = NULL;
	ITEM_TAROT_RESULT* itemTarotResult = (ITEM_TAROT_RESULT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_ITEM_TAROT_RESULT );

	itemTarotResult->itemDefineIndex = mTarotResultIndex;
	itemTarotResult->value           = mTarotResultValue;

	if ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(ITEM_TAROT_RESULT) ) == false )
		throw ERROR_TAROT_SEEKER_ACCEPT_FAIL;		// SQL 오류.
}

// TarotReaderDie Method
void cPlayer::TarotReaderDie( )
{
	ReleaseTarotAllGuests( );
}

// TarotSeekerDie Method
void cPlayer::TarotSeekerDie( )
{
	cPlayer* player = OBJECTMANAGER->GetPlayer( mTarotReaderIdx );
	if ( player != NULL )
		player->ReleaseTarotGuest( mObject.index );
}

// TarotReaderShutdown Method
void cPlayer::TarotReaderShutdown(bool shutdown)
{
	if ( mTarotCloseReserved == false )
		mTarotCloseReserved = true;

	if ( IsTarotReaderClose( ) == true || shutdown == true )
	{
		ReleaseTarotAllGuests( );

		ChangeState( eOBJECT_STATE_STOP );
		SetStateStop( eSTOP_GAMEFINISH );
	}
}

// TarotSeekerShutdown Method
void cPlayer::TarotSeekerShutdown(bool /*shutdown*/)
{
	cPlayer* player = OBJECTMANAGER->GetPlayer( mTarotReaderIdx );

	if ( player != NULL )
		player->ReleaseTarotGuest( mObject.index );

	ChangeState( eOBJECT_STATE_STOP );
	SetStateStop( eSTOP_GAMEFINISH );
}

// SendMsgResTarot Method
bool cPlayer::SendMsgResTarot(char category, char protocol, int error)
{
	HANDLE     handle  = NULL;
	MSG_ERROR* sendMsg = (MSG_ERROR*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, category, protocol );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = error;
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_ERROR) );
	}
	return false;
}

// SendTarotStoreOpen Method.
bool cPlayer::SendTarotStoreOpen(wchar_t* readerName, wchar_t* tarotTitle, unsigned long price)
{
	HANDLE                     handle  = NULL;
	MSG_RES_TAROT_SEEKER_OPEN* sendMsg = (MSG_RES_TAROT_SEEKER_OPEN*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_TAROT, NM_TAROT_SEEKER_OPEN_RES );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = ERROR_TAROT_SEEKER_OPEN_SUCCESS;

		// 타로카드 최대 22장.
		unsigned long* tarotCardIndex = sendMsg->TarotCardIndex;

		for ( long i = 0; i < MAX_TAROT_CARDS; i++, tarotCardIndex++ )
		{
			(*tarotCardIndex) = mTarotCards[ i ]->itemDefineIndex;
		}

		// 스프레드 최대 5장.
		unsigned long* spreadIndex = sendMsg->SpreadIndex;

		for ( long i = 0; i < mSpreadOffset; i++, spreadIndex++ )
		{
			(*spreadIndex) = mSpread[ i ]->itemDefineIndex;
		}

		wcscpy( sendMsg->Name, readerName );
		wcscpy( sendMsg->Title, tarotTitle );
		sendMsg->Price = price;

		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_TAROT_SEEKER_OPEN) );
	}
	return false;
}

// SendTarotStoreOpen Method.
bool cPlayer::SendTarotReaderJoin(wchar_t* seekerName)
{
	HANDLE                     handle  = NULL;
	MSG_RES_TAROT_READER_JOIN* sendMsg = (MSG_RES_TAROT_READER_JOIN*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_TAROT, NM_TAROT_READER_JOIN_RES );
	if ( sendMsg != NULL )
	{
		wcscpy( sendMsg->seekerName, seekerName );
		sendMsg->userCount   = (++mTarotUserCount);
		sendMsg->point       = (unsigned short)(++mHeroInfo.TarotPoint);
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_TAROT_READER_JOIN) );
	}
	return false;
}

// SendTarotReaderLeave Method.
bool cPlayer::SendTarotReaderLeave(wchar_t* seekerName)
{
	HANDLE                      handle  = NULL;
	MSG_RES_TAROT_READER_LEAVE* sendMsg = (MSG_RES_TAROT_READER_LEAVE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_TAROT, NM_TAROT_READER_LEAVE_RES );
	if ( sendMsg != NULL )
	{
		wcscpy( sendMsg->seekerName, seekerName );
		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_TAROT_READER_LEAVE) );
	}
	return false;
}

// SendTarotStoreJoin Method.
bool cPlayer::SendTarotStoreJoin(wchar_t* tarotTitle, unsigned long price)
{
	HANDLE                     handle  = NULL;
	MSG_RES_TAROT_SEEKER_JOIN* sendMsg = (MSG_RES_TAROT_SEEKER_JOIN*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_TAROT, NM_TAROT_SEEKER_JOIN_RES );

	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = ERROR_TAROT_SEEKER_JOIN_SUCCESS;

		/*-- 카드섞이(shuffle).
		*/
		TB_INVENTORY* temp;
		int           s;
		for ( int i = 0; i < MAX_TAROT_CARDS; i++ )
		{
			s = (int)(rand( ) % MAX_TAROT_CARDS);	// 0 ~ 21의 램덤 수를 생성한다.

			temp             = mTarotCards[ i ];	// 교환을 위해 임시 저장.
			mTarotCards[ i ] = mTarotCards[ s ];	// 교환 시작.
			mTarotCards[ s ] = temp;				// 교환 완료.
		}

		// 카드선택.
		for ( int i = 0; i < MIN_TAROT_CARDS; i++ )
		{
			sendMsg->TarotCardIndex[ i ] = mTarotCards[ i ]->itemDefineIndex;
		}

		wcscpy( sendMsg->strTitle, tarotTitle );
		sendMsg->Price = price;

		return NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_TAROT_SEEKER_JOIN) );
	}

	return false;
}

// ClearFriends Method.
void cPlayer::ClearFriends( )
{
	memset( mFriends, 0, sizeof(mFriends) );
}

// SelectFriend Method.
TB_FRIEND* cPlayer::SelectFriend(short offset)
{
	return (offset < MAX_FRIEND) ? (mFriends+offset) : NULL;
}

// UpdateFriend Method.
bool cPlayer::UpdateFriend( )
{
	TB_FRIEND* friends = SelectFriend( );
	cPlayer*   player  = NULL;
	bool       retcode = false;

	for ( long i = 0; i < MAX_FRIEND && friends->idx > 0; i++, friends++ )
	{
		if ( friends->block == 0 )
		{
			if ( friends->gameIn == 0 )
			{
				// 게임중 친구확인 - 신규접속 검토.
				player = OBJECTMANAGER->GetPlayer( friends->characterIdx );
				if ( player != NULL )
				{
					friends->level  = player->GetLevel( );
					friends->job    = (unsigned long)player->GetJob( );
					friends->mapNum = player->GetMapNumber( );
					friends->gameIn = 1;
					retcode = true;
				}
			}
			else
			{
				// 게임중 친구확인 - 변경내용 검토.
				player = OBJECTMANAGER->GetPlayer( friends->characterIdx );
				if ( player != NULL )
				{
					if ( friends->level != player->GetLevel( ) )
					{
						friends->level = player->GetLevel( );
						retcode = true;
					}
					if ( friends->job != (unsigned long)player->GetJob( ) )
					{
						friends->job = (unsigned long)player->GetJob( );
						retcode = true;
					}
					if ( friends->mapNum != player->GetMapNumber( ) )
					{
						friends->mapNum = player->GetMapNumber( );
						retcode = true;
					}
				}
				else
				{
					friends->gameIn = 0;
					retcode = true;
				}
			}
		}
	}
	return retcode;
}

// SendFriendList Method.
void cPlayer::SendFriendList(ULONG_PTR socketContext)
{
	HANDLE               handle  = NULL;
	MSG_RES_FRIEND_LIST* sendMsg = (MSG_RES_FRIEND_LIST*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)socketContext );
	unsigned long        length  = sizeof(MSG_RES_FRIEND_LIST) - sizeof(sendMsg->table);

	TB_FRIEND*           friends = SelectFriend( );

	sendMsg->Category = NM_CHAT;
	sendMsg->Protocol = NM_CHAT_FRIEND_LIST_RES;
	sendMsg->RowCount = 0;

	for ( long i = 0; i < 50 && friends->idx > 0; i++, friends++ )
	{
		sendMsg->table[ i ].Idx          = friends->idx;
		sendMsg->table[ i ].CharacterIdx = friends->characterIdx;
		wcscpy( sendMsg->table[ i ].CharacterName, friends->characterName );
		sendMsg->table[ i ].Race         = friends->race;
		sendMsg->table[ i ].Gender       = friends->gender;
		sendMsg->table[ i ].Status       = friends->status;
		sendMsg->table[ i ].Level        = friends->level;
		sendMsg->table[ i ].Job          = friends->job;
		sendMsg->table[ i ].MapNum       = friends->mapNum;
		sendMsg->table[ i ].GameIn       = friends->gameIn;;
		sendMsg->RowCount++;
	}

	length += (sendMsg->RowCount * sizeof(sendMsg->table));
	NETWORK2->SendMsgRoot( handle, length );
}

// SendPlayerInfo Method - [cGameProcess/CallbackComplete/SocketManager]에서 호출.
bool cPlayer::SendPlayerInfo(char category, char protocol, ULONG_PTR perSocketContext)
{
	HANDLE          handle  = NULL;
	MSG_PLAYERINFO* sendMsg = (MSG_PLAYERINFO*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)perSocketContext );
	if ( sendMsg != NULL )
	{
		sendMsg->Category   = category;
		sendMsg->Protocol   = protocol;
		sendMsg->ErrorCode  = 0;
		sendMsg->PlayerInfo = mPlayerInfo;
		sendMsg->WeaponInfo = mPlayerWeaponInfo;
		sendMsg->WearInfo   = mPlayerWearInfo;
		sendMsg->Exrinfo    = mPlayerExrInfo;
		if( mPlayerExrInfo.mIsPvPJoin == true )
			sendMsg->MapNum     = mPvPMapDataNumber;
		else
			sendMsg->MapNum     = mMapNumber;
		sendMsg->XPos       = mObjectPos.x;
		sendMsg->YPos       = mObjectPos.y;
		sendMsg->RotAngle	= mMapTargetPos.mRotAngle;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_PLAYERINFO) );

		return true;
	}
	return false;
}

// SendSightIn Method - [cGridManager/SendPlayerGameIn/SendSightInPlayer]에서 호출.
bool cPlayer::SendSightIn(char category, char protocol, unsigned long connectionIdx)
{
	/// 클라이언트 전송 수치 계산
	mPlayerExrInfo.mMoveSpeed = GetMoveSpeed();
	mPlayerExrInfo.mAttackSpeed = GetStatus2()->mAttackSpeed;

	HANDLE          handle  = NULL;
	MSG_PLAYERINFO* sendMsg = (MSG_PLAYERINFO*)NETWORK2->GetMsgRoot( &handle, connectionIdx );
	if ( sendMsg != NULL )
	{
		sendMsg->Category   = category;
		sendMsg->Protocol   = protocol;
		sendMsg->ErrorCode  = 0;
		sendMsg->PlayerInfo = mPlayerInfo;
		sendMsg->WeaponInfo = mPlayerWeaponInfo;
		sendMsg->WearInfo   = mPlayerWearInfo;
		sendMsg->Exrinfo    = mPlayerExrInfo;
		sendMsg->MapNum     = mMapNumber;
		if( mPlayerExrInfo.mIsPvPJoin == true )
			sendMsg->MapNum     = mPvPMapDataNumber;
		else
			sendMsg->MapNum     = mMapNumber;
		sendMsg->XPos       = mObjectPos.x;
		sendMsg->YPos       = mObjectPos.y;
		sendMsg->RotAngle	= mDirection;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_PLAYERINFO) );
	}

	SKILLMANAGER->SendAddSynMsgInfluenceToPlayer( connectionIdx, mPlayerInfo.CharacterIdx );

	if ( mPlayerExrInfo.mState == eOBJECT_STATE_MOVE )
	{
		HANDLE        handle  = NULL;
		MSG_SYN_MOVE* sendMsg = (MSG_SYN_MOVE*)NETWORK2->GetMsgRoot( &handle, connectionIdx );
		if ( sendMsg != NULL )
		{
			sendMsg->Category     = NM_PLAYER;
			sendMsg->Protocol     = NM_PLAYER_MOVE_SYN;
			sendMsg->characterIdx = mObject.index;
			sendMsg->destX        = mGotoX;
			sendMsg->destY        = mGotoY;
			sendMsg->moveSpeed    = GetMoveSpeed();
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_MOVE) );
		}
	}
	if ( mPlayerExrInfo.mStateStop == eSTOP_OPENTAROT )
	{
		HANDLE                     handle  = NULL;
		MSG_SYN_TAROT_READER_OPEN* sendMsg = (MSG_SYN_TAROT_READER_OPEN*)NETWORK2->GetMsgRoot( &handle, connectionIdx, NM_TAROT, NM_TAROT_READER_OPEN_SYN );
		if ( sendMsg != NULL )
		{
			sendMsg->mCharacterIdx = mObject.index;
			wcscpy( sendMsg->mTitle, mTarotTitle );
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_TAROT_READER_OPEN) );
		}
	}
	if ( mPlayerExrInfo.mStateStop == eSTOP_OPENSTALL )
	{
		HANDLE                        handle  = NULL;
		MSG_SYN_ITEM_STALL_SELL_OPEN* sendMsg = (MSG_SYN_ITEM_STALL_SELL_OPEN*)NETWORK2->GetMsgRoot( &handle, connectionIdx, NM_ITEM, NM_ITEM_STALL_SELL_OPEN_SYN );
		if ( sendMsg != NULL )
		{
			sendMsg->characterIdx = mObject.index;
			wcscpy( sendMsg->title, mStallSellTitle );
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_ITEM_STALL_SELL_OPEN) );
		}
	}
	return false;
}

// SendSightOut Method - [cGridManager::SendPlayerGameOut/SendSightOutPlayer]에서 호출.
bool cPlayer::SendSightOut(char category, char protocol, unsigned long connectionIdx)
{
	//SKILLMANAGER->SendDelSynMsgInfluenceToPlayer( connectionIdx, mPlayerInfo.CharacterIdx );

	HANDLE            handle  = NULL;
	MSG_CHARACTERIDX* sendMsg = (MSG_CHARACTERIDX*)NETWORK2->GetMsgRoot( &handle, connectionIdx );
	if ( sendMsg != NULL )
	{
		sendMsg->Category      = category;
		sendMsg->Protocol      = protocol;
		sendMsg->mCharacterIdx = mObject.index;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_CHARACTERIDX) );
	}

	return false;
}

bool cPlayer::SendHeroInfo()
{
	HANDLE          handle  = NULL;
	MSG_HEROINFO* sendMsg = (MSG_HEROINFO*)NETWORK2->NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	if ( sendMsg == NULL )
		return false;
		
	sendMsg->Category   = NM_PLAYER;
	sendMsg->Protocol   = NM_PLAYER_HEROINFO_RES;
	sendMsg->mHeroInfo  = mHeroInfo;
	sendMsg->mExrInfo   = mPlayerExrInfo;
	NETWORK2->SendMsgRoot( handle, sizeof(MSG_HEROINFO) );

	return true;
}

/// 배열인덱스, DB 고유인덱스
int cPlayer::IsKeepQuest( int arrIdx, long questIdx )
{
	if( mQuest[arrIdx].questIdx == questIdx )
		return mQuest[arrIdx].idx;
	return -1;
}

int cPlayer::IsKeepQuest( long questIdx )
{
	for( int i = 0; i < MAX_KEEPQUEST; ++i )
	{
		if( mQuest[i].idx == 0 )
			return -1;

		if( mQuest[i].questIdx == questIdx )
			return i;
	}
	return -1;
}

/// 해당 퀘스트와 동일한 그룹의 퀘스트를 보유중인지 검사
int cPlayer::IsKeepGroupQuest( long questIdx )
{
	for( int i = 0; i < MAX_KEEPQUEST; ++i )
	{
		if( mQuest[i].idx == 0 )
			return -1;

		if( mQuest[i].questIdx == questIdx )
			return i;

		/// 동일한 그룹의 퀘스트 보유중이면 리턴
		cQuestDefine* keepDefine = QUESTMAN->GetQuestDefine( mQuest[i].questIdx );
		cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
		if( keepDefine && define )
		{
			if( define->mGroup > 0 && define->mGroup == keepDefine->mGroup )
				return i;
		}
	}
	return -1;
}

/// 퀘스트 리스트 빈자리 찾기
int cPlayer::FindEmptyQuest()
{
	for( int i = 0; i < MAX_KEEPQUEST; ++i )
	{
		if( mQuest[i].idx == 0 && mQuest[i].questIdx == 0 )
			return i;
	}
	return -1;
}

///
bool cPlayer::IsCompleteQuest( long questIdx )
{
	cCompleteQuestSet::cIterator i = mCompleteQuestSet.Find( questIdx );
	if( i != mCompleteQuestSet.End() )
		return true;
	return false;
}

/// 동일한 그룹의 퀘스트를 완료하였는지 검사
bool cPlayer::IsCompleteGrouptQuest( long questIdx )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( define )
	{
		cCompleteGroupQuestSet::cIterator i = mCompleteGroupQuestSet.Find( define->mGroup );
		if( i != mCompleteGroupQuestSet.End() )
			return true;
	}
	return false;
}

//
bool cPlayer::AddCompleteQuest( unsigned long questIdx )
{
	if( mCompleteQuestSet.Insert( questIdx ) == false )
		return false;

	/// 완료 그룹정보도 저장
	cQuestDefine* compDefine = QUESTMAN->GetQuestDefine( questIdx );
	if( compDefine && compDefine->mGroup > 0 )
		mCompleteGroupQuestSet.Insert( compDefine->mGroup );
	return true;
}

void cPlayer::DeleteQuest( int arrIdx )
{
	TB_QUEST_PROGRESS* delQuest = mQuest + arrIdx;
	memset( delQuest, 0, sizeof(TB_QUEST_PROGRESS) );

	/// 이동시키기
	TB_QUEST_PROGRESS* quest1 = mQuest + arrIdx;
	TB_QUEST_PROGRESS* quest2 = 0;
	for( int i = arrIdx; i < MAX_KEEPQUEST - 1; ++i, quest1++ )
	{
		quest2 = quest1 + 1;
		*quest1 = *quest2;
	}

	TB_QUEST_PROGRESS* lastQuest = mQuest + MAX_KEEPQUEST - 1;
	memset( lastQuest, 0, sizeof(TB_QUEST_PROGRESS) );
}

/// 퀘스트 목록 발송 
bool cPlayer::SendQuest( ULONG_PTR socketContext )
{
	HANDLE				 handle = NULL;
	MSG_RES_QUEST_LIST*	 sendMsg = (MSG_RES_QUEST_LIST*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)socketContext );
	sQuestList*			 questlist = sendMsg->table;

	unsigned long		length  = sizeof(MSG_RES_QUEST_LIST) - sizeof(sendMsg->table);

	sendMsg->Category = NM_QUEST;
	sendMsg->Protocol = NM_QUEST_LIST_RES;
	sendMsg->rowCount = 0;

	TB_QUEST_PROGRESS* data = mQuest;
	for( int i = 0; i < MAX_KEEPQUEST; ++i )
	{
		if( data->idx == 0 )
			break;

		questlist->arrIdx		= i;
		questlist->questIdx		= data->questIdx;
		questlist->check		= data->check;
		questlist->status		= data->status;
		questlist->restTime		= 0;

		/// 남은시간 재계산
		cQuestDefine* define = QUESTMAN->GetQuestDefine( data->questIdx );
		if( define && define->mQuestLimit && define->mQuestLimit->mTimeType != eTIME_NONE )
		{
			long accTime = NETWORK2->GetAccumTime();
			long restTime = data->restTime - (accTime - data->startTime);
			questlist->restTime = ( restTime > 0 ) ? restTime : 0;
		}

		questlist++;
		sendMsg->rowCount++;
		data++;
	}

	length += (sendMsg->rowCount * sizeof(sendMsg->table));
	return NETWORK2->SendMsgRoot( handle, length );
}

/// DB에 퀘스트 진행정보 저장
bool cPlayer::SaveQuestProgress(ULONG_PTR socketContext)
{
	HANDLE        handle      = NULL;
	QUEST_UPDATE* questUpdate = (QUEST_UPDATE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_QUEST_UPDATE );

	if ( questUpdate != NULL )
	{
		TB_QUEST_PROGRESS* table	   = questUpdate->table;
		int                length      = sizeof(QUEST_UPDATE) - sizeof(questUpdate->table);

		questUpdate->characterIdx = GetObjectID();

		TB_QUEST_PROGRESS* quest = mQuest;
		for( long i = 0; i < MAX_KEEPQUEST; i++, quest++ )
		{			
			if( quest->idx > 0 )
			{
				cQuestDefine* define = QUESTMAN->GetQuestDefine( quest->questIdx );
				if( define )
				{
					(*table) = (*quest);

					/// 시간타입 저장
					questUpdate->titmeType = eTIME_NONE;

					/// 시간 제한퀘스트 - 게임시간 일 경우 시간 검사
					cQuestLimit* limit = define->mQuestLimit;
					if( limit )
					{
						questUpdate->titmeType = limit->mTimeType;

						/// 게임시간 - 남은시간 저장하기
						if( limit->mTimeType == eTIME_GAME )
						{
							long accTime = NETWORK2->GetAccumTime();
							long restTime = table->restTime - (accTime - table->startTime);
							table->restTime = ( restTime > 0 ) ? restTime : 0;
						}
					}
					
					table++;
					questUpdate->rowCount++;
				}
			}
			else
				break;
		}
		length += (questUpdate->rowCount * sizeof(questUpdate->table));
		return NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length );
	}
	return false;
}

int cPlayer::IsRegistQuest( eQUESTADD_TYPE type, unsigned long questIdx, unsigned long npcIdx )
{
	/// 퀘스트번호 유효성 검사
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
		return ERROR_QUEST_ADD_FAIL;

	/// NPC에 의한 퀘스트일경우, NPC 검사
	if( type == eQUESTADD_NPC )
	{
		cNpc* pNpc = OBJECTMANAGER->GetNpc( npcIdx );
		if( !pNpc )
			return ERROR_QUEST_ADD_FAIL;

		unsigned int npcId = pNpc->GetRaceGender();

		/// 실제로 npc가 소유한 퀘스트인지 검사
		if( !QUESTMAN->IsHaveQuest( questIdx, npcId ) )
			return ERROR_QUEST_ADD_NPC;
	}

	/// 완전 완료된 퀘스트인지 검사 (반복퀘 제외)
	if( define->mRepeatType != eQUEST_REPEAT )
	{
		if( IsCompleteQuest( questIdx ) )
			return ERROR_QUEST_ADD_COMPLETE;

		/// 해당 그룹이 완료되었으면 리턴 (반복퀘 제외)
		if( IsCompleteGrouptQuest( questIdx ) == true )
			return ERROR_QUEST_ADD_COMPLETE;
	}

	/// 퀘스트 목록 갯수 검사
	if( FindEmptyQuest() == -1 )
		return ERROR_QUEST_ADD_MAX;

	/// 가지고 있는 퀘스트인지 검사.
	if( IsKeepGroupQuest( questIdx ) != -1 )
		return ERROR_QUEST_ADD_EXIST;

	/// 트리거 검사( 조건 검사 )
	if( !TRIGGERMAN->CheckTrigger( GetObjectID(), questIdx ) )
		return ERROR_QUEST_ADD_TRIGGER;

	return ERROR_QUEST_ADD_SUCCESS;
}

int cPlayer::IsRegistQuestItem( unsigned long questIdx )
{
	/// 퀘스트번호 유효성 검사
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
		return ERROR_QUEST_ADDBYITEM_FAIL;

	/// 완전 완료된 퀘스트인지 검사 (반복퀘 제외)
	if( define->mRepeatType != eQUEST_REPEAT )
	{
		if( IsCompleteQuest( questIdx ) )
			return ERROR_QUEST_ADDBYITEM_COMPLETE;

		/// 해당 그룹이 완료되었으면 리턴 (반복퀘 제외)
		if( IsCompleteGrouptQuest( questIdx ) == true )
			return ERROR_QUEST_ADDBYITEM_COMPLETE;
	}

	/// 퀘스트 목록 갯수 검사
	if( FindEmptyQuest() == -1 )
		return ERROR_QUEST_ADDBYITEM_MAX;

	/// 가지고 있는 퀘스트인지 검사.
	if( IsKeepGroupQuest( questIdx ) != -1 )
		return ERROR_QUEST_ADDBYITEM_EXIST;

	/// 트리거 검사( 조건 검사 )
	if( !TRIGGERMAN->CheckTrigger( GetObjectID(), questIdx ) )
		return ERROR_QUEST_ADDBYITEM_TRIGGER;

	return ERROR_QUEST_ADDBYITEM_SUCCESS;
}

/// 맵체인지시 퀘스트 npc 상태 변경
bool cPlayer::ResetQuestNpcStatus()
{
	/// 삭제 
	mNpcStatusMap.Clear();

	/// 해당맵의 있는 npc로 재 세팅
	typedef tPointerHashMap<unsigned long, cNpc*> cNpcMap;
	cNpcMap* npcMap = (cNpcMap*)OBJECTMANAGER->GetNpcMap();
	if( npcMap )
	{
		/// npc 목록중 동일한 맵에 있는 npc만 처리
		cNpcMap::cIterator i = npcMap->Begin();
		cNpcMap::cIterator end = npcMap->End();

		for( ; i != end; ++i )
		{
			cNpc* pNpc = (cNpc*)(i->mSecond);
			if( pNpc && pNpc->GetMapNumber() == mMapNumber )
			{
				/// 목록 삽입
				if( mNpcStatusMap.Insert( pNpc->GetObjectID(), eNPCQUEST_NONE ) == false )
					return false;
			}
		}
	}
	else
		return false;

	return true;
}

/// NPC 클릭시 신규 가능퀘만 체크후 리스트로 보내기
bool cPlayer::SendNpcNewQuestList( unsigned long npcIdx )
{
	cNpc* pNpc = OBJECTMANAGER->GetNpc( npcIdx );
	if( !pNpc )
		return false;

	/// 동일 맵에 있는 npc면 처리
	if( pNpc->GetMapNumber() != mMapNumber )
		return false;

	/// 보유 퀘스트중 신규 가능한것이 있는지 체크
	typedef tHashSet<unsigned long> cQuestSet;
	sNpcQuestList* list = QUESTMAN->GetNpcQuestMap( pNpc->GetRaceGender() );
	if( list )
	{
		HANDLE					handle  = NULL;
		MSG_RES_NPC_QUESTLIST*	sendMsg = (MSG_RES_NPC_QUESTLIST*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );

		unsigned long	length    = sizeof(MSG_RES_NPC_QUESTLIST) - sizeof(sendMsg->questIdxList);
		unsigned long*	questlist = sendMsg->questIdxList;

		sendMsg->Category  = NM_NPC;
		sendMsg->Protocol  = NM_NPC_QUESTLIST_RES;
		sendMsg->rowCount  = 0;
		sendMsg->npcId     = pNpc->GetRaceGender();
		sendMsg->ErrorCode = ERROR_QUESTLIST_SUCCESS;

		///
		cQuestSet::cIterator s = list->mQuestSet.Begin();
		cQuestSet::cIterator send = list->mQuestSet.End();
		for( ; s != send; ++s )
		{
			unsigned long questIdx = (unsigned long)(*s);
			cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
			if( !define )
			{
				NETWORK2->PostServerEvent( "Error(%d): QUEST SendNpcNewQuestList() - failed to search quest define", questIdx );
				assert(0);
				break;
			}

			/// 반복퀘스트가 아닌경우
			if( define->mRepeatType != eQUEST_REPEAT )
			{
				// 1. 완전 완료한 퀘스트면 skip
				if( IsCompleteQuest( questIdx ) == true )
					continue;

				// 2. 해당 그룹 퀘스트 완료면 skip
				if( IsCompleteGrouptQuest( questIdx ) == true )
					continue;
			}

			/// 3. player 가 보유중인 퀘스트거나 동일한 그룹의 퀘스트 보유중이면 skip
			if( IsKeepGroupQuest( questIdx ) != -1 )
				continue;

			/// 트리거 검사 후 가능하면 신규 
			if( TRIGGERMAN->CheckTrigger( mObject.index, questIdx ) == true )
			{
				/// 보낼내용
				(*questlist) = questIdx;
				questlist++;
				sendMsg->rowCount++;
			}
		}

		length += (sendMsg->rowCount * sizeof(sendMsg->questIdxList));
		NETWORK2->SendMsgRoot( handle, length );
	}
	else
	{
		/// 퀘스트 리스트 결과를 보낸다
		HANDLE	                handle  = NULL;
		MSG_RES_NPC_QUESTLIST*	sendMsg = (MSG_RES_NPC_QUESTLIST*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
		unsigned long           length  = sizeof(MSG_RES_NPC_QUESTLIST) - sizeof(sendMsg->questIdxList);

		sendMsg->Category  = NM_NPC;
		sendMsg->Protocol  = NM_NPC_QUESTLIST_RES;
		sendMsg->rowCount  = 0;
		sendMsg->npcId     = pNpc->GetRaceGender();
		sendMsg->ErrorCode = ERROR_QUESTLIST_SUCCESS;
		NETWORK2->SendMsgRoot( handle, length );
	}
	return true;
}

/// npc 의 상태 결과 값 send
bool cPlayer::ResNpcQuestStatus()
{
	HANDLE						handle = NULL;
	MSG_RES_NPC_QUESTSTATUS*	sendMsg = (MSG_RES_NPC_QUESTSTATUS*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_NPC, NM_NPC_QUESTSTATUS_RES );
	unsigned long				length = sizeof(MSG_RES_NPC_QUESTSTATUS) - sizeof(sendMsg->table);
	sNpcQuestStatus*			data = sendMsg->table;

	sendMsg->rowCount = 0;

	cNpcQuestStatusMap::cIterator b = mNpcStatusMap.Begin();
	cNpcQuestStatusMap::cIterator end = mNpcStatusMap.End();
	for( ; b != end; ++b )
	{
		unsigned char status = (unsigned char)(b->mSecond);
		if( status == eNPCQUEST_NONE )
			continue;

		data->npcIdx = (unsigned long)(b->mFirst);
		data->status = status;

		data++;
		sendMsg->rowCount++;
	}

	if( sendMsg->rowCount > 0 )
	{
		length += (sendMsg->rowCount * sizeof(sendMsg->table));
		return NETWORK2->SendMsgRoot( handle, length );
	}
	else
	{
		NETWORK2->ReleaseMsgRoot( (PerIoContext*)handle, length );
		return true;
	}
}

/// npc 의 상태 결과 값 send
bool cPlayer::ResNpcQuestStatus(HANDLE handle, void* msg)
{	
	MSG_RES_NPC_QUESTSTATUS* sendMsg = (MSG_RES_NPC_QUESTSTATUS*)msg;
	unsigned long length  = sizeof(MSG_RES_NPC_QUESTSTATUS) - sizeof(sendMsg->table);

	sendMsg->Category = NM_NPC;
	sendMsg->Protocol = NM_NPC_QUESTSTATUS_RES;

	if( sendMsg->rowCount > 0 )
	{
		length += (sendMsg->rowCount * sizeof(sendMsg->table));
		return NETWORK2->SendMsgRoot( handle, length );
	}
	else
	{
		NETWORK2->ReleaseMsgRoot( (PerIoContext*)handle, length );
		return true;
	}
}

/// 맵체인지시 : 해당맵의 npc 퀘스트 관련 상태 보내기
bool cPlayer::SendMapChangeNpcStatus()
{
	if( ResetQuestNpcStatus() == false )
		return false;

	typedef tPointerHashMap<unsigned long, cNpc*> cNpcMap;

	/// 1. 소유중인 퀘스트로 NPC 상태 결정
	for( int index = 0; index < MAX_KEEPQUEST; ++index )
	{
		TB_QUEST_PROGRESS* quest = mQuest + index;
		if( quest->idx <= 0 )
			break;

		cQuestDefine* define = QUESTMAN->GetQuestDefine( quest->questIdx );
		if( !define )
		{
			NETWORK2->PostServerEvent( "Error(%d): QUEST SendMapChangeNpcStatus() - failed to search quest define", quest->questIdx );
			assert(0);
			return false;
		}

		/// 그리드 기준으로 바꾸기!!!!!!!!!!!!!!!!!!!
		cNpcMap* npcMap = (cNpcMap*)OBJECTMANAGER->GetNpcMap();
		if( !npcMap )
		{
			NETWORK2->PostServerEvent( "Error(%d): QUEST SendMapChangeNpcStatus() - failed to search npc map", quest->questIdx );
			assert(0);
			return false;
		}

		/// 완료 & 진행정보는 보상 npc를 기준으로 체크 ( 즉, 보상 npc 맵을 확인해야햠 )
		cNpcMap::cIterator i = npcMap->Begin();
		cNpcMap::cIterator end = npcMap->End();
		for( ; i != end; ++i )
		{
			cNpc* pNpc = (cNpc*)(i->mSecond);
			if( pNpc && pNpc->GetRaceGender() == define->mTakeNpcIndex )
			{
				/// 동일맵 npc 아니면 리턴
				if( pNpc->GetMapNumber() != mMapNumber )
					continue;

				/// 보상완료 맵 번호가 맞지 않으면 리턴
				if( pNpc->GetMapNumber() != define->mTakeMapIndex )
					continue;

				/// 새로 체크된 상태저장
				eNpcQuestStatus newStatus = eNPCQUEST_NONE;
				switch( quest->status )
				{
				case eQUEST_PLAYING:		/// 진행중 (진행 or 완료 결정 )
					newStatus = ( quest->check == define->mComplete ) ? eNPCQUEST_COMPLETE : eNPCQUEST_PLAYING;
					break;
				case eQUEST_COMPLETE:		/// 진행완료
					newStatus = eNPCQUEST_REWARD;
					break;
				}

				/// 우선순위 : 보상가능 > 완료가능 > 신규가능 > 진행중
				cNpcQuestStatusMap::cIterator b = mNpcStatusMap.Find( pNpc->GetObjectID() );
				if( b != mNpcStatusMap.End() )
				{
					eNpcQuestStatus& npcStatus = b->mSecond;

					/// 기존의 NPC 상태와 비교해서 최우선순위 상태 결정
					if( npcStatus < newStatus )
						npcStatus = newStatus;
				}
				else
				{
					NETWORK2->PostServerEvent( "Error(%d): QUEST SendMapChangeNpcStatus() - failed to search npc index", quest->questIdx );
					assert(0);
				}
				break;
			}
		}
	}

	/// 우선순위 : 보상가능 > 완료가능 > 신규가능 > 진행중
	/// 2. 신규가능한 퀘스트로 NPC 상태 결정
	cNpcQuestStatusMap::cIterator b = mNpcStatusMap.Begin();
	cNpcQuestStatusMap::cIterator end = mNpcStatusMap.End();
	typedef tHashSet<unsigned long> cQuestSet;
	for( ; b != end; ++b )
	{
		short status = (short)(b->mSecond);
		if( !(status == eNPCQUEST_PLAYING || status == eNPCQUEST_NONE) )
			continue;

		/// 해당 npc가 보유한 퀘스트 목록 가져오기
		unsigned long npcIdx = (unsigned long)(b->mFirst);
		cNpc* pNpc = OBJECTMANAGER->GetNpc( npcIdx );
		if( !pNpc )
			return false;

		sNpcQuestList* list = QUESTMAN->GetNpcQuestMap( pNpc->GetRaceGender() );
		if( !list )
			continue;

		/// 보유 퀘스트중 신규 가능한것이 있는지 체크
		cQuestSet::cIterator s = list->mQuestSet.Begin();
		cQuestSet::cIterator send = list->mQuestSet.End();
		for( ; s != send; ++s )
		{
			unsigned long questIdx = (unsigned long)(*s);
			cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
			if( !define )
			{
				NETWORK2->PostServerEvent( "Error(%d): QUEST SendMapChangeNpcStatus() - failed to search npc index", questIdx );
				assert(0);
				return false;
			}

			/// 반복퀘스트가 아닌경우
			if( define->mRepeatType != eQUEST_REPEAT )
			{
				// 1. 완전 완료한 퀘스트면 skip
				if( IsCompleteQuest( questIdx ) == true )
					continue;

				// 2. 해당 그룹 퀘스트 완료면 skip
				if( IsCompleteGrouptQuest( questIdx ) == true )
					continue;
			}

			/// 3. player 가 보유중인 퀘스트거나 동일한 그룹의 퀘스트 보유중이면 skip
			if( IsKeepGroupQuest( questIdx ) != -1 )
				continue;

			/// 트리거 검사 후 가능하면 신규. 아니면 넘김.
			if( TRIGGERMAN->CheckTrigger( mObject.index, questIdx ) == true )
			{
				eNpcQuestStatus& npcStatus = b->mSecond;
				npcStatus = eNPCQUEST_NEW;
				break;
			}
		}
	}

	/// client send
	if( ResNpcQuestStatus() == false )
		return false;
	return true;
}

/// npc 의 상태 변경시 send
bool cPlayer::SendNpcQuestStatus()
{
	HANDLE						handle  = NULL;
	MSG_RES_NPC_QUESTSTATUS*	sendMsg = (MSG_RES_NPC_QUESTSTATUS*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx );
	sNpcQuestStatus*			data	= sendMsg->table;

	sendMsg->rowCount = 0;

	/// 기존 npc status 맵 값이랑, 완료, 진행 퀘 비교검사 (맵단위)
	cNpcQuestStatusMap::cIterator b = mNpcStatusMap.Begin();
	cNpcQuestStatusMap::cIterator end = mNpcStatusMap.End();
	typedef tHashSet<unsigned long> cQuestSet;

	for( ; b != end; ++b )
	{
		unsigned long npcIdx = (unsigned long)(b->mFirst);
		cNpc* pNpc = OBJECTMANAGER->GetNpc( npcIdx );
		if( !pNpc )
			continue;

		/// 맵번호 다른 npc이면 리턴
		if( pNpc->GetMapNumber() != mMapNumber )
			continue;

		unsigned int npcId = pNpc->GetRaceGender();

		/// 기존 상태 받아오기
		eNpcQuestStatus& npcStatus = b->mSecond;

		/// 새로운 상태
		eNpcQuestStatus newStatus = eNPCQUEST_NONE;

		/// 변경해야하는지 검사한다
		/// 1. 진행 OR 완료 상태 검사
		TB_QUEST_PROGRESS* quest = mQuest;
		for( int index = 0; index < MAX_KEEPQUEST; index++, quest++ )
		{
			if( quest->idx <= 0 )
				break;

			cQuestDefine* define = QUESTMAN->GetQuestDefine( quest->questIdx );
			if( !define )
			{
				NETWORK2->PostServerEvent( "Error(%d): QUEST SendNpcQuestStatus() - failed to search questIdx", quest->questIdx );
				assert(0);
				continue;
			}

			if( define->mTakeNpcIndex != npcId )
				continue;

			/// 보상완료 맵 번호가 맞지 않으면 리턴
			if( pNpc->GetMapNumber() != define->mTakeMapIndex )
				continue;

			/// 우선순위 : 보상가능 > 완료가능 > 신규가능 > 진행중
			/// 새로 체크된 상태저장
			eNpcQuestStatus checkStatus = eNPCQUEST_NONE;
			switch( quest->status )
			{
			case eQUEST_PLAYING:		/// 진행중 (진행 or 완료 결정 )
				checkStatus = ( quest->check == define->mComplete ) ? eNPCQUEST_COMPLETE : eNPCQUEST_PLAYING;
				break;
			case eQUEST_COMPLETE:		/// 진행완료
				checkStatus = eNPCQUEST_REWARD;
				break;
			}

			/// 우선순위가 더 낮으면 갱신
			if( newStatus < checkStatus )
				newStatus = checkStatus;

			/// 갱신된 상태가 최고 우선순위이면 더이상 검사 하지 않음
			if( newStatus == eNPCQUEST_REWARD )
				break;
		}

		/// 우선순위 : 보상가능 > 완료가능 > 신규가능 > 진행중
		/// 2. 신규 상태 검사
		sNpcQuestList* list = QUESTMAN->GetNpcQuestMap( npcId );
		if( list && (newStatus == eNPCQUEST_PLAYING || newStatus == eNPCQUEST_NONE) )
		{
			/// 보유 퀘스트중 신규 가능한것이 있는지 체크
			cQuestSet::cIterator s = list->mQuestSet.Begin();
			cQuestSet::cIterator send = list->mQuestSet.End();
			for( ; s != send; ++s )
			{
				unsigned long questIdx = (unsigned long)(*s);
				cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
				if( !define )
				{
					NETWORK2->PostServerEvent( "Error(%d): QUEST SendNpcQuestStatus() - failed to search questIdx", questIdx );
					assert(0);
					continue;
				}

				/// 반복퀘스트가 아니면 검사
				if( define->mRepeatType != eQUEST_REPEAT )
				{
					// 1. 완전 완료한 퀘스트면 skip
					if( IsCompleteQuest( questIdx ) == true )
						continue;

					// 2. 해당 그룹 퀘스트 완료면 skip
					if( IsCompleteGrouptQuest( questIdx ) == true )
						continue;
				}

				/// 3. player 가 보유중인 퀘스트거나 동일한 그룹의 퀘스트 보유중이면 skip
				if( IsKeepGroupQuest( questIdx ) != -1 )
					continue;

				/// 트리거 검사 후 가능하면 신규. 아니면 넘김.
				if( TRIGGERMAN->CheckTrigger( mObject.index, questIdx ) == true )
				{
					newStatus = eNPCQUEST_NEW;
					break;
				}
			}
		}

		/// 기존 상태와 신규 상태 비교
		if( npcStatus != newStatus )
		{
			/// 변경됨.
			npcStatus = newStatus;
			data->status = (unsigned char)npcStatus;
			data->npcIdx = npcIdx;
			data++;
			sendMsg->rowCount++;
		}
	}

	ResNpcQuestStatus( handle, sendMsg );
	return true;
}

void cPlayer::UpdateDutyItem()
{
	bool updateNpcStatus = false;
	bool updateCount = false;

	HANDLE                handle	= NULL;
	MSG_RES_QUEST_UPDATE* sendMsg	= (MSG_RES_QUEST_UPDATE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_QUEST, NM_QUEST_UPDATE_RES );
	sQuestList*			  questlist = sendMsg->table;
	unsigned long		  length	= sizeof(MSG_RES_QUEST_UPDATE) - sizeof(sendMsg->table);
	sendMsg->rowCount				= 0;

	TB_QUEST_PROGRESS* quest = mQuest;
	for( unsigned int arrIdx = 0; arrIdx < MAX_KEEPQUEST; ++arrIdx, ++quest )
	{
		if( quest->idx == 0 )
			break;

		cQuestDefine* define = QUESTMAN->GetQuestDefine( quest->questIdx );
		if( !define )
		{
			NETWORK2->PostServerEvent( "Error(%d): QUEST UpdateDutyItem() - failed to search questIdx", quest->questIdx );
			assert(0);
			break;
		}

		if( quest->status != eQUEST_PLAYING )
			continue;

		cQuestDuty* duty = define->mQuestDuty;
		if( !duty )
			continue;

		/// 수집 아이템 갯수 갱신
		for( int i = 0; i < MAX_DUTY; ++i )
		{
			if( duty->mDuty[i].dutyType == eDUTY_NONE )
				break;

			/// 아이템 수집 의무 찾기
			if( duty->mDuty[i].dutyType == eDUTY_COLLECT )
			{
				PerItemCount* itemCount = ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, duty->mDuty[i].targetIdx );
				unsigned int count = ( itemCount != NULL ) ? itemCount->bag : 0;

				__int64 end = 0xFF  << ( i * 8 );
				__int64 cnt = count << ( i * 8 );
				__int64 mask = (0xFFFFFFFFFFFFFFFF ^ end);

				__int64 update = ( quest->check & mask ) | cnt;
	
				if( quest->check != update ) 
				{
					quest->check = update;

					/// 완료가능시 상태 갱신 요청
					if ( quest->check == define->mComplete )
						updateNpcStatus = true;

					//if ( quest->check <= define->mComplete )
					updateCount = true;
				}
			}
		}

		if( updateCount == true )
		{
			/// 클라이언트 쏘기
			questlist->arrIdx		= arrIdx;
			questlist->questIdx		= quest->questIdx;
			questlist->check		= quest->check;
			questlist->status		= quest->status;

			questlist++;
			sendMsg->rowCount++;
		}
	}

	/// 쏘기 
	if( sendMsg->rowCount > 0 )
	{
		length += (sendMsg->rowCount * sizeof(sendMsg->table));
		NETWORK2->SendMsgRoot( handle, length );
	}
	else
		NETWORK2->ReleaseMsgRoot( (PerIoContext*)handle, length );

	/// 의무조건 충족에 따른 npc 상태 갱신 요청
	if( updateNpcStatus == true )
	{
		SendNpcQuestStatus();
	}
}

/// 퀘스트 의무 - 아이템 수집 관련 갱신
/// 오로지 획득만 가능하므로( 퀘스트 아이템 타입 ) , 
/// 아이템 획득시만 호출하도록 한다
//void cPlayer::UpdateDutyItem()
//{
//	bool checkStatus = false;
//
//	HANDLE                handle  = NULL;
//	MSG_RES_QUEST_UPDATE* sendMsg = (MSG_RES_QUEST_UPDATE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_QUEST, NM_QUEST_UPDATE_RES );
//	sQuestList*			  questlist = sendMsg->table;
//	unsigned long		  length  = sizeof(MSG_RES_QUEST_UPDATE) - sizeof(sendMsg->table);
//
//	/// 보유 퀘스트 목록중, 아이템 수집 의무 찾기
//	sendMsg->rowCount = 0;
//	TB_QUEST_PROGRESS* quest = mQuest;
//	for( unsigned int arrIdx = 0; arrIdx < MAX_KEEPQUEST; arrIdx++, quest++ )
//	{
//		if( quest->idx == 0 )
//			break;
//
//		cQuestDefine* define = QUESTMAN->GetQuestDefine( quest->questIdx );
//		if( !define )
//		{
//			NETWORK2->PostServerEvent( "Error(%d): QUEST UpdateDutyItem() - failed to search questIdx", quest->questIdx );
//			assert(0);
//			break;
//		}
//
//		if( quest->status != eQUEST_PLAYING )
//			continue;
//
//		cQuestDuty* duty = define->mQuestDuty;
//		if( !duty )
//			continue;
//
//		bool search = false;
//		for( int i = 0; i < MAX_DUTY; ++i )
//		{
//			/// 아이템 수집 의무 찾기
//			if( duty->mDuty[i].dutyType == eDUTY_COLLECT )
//			{
//				__int64 end = 0xFF << ( i * 8 );
//
//				/// 이미 해당 의무가 완료이면 리턴
//				if( (quest->check & end) == (define->mComplete & end) )
//					continue;
//
//				/// 총갯수 세팅
//				unsigned int total = GetQuestItemCount( duty->mDuty[i].targetIdx, duty->mDuty[i].count );
//				if( total == 0 )
//					continue;
//
//				__int64 cnt = total << ( i * 8 );
//
//				/// 목표수량보다 많으면 목표수량만큼으로 세팅
//				__int64 mask = (0xFFFFFFFFFFFFFFFF ^ end);
//				quest->check = ( quest->check & mask ) | cnt;
//
//				/// 완료가능시 상태 갱신 요청
//				if ( quest->check == define->mComplete )
//				{
//					checkStatus = true;
//				}
//				search = true;
//			}
//		}
//
//		if( search == true )
//		{
//			/// 클라이언트 쏘기
//			questlist->arrIdx		= arrIdx;
//			questlist->questIdx		= quest->questIdx;
//			questlist->check		= quest->check;
//			questlist->status		= quest->status;
//
//			questlist++;
//			sendMsg->rowCount++;
//		}
//	}
//
//	/// 쏘기 
//	if( sendMsg->rowCount > 0 )
//	{
//		length += (sendMsg->rowCount * sizeof(sendMsg->table));
//		NETWORK2->SendMsgRoot( handle, length );
//	}
//	else
//		NETWORK2->ReleaseMsgRoot( (PerIoContext*)handle, length );
//
//	/// 의무조건 충족에 따른 npc 상태 갱신 요청
//	if( checkStatus == true )
//	{
//		SendNpcQuestStatus();
//	}
//}

/// 퀘스트 진행상황 갱신 
void cPlayer::UpdateDutyHunt( unsigned int classIdx )
{
	bool				  checkStatus = false;

	HANDLE                handle  = NULL;
	MSG_RES_QUEST_UPDATE* sendMsg = (MSG_RES_QUEST_UPDATE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_QUEST, NM_QUEST_UPDATE_RES );
	sQuestList*			  questlist = sendMsg->table;
	unsigned long		  length  = sizeof(MSG_RES_QUEST_UPDATE) - sizeof(sendMsg->table);

	sendMsg->rowCount = 0;
	TB_QUEST_PROGRESS* quest = mQuest;
	for( unsigned int arrIdx = 0; arrIdx < MAX_KEEPQUEST; arrIdx++, quest++ )
	{
		if( quest->idx == 0 )
			break;

		cQuestDefine* define = QUESTMAN->GetQuestDefine( quest->questIdx );
		if( !define )
		{
			NETWORK2->PostServerEvent( "Error(%d): QUEST UpdateDuty() - failed to search questIdx", quest->questIdx );
			assert(0);
			break;
		}

		if( quest->status != eQUEST_PLAYING )
			continue;

		/// 해당 의무조건이 있는지 찾기
		int number = QUESTMAN->IsDuty( quest->questIdx, eDUTY_HUNT, classIdx );
		if( number == -1 )
			continue;

		/// 퀘스트 아이템 착용 검사
		if( IsEquipQuestItem( quest->questIdx ) == false )
			continue;

		/// 퀘스트 완료이면 skip
		if( quest->check == define->mComplete )
			continue;

		__int64 end = 0xFF;
		for( int i = 0; i < number; ++i )
			end = end << 8;

		/// 부분완료인경우 skip
		/// 현재 관련된 내용이 완료인지 검사해서 맞으면, 완료된 퀘므로 카운트 하지 않는다
		if( (quest->check & end) == (define->mComplete & end) )
			continue;

		/// 진행상황 갱신
		__int64 cnt = 0x01;
		for( int i = 0; i < number; ++i )
			cnt = cnt << 8;

		quest->check += cnt;

		/// 완료가능시 상태 갱신 요청
		if( quest->check == define->mComplete )
		{
			checkStatus = true;
		}

		/// 클라이언트 쏘기
		questlist->arrIdx		= arrIdx;
		questlist->questIdx		= quest->questIdx;
		questlist->check		= quest->check;
		questlist->status		= quest->status;

		questlist++;
		sendMsg->rowCount++;
	}

	/// 쏘기 
	if( sendMsg->rowCount > 0 )
	{
		length += (sendMsg->rowCount * sizeof(sendMsg->table));
		NETWORK2->SendMsgRoot( handle, length );
	}
	else
		NETWORK2->ReleaseMsgRoot( (PerIoContext*)handle, length );

	/// 의무조건 충족에 따른 npc 상태 갱신 요청
	if( checkStatus == true )
	{
		SendNpcQuestStatus();
	}
}

/// 퀘스트진행시 아이템착용 여부 검사
bool cPlayer::IsEquipQuestItem( unsigned long questIdx )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST IsEquipQuestItem() - failed to search questIdx", questIdx );
		assert(0);
		return false;
	}

	cQuestLimit* limit = define->mQuestLimit;
	if( limit != NULL )
	{
		long* itemIndex = (long*)limit->mLimitEquipItem;
		for( int i = 0; i < QUEST_ITEM_MAX && (*itemIndex) != 0; ++i, ++itemIndex )
		{
			TB_INVENTORY*   inventory  = 0;
			TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefineByIndex( (*itemIndex) );

			if( itemDefine != NULL )
			{
				switch( itemDefine->type )
				{
				case ITEM_WEAPON:
					switch( itemDefine->subType )
					{
					case ITEM_WEAPON_SWORD: 	// 한손검
					case ITEM_WEAPON_BLADE:		// 양손검
					case ITEM_WEAPON_DUAL:		// 이도류
					case ITEM_WEAPON_CUTTER:	// 단  검
					case ITEM_WEAPON_GUN:		// 총
					case ITEM_WEAPON_STAFF:		// 지팡이
						inventory = SelectInventory( (short)((mItemActiveWeapon == ItemActiveFront) ? INVENTORY_HAND_RIGHT2 : INVENTORY_HAND_RIGHT1) );
						break;
					case ITEM_WEAPON_SHIELD:			// 방패
						inventory = SelectInventory( (short)((mItemActiveWeapon == ItemActiveFront) ? INVENTORY_HAND_LEFT2 : INVENTORY_HAND_LEFT1) );
						break;
					default: 
						NETWORK2->PostServerEvent( "Error(%d): QUEST IsEquipQuestItem() - error equip type", (*itemIndex) );
						assert(0);	
						break;
					}
					break;
				case ITEM_WEAR:
					switch( itemDefine->subType )
					{
					case ITEM_WEAR_HEAD:	inventory   = SelectInventory( INVENTORY_WEAR_HAT ); break;
					case ITEM_WEAR_UPPER:	inventory   = SelectInventory( INVENTORY_WEAR_BODY1 ); break;	/// 상의
					case ITEM_WEAR_LOWER:	inventory   = SelectInventory( INVENTORY_WEAR_BODY2 ); break;	// 하의
					case ITEM_WEAR_HANDS:	inventory   = SelectInventory( INVENTORY_WEAR_HAND ); break;	// 손
					case ITEM_WEAR_FEET:	inventory   = SelectInventory( INVENTORY_WEAR_FOOT ); break;	// 발
					case ITEM_WEAR_ONEPIECE: inventory  = SelectInventory( INVENTORY_WEAR_BODY1 ); break;	// 원피스
					default: 
						NETWORK2->PostServerEvent( "Error(%d): QUEST IsEquipQuestItem() - error equip type", (*itemIndex) );
						assert(0);	
						break;
					}
					break;
				case ITEM_ACCESSORY:
					switch( itemDefine->subType )
					{
					case ITEM_ACCESSORY_EARRING:	inventory   = SelectInventory( INVENTORY_WEAR_EARRING ); break;		// 귀걸이
					case ITEM_ACCESSORY_NECKLACE:	inventory   = SelectInventory( INVENTORY_WEAR_NECKLACE ); break;	// 목걸이
					case ITEM_ACCESSORY_BROOCH:		inventory   = SelectInventory( INVENTORY_WEAR_BROOCH ); break;		// 브로치
					case ITEM_ACCESSORY_BRACELET:	inventory   = SelectInventory( INVENTORY_WEAR_BRACELET ); break;	// 팔찌
					case ITEM_ACCESSORY_RING:		inventory   = SelectInventory( INVENTORY_WEAR_RING ); break;		// 반지
					default: 
						NETWORK2->PostServerEvent( "Error(%d): QUEST IsEquipQuestItem() - error equip type", (*itemIndex) );
						assert(0);	
						break;
					}
					break;
				case ITEM_CARD:
					if( itemDefine->subType == ITEM_CARD_EQUIP_C )
					{
						for( short i = INVENTORY_WEAR_CARD1; i <= INVENTORY_WEAR_CARD3; ++i )
						{
							inventory = SelectInventory(i);
							if( inventory && inventory->itemDefineIndex == (*itemIndex) )
								break;
						}
					}
					break;
				default: 
					NETWORK2->PostServerEvent( "Error(%d): QUEST IsEquipQuestItem() - error equip type", (*itemIndex) );
					assert(0);	
					break;
				}

				/// 동일한지 검사
				if( inventory && inventory->itemDefineIndex != (*itemIndex) )
					return false;
			}
		}
	}	
	return true;
}

/// 의뢰아이템 사용 가능 여부 체크
int cPlayer::IsUseQuestItem( unsigned int number, unsigned long& questIdx)
{
	/*-- 아이템 사용시 검사할 내용들.
	*/
	if( GetStateDie() == true )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;

	if( number < INVENTORY_BAG_BEGIN || number > INVENTORY_BAG_END )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;

	TB_INVENTORY* inventory = SelectInventory( (short)number );
	if( IsInventory( inventory ) == false )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;

	if( inventory->apply != InventoryApplyNone )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;

	TB_ITEM_DEFINE* itemDefine = ITEMMANAGER->GetItemDefine( inventory->itemDefineIdx );
	if( itemDefine == NULL )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;

	/*unsigned int getState = GetState( );
	if ( !( getState == eOBJECT_STATE_IDLE || getState == ePLAYER_STATE_ITEMPICK ) )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;*/

	/*-- Item Limit Table(TB_ITEM_LIMIT) 적용
	*/
	TB_ITEM_LIMIT* itemLimit = ITEMMANAGER->GetItemLimit( inventory->itemDefineIdx );
	if( itemLimit != NULL )
	{
		// 종족; TB_ITEM_LIMIT::charRace 와 sPlayerInfo::Race 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charRace != ITEM_RACE_ALL )
		{
			if ( itemLimit->charRace != mPlayerInfo.Race )
				return ERROR_QUEST_ADDBYITEM_ITEMFAIL;
		}

		// 성별; TB_ITEM_LIMIT::charGender 와 sPlayerInfo::Gender 는 정의는 다르나 같은 값을 사용.
		if( itemLimit->charGender != ITEM_GENDER_ALL )
		{
			if ( itemLimit->charGender != mPlayerInfo.Gender )
				return ERROR_QUEST_ADDBYITEM_ITEMFAIL;
		}

		// 직업; TB_ITEM_LIMIT::charJob 와 sPlayerInfo::Job 은 정의는 다르나 같은 값을 사용.
		if( itemLimit->charJob != ITEM_JOB_ALL )
		{
			long charJob = mPlayerInfo.Job - (mPlayerInfo.Job % ITEM_JOB_FIGHTER);
			if ( itemLimit->charJob != charJob )
				return ERROR_QUEST_ADDBYITEM_ITEMFAIL;
		}

		// 레벨; 이하일 경우만 제어를 한다.
		if(  itemLimit->charLevel > (BYTE)mPlayerInfo.Level )
			return ERROR_QUEST_ADDBYITEM_ITEMFAIL;
	}

	/*-- 아이템 효과 적용 - 스킬과 연결하여 사용한다.
	*/
	TB_ITEM_ABILITY* itemAbility = ITEMMANAGER->GetItemAbility( inventory->itemDefineIdx );
	if( itemAbility == NULL )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;

	/// 퀘스트 의뢰 아이템인지 확인
	if( !(itemAbility->influence_idx > 0 && (itemDefine->type == ITEM_ETC1 && itemDefine->subType == ITEM_ETC1_QUEST)) )
		return ERROR_QUEST_ADDBYITEM_ITEMFAIL;

	questIdx = itemAbility->influence_idx;	// 퀘스트 인덱스
	
	return ERROR_QUEST_ADDBYITEM_SUCCESS;
}

/// 채집 관련 완료 검사
bool cPlayer::IsCompleteGathering( unsigned long questIndex )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIndex );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST IsCompleteGathering() - failed to search questIndex", questIndex );
		assert(0);
		return true;
	}

	cQuestDuty* duty = define->mQuestDuty;
	if( !duty )
	{
		NETWORK2->PostServerEvent( "Error: QUEST IsCompleteGathering() - failed to search duty" );
		assert(0);
		return true;
	}

	int arrIdx = IsKeepQuest( questIndex );
	if( arrIdx == -1 )
		return true;

	/// 퀘스트 자체가 완료되었으면 리턴
	if( mQuest[arrIdx].check == define->mComplete )
		return true;
	
	/// 채집 관련부분 완료되었는지 검사
	__int64 mask = 0x00;
	for( unsigned int i = 0; i < MAX_DUTY; ++i )
	{
		eDutyType type = duty->mDuty[i].dutyType;

		/// 의무가 없는게 나오면 뒤에도 없으므로 끝
		if( type == eDUTY_NONE )
			break;

		if( type == eDUTY_COLLECT )
		{
			__int64 bit = 0xFF << ( i * 8 );
			mask = mask | bit;
		}
	}

	/// 모든 채집 조건을 완료하였으면
	if( (mQuest[arrIdx].check & mask) == (define->mComplete & mask) )
		return true;
	return false;
}

/// 기본 보상 처리
bool cPlayer::SendDefaultReward( unsigned long questIdx, unsigned long* skillList, unsigned char skillListCnt )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SendDefaultReward() - failed to search questIdx", questIdx );
		assert(0);
		return false;
	}

	/// 기본 보상 없는 퀘스트도 있다
	cQuestReward* reward = define->mDefaultReward;
	if( !reward )
		return true;

	if( reward->mSkillPoint > 0 )
		SkillPointPlus( (unsigned short)reward->mSkillPoint );

	if( reward->mExp > 0 || reward->mSxp > 0 )
		AddExpSxpNoPer( reward->mExp, reward->mSxp );

	/// 보상 버프 
	for( int i = 0; i < QUEST_BUFF_MAX; ++i )
	{
		unsigned long buffIdx = reward->mRewardBuff[i];
		SendRewardBuff( buffIdx );
	}

	/// 전직 보상
	if( reward->mChangeJobIndex > 0 )
	{
		SetJob( (ePLAYER_JOB)reward->mChangeJobIndex, skillList, skillListCnt );
	}
	
	/// 세력 세팅
	if( reward->mForceType > eFORCETYPE_NONE && reward->mForceType < eFORCETYPE_MAX )
	{
		SetForceType( reward->mForceType );
	}

	/// 호칭 보상
	if( reward->mGiveTitleIndex > 0  )
	{
		/// 타이틀은 기본 보상으로 주어지기 때문에 따로 쿼리문을 적용시키지 않았다!
		if( AddHaveTitle( reward->mGiveTitleIndex ) == true )
			SendHaveTitle( reward->mGiveTitleIndex );
	}
	return true;
}

/// 보상 버프 추가
bool cPlayer::SendRewardBuff( unsigned long buffIdx )
{
	if( buffIdx > 0 )
	{
		sInfluenceScript* info = SKILLSCRIPT->GetInfluenceInfo( buffIdx );
		if( info )
		{
			/// 오라타입
			if( info->mContinuanceTime == -1 )
			{
				int retvalue = SKILLMANAGER->AddInfSwitch( mObject, mObject, buffIdx, 0, true );
				if( retvalue == 0 )
				{
					NETWORK2->PostServerEvent( "Error(%d): SendRewardBuff mpSkillManager->AddInfSwitch", buffIdx );
					assert(0);
				}
			}
			/// 그외 타입
			else
			{
				bool retvalue = SKILLMANAGER->AddInfluence( mObject, mObject, buffIdx, 0, true );
				if( retvalue == false )
				{
					NETWORK2->PostServerEvent( "Error(%d): SendRewardBuff mpSkillManager->AddInfluence", buffIdx );
					assert(0);
				}
			}
		}
		else
		{
			NETWORK2->PostServerEvent( "Error(%d): SendRewardBuff mpSkillManager->GetInfluenceInfo", buffIdx );
			assert(0);
		}
	}
	return true;
}

/// 퀘스트 수락에 의한 제약 조건 걸기
bool cPlayer::SendLimitPenalty( unsigned long questIdx, bool gameIn )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SendLimitPenalty() - failed to search questIdx", questIdx );
		assert(0);
		return false;
	}

	cQuestLimit* limit = define->mQuestLimit;
	if( !limit )
		return true;

	/// 제약 버프
	for( int i = 0; i < QUEST_BUFF_MAX; ++i )
	{
		unsigned long buffIdx = limit->mLimitBuff[i];
		if( buffIdx > 0 )
		{
			sInfluenceScript* info =  SKILLSCRIPT->GetInfluenceInfo( buffIdx );
			if( info )
			{
				/// 오라타입
				if( info->mContinuanceTime == -1 )
				{
					int retvalue = SKILLMANAGER->AddInfSwitch( mObject, mObject, buffIdx, 0, true );
					if( retvalue == 0 )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST LIMIT PENALTY mpSkillManager->AddInfSwitch", buffIdx );
						assert(0);
						return false;
					}
				}
				/// 그외 타입
				else
				{
					bool retvalue = SKILLMANAGER->AddInfluence( mObject, mObject, buffIdx, 0, true );
					if( retvalue == false )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST LIMIT PENALTY mpSkillManager->AddInfluence", buffIdx );
						assert(0);
						return false;
					}
				}
			}
			else
			{
				NETWORK2->PostServerEvent( "Error(%d): QUEST LIMIT PENALTY mpSkillManager->GetInfluenceInfo", buffIdx );
				assert(0);
			}
		}
	}

	/// 제약 디버프
	for( int i = 0; i < QUEST_BUFF_MAX; ++i )
	{
		unsigned long debuffIdx = limit->mLimitDeBuff[i];
		if( debuffIdx > 0 )
		{
			sInfluenceScript* info =  SKILLSCRIPT->GetInfluenceInfo( debuffIdx );
			if( info )
			{
				/// 오라타입
				if( info->mContinuanceTime == -1 )
				{
					int retvalue = SKILLMANAGER->AddInfSwitch( mObject, mObject, debuffIdx, 0, true );
					if( retvalue == 0 )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST LIMIT PENALTY mpSkillManager->AddInfSwitch debuff", debuffIdx );
						assert(0);
						return false;
					}
				}
				/// 그외 타입
				else
				{
					bool retvalue = SKILLMANAGER->AddInfluence( mObject, mObject, debuffIdx, 0, true );
					if( retvalue == false )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST LIMIT PENALTY mpSkillManager->AddInfluence debuff", debuffIdx );
						assert(0);
						return false;
					}
				}
			}
			else
			{
				NETWORK2->PostServerEvent( "Error(%d): QUEST LIMIT PENALTY mpSkillManager->GetInfluenceInfo debuff", debuffIdx );
				assert(0);
				return false;
			}
		}
	}

	/// gamein 시에는 적용하지 않는다
	if( gameIn == false )
	{
		/// 맵 체인지
		if( limit->mMapChangeIndex > 0 )
		{
			if( QuestMapChange( limit->mMapChangeIndex ) == false )
			{
				NETWORK2->PostServerEvent( "Error(%d,%d): QUEST LIMIT PENALTY MapChange", questIdx, limit->mMapChangeIndex );
				return false;
			}
		}

		/// 몬스터 소환
		if( limit->mMonsterClassIndex > 0 && limit->mMonsterCount > 0 )
		{
			for( unsigned int i = 0; i < limit->mMonsterCount; ++i )
			{
				unsigned long monsterIdx = AIMANAGER->QuestSummonMonRegen( mMapNumber, GetXPos(), GetYPos(), limit->mMonsterClassIndex, limit->mMonsterTime * 1000 );
				if( monsterIdx > 0 )
				{
					if( QUESTMAN->AddQuestMonster( mObject.index, questIdx, monsterIdx ) == false )
					{
						NETWORK2->PostServerEvent( "Error(%d,%d,%d): QUEST LIMIT PENALTY AddQuestMonster", questIdx, limit->mMonsterClassIndex, monsterIdx );
						return false;
					}
				}
			}
		}
	}
	return true;
}

/// 실패로 인한 패널티 주기
bool cPlayer::SendFailPenalty( unsigned long questIdx )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SendFailPenalty() - failed to search questIdx", questIdx );
		assert(0);
		return false;
	}

	cQuestFail* fail = define->mQuestFail;
	if( !fail )
		return true;

	/// 실패버프 
	for( int i = 0; i < QUEST_BUFF_MAX; ++i )
	{
		unsigned long buffIdx = fail->mFailBuff[i];
		if( buffIdx > 0 )
		{
			sInfluenceScript* info =  SKILLSCRIPT->GetInfluenceInfo( buffIdx );
			if( info )
			{
				/// 오라타입
				if( info->mContinuanceTime == -1 )
				{
					int retvalue = SKILLMANAGER->AddInfSwitch( mObject, mObject, buffIdx, 0, true );
					if( retvalue == 0 )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST FAIL PENALTY mpSkillManager->AddInfSwitch", buffIdx );
						assert(0);
					}
				}
				/// 그외 타입
				else
				{
					bool retvalue = SKILLMANAGER->AddInfluence( mObject, mObject, buffIdx, 0, true );
					if( retvalue == false )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST FAIL PENALTY mpSkillManager->AddInfluence", buffIdx );
						assert(0);
					}
				}
			}
			else
			{
				NETWORK2->PostServerEvent( "Error(%d): QUEST FAIL PENALTY mpSkillManager->GetInfluenceInfo", buffIdx );
				assert(0);
			}
		}
	}

	/// 실패 디버프
	for( int i = 0; i < QUEST_BUFF_MAX; ++i )
	{
		unsigned long debuffIdx = fail->mFailDeBuff[i];
		if( debuffIdx > 0 )
		{
			sInfluenceScript* info =  SKILLSCRIPT->GetInfluenceInfo( debuffIdx );
			if( info )
			{
				/// 오라타입
				if( info->mContinuanceTime == -1 )
				{
					int retvalue = SKILLMANAGER->AddInfSwitch( mObject, mObject, debuffIdx, 0, true );
					if( retvalue == 0 )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST FAIL PENALTY mpSkillManager->AddInfSwitch debuff", debuffIdx );
						assert(0);
					}
				}
				/// 그외 타입
				else
				{
					bool retvalue = SKILLMANAGER->AddInfluence( mObject, mObject, debuffIdx, 0, true );
					if( retvalue == false )
					{
						NETWORK2->PostServerEvent( "Error(%d): QUEST FAIL PENALTY mpSkillManager->AddInfluence debuff", debuffIdx );
						assert(0);
					}
				}
			}
			else
			{
				NETWORK2->PostServerEvent( "Error(%d): QUEST FAIL PENALTY mpSkillManager->GetInfluenceInfo debuff", debuffIdx );
				assert(0);
			}
		}
	}
	return true;
}

bool cPlayer::SendEndPenalty( unsigned long questIdx )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SendEndPenalty() - failed to search questIdx", questIdx );
		assert(0);
		return false;
	}

	cQuestEnd* end = define->mQuestEnd;
	if( !end )
		return true;

	/// 버프, 디버프 해제
	for( int i = 0; i < QUEST_BUFF_MAX; ++i )
	{
		unsigned long buffIdx = end->mEndBuff[i];
		if( buffIdx > 0 )
		{
			SKILLMANAGER->InfluenceOff( mObject.index, buffIdx );
		}
	}
	for( int i = 0; i < QUEST_BUFF_MAX; ++i )
	{
		unsigned long debuffIdx = end->mEndDeBuff[i];
		if( debuffIdx > 0 )
		{
			SKILLMANAGER->InfluenceOff( mObject.index, debuffIdx );
		}
	}

	/// 맵 체인지
	if( end->mMapChangeIndex > 0 )
	{
		if( QuestMapChange( end->mMapChangeIndex ) == false )
		{
			NETWORK2->PostServerEvent( "Error(%d,%d): SendEndPenalty MapChange", questIdx, end->mMapChangeIndex );
			assert(0);
		}
	}

	/// 몬스터 소환정보 삭제
	cQuestLimit* limit = define->mQuestLimit;
	if( limit && limit->mMonsterClassIndex > 0 && limit->mMonsterCount > 0 )
	{
		QUESTMAN->DeleteQuestMonster( mObject.index, questIdx );
	}
	return true;
}

/// SaveQuestInsert Method.
int cPlayer::SaveQuestInsert( ULONG_PTR socketContext, long characterIdx, long questIdx )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIdx );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestInsert() - failed to search questIdx", questIdx );
		assert(0);
		return ERROR_QUEST_ADD_FAIL;
	}

	/// 퀘스트시작시 제공할 아이템을 받을수 있는지 체크
	unsigned long listCnt = 0;
	cQuestLimit* questIimit = define->mQuestLimit;
	if( questIimit )
	{
		sQuestItem* item = questIimit->mGiveItem;
		for( int i = 0; i < QUEST_ITEM_MAX; ++i, ++item )
		{
			if( item->itemIndex != 0 && item->count > 0 )
				listCnt++;
		}

		/// 인벤 검사
		if( IsItemReward( questIimit->mGiveItem, listCnt ) < listCnt )
			return ERROR_QUEST_ADD_ITEM;

	}
	
	/// 퀘스트 삽입시 처리
	__int64 check = 0;
	cQuestDuty* duty = define->mQuestDuty;
	if( duty )
	{
		for( int i = 0; i < MAX_DUTY; ++i )
		{
			if( duty->mDuty[i].dutyType == eDUTY_MEET )
			{
				/// 갱신 
				__int64 end = 0xFF;
				for( int e = 0; e < i; ++e )
					end = end << 8;

				/// 진행상황 갱신
				__int64 cnt = 0x01;
				for( int c = 0; c < i; ++c )
					cnt = cnt << 8;

				check += cnt;
				break;
			}
		}
	}
	else
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestInsert() - failed to search duty", questIdx );
		return ERROR_QUEST_ADD_FAIL;
	}

	///
	HANDLE			handle      = NULL;
	QUEST_INSERT*	questInsert = (QUEST_INSERT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_QUEST_INSERT );
	long&           rowCount    = questInsert->rowCount;
	TB_INVENTORY*   table       = questInsert->inventory;
	unsigned long   length      = sizeof(QUEST_INSERT) - sizeof(questInsert->inventory);

	questInsert->characterIdx = characterIdx;
	questInsert->questIdx = questIdx;
	questInsert->timeType = eTIME_NONE;
	questInsert->restTime = 0;
	questInsert->check = check;

	/// 남은시간 세팅
	if( questIimit && questIimit->mTimeType != eTIME_NONE )
	{
		questInsert->timeType = questIimit->mTimeType;
		questInsert->restTime = questIimit->mTime * 1000;
	}

	/*-- 제공 아이템
	*/
	if( questIimit->mGiveItem )
	{
		sQuestItem* items = questIimit->mGiveItem;

		for ( unsigned int i0 = 0; i0 < listCnt; i0++, items++ )
		{
			TB_ITEM_DEFINE* itemDefine  = ITEMMANAGER->GetItemDefineByIndex( items->itemIndex );
			u_short         remainCount = (u_short)items->count;
			TB_INVENTORY*   inventory;
			short           shortCount;

			if ( itemDefine != NULL && remainCount > 0 )
			{
				// 제공 아이템1. 겹치기 검사 및 처리.
				inventory = SelectInventory( INVENTORY_BAG_BEGIN );
				for ( short i1 = INVENTORY_BAG_BEGIN; i1 <= INVENTORY_BAG_END && remainCount > 0; i1++, inventory++ )
				{
					if ( inventory->itemDefineIndex == (long)items->itemIndex )
					{
						if ( inventory->count < itemDefine->capacity )
						{
							shortCount = UpdateInventory( inventory, remainCount );

							table->idx             = inventory->idx;
							table->itemDefineIdx   = inventory->itemDefineIdx;
							table->itemDefineIndex = inventory->itemDefineIndex;
							table->number          = inventory->number;
							table->count           = shortCount;

							table++; rowCount++;

							remainCount = remainCount - shortCount;
						}
					}
				}
				// 제공 아이템2. 생성 검사 및 등록.
				if ( remainCount > 0 )
				{
					short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );

					while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
					{
						CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

						table->idx             = 0;
						table->itemDefineIdx   = itemDefine->idx;
						table->itemDefineIndex = itemDefine->index;
						table->number          = number;
						table->count           = shortCount;

						table++; rowCount++;

						remainCount = remainCount - shortCount;
						number      = GetEmptyBagNumber( (++number) );
					}
				}
			}
		}
	}

	length += (sizeof(questInsert->inventory) * rowCount);
	return (NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) ) ? ERROR_QUEST_ADD_SUCCESS : ERROR_QUEST_ADD_FAIL;
}

// SaveQuestDelete - 의뢰아이템으로 퀘스트 발급
int cPlayer::SaveQuestInsertByItem( ULONG_PTR socketContext, long characterIdx, unsigned long questIndex, unsigned short number )
{
	/// 1. 퀘스트 검사
	cQuestDefine* define = QUESTMAN->GetQuestDefine( questIndex );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestInsertByItem() - failed to search questIdx", questIndex );
		assert(0);
		return ERROR_QUEST_ADDBYITEM_FAIL;
	}

	TB_INVENTORY* inventory = SelectInventory( number );
	if ( IsInventory( inventory ) == false )
		return ERROR_QUEST_ADDBYITEM_ITEM;

	/// 2. 아이템 제공 체크 - 퀘스트시작시 제공할 아이템을 받을수 있는지 체크
	unsigned long listCnt = 0;
	cQuestLimit* questIimit = define->mQuestLimit;
	if( questIimit )
	{
		sQuestItem* item = questIimit->mGiveItem;
		for( int i = 0; i < QUEST_ITEM_MAX; ++i, ++item )
		{
			if( item->itemIndex != 0 && item->count > 0 )
				listCnt++;
		}

		/// 인벤 검사
		if( IsItemReward( questIimit->mGiveItem, listCnt ) < listCnt )
			return ERROR_QUEST_ADDBYITEM_ITEM;
	}

	/// 퀘스트 삽입시 처리
	__int64 check = 0;
	cQuestDuty* duty = define->mQuestDuty;
	if( duty )
	{
		for( int i = 0; i < MAX_DUTY; ++i )
		{
			if( duty->mDuty[i].dutyType == eDUTY_MEET )
			{
				/// 갱신 
				__int64 end = 0xFF;
				for( int e = 0; e < i; ++e )
					end = end << 8;

				/// 진행상황 갱신
				__int64 cnt = 0x01;
				for( int c = 0; c < i; ++c )
					cnt = cnt << 8;

				check += cnt;
				break;
			}
		}
	}
	else
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestInsert() - failed to search duty", questIndex );
		return ERROR_QUEST_ADD_FAIL;
	}

	/// 3. sql 호출
	HANDLE              handle		= NULL;
	QUEST_INSERTBYITEM* questInsert = (QUEST_INSERTBYITEM*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_QUEST_INSERTBYITEM );
	long&               rowCount    = questInsert->rowCount;
	TB_INVENTORY*       table       = questInsert->inventory;
	unsigned long       length      = sizeof(QUEST_INSERTBYITEM) - sizeof(questInsert->inventory);
	short               shortCount;

	questInsert->characterIdx = characterIdx;
	questInsert->questIdx = questIndex;
	questInsert->check    = check;
	questInsert->timeType = eTIME_NONE;
	questInsert->restTime = 0;

	/// 사용할 아이템
	shortCount = UpdateInventory( inventory, (-1) );
	table->idx             = inventory->idx;
	table->itemDefineIdx   = inventory->itemDefineIdx;
	table->itemDefineIndex = inventory->itemDefineIndex;
	table->number          = inventory->number;
	table->count           = shortCount;

	table++; rowCount++;

	/// 갯수가 0개이면 완전 삭제므로, 서버정보 미리 삭제
	if( inventory->count == 0 )
		RemoveInventory( inventory );

	/*-- 제공 아이템
	*/
	if( questIimit->mGiveItem )
	{
		sQuestItem* items = questIimit->mGiveItem;

		for ( unsigned int i0 = 0; i0 < listCnt; i0++, items++ )
		{
			TB_ITEM_DEFINE* itemDefine  = ITEMMANAGER->GetItemDefineByIndex( items->itemIndex );
			u_short         remainCount = (u_short)items->count;

			if ( itemDefine != NULL && remainCount > 0 )
			{
				TB_INVENTORY* inventory;
				short         shortCount;

				if ( itemDefine->type == ITEM_QUEST )
				{
					// 제공 아이템1. 겹치기 검사 및 처리.
					inventory = SelectInventory( INVENTORY_QUEST_BEGIN );
					for ( short i1 = INVENTORY_QUEST_BEGIN; i1 <= INVENTORY_QUEST_END && remainCount > 0; i1++, inventory++ )
					{
						if ( inventory->itemDefineIndex == (long)items->itemIndex )
						{
							if ( inventory->count < itemDefine->capacity )
							{
								shortCount = UpdateInventory( inventory, remainCount );

								table->idx             = inventory->idx;
								table->itemDefineIdx   = inventory->itemDefineIdx;
								table->itemDefineIndex = inventory->itemDefineIndex;
								table->number          = inventory->number;
								table->count           = shortCount;

								table++; rowCount++;

								remainCount = remainCount - shortCount;
							}
						}
					}
					// 제공 아이템2. 생성 검사 및 등록.
					if ( remainCount > 0 )
					{
						short number = GetEmptyQuestNumber( );
						while ( remainCount > 0 && number > 0 )
						{
							CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

							table->idx             = 0;
							table->itemDefineIdx   = itemDefine->idx;
							table->itemDefineIndex = itemDefine->index;
							table->number          = number;
							table->count           = shortCount;

							table++; rowCount++;

							remainCount = remainCount - shortCount;
							number      = GetEmptyQuestNumber( (++number) );
						}
					}
				}
				else
				{
					// 제공 아이템1. 겹치기 검사 및 처리.
					inventory = SelectInventory( INVENTORY_BAG_BEGIN );
					for ( short i1 = INVENTORY_BAG_BEGIN; i1 <= INVENTORY_BAG_END && remainCount > 0; i1++, inventory++ )
					{
						if ( inventory->itemDefineIndex == (long)items->itemIndex )
						{
							if ( inventory->count < itemDefine->capacity )
							{
								shortCount = UpdateInventory( inventory, remainCount );

								table->idx             = inventory->idx;
								table->itemDefineIdx   = inventory->itemDefineIdx;
								table->itemDefineIndex = inventory->itemDefineIndex;
								table->number          = inventory->number;
								table->count           = shortCount;

								table++; rowCount++;

								remainCount = remainCount - shortCount;
							}
						}
					}
					// 제공 아이템2. 생성 검사 및 등록.
					if ( remainCount > 0 )
					{
						short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );
						while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
						{
							CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

							table->idx             = 0;
							table->itemDefineIdx   = itemDefine->idx;
							table->itemDefineIndex = itemDefine->index;
							table->number          = number;
							table->count           = shortCount;

							table++; rowCount++;

							remainCount = remainCount - shortCount;
							number      = GetEmptyBagNumber( (++number) );
						}
					}
				}
			}
		}
	}

	length += (sizeof(questInsert->inventory) * rowCount);
	return (NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) ) ? ERROR_QUEST_ADDBYITEM_SUCCESS : ERROR_QUEST_ADDBYITEM_FAIL;
}

// SaveQuestDelete - 퀘스트 자체를 포기 시킨다
bool cPlayer::SaveQuestDelete( ULONG_PTR socketContext, MSG_REQ_QUEST_DEL* msg, unsigned int dbIdx )
{
	/// 퀘스트 유효성 검사
	cQuestDefine* define = QUESTMAN->GetQuestDefine( msg->questIndex );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestDelete() - failed to search questIdx", msg->questIndex );
		assert(0);
		return false;
	}

	/// 이미 진행완료된 퀘스트인지 검사
	if( mQuest[msg->arrayIdx].status != eQUEST_PLAYING )
		return false;

	/// 회수해야할 아이템 목록 만들기 - 퀘스트 포기로인한 회수
	// count = 0 : 한 슬롯의 해당 아이템 전체 삭제
	// count = x : 전체 범위중 x 개만큼 삭제

	// 퀘스트아이템은 분리가 안되고, capacity 가 획득갯수보다 크다는 전제하에, 
	// count 를 0 으로 세팅해서 슬롯 자체 삭제를 하도록 한다 
	sQuestItem	takeList[MAX_DUTY+QUEST_ITEM_MAX];
	unsigned int takeCnt = 0;
	cQuestDuty* duty = define->mQuestDuty;
	if( duty != NULL )
	{
		for ( int i = 0; i < MAX_DUTY; ++i )
		{
			if( duty->mDuty[i].dutyType == eDUTY_COLLECT )
			{
				// 일반 아이템인경우 회수하지 않는다
				TB_ITEM_DEFINE* define		= ITEMMANAGER->GetItemDefine( duty->mDuty[i].targetIdx );
				PerItemCount*   itemCount	= ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, duty->mDuty[i].targetIdx );
				if( define && define->type == ITEM_QUEST && itemCount != NULL )
				{
					takeList[takeCnt].itemIndex = duty->mDuty[i].targetIdx;
					takeList[takeCnt].count		= itemCount->bag;		// 퀘스트아이템으로 전체 삭제
					takeCnt++;	
				}
			}
		}
	}

	/// 제공한 아이템 회수 - 있는만큼 회수
	if( define->mQuestEnd ) 
	{
		cQuestEnd* questEnd = define->mQuestEnd;

		for( int i = 0; i < QUEST_ITEM_MAX; ++i )
		{
			unsigned long itemIndex		= questEnd->mTakeItem[i].itemIndex;
			unsigned long count			= questEnd->mTakeItem[i].count;
			PerItemCount* itemCount		= ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, itemIndex );
			if( itemIndex > 0 && count > 0 && itemCount != NULL )
			{
				// 아이템이 존재하면 있는 갯수만큼 회수
				takeList[takeCnt].itemIndex = itemIndex;
				takeList[takeCnt].count		= ( (long)count > itemCount->bag ) ? itemCount->bag : count;
				takeCnt++;
			}
		}
	}

	///
	HANDLE         handle      = NULL;
	QUEST_DELETE*  questDelete = (QUEST_DELETE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_QUEST_DELETE );
	long&          rowCount    = questDelete->rowCount;
	TB_INVENTORY*  table       = questDelete->table;
	unsigned long  length	   = sizeof(QUEST_DELETE) - sizeof(questDelete->table);

	// 퀘스트 포기
	questDelete->idx          = dbIdx;
	questDelete->arrIdx       = msg->arrayIdx;
	questDelete->characterIdx = mObject.index;

	// 회수 아이템
	sQuestItem*   items = takeList;
	TB_INVENTORY* tempInv;
	short         remainCount;
	bool          endLoop;

	for ( unsigned int i0 = 0; i0 < takeCnt; i0++, items++ )
	{
		tempInv     = SelectInventory( INVENTORY_BAG0_BEGIN );
		remainCount = (short)items->count;
		endLoop     = false;

		for ( int i1 = INVENTORY_BAG0_BEGIN; (i1 <= INVENTORY_QUEST_END) && (endLoop == false); i1++, tempInv++ )
		{
			if ( tempInv->itemDefineIndex == (long)items->itemIndex )
			{
				short shortCount;

				table->idx             = tempInv->idx;
				table->itemDefineIdx   = tempInv->itemDefineIdx;
				table->itemDefineIndex = tempInv->itemDefineIndex;
				table->number          = tempInv->number;
					if ( remainCount > 0 )
					{
						shortCount  = UpdateInventory( tempInv, (-remainCount) );
						remainCount = remainCount + shortCount;
						if ( !(remainCount > 0) )
							endLoop = true;
					}
					else
					{
						shortCount = UpdateInventory( tempInv, (-tempInv->count) );
						endLoop = true;
					}
				table->count = shortCount;

				table++;
				rowCount++;
			}
		}
	}

	length += (sizeof(questDelete->table) * rowCount);
	return NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length );
}

bool cPlayer::SaveQuestDeleteAuto( ULONG_PTR socketContext, MSG_REQ_QUEST_DELAUTO* msg, unsigned int dbIdx )
{
	/// 퀘스트 유효성 검사
	cQuestDefine* define = QUESTMAN->GetQuestDefine( msg->questIndex );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestDelete() - failed to search questIdx", msg->questIndex );
		assert(0);
		return false;
	}

	/// 이미 진행완료된 퀘스트인지 검사
	if( mQuest[msg->arrayIdx].status != eQUEST_PLAYING )
		return false;

	/// 수집 아이템 회수
	sQuestItem	takeList[MAX_DUTY+QUEST_ITEM_MAX];
	unsigned int takeCnt = 0;
	cQuestDuty* duty = define->mQuestDuty;
	if( duty != NULL )
	{
		for ( int i = 0; i < MAX_DUTY; ++i )
		{
			if( duty->mDuty[i].dutyType == eDUTY_COLLECT )
			{
				// 일반 아이템인경우 회수하지 않는다
				TB_ITEM_DEFINE* define		= ITEMMANAGER->GetItemDefine( duty->mDuty[i].targetIdx );
				PerItemCount*   itemCount	= ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, duty->mDuty[i].targetIdx );
				if( define && define->type == ITEM_QUEST && itemCount != NULL )
				{
					takeList[takeCnt].itemIndex = duty->mDuty[i].targetIdx;
					takeList[takeCnt].count		= itemCount->bag;		// 퀘스트아이템으로 전체 삭제
					takeCnt++;	
				}
			}
		}
	}

	/// 제공한 아이템 회수 - 있는만큼 회수
	cQuestEnd* questEnd = define->mQuestEnd;
	if( questEnd )
	{
		for( int i = 0; i < QUEST_ITEM_MAX; ++i )
		{
			unsigned long itemIndex		= questEnd->mTakeItem[i].itemIndex;
			unsigned long count			= questEnd->mTakeItem[i].count;
			PerItemCount* itemCount		= ITEMCOUNTPOOL->SearchItemCount( &mItemCountRoot, itemIndex );
			if( itemIndex > 0 && count > 0 && itemCount != NULL )
			{
				// 아이템이 존재하면 있는 갯수만큼 회수
				takeList[takeCnt].itemIndex = itemIndex;
				takeList[takeCnt].count		= ( (long)count > itemCount->bag ) ? itemCount->bag : count;
				takeCnt++;
			}
		}
	}
	

	///
	HANDLE         handle      = NULL;
	QUEST_DELETE*  questDelete = (QUEST_DELETE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_QUEST_DELETEAUTO );
	long&          rowCount    = questDelete->rowCount;
	TB_INVENTORY*  table       = questDelete->table;
	unsigned long  length	   = sizeof(QUEST_DELETE) - sizeof(questDelete->table);

	// 퀘스트 포기
	questDelete->idx          = dbIdx;
	questDelete->arrIdx       = msg->arrayIdx;
	questDelete->characterIdx = mObject.index;

	/*-- 회수 아이템
	*/
	sQuestItem*   items = takeList;
	TB_INVENTORY* tempInv;
	short         remainCount;
	bool          endLoop;

	for ( unsigned int i0 = 0; i0 < takeCnt; i0++, items++ )
	{
		tempInv     = SelectInventory( INVENTORY_BAG0_BEGIN );
		remainCount = (short)items->count;
		endLoop     = false;

		for ( int i1 = INVENTORY_BAG0_BEGIN; (i1 <= INVENTORY_QUEST_END) && (endLoop == false); i1++, tempInv++ )
		{
			if ( tempInv->itemDefineIndex == (long)items->itemIndex )
			{
				short shortCount;

				table->idx             = tempInv->idx;
				table->itemDefineIdx   = tempInv->itemDefineIdx;
				table->itemDefineIndex = tempInv->itemDefineIndex;
				table->number          = tempInv->number;
					if ( remainCount > 0 )
					{
						shortCount = UpdateInventory( tempInv, (-remainCount) );
						remainCount = remainCount + shortCount;
						if ( !(remainCount > 0) )
							endLoop = true;
					}
					else
					{
						shortCount = UpdateInventory( tempInv, (-tempInv->count) );
						endLoop = true;
					}
				table->count = shortCount;

				table++;
				rowCount++;
			}
		}
	}

	length += (sizeof(questDelete->table) * rowCount);
	return NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length );
}

/// SaveQuestComplete Method.
int cPlayer::SaveQuestComplete( ULONG_PTR socketContext, MSG_REQ_NPC_QUEST_COMPLETE* msg, unsigned int dbIdx )
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( msg->questIndex );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestComplete() - failed to search questIdx", msg->questIndex );
		assert(0);
		return ERROR_QUEST_COMPLETE_FAIL;
	}

	/// 시간 제한퀘스트일 경우 시간 검사
	cQuestLimit* limit = define->mQuestLimit;
	if( limit && limit->mTimeType != eTIME_NONE )
	{
		long startTime = mQuest[msg->arrayIdx].startTime;
		long restTime = mQuest[msg->arrayIdx].restTime;
		long accTime = NETWORK2->GetAccumTime();
		
		/// 지난시간 > 남은시간 : 시간오버
		if( accTime - startTime > restTime )
			return ERROR_QUEST_COMPLETE_TIME;
	}

	/// 이미 진행완료된 퀘스트인지 검사
	if( mQuest[msg->arrayIdx].status != eQUEST_PLAYING )
	{
		if( mQuest[msg->arrayIdx].status == eQUEST_COMPLETE )
			return ERROR_QUEST_COMPLETE_ALREADY;
		else
			return ERROR_QUEST_COMPLETE_FAIL;
	}

	/// 수집 의무 아이템 회수 
	sQuestItem   takeList[MAX_DUTY+QUEST_ITEM_MAX];
	unsigned int takeCnt = 0;
	cQuestDuty*  duty    = define->mQuestDuty;
	if ( duty != NULL )
	{
		for ( int i = 0; i < MAX_DUTY; ++i )
		{
			if ( duty->mDuty[i].dutyType == eDUTY_COLLECT )
			{
				takeList[takeCnt].itemIndex = duty->mDuty[i].targetIdx;
				takeList[takeCnt].count     = duty->mDuty[i].count;
				takeCnt++;	
			}
		}
	}

	/// 제공한 아이템 회수
	cQuestEnd* questEnd = define->mQuestEnd;
	if( questEnd )
	{
		for( int i = 0; i < QUEST_ITEM_MAX; ++i )
		{
			unsigned long itemIndex = questEnd->mTakeItem[i].itemIndex;
			unsigned long count = questEnd->mTakeItem[i].count;
			if( itemIndex > 0 && count > 0 )
			{
				takeList[takeCnt].itemIndex = itemIndex;
				takeList[takeCnt].count = count;
				takeCnt++;
			}
		}
	}
	
	// 검사 - 회수 아이템.
	if ( IsItemTake( takeList, takeCnt ) < takeCnt )
		return ERROR_QUEST_COMPLETE_TAKE;

	// DATABASE - 퀘스트 완료 처리.
	HANDLE			 handle        = NULL;
	QUEST_COMPLETE*  questComplete = (QUEST_COMPLETE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_QUEST_COMPLETE );
	long&            rowCount      = questComplete->rowCount;
	TB_INVENTORY*    table         = questComplete->table;
	unsigned long	 length        = sizeof(QUEST_COMPLETE) - sizeof(questComplete->table);

	/*-- 퀘스트 완료
	*/
	questComplete->idx          = dbIdx;
	questComplete->arrIdx       = msg->arrayIdx;
	questComplete->characterIdx = mObject.index;
	questComplete->repeatType	= define->mRepeatType;
	questComplete->check		= mQuest[msg->arrayIdx].check;

	/*-- 회수 아이템
	*/
	sQuestItem*   items = takeList;
	TB_INVENTORY* tempInv;
	int           remainCount;
	bool          endLoop;

	for ( unsigned int i0 = 0; i0 < takeCnt; i0++, items++ )
	{
		tempInv     = SelectInventory( INVENTORY_BAG0_BEGIN );
		remainCount = items->count;
		endLoop     = false;

		for ( int i1 = INVENTORY_BAG0_BEGIN; (i1 <= INVENTORY_QUEST_END) && (endLoop == false); i1++, tempInv++ )
		{
			if ( tempInv->itemDefineIndex == (long)items->itemIndex )
			{
				short shortCount;

				table->idx             = tempInv->idx;
				table->itemDefineIdx   = tempInv->itemDefineIdx;
				table->itemDefineIndex = tempInv->itemDefineIndex;
				table->number          = tempInv->number;
					if ( remainCount > 0 )
					{
						shortCount = UpdateInventory( tempInv, (short)(-remainCount) );
						remainCount = remainCount + shortCount;
						if ( !(remainCount > 0) )
							endLoop = true;
					}
					else
					{
						shortCount = UpdateInventory( tempInv, (-tempInv->count) );
						endLoop = true;
					}
				table->count = shortCount;

				table++;
				rowCount++;
			}
		}
	}

	length += (sizeof(questComplete->table) * rowCount);
	return ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) ) ? ERROR_QUEST_COMPLETE_SUCCESS : ERROR_QUEST_COMPLETE_FAIL; 
}

/// SaveQuestReward Method.
int cPlayer::SaveQuestReward( ULONG_PTR socketContext, MSG_REQ_NPC_QUEST_REWARD* msg, unsigned int dbIdx)
{
	cQuestDefine* define = QUESTMAN->GetQuestDefine( msg->questIndex );
	if( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST SaveQuestReward() - failed to search questIdx", msg->questIndex );
		assert(0);
		return ERROR_QUEST_REWARD_FAIL;
	}

	/// 진행완료된 퀘스트인지 검사
	if( mQuest[msg->arrayIdx].status != eQUEST_COMPLETE )
		return ERROR_QUEST_REWARD_COMPLETE;

	/// 제공 아이템 총 목록
	sQuestItem itemList[QUEST_ITEM_MAX];
	unsigned int listCnt = 0;

	// 기본보상 아이템 목록
	cQuestReward* defaultReward = define->mDefaultReward;
	if ( defaultReward )
	{
		sQuestItem* item = defaultReward->mRewardItem;
		for( int i = 0; i < QUEST_ITEM_MAX; ++i, ++item )
		{
			if( item->itemIndex != 0 && item->count > 0 )
			{
				itemList[ listCnt ].itemIndex = item->itemIndex;
				itemList[ listCnt ].count = item->count;
				listCnt++;
			}
		}
	}

	// 선택 보상 처리
	if( define->mSelectReward && define->mSelectCount > 0 )
	{
		/// 검사 - 선택 갯수 검사
		if( define->mSelectCount != msg->rowCount )
			return ERROR_QUEST_REWARD_COUNT;

		for( long i = 0; i < msg->rowCount; ++i )
		{
			sRewardData& data = msg->table[i];
			if( data.type != eREWARD_ITEM )
				continue;

			/// 선택보상 찾기
			int count = QUESTMAN->IsSelectRewardItem( msg->questIndex, data.index );
			if( count == 0 )
				continue;

			itemList[ listCnt ].itemIndex = data.index;
			itemList[ listCnt ].count = count;
			listCnt++;	
		}
	}

	//
	if ( !(msg->rowCount < QUEST_BUFF_MAX) )
		return ERROR_QUEST_REWARD_FAIL;

	// 검사 - 보상 금  액 .
	if ( IsAddMoney( defaultReward->mMoney ) == false )
		return ERROR_QUEST_REWARD_MONEY;

	// 검사 - 보상 아이템.
	if ( IsItemReward( itemList, listCnt ) < listCnt )
		return ERROR_QUEST_REWARD_ITEM;

	// DATABASE - 퀘스트 보상 처리.
	HANDLE         handle       = NULL;
	QUEST_REWARD*  questReward  = (QUEST_REWARD*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_QUEST_REWARD );
	long&          itemRowCount = questReward->itemRowCount;
	TB_INVENTORY*  table        = questReward->table;
	unsigned long  length       = sizeof(QUEST_REWARD) - sizeof(questReward->skillClassIdx);

	/*-- 퀘스트 완료
	*/
	questReward->idx          = dbIdx;
	questReward->arrIdx       = msg->arrayIdx;
	questReward->characterIdx = mObject.index;

	/*-- 보상 금  액
	*/
	questReward->characterMoney = defaultReward->mMoney;

	/*-- 보상 전  직 ( 기본 보상만 가능 ) 
	*/
	questReward->jobIdx = defaultReward->mChangeJobIndex;
	questReward->race = mPlayerInfo.Race;

	/*-- 보상 스  킬 ( 기본 보상만 가능 ) 
	*/
	questReward->skillPoint = defaultReward->mSkillPoint;


	/*-- 보상 세  력 ( 기본 보상만 가능 ) 
	*/
	questReward->forceType = defaultReward->mForceType;

	/*-- 보상 호  칭 ( 기본 보상만 가능 ) 
	*/
	questReward->titleIdx = defaultReward->mGiveTitleIndex;

	/*-- 선택 보상 버  프
	*/
	int buffCnt = 0;
	for( long i = 0; i < msg->rowCount; ++i )
	{
		sRewardData& data = msg->table[i];
		if( data.type != eREWARD_SKILL )
			continue;

		bool ret = QUESTMAN->IsSelectRewardBuff( msg->questIndex, data.index );
		if( ret == true )
		{
			questReward->bufIndex[ buffCnt ] = data.index;
			buffCnt++;
		}
	}
	questReward->bufRowCount = buffCnt;

	/*-- 보상 아이템
	*/
	sQuestItem* items = itemList;

	for ( unsigned int i0 = 0; i0 < listCnt; i0++, items++ )
	{
		TB_ITEM_DEFINE* itemDefine  = ITEMMANAGER->GetItemDefineByIndex( items->itemIndex );
		u_short         remainCount = (u_short)items->count;
		TB_INVENTORY*   tempInv;
		short           shortCount;

		if ( itemDefine != NULL && remainCount > 0 )
		{
			// 보상 아이템1. 겹치기 검사 및 처리.
			tempInv = SelectInventory( INVENTORY_BAG_BEGIN );
			for ( short i1 = INVENTORY_BAG_BEGIN; i1 <= INVENTORY_BAG_END && remainCount > 0; i1++, tempInv++ )
			{
				if ( tempInv->itemDefineIndex == (long)items->itemIndex )
				{
					if ( tempInv->count < itemDefine->capacity )
					{
						shortCount = UpdateInventory( tempInv, remainCount );

						table->idx             = tempInv->idx;
						table->itemDefineIdx   = tempInv->itemDefineIdx;
						table->itemDefineIndex = tempInv->itemDefineIndex;
						table->number          = tempInv->number;
						table->count           = shortCount;

						table++; itemRowCount++;

						remainCount = remainCount - shortCount;
					}
				}
			}
			// 보상 아이템2. 생성 검사 및 등록.
			if ( remainCount > 0 )
			{
				short number = GetEmptyBagNumber( INVENTORY_BAG_BEGIN );

				while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
				{
					CreateInventory( itemDefine, number, remainCount, &(shortCount=0) );

					table->idx             = tempInv->idx;
					table->itemDefineIdx   = itemDefine->idx;
					table->itemDefineIndex = itemDefine->index;
					table->number          = number;
					table->count           = shortCount;

					table++; itemRowCount++;

					remainCount = remainCount - shortCount;
					number      = GetEmptyBagNumber( (++number) );
				}
			}
		}
	}

	return ( NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, length ) ) ? ERROR_QUEST_REWARD_SUCCESS : ERROR_QUEST_REWARD_FAIL; 
}

///
void cPlayer::GetQuestStatus( eNpcQuestStatus& status, unsigned long npcIdx )
{
	mNpcStatusMap.GetAt( &status, npcIdx );
}

// 퀘스트 관련 드랍해야할 아이템 갯수 획득
unsigned int cPlayer::GetDropItemCount( unsigned int arrIdx, unsigned long itemIdx )
{
	if( mQuest[arrIdx].idx == 0 )
		return 0;

	cQuestDefine* define = QUESTMAN->GetQuestDefine( mQuest[arrIdx].questIdx );
	if ( !define )
	{
		NETWORK2->PostServerEvent( "Error(%d): QUEST GetDropItemCount() - failed to search questIdx", mQuest[arrIdx].questIdx );
		assert(0);
		return 0;
	}

	/// 해당 퀘스트의 의무중, 찾는 아이템이 있는지 찾아서, 있으면 필요 갯수를 리턴한다
	cQuestDuty* duty = define->mQuestDuty;
	if ( duty == NULL )
		return 0;

	/// 동일한 아이템을 찾아 갯수 세팅
	unsigned int count = 0;
	int          num   = 0;
	for( int i = 0; i < MAX_DUTY; ++i )
	{
		if( duty->mDuty[i].dutyType == eDUTY_COLLECT && 
			duty->mDuty[i].targetIdx == itemIdx )
		{
			count = duty->mDuty[i].count;
			num = i;
			break;
		}
	}

	/// 필요갯수가 0 이상이면, 실제 가지고있는 갯수와 비교한다
	if( count > 0 )
	{
		TB_QUEST_PROGRESS* quest = mQuest + arrIdx;

		/// 이미 완료된 퀘스트이면 필요갯수 0
		if( quest->check == define->mComplete )
			return 0;

		__int64 end = 0xFF;
		for( int e = 0; e < num; ++e )
			end = end << 8;

		/// 해당 의무가 완료이면 필요갯수 0
		if( (quest->check & end) == (define->mComplete & end) )
			return 0;

		/// 미완료이면 필요갯수 비교 검사
		__int64 itemCount = (define->mComplete & end) - (quest->check & end);
		for( int c = 0; c < num; ++c )
			itemCount = itemCount >> 8;

		count = (unsigned int)itemCount;
	}
	return count;
}

/// 퀘스트로 인한 오라 버프 켜주기
void cPlayer::InitQuestAura()
{
	TB_QUEST_PROGRESS* quest = mQuest;
	for( unsigned int arrIdx = 0; arrIdx < MAX_KEEPQUEST; arrIdx++, quest++ )
	{
		if( quest->idx == 0 )
			break;

		if( quest->status != eQUEST_PLAYING )
			continue;

		if( SendLimitPenalty( quest->questIdx, true ) == false )
		{
			NETWORK2->PostServerEvent( "Error(%d): QUEST InitQuestAura() - failed to SendLimitPenalty", quest->questIdx );
			assert(0);
		}
	}
}

void cPlayer::SetCheatHideMode( bool mode )
{
	GRIDMANAGER->SetHidePlayer( this, mode );
	mCheatHideMode = mode;
}

void cPlayer::CheatRecover()
{
	InitHP( GetMaxHP() );
	InitMP( GetMaxMP() );

	//if( ChangeState( eOBJECT_STATE_IDLE, true ) == false )
	//{
	//	assert(NULL);
	//	NETWORK2->PostServerEvent("Player[%d] CheatRebirth ChangeState( eOBJECT_STATE_IDLE, true ) == false", mObject.index );
	//}

	//mSkillResurrectionHP = 0;
	//mSkillResurrectionMP = 0;
	//mIsSkillResurrection = false;
}

void cPlayer::CheatRebirth()
{
	InitHP( GetMaxHP() );
	InitMP( GetMaxMP() );

	if( ChangeState( eOBJECT_STATE_IDLE, true ) == false )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("Player[%d] CheatRebirth ChangeState( eOBJECT_STATE_IDLE, true ) == false", mObject.index );
	}

	mSkillResurrectionHP = 0;
	mSkillResurrectionMP = 0;
	mIsSkillResurrection = false;
}

void cPlayer::CheatStop( unsigned long duration )
{

	int error = ERROR_CHEAT_STOP_FAIL;

	/// 정지 상태 변경
	if( ChangeState( eOBJECT_STATE_STOP ) == true )
	{

		SetStateStop( eSTOP_CHEATSTOP );

		error = ERROR_CHEAT_STOP_SUCCESS;
		mCheatStopDuration = NETWORK2->GetAccumTime() + duration;
		SetCheatUndeadMode( true );
	}

	/// 정지 메세지 발송
	HANDLE              handle = NULL;
	MSG_ERROR* resMsg = (MSG_ERROR*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_CHEAT, NM_CHEAT_STOP_RES );
	if ( resMsg != NULL )
	{
		resMsg->ErrorCode = error;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_ERROR) );
	}

	/// 성공시 싱크 발송
	if( error == ERROR_CHEAT_STOP_SUCCESS )
	{
		/// 동기
		MSG_SYN_CHEAT_STOP syn;
			syn.Category = NM_CHEAT;
			syn.Protocol = NM_CHEAT_STOP_SYN;
			syn.mObjIndex = mObject.index;
		NETWORK2->QuickSend( this, (char*)&syn, sizeof(syn) );
	}
}

void cPlayer::CheatCancelStop()
{
	int error = ERROR_CHEAT_STOP_FAIL;

	if( GetStateStop() == eSTOP_CHEATSTOP )
	{
		/// 정지 상태 해제
		if( ChangeState( eOBJECT_STATE_IDLE ) == false )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cPlayer[%d]::CheatCancelStop[%d,%d] State ERROR", mObject.index, GetState(), GetStateStop() );
		}

		SetStateStop( eSTOP_NONE );

		error = ERROR_CHEAT_STOP_SUCCESS;
		mCheatStopDuration = 0;
		SetCheatUndeadMode( false );
	}
	else
		return;
		//error = ERROR_CHEAT_STOP_NOT;

	/// 정지 해제 메세지 발송
	HANDLE              handle = NULL;
	MSG_ERROR* resMsg = (MSG_ERROR*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_CHEAT, NM_CHEAT_CANCEL_STOP_RES );
	if ( resMsg != NULL )
	{
		resMsg->ErrorCode = error;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_ERROR) );
	}

	/// 성공시 싱크 발송
	if( error == ERROR_CHEAT_STOP_SUCCESS )
	{
		/// 동기
		MSG_SYN_CHEAT_CANCEL_STOP syn;
			syn.Category = NM_CHEAT;
			syn.Protocol = NM_CHEAT_CANCEL_STOP_SYN;
			syn.mObjIndex = mObject.index;;
		NETWORK2->QuickSend( this, (char*)&syn, sizeof(syn) );
	}
}

void cPlayer::SetCheatSpeedUp( unsigned long speedUp )
{
	if( speedUp >= eCHEATSPEEDUP_MAX )
		return;

	mCheatSpeedUp = (eCHEAT_SPEEDUP)speedUp;

	STATUSCALC->CalcPlayerExtensionGlobal( mObject );
}

void cPlayer::CalcCheatSpeedUp()
{
	switch( mCheatSpeedUp )
	{
	case eCHEATSPEEDUP_50UP:	mStatus2.mMoveSpeed = mStatus2.mMoveSpeed * 1.5f; break;
	case eCHEATSPEEDUP_100UP:	mStatus2.mMoveSpeed = mStatus2.mMoveSpeed * 2.0f; break;
	case eCHEATSPEEDUP_150UP:	mStatus2.mMoveSpeed = mStatus2.mMoveSpeed * 2.5f; break;
	case eCHEATSPEEDUP_200UP:	mStatus2.mMoveSpeed = mStatus2.mMoveSpeed * 3.0f; break;
	case eCHEATSPEEDUP_250UP:	mStatus2.mMoveSpeed = mStatus2.mMoveSpeed * 3.5f; break;
	}
}

/// 호칭관리
void cPlayer::ClearHaveTitleList()
{
	mHaveTitleSet.Clear();
}

bool cPlayer::AddHaveTitle( unsigned long titleIdx )
{
	cTitleDefine* define = TITLEMAN->GetTitleDefine( titleIdx );
	if( !define )
	{
		assert(0);
		return false;
	}

	if( mHaveTitleSet.Insert( titleIdx ) == false )
	{
		assert(0);
		return false;
	}
	return true;
}

/// 클라이언트에 보유호칭 목록 send
bool cPlayer::SendHaveTitleList( ULONG_PTR socketContext )
{
	HANDLE						handle    = NULL;
	MSG_RES_PLAYER_TITLELIST*	sendMsg   = (MSG_RES_PLAYER_TITLELIST*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)socketContext, NM_PLAYER, NM_PLAYER_TITLE_LIST_RES );
	unsigned long				length    = sizeof(MSG_RES_PLAYER_TITLELIST) - sizeof(sendMsg->mTitleIndex);
	unsigned long*              titleList = sendMsg->mTitleIndex;

	cHaveTitleSet::cIterator i = mHaveTitleSet.Begin();
	cHaveTitleSet::cIterator end = mHaveTitleSet.End();
	for( ; i != end; ++i, ++titleList )
	{
		unsigned long idx = (unsigned long)(*i);
		*titleList = idx;

		sendMsg->rowCount++;
	}

	length += (sendMsg->rowCount * sizeof(sendMsg->mTitleIndex));
	return NETWORK2->SendMsgRoot( handle, length );
}

/// 해당 호칭 쏘기
void cPlayer::SendHaveTitle( unsigned long titleIdx )
{
	HANDLE                   handle  = NULL;
	MSG_RES_PLAYER_TITLEADD* sendMsg = (MSG_RES_PLAYER_TITLEADD*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_TITLE_ADD_RES );
	if ( sendMsg !=NULL )
	{
		sendMsg->mTitleIndex = titleIdx;
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_TITLEADD) );
	}
}

bool cPlayer::InitTitle()
{
	cTitleDefine* define = TITLEMAN->GetTitleDefine( mPlayerExrInfo.mTitleIndex );
	if( !define )
	{
		assert(0);
		return false;
	}
	
	if( define->mInfluenceIndex > 0 )
	{
		unsigned long uniqueIdx = SKILLMANAGER->AddInfSwitch( mObject, mObject, define->mInfluenceIndex, 0, false );
		if( uniqueIdx != 0 )
			mTitleUniqIndex = uniqueIdx;
		else
			return false;
	}
	return true;
}

/// 현재 호칭으로 설정
bool cPlayer::ChangeTitle( unsigned long titleIdx )
{
	if( mPlayerExrInfo.mTitleIndex == titleIdx )
		return true;

	/// 효과적용
	if( titleIdx > 0 )
	{
		cTitleDefine* define = TITLEMAN->GetTitleDefine( titleIdx );
		if( !define )
		{
			assert(0);
			return false;
		}

		unsigned long uniqueIdx = SKILLMANAGER->AddInfSwitch( mObject, mObject, define->mInfluenceIndex, 0, true );
		if( uniqueIdx == 0 )
			return false;

		mTitleUniqIndex = uniqueIdx;
	}
	else
	{
		SKILLMANAGER->DeleteInfluence( mObject, mTitleUniqIndex );
		mTitleUniqIndex = 0;
	}

	/// 설정
	mPlayerExrInfo.mTitleIndex = titleIdx;

	/// 다른 유저에게 쏘기
	MSG_SYN_PLAYER_TITLE_SET sendMsg;
		::memset( &sendMsg, 0, sizeof(sendMsg) );
		sendMsg.Category = NM_PLAYER;
		sendMsg.Protocol = NM_PLAYER_TITLE_SET_SYN;
		sendMsg.mCharacterIndex = GetObjectID();
		sendMsg.mTitleIndex = titleIdx;
	NETWORK2->QuickSendExcept( this, (char*)&sendMsg, sizeof(MSG_SYN_PLAYER_TITLE_SET) );
	return true;
}

void cPlayer::SetPartyIndex( unsigned long idx )
{ 
	mPlayerExrInfo.mPartyIndex = idx;

	MSG_SYN_PLAYER_PARTY_SET sendMsg;
		::memset( &sendMsg, 0, sizeof(sendMsg) );
		sendMsg.Category = NM_PLAYER;
		sendMsg.Protocol = NM_PLAYER_PARTY_SET_SYN;
		sendMsg.mCharacterIndex = GetObjectID();
		sendMsg.mPartyIndex = mPlayerExrInfo.mPartyIndex;
	NETWORK2->QuickSendExcept( this, (char*)&sendMsg, sizeof(MSG_SYN_PLAYER_PARTY_SET) );
}

/// 길드 정보 갱신
bool cPlayer::ChangeGuild( unsigned long guildIndex, char position, wchar_t* name, unsigned long markIndex )
{
	mPlayerExrInfo.mGuildIndex = guildIndex;
	mPlayerExrInfo.mGuildPosition = position;
	mPlayerExrInfo.mGuildMarkIndex = markIndex;

	if ( name == NULL )
		memset( &mPlayerExrInfo.mGuildName, 0, sizeof(mPlayerExrInfo.mGuildName) );
	else
		wcsncpy( mPlayerExrInfo.mGuildName, name, MAX_GUILDNAME_BUFFER_SIZE ); 
	
	MSG_SYN_PLAYER_GUILD_SET sendMsg;
		::memset( &sendMsg, 0, sizeof(sendMsg) );
		sendMsg.Category = NM_PLAYER;
		sendMsg.Protocol = NM_PLAYER_GUILD_SET_SYN;
		sendMsg.mCharacterIndex = GetObjectID();
		sendMsg.mGuildIndex = guildIndex;
		sendMsg.mPosition = position;
		sendMsg.mMarkIndex = mPlayerExrInfo.mGuildMarkIndex;
		if( name != 0 )
			wcsncpy( sendMsg.mName, mPlayerExrInfo.mGuildName, MAX_GUILDNAME_BUFFER_SIZE ); 
	NETWORK2->QuickSendExcept( this, (char*)&sendMsg, sizeof(MSG_SYN_PLAYER_GUILD_SET) );
	return true;
}

void cPlayer::ChangeGuildPosition( char position )
{
	mPlayerExrInfo.mGuildPosition = position;
}

void cPlayer::ChangeGuildMarkIndex( unsigned long markIndex )
{
	mPlayerExrInfo.mGuildMarkIndex = markIndex;
}

unsigned short cPlayer::GetJobUsedSkillPoint( unsigned char jobstep )
{
	if( jobstep > mJobUsedSpArr.GetSize() )
	{
		assert(0);
		return 0;
	}
	return mJobUsedSpArr[jobstep];
}

void cPlayer::AddTargetingMonster( unsigned long monsterIdx )
{
	mTargetingMonsterSet.Insert( monsterIdx );
}

void cPlayer::DelTargetingMonster( unsigned long monsterIdx )
{
	mTargetingMonsterSet.Erase( monsterIdx );
}

void cPlayer::ClearTargetingMonster()
{
	cTargetingMonsterSet::cIterator start = mTargetingMonsterSet.Begin();
	cTargetingMonsterSet::cIterator end = mTargetingMonsterSet.End();

	while( start != end )
	{
		unsigned long monsterIdx = (*start++);
		if ( monsterIdx == 0 )
			continue;

		cMonster* mon = GRIDMANAGER->GetMonster( monsterIdx ); //OBJECTMANAGER->GetMonster( monsterIdx );
		if ( mon != NULL )
			mon->DelTakeDamagePlayer( GetObjectID() );
	}
	mTargetingMonsterSet.Clear();
}

void cPlayer::DietargetingMonster()
{
	cTargetingMonsterSet::cIterator start = mTargetingMonsterSet.Begin();
	cTargetingMonsterSet::cIterator end = mTargetingMonsterSet.End();

	while( start != end )
	{
		unsigned long monsterIdx = (*start++);
		if ( monsterIdx == 0 )
			continue;

		cMonster* mon = OBJECTMANAGER->GetMonster( monsterIdx );
		if ( mon != NULL )
			mon->ClearDistressPointPlayer( GetObjectID() );
	}
}

void cPlayer::HealAttackToMonster( unsigned long healerIdx, unsigned long heal, long distressPoint, eTAKEDAMAGE_TYPE type )
{
	cTargetingMonsterSet::cIterator start = mTargetingMonsterSet.Begin();
	cTargetingMonsterSet::cIterator end = mTargetingMonsterSet.End();

	sObject healer = { eOBJECTTYPE_PLAYER, healerIdx };

	while( start != end )
	{
		unsigned long monsterIdx = (*start++);
		if ( monsterIdx == 0 )
			continue;

		cMonster* mon = OBJECTMANAGER->GetMonster( monsterIdx );
		if ( mon != NULL )
			mon->AddTakeDamage( healer, heal, distressPoint, type );
	}
}

/// DB에 저장
bool cPlayer::SaveFortuneData(ULONG_PTR socketContext)
{
	HANDLE          handle        = NULL;
	FORTUNE_UPDATE* fortuneUpdate = (FORTUNE_UPDATE*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_FORTUNE_UPDATE );
	if ( fortuneUpdate != NULL )
	{
		TIMESTAMP_STRUCT* validThru = fortuneUpdate->thru;
		TIMESTAMP_STRUCT* thru      = mFortuneThru;
				
		/// 정보 담기
		for( int i = 0; i < 5; ++i, ++validThru, ++thru )
		{
			(*validThru) = (*thru);
		}

		fortuneUpdate->characterIdx = mObject.index;
		fortuneUpdate->color = mPlayerExrInfo.mTodayColor;
		::wcsncpy( fortuneUpdate->word, mPlayerExrInfo.mTodayWord, MAX_TODAYWORD_BUFFER_SIZE ); 
	}
	return NETWORK2->SendSQL( (PerSocketContext*)socketContext, handle, sizeof(FORTUNE_UPDATE));
}

bool cPlayer::SendInitTodayWord()
{
	if ( wcslen( mPlayerExrInfo.mTodayWord ) > 0 )
	{
		HANDLE                           handle = NULL;
		MSG_RES_PLAYER_TODAYWORD_CHANGE* sendMsg = (MSG_RES_PLAYER_TODAYWORD_CHANGE*)NETWORK2->GetMsgRoot( &handle, mConnectionIdx, NM_PLAYER, NM_PLAYER_TODAYWORD_CHANGE_RES );
		if ( sendMsg != NULL )
		{
			sendMsg->mWordColor = mPlayerExrInfo.mTodayColor;
			sendMsg->ErrorCode = ERROR_PLAYER_TODAYWORD_CHANGE_INIT;
			::wcsncpy( sendMsg->mWord, mPlayerExrInfo.mTodayWord, MAX_TODAYWORD_BUFFER_SIZE ); 
			NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_TODAYWORD_CHANGE) );
		}
	}
	return true;
}

/// 오늘의 한마디 변경
bool cPlayer::ChangeTodayWord( ULONG_PTR socketContext, wchar_t* word, unsigned long color )
{	
	mPlayerExrInfo.mTodayColor = color;

	if ( word == NULL )
		memset( &mPlayerExrInfo.mTodayWord, 0, sizeof(mPlayerExrInfo.mTodayWord) );
	else
		wcsncpy( mPlayerExrInfo.mTodayWord, word, MAX_TODAYWORD_BUFFER_SIZE ); 

	/// HERO에게 쏘기
	HANDLE                           handle = NULL;
	MSG_RES_PLAYER_TODAYWORD_CHANGE* sendMsg = (MSG_RES_PLAYER_TODAYWORD_CHANGE*)NETWORK2->GetMsgRoot( &handle, (PerSocketContext*)socketContext, NM_PLAYER, NM_PLAYER_TODAYWORD_CHANGE_RES  );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = ERROR_PLAYER_TODAYWORD_CHANGE_SUCCESS;
		sendMsg->mWordColor = mPlayerExrInfo.mTodayColor;
		wcsncpy( sendMsg->mWord, mPlayerExrInfo.mTodayWord, MAX_TODAYWORD_BUFFER_SIZE ); 
		NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_TODAYWORD_CHANGE) );
	}

	MSG_SYN_PLAYER_TODAYWORD_CHANGE synMsg;
		memset( &synMsg, 0, sizeof(synMsg) );
		synMsg.Category = NM_PLAYER;
		synMsg.Protocol = NM_PLAYER_TODAYWORD_CHANGE_SYN;
		synMsg.mWordColor = mPlayerExrInfo.mTodayColor;
		synMsg.mCharacterIndex = mObject.index;
		if( word != NULL )
			wcsncpy( synMsg.mWord, word, MAX_TODAYWORD_BUFFER_SIZE );
	NETWORK2->QuickSendExcept( this, (char*)&synMsg, sizeof(MSG_SYN_PLAYER_TODAYWORD_CHANGE) );
	return true;
}

// IsBlock Method.
bool cPlayer::IsBlock( )
{
	if ( mBlockApply == true )
	{
		time_t ltime;
		time( &ltime );

		if ( difftime( mBlockValidThru, ltime ) <= 0 )
			mBlockApply = false;
	}
	return mBlockApply;
}

// SetBlock Method.
bool cPlayer::SetBlock(bool apply, unsigned long second)
{
	mBlockApply = apply;
	if ( mBlockApply == true )
	{
		struct tm validThru;
		time_t    ltime;
		time_t    result;

		time( &ltime );

		validThru = *localtime( &ltime );
		validThru.tm_sec += second;

		result = mktime( &validThru );
		if ( result != (time_t)-1 )
		{
			mBlockValidThru = result;
			return true;
		}
		else
			return false;
	}
	else
		return true;
}
