#include "stdafx.h"
#include "statuscalc_Server.h"
#include "GameSrv.h"
#include "Player.h"
#include "Monster.h"
#include "ObjectManager.h"
#include "StatusScript.h"
#include "Skill_Common.h"
#include "HaveSkillObject.h"
#include "InfluenceObject.h"
#include "SkillManager.h"
#include "SkillScript.h"
#include "MonsterScript.h"

cStatusCalc_Server* cStatusCalc_Server::mpStatusCalc = NULL;


cStatusCalc_Server::cStatusCalc_Server(void)
{
	if( !mpStatusCalc )
	{
		mpStatusCalc = this;
	}
}



cStatusCalc_Server::~cStatusCalc_Server(void)
{
}



void cStatusCalc_Server::CalcPlayerStatusItemSkillAddDetail( eSTATUS_PLUS statusType, float value )
{
	unsigned long arrayNum = 0;

	if( eSTATUSPLUS_STR_PLUS <= statusType && statusType < eSTATUSPLUS_BASE_MAX )
	{
		arrayNum = statusType - eSTATUSPLUS_STR_PLUS;
		mBaseStatusPlus[arrayNum] = mBaseStatusPlus[arrayNum] + value;
	}
	else if( eSTATUSPLUS_ATK_PLUS <= statusType && statusType <= eSTATUSPLUS_SKILL_HP_PLUS_PLUS )
	{
		arrayNum = statusType - eSTATUSPLUS_ATK_PLUS;
		mExtStatusPlus[arrayNum] = mExtStatusPlus[arrayNum] + value;
	}
	/// 공격력 전체를 변경+
	else if( eSTATUSPLUS_ATKTOTAL_PLUS == statusType )
	{
		mExtStatusPlus[ePLAYER_STATUS_EXT_ATK] += value;
		mExtStatusPlus[ePLAYER_STATUS_EXT_RATK] += value;
		mExtStatusPlus[ePLAYER_STATUS_EXT_MATK] += value;			
	}
	/// 방어력 전체를 변경+
	else if( eSTATUSPLUS_DEFTOTAL_PLUS == statusType )
	{
		mExtStatusPlus[ePLAYER_STATUS_EXT_DEF] += value;
		mExtStatusPlus[ePLAYER_STATUS_EXT_MDEF] += value;
	}
	else if( eSTATUSPLUS_ATK_PER <= statusType && statusType <= eSTATUSPLUS_SKILL_HP_PLUS_PER )
	{
		arrayNum = statusType - eSTATUSPLUS_ATK_PER;
		mExtStatusPer[arrayNum] = mExtStatusPer[arrayNum] + value;
	}
	/// 공격력 전체를 변경%
	else if( eSTATUSPLUS_ATKTOTAL_PER == statusType )
	{
		mExtStatusPer[ePLAYER_STATUS_EXT_ATK] += value;
		mExtStatusPer[ePLAYER_STATUS_EXT_RATK] += value;
		mExtStatusPer[ePLAYER_STATUS_EXT_MATK] += value;			
	}
	/// 방어력 전체를 변경%
	else if( eSTATUSPLUS_DEFTOTAL_PER == statusType )
	{
		mExtStatusPer[ePLAYER_STATUS_EXT_DEF] += value;
		mExtStatusPer[ePLAYER_STATUS_EXT_MDEF] += value;
	}
	else if( eSTATUSPLUS_EXPADDPER <= statusType && statusType < eSTATUSPLUS_BUFF_ETC_MAX )
	{
		arrayNum = statusType - eSTATUSPLUS_EXPADDPER;
		mExtAddStatus[arrayNum] = mExtAddStatus[arrayNum] + value;
	}
	else if( eSTATUSPLUS_CHAR_SIZE <= statusType && statusType < eSTATUSPLUS_TAROT_MAX )
	{
		switch( statusType )
		{
			/// 캐릭터 크기 변경
		case eSTATUSPLUS_CHAR_SIZE:		
			mSizePer = mSizePer + (short)FloatToInt(value); 
			break;
		}
	}
	else
	{
		switch( statusType )
		{
		case eSTATUSPLUS_STOPMOVE:	mOddity[eODDITYTYPE_CANTMOVE] += 1; break;
		case eSTATUSPLUS_STOPSKILL:	mOddity[eODDITYTYPE_CANTSKILL] += 1; break;
		case eSTATUSPLUS_SLEEP:		mOddity[eODDITYTYPE_SLEEP] += 1; break;
		case eSTATUSPLUS_STUN:		mOddity[eODDITYTYPE_STUN] += 1; break;
		case eSTATUSPLUS_INVINCIBILITY_PHY:	mOddity[eODDITYTYPE_INVINCIBILITY_PHY] += 1; break;
		case eSTATUSPLUS_INVINCIBILITY_MAG:	mOddity[eODDITYTYPE_INVINCIBILITY_MAG] += 1; break;
		case eSTATUSPLUS_HP_ABSORB_LONG:	mOddity[eODDITYTYPE_HP_ABSORB] += (long)value; break;
		case eSTATUSPLUS_MP_ABSORB_LONG:	mOddity[eODDITYTYPE_MP_ABSORB] += (long)value; break;
		case eSTATUSPLUS_STOPITEM_KIND:		mOddity[eODDITYTYPE_STOPITEM_KIND] += (long)value; break;
		case eSTATUSPLUS_STOPITEM_IDX:		mOddity[eODDITYTYPE_STOPITEM_IDX] += (long)value; break;
		case eSTATUSPLUS_DAMAGE_ABSORB_PHY:	mOddity[eODDITYTYPE_DAMAGE_ABSORB_PHY] += (long)value; break;
		case eSTATUSPLUS_DAMAGE_ABSORB_MAG:	mOddity[eODDITYTYPE_DAMAGE_ABSORB_MAG] += (long)value; break;
		case eSTATUSPLUS_DAMAGE_REFLECT_PHY:	mOddity[eODDITYTYPE_DAMAGE_REFLECTION_PHY] += (long)value; break;
		case eSTATUSPLUS_DAMAGE_REFLECT_MAG:	mOddity[eODDITYTYPE_DAMAGE_REFLECTION_MAG] += (long)value; break;
		}
	}
}



void cStatusCalc_Server::CalcPlayerBaseOnly( cPlayer* pPlayer )
{

	/// 기본 스텟 초기화
	for( unsigned char i = ePLAYER_STATUS_STR ; ePLAYER_STATUS_LEVEL > i ; ++i )
		mBaseStatus[i] = 0.0f;

	/// 스크립트에서 읽어들인 순수한 기본 스텟값 계산
	CalcPlayerStatus( pPlayer );

	/// 기본 스텟 결과를 플레이어에 기록
	for( unsigned char i = ePLAYER_STATUS_STR ; i < ePLAYER_STATUS_LEVEL ; ++i )
		pPlayer->SetStatus1( (ePLAYER_STATUS)i, mBaseStatus[i] );
}



void cStatusCalc_Server::CalcPlayerExtensionOnly( cPlayer* pPlayer )
{

	/// 스텟1 + 초기화
	memset( mBaseStatusPlus, 0, sizeof(mBaseStatusPlus) );

	/// 스텟2 + 초기화
	memset( mExtStatusPlus, 0, sizeof(mExtStatusPlus) );

	/// 스텟2 % 초기화
	memset( mExtStatusPer, 0, sizeof(mExtStatusPer) );

	/// 경험치 관련
	memset( mExtAddStatus, 0, sizeof(mExtAddStatus) );

	/// 상태이상 관련
	memset( mOddity, 0, sizeof(mOddity) );

	/// 스텟 고정 항목 외 상태 변화	
	mSizePer = 0;		/// 캐릭터 크기변화

	/// 스킬효과, 스킬패시브, 아이템 데이타를 읽어와 스텟 수치 적용
	CalcPlayerStatusSkillInfluenceAdd( pPlayer );
	CalcPlayerStatusSkillPassiveAdd( pPlayer );
	CalcPlayerStatusItemAdd( pPlayer );

	pPlayer->StateOddity( mOddity );

	/// 확장 결과를 플레이어에 기록
	memcpy( pPlayer->GetPointerStatus2Plus(), mExtStatusPlus, sizeof(pPlayer->GetPointerStatus2Plus()) * ePLAYER_STATUS_EXT_MAX );
	memcpy( pPlayer->GetPointerStatus2Per(), mExtStatusPer, sizeof(pPlayer->GetPointerStatus2Per()) * ePLAYER_STATUS_EXT_MAX );

	for( unsigned char i = ePLAYER_STATUS_STR ; i < ePLAYER_STATUS_LEVEL ; ++i )
		pPlayer->SetStatus1Plus( (ePLAYER_STATUS)i, mBaseStatusPlus[i] );

	for( unsigned char i = ePLAYER_STATUS_EXT_ADD_EXPADDPER ; i < ePLAYER_STATUS_EXT_ADD_MAX ; ++i )
		pPlayer->SetStatusEtc( (ePLAYER_STATUS_EXT_ADD)i, mExtAddStatus[i] );
}



unsigned long cStatusCalc_Server::GetParantJob( unsigned long job, unsigned int jobPos )
{

	unsigned long jobCode;

	/// 현재 직업의 부모 직업들을 배열로 받아온다.
	tArray<unsigned long>* pParentJob = NULL;

	/// 상위 직업 코드를 읽어옮
	pParentJob = STATUSSCRIPT->GetParentJob( job );

	/// 데이터가 없다 - 현재 직업이 기본 직업 이다.
	if( pParentJob == NULL )
		return NULL;

	/// 사이즈보다 크다 - 현재 직업이 최종 직업이다.
	if( jobPos > pParentJob->GetSize() - 1 )
		return NULL;

	/// 전직 차수에 해당하는 직업의 코드드를 읽어간다.
	pParentJob->GetAt( &jobCode, jobPos );

	return jobCode;
}



void cStatusCalc_Server::CalcPlayerStatusSkillInfluenceAdd( cPlayer* pPlayer )
{
	cHashSet::cIterator iter;

	/// 서버에서의 효과 증가치 계산
	cHashSet* influenceSet = pPlayer->GetInfluenceSet();
	if( influenceSet == NULL )
		return;

	cInfluenceObject* pInfluence = NULL;
	sInfluenceScript* pInfluenceScript = NULL;
	unsigned long infIdx = 0;
	for( iter = influenceSet->Begin() ; iter != influenceSet->End() ; ++iter )
	{
		infIdx = *iter;
		pInfluence = SKILLMANAGER->GetInfluence( infIdx );
		if( pInfluence == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcPlayerStatusSkillInfluenceAdd pInfluence[%d]==NULL", *iter);
			//-/-//SKILLMANAGER->InsertDeleteInfluenceObject( pInfluence );	/// 객체 삭제 대기열에 넣는다.
			continue;
		}		

		pInfluenceScript = SKILLSCRIPT->GetInfluenceInfo( pInfluence->GetInfluenceClassIdx() );
		/// 지속(데미지) 적인 효과가 아닌경우만
		if( pInfluenceScript->mTime1 == 0 )
		{
			if( pInfluenceScript->mStatusPlusIdx1 != 0 )
			{
				CalcPlayerStatusItemSkillAddDetail( (eSTATUS_PLUS)(pInfluenceScript->mStatusPlusIdx1),
					(float)(pInfluenceScript->mValue1) );
			}
		}
		if( pInfluenceScript->mTime2 == 0 )
		{
			if( pInfluenceScript->mStatusPlusIdx2 != 0 )
			{
				CalcPlayerStatusItemSkillAddDetail( (eSTATUS_PLUS)(pInfluenceScript->mStatusPlusIdx2),
					(float)(pInfluenceScript->mValue2) );
			}
		}
		if( pInfluenceScript->mStatusPlusIdx3 != 0 )
		{
			CalcPlayerStatusItemSkillAddDetail( (eSTATUS_PLUS)(pInfluenceScript->mStatusPlusIdx3),
				(float)(pInfluenceScript->mValue3) );
		}
	}
}



void cStatusCalc_Server::CalcPlayerStatusSkillPassiveAdd( cPlayer* pPlayer )
{
	/// 서버에서의 효과 증가치 계산
	cPointerHashMap::cIterator iter;

	cPointerHashMap* pHaveMap = (cPointerHashMap*)SKILLMANAGER->GetPlayerHaveSkill( pPlayer->GetObjectID() );
	if( pHaveMap == NULL )
		return;

	cHaveSkillObject* pSkillObject = NULL;
	sPlayerSkillBaseInfo* pSkillInfo = NULL;
	sPlayerSkillStepInfo* pSkillStep = NULL;
	for( iter = pHaveMap->Begin() ; iter != pHaveMap->End() ; ++iter )
	{

		pSkillObject = (cHaveSkillObject*)((*iter).mSecond);
		if( pSkillObject == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcPlayerStatusSkillPassiveAdd pSkillObject == NULL");
			continue;
		}

		pSkillInfo = SKILLSCRIPT->GetPlayerSkillInfo( pSkillObject->GetSkillIdx() );
		if( pSkillInfo == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcPlayerStatusSkillPassiveAdd pSkillInfo == NULL");
			continue;
		}

		if( pSkillInfo->mType != eSKILLTYPE_PASSIVE )
			continue;

		pSkillStep = &pSkillInfo->mpSetpInfoArray[pSkillObject->GetSkillStep()];
		if( pSkillStep == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcPlayerStatusSkillPassiveAdd pSkillStep == NULL");
			continue;
		}


		/// 내 장비가 맞는지 체크
		bool IsEquipment = true;
		if( pSkillInfo->mUseEquipment )
		{
			eWEAPON_STATE weaponState = pPlayer->GetWeaponState();

			/// 착용 장비 체크
			switch( pSkillInfo->mUseEquipment )
			{
			case eEQUIPTYPE_SWORD:
				if( weaponState != eWEAPON_STATE_SWORD &&
					weaponState != eWEAPON_STATE_SWORD_SHEILD )
					IsEquipment = false;
				break;
			case eEQUIPTYPE_LONGSWORD:
				if( weaponState != eWEAPON_STATE_LONGSWORD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_DOUBLESWORD:
				if( weaponState != eWEAPON_STATE_DOUBLESWORD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_SHORTSWORD:
				if( weaponState != eWEAPON_STATE_SHORTSWORD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_GUN:
				if( weaponState != eWEAPON_STATE_GUN )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_STAFF:
				if( weaponState != eWEAPON_STATE_STAFF )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_SHIELD:
				if( weaponState != eWEAPON_STATE_SHEILD &&
					weaponState != eWEAPON_STATE_SWORD_SHEILD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_SWORD_LONGSWORD:
				if( weaponState != eWEAPON_STATE_SWORD &&
					weaponState != eWEAPON_STATE_SWORD_SHEILD &&
					weaponState != eWEAPON_STATE_LONGSWORD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_SWORD_DOUBLESWORD:
				if( weaponState != eWEAPON_STATE_SWORD &&
					weaponState != eWEAPON_STATE_SWORD_SHEILD &&
					weaponState != eWEAPON_STATE_DOUBLESWORD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_LONGSWORD_DOUBLESWORD:
				if( weaponState != eWEAPON_STATE_LONGSWORD &&
					weaponState != eWEAPON_STATE_DOUBLESWORD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_SWORD_LONGSWORD_DOUBLESWORD:
				if( weaponState != eWEAPON_STATE_SWORD &&
					weaponState != eWEAPON_STATE_SWORD_SHEILD &&
					weaponState != eWEAPON_STATE_LONGSWORD &&
					weaponState != eWEAPON_STATE_DOUBLESWORD )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_SHORTSWORD_GUN:
				if( weaponState != eWEAPON_STATE_SHORTSWORD &&
					weaponState != eWEAPON_STATE_GUN )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_CLOTH:
				if( pPlayer->GetArmorSets() != ARMOR_SETS_ROBES )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_LIGHTARMOR:
				if( pPlayer->GetArmorSets() != ARMOR_SETS_LIGHT_ARMOR )
					IsEquipment =  false;
				break;
			case eEQUIPTYPE_HEAVYARMOR:
				if( pPlayer->GetArmorSets() != ARMOR_SETS_HEAVY_ARMOR )
					IsEquipment =  false;
				break;
			}
		}

		/// 장비가 맞는 경우만 계산
		if( IsEquipment == true )
		{
			if( pSkillStep->mApplyValueType1 != 0 )
			{
				CalcPlayerStatusItemSkillAddDetail( (eSTATUS_PLUS)(pSkillStep->mApplyValueType1),
					(float)(pSkillStep->mApplyValue1) );
			}
			if( pSkillStep->mApplyValueType2 != 0 )
			{
				CalcPlayerStatusItemSkillAddDetail( (eSTATUS_PLUS)(pSkillStep->mApplyValueType2),
					(float)(pSkillStep->mApplyValue2) );
			}
		}
	}
}



void cStatusCalc_Server::CalcPlayerStatusItemAdd( cPlayer* pPlayer )
{
	sEquipAbility* pEquipAbility = pPlayer->GetEquipAbility();
	if( pEquipAbility == NULL )
		return;

	/// 스텟 계산 마지막 시점에 계산되는 항목은 제외
	if( pEquipAbility->mOptStr != 0 )	/// 증가:힘
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_STR_PLUS, (float)pEquipAbility->mOptStr );
	}
	if( pEquipAbility->mOptDex != 0 )	/// 증가:민첩
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_DEX_PLUS, (float)pEquipAbility->mOptDex );
	}
	if( pEquipAbility->mOptCon != 0 )	/// 증가:체력
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_CON_PLUS, (float)pEquipAbility->mOptCon );
	}
	if( pEquipAbility->mOptInt != 0 )	/// 증가:지능
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_INT_PLUS, (float)pEquipAbility->mOptInt );
	}
	if( pEquipAbility->mOptWis != 0 )	/// 증가:지혜
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_WIS_PLUS, (float)pEquipAbility->mOptWis );
	}

	if( pEquipAbility->mOptPlusWei != 0 )	/// 증가:무게
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_WEIGHT_PLUS, (float)pEquipAbility->mOptPlusWei );
	}
	if( pEquipAbility->mOptPlusMov != 0 )	/// 증가:이동속도
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_SPD_PLUS, (float)pEquipAbility->mOptPlusMov );
	}
	if( pEquipAbility->mOptPlusMaxHp != 0 )	/// 증가:최대HP 
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_HP_PLUS, (float)pEquipAbility->mOptPlusMaxHp );
	}
	if( pEquipAbility->mOptPlusMaxMp != 0 )	/// 증가:최대MP 
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_MP_PLUS, (float)pEquipAbility->mOptPlusMaxMp );
	}

	if( pEquipAbility->mOptPercentDrainHp != 0 )	// HP 흡수
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_HP_ABSORB_LONG, (float)pEquipAbility->mOptPercentDrainHp );
	}
	if( pEquipAbility->mOptPercentDrainMp != 0 )	// MP 흡수
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_MP_ABSORB_LONG, (float)pEquipAbility->mOptPercentDrainMp );
	}
	if( pEquipAbility->mOptPercentDamageDec != 0 )	// 데미지 흡수/감소
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_DAMAGE_ABSORB_PHY, (float)pEquipAbility->mOptPercentDamageDec );
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_DAMAGE_ABSORB_MAG, (float)pEquipAbility->mOptPercentDamageDec );
	}
	if( pEquipAbility->mOptPercentDamageRef != 0 )	// 데미지 반사
	{
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_DAMAGE_REFLECT_PHY, (float)pEquipAbility->mOptPercentDamageRef );
		CalcPlayerStatusItemSkillAddDetail( eSTATUSPLUS_DAMAGE_REFLECT_MAG, (float)pEquipAbility->mOptPercentDamageRef );
	}
}



void cStatusCalc_Server::CalcStatusPlusDetailMonster( unsigned long monsterIdx, eSTATUS_PLUS statusType, float value )
{
	/// 몬스터 정보를 가져옴
	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster == NULL ) 
	{ 
		assert(NULL);
		NETWORK2->PostServerEvent("cStatusCalc_Server::CalcStatusPlusDetailMonster pMonster == NULL");
		return; 
	}

	switch( statusType )
	{
	case eSTATUSPLUS_ATK_PLUS:	{ pMonster->GetStatusPlus()->mPhysicAttack += value; }break;
	case eSTATUSPLUS_RATK_PLUS:	{ pMonster->GetStatusPlus()->mPhysicRangeAttack += value; }break;
	case eSTATUSPLUS_DEF_PLUS:	{ pMonster->GetStatusPlus()->mPhysicDefense += value; }break;
	case eSTATUSPLUS_MATK_PLUS:	{ pMonster->GetStatusPlus()->mMagicAttack += value; }break;
	case eSTATUSPLUS_MDEF_PLUS:	{ pMonster->GetStatusPlus()->mMagicDefense += value; }break;
	case eSTATUSPLUS_CRI_PLUS:	{ pMonster->GetStatusPlus()->mPhysicCritical += value; }break;
	case eSTATUSPLUS_MCRI_PLUS:	{ pMonster->GetStatusPlus()->mMagicCritical += value; }break;
	case eSTATUSPLUS_HP_PLUS:	{ pMonster->GetStatusPlus()->mMaxHP += value; } break;
	case eSTATUSPLUS_MP_PLUS:	{ pMonster->GetStatusPlus()->mMaxMP += value; } break;
	case eSTATUSPLUS_HIT_PLUS:	{ pMonster->GetStatusPlus()->mPhysicAttackRate += value; }break;
	case eSTATUSPLUS_MHIT_PLUS:	{ pMonster->GetStatusPlus()->mMagicAttackRate += value; }break;
	case eSTATUSPLUS_AVOID_PLUS:	{ pMonster->GetStatusPlus()->mAvoid += value; }break;
	case eSTATUSPLUS_ASPD_PLUS:	{ pMonster->GetStatusPlus()->mAttackSpeed += value; }break;
	case eSTATUSPLUS_COOLTIME_PLUS:	{ pMonster->GetStatusPlus()->mCooltime += value; }break;
	case eSTATUSPLUS_RANGE_PLUS:	{ pMonster->GetStatusPlus()->mInRange += value; }break;
	case eSTATUSPLUS_RRANGE_PLUS:	{ pMonster->GetStatusPlus()->mOutRange += value; }break;
	case eSTATUSPLUS_SPD_PLUS:	{ pMonster->GetStatusPlus()->mMoveSpeed += value; }break;

	case eSTATUSPLUS_ATK_PER:	{ pMonster->GetStatusPer()->mPhysicAttack += value; }break;
	case eSTATUSPLUS_RATK_PER:	{ pMonster->GetStatusPer()->mPhysicRangeAttack += value; }break;
	case eSTATUSPLUS_DEF_PER:	{ pMonster->GetStatusPer()->mPhysicDefense += value; }break;
	case eSTATUSPLUS_MATK_PER:	{ pMonster->GetStatusPer()->mMagicAttack += value; }break;
	case eSTATUSPLUS_MDEF_PER:	{ pMonster->GetStatusPer()->mMagicDefense += value; }break;
	case eSTATUSPLUS_CRI_PER:	{ pMonster->GetStatusPer()->mPhysicCritical += value; }break;
	case eSTATUSPLUS_MCRI_PER:	{ pMonster->GetStatusPer()->mMagicCritical += value; }break;
	case eSTATUSPLUS_HP_PER:	{ pMonster->GetStatusPer()->mMaxHP += value; } break;
	case eSTATUSPLUS_MP_PER:	{ pMonster->GetStatusPer()->mMaxMP += value; } break;
	case eSTATUSPLUS_HIT_PER:	{ pMonster->GetStatusPer()->mPhysicAttackRate += value; }break;
	case eSTATUSPLUS_MHIT_PER:	{ pMonster->GetStatusPer()->mMagicAttackRate += value; }break;
	case eSTATUSPLUS_AVOID_PER:	{ pMonster->GetStatusPer()->mAvoid += value; }break;
	case eSTATUSPLUS_ASPD_PER:	{ pMonster->GetStatusPer()->mAttackSpeed += value; }break;
	case eSTATUSPLUS_COOLTIME_PER:	{ pMonster->GetStatusPer()->mCooltime += value; }break;
	case eSTATUSPLUS_RANGE_PER:	{ pMonster->GetStatusPer()->mInRange += value; }break;
	case eSTATUSPLUS_RRANGE_PER:	{ pMonster->GetStatusPer()->mOutRange += value; }break;
	case eSTATUSPLUS_SPD_PER:	{ pMonster->GetStatusPer()->mMoveSpeed += value; }break;

	case eSTATUSPLUS_STOPMOVE:	mOddity[eODDITYTYPE_CANTMOVE] += 1; break;
	case eSTATUSPLUS_STOPSKILL:	mOddity[eODDITYTYPE_CANTSKILL] += 1; break;
	case eSTATUSPLUS_SLEEP:		mOddity[eODDITYTYPE_SLEEP] += 1; break;
	case eSTATUSPLUS_STUN:		mOddity[eODDITYTYPE_STUN] += 1; break;
	case eSTATUSPLUS_INVINCIBILITY_PHY:	mOddity[eODDITYTYPE_INVINCIBILITY_PHY] += 1; break;
	case eSTATUSPLUS_INVINCIBILITY_MAG:	mOddity[eODDITYTYPE_INVINCIBILITY_MAG] += 1; break;
	case eSTATUSPLUS_HP_ABSORB_LONG:	mOddity[eODDITYTYPE_HP_ABSORB] += (long)value; break;
	case eSTATUSPLUS_MP_ABSORB_LONG:	mOddity[eODDITYTYPE_MP_ABSORB] += (long)value; break;
	case eSTATUSPLUS_STOPITEM_KIND:		mOddity[eODDITYTYPE_STOPITEM_KIND] += (long)value; break;
	case eSTATUSPLUS_STOPITEM_IDX:		mOddity[eODDITYTYPE_STOPITEM_IDX] += (long)value; break;
	case eSTATUSPLUS_DAMAGE_ABSORB_PHY:	mOddity[eODDITYTYPE_DAMAGE_ABSORB_PHY] += (long)value; break;
	case eSTATUSPLUS_DAMAGE_ABSORB_MAG:	mOddity[eODDITYTYPE_DAMAGE_ABSORB_MAG] += (long)value; break;
	case eSTATUSPLUS_DAMAGE_REFLECT_PHY:	mOddity[eODDITYTYPE_DAMAGE_REFLECTION_PHY] += (long)value; break;
	case eSTATUSPLUS_DAMAGE_REFLECT_MAG:	mOddity[eODDITYTYPE_DAMAGE_REFLECTION_MAG] += (long)value; break;
	}
}



void cStatusCalc_Server::CalcMonster( unsigned long monsterIdx )
{

	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster == NULL )	
	{ 
		NETWORK2->PostServerEvent("cStatusCalc_Server::CalcMonster pMonster[%d] == NULL", monsterIdx );
		return; 
	}

	mMoveSpeed = pMonster->GetMoveSpeed();

	/// 몬스터에 걸린 효과 정보를 읽어옴
	cHashSet* influenceSet = pMonster->GetInfluenceSet();
	if( influenceSet == NULL )
		return;

	sMonsterStatus* pStatusPlus = pMonster->GetStatusPlus();
	if( pStatusPlus == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cStatusCalc_Server::CalcMonster pStatusPlus == NULL" );
		return;
	}

	sMonsterStatus* pStatusPer = pMonster->GetStatusPer();
	if( pStatusPer == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cStatusCalc_Server::CalcMonster pStatusPer == NULL" );
		return;
	}

	memset( pStatusPlus, 0, sizeof(sMonsterStatus) );
	memset( pStatusPer, 0, sizeof(sMonsterStatus) );
	memset( mOddity, 0, sizeof(mOddity) );

	cInfluenceObject* pInfluence = NULL;
	sInfluenceScript* pInfluenceScript = NULL;

	cHashSet::cIterator iter = influenceSet->Begin();
	cHashSet::cIterator end = influenceSet->End();

	while( iter != end )
	{
		pInfluence = SKILLMANAGER->GetInfluence( *iter++ );
		if( pInfluence == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcMonster pInfluence == NULL" );
			//-/-//SKILLMANAGER->InsertDeleteInfluenceObject( pInfluence );	/// 객체 삭제 대기열에 넣는다.
			continue;
		}		

		pInfluenceScript = SKILLSCRIPT->GetInfluenceInfo( pInfluence->GetInfluenceClassIdx() );
		/// 지속(데미지) 적인 효과가 아닌경우만
		if( pInfluenceScript->mTime1 == 0 )
		{
			if( pInfluenceScript->mStatusPlusIdx1 != 0 )
			{
				CalcStatusPlusDetailMonster( monsterIdx, (eSTATUS_PLUS)(pInfluenceScript->mStatusPlusIdx1),
					(float)(pInfluenceScript->mValue1) );
			}
		}
		if( pInfluenceScript->mStatusPlusIdx2 != 0 )
		{
			CalcStatusPlusDetailMonster( monsterIdx, (eSTATUS_PLUS)(pInfluenceScript->mStatusPlusIdx2),
				(float)(pInfluenceScript->mValue2) );
		}
		if( pInfluenceScript->mStatusPlusIdx3 != 0 )
		{
			CalcStatusPlusDetailMonster( monsterIdx, (eSTATUS_PLUS)(pInfluenceScript->mStatusPlusIdx3),
				(float)(pInfluenceScript->mValue3) );
		}
	}

	pMonster->StateOddity( mOddity );

	/// 이동 속도가 변한경우 클라이언트에 전송
	if( pMonster->GetMoveSpeed() != mMoveSpeed )
	{
		pMonster->SendMoveSpeed();
	}
}


void cStatusCalc_Server::CalcPlayerStatus( cPlayer* pPlayer )
{
	unsigned char level = pPlayer->GetLevel();
	ePLAYER_JOB job = pPlayer->GetJob();

	/// 전직클래스1 기준 레벨 
	unsigned char class1 = 30;
	/// 전직클래스2 기준 레벨
	unsigned char class2 = 70;

	sStatusBaseScript* pStatusBase = NULL;

	/// 자기 자신에 해당하는 직업의 statusbase 정보를 가져옴
	pStatusBase = STATUSSCRIPT->GetStatusBaseInfo( job );

	/// 자신에 해당하는 직업이 없으면 상위 직업을 읽어와서 처리한다.
	if( pStatusBase == NULL )
	{
		/// 상위 직업 배열을 읽어온다.
		tArray<unsigned long>* playerJobParent = STATUSSCRIPT->GetParentJob( job );
		if( playerJobParent == NULL )
		{
			/// 자신의 직업도 없는데 부모의 직업도 없으면 오류
			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcPlayerStatus parantJob[%d] == NULL", job );
			return; 
		}
		else
		{

			/// 상위 직업이 있으면 
			for( int i = playerJobParent->GetSize() - 1 ; 0 <= i  ; --i )
			{
				/// 상위 직업코드로 스크립트 값을 읽어옴
				pStatusBase = STATUSSCRIPT->GetStatusBaseInfo( (*playerJobParent)[i] );
				if( pStatusBase != NULL )
				{
					break;
				}
			}

			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcPlayerStatus playerJobParentAry == NULL" );
		}
	}

	/// 입력 값 기본 스텟 수치로 적용 
	for( unsigned char i = ePLAYER_STATUS_STR ; i < ePLAYER_STATUS_LEVEL ; ++i )
	{
		mBaseStatus[i] = pStatusBase->mBaseStatus[i];
	}

	/// 전직 차수와 레벨에 따른 실제 적용 받아야할 직업코드
	unsigned long fixjob = job;

	sStatusMultiplScript* pStatusMultipl;

	/// 현재 레벨에서 1레벨까지 스텟 계산 ( 0레벨은 기본값 1레벨부터 증가치 )
	for( unsigned char j = 1 ; j <= level ; ++j )
	{

		/// 1레벨에서 1차전직레벨(30)전까지 기본직업 코드로 스텟 계산
		if( ( 1 <= j ) && ( j < class1 ) )
		{
			/// 상위 직업 리스트에서 1차전직 직업을 가져옴
			fixjob = GetParantJob( job, 0 );
			/// 없는 경우 현재 직업이 기본 직업이라 부모가 없는 경우임
			if( fixjob == NULL )
			{
				/// 현재 직업을 스텟 계산 기준 코드로 등록
				fixjob = job;
			}
		}

		/// 1차전직레벨(30)에서 2차전직레벨(70)까지 1차전직
		else if( ( class1 <= j ) && ( j < class2 ) )
		{
			/// 상위 직업 리스트에서 1차전직 직업을 가져옴
			fixjob = GetParantJob( job, 1 );
			/// 없는 경우 현재 직업이 1차 직업임
			if( fixjob == NULL )
			{
				fixjob = job;
			}
		}

		/// 2차전직레벨(30)에서 레벨 한계점 까지
		else if( class2 <= j )
		{
			/// 상위 직업 리스트에서 2차전직 직업을 가져옴
			fixjob = GetParantJob( job, 2 );
			/// 없는 경우 현재 직업이 2차 직업임
			if( fixjob == NULL )
			{
				fixjob = job;
			}
		}

		/// 현재 레벨에 맞는 직업 코드로 스텟 증가치를 얻어옴
		pStatusMultipl = STATUSSCRIPT->GetStatusMultiplInfo( fixjob );
		if( pStatusMultipl == NULL )
		{
			assert(NULL);
			NETWORK2->PostServerEvent("cStatusCalc_Server::CalcPlayerStatus pStatusMultipl[%d] == NULL", fixjob);
			return;
		}

		for( unsigned char i = ePLAYER_STATUS_STR ; i < ePLAYER_STATUS_LEVEL ; ++i )
		{
			/// 클래스기준에 따른 증가치 만큼 증가
			mBaseStatus[i] = mBaseStatus[i] + pStatusMultipl->mBaseStatus[i];
		}
	}
}



void cStatusCalc_Server::CalcPlayerExtStatus( cPlayer* pPlayer, ePLAYER_STATUS_EXT statusExt )
{
	ePLAYER_JOB job = pPlayer->GetJob();

	/// 플레이어의 스텟1 최종값을 읽어옴 + 레벨값까지
	float baseStatus[ePLAYER_STATUS_LEVEL_MAX];
	unsigned char i = ePLAYER_STATUS_STR;
	for( ; i < ePLAYER_STATUS_LEVEL ; ++i )
	{
		baseStatus[i] = mBaseStatus[i] + mBaseStatusPlus[i];
		if( baseStatus[i] < 0.0f )
			baseStatus[i] = 0.0f;
	}
	baseStatus[i] = pPlayer->GetLevel(); 

	/// 스텟의 계산 결과 값
	float			statusValue = 0.0f;

	/// 자연회복HP/MP 기본값은 [캐릭터스탯회복수치]에 더하지 않고 자연회복계산시 사용한다.
	if( statusExt == ePLAYER_STATUS_EXT_RECOVHP || statusExt == ePLAYER_STATUS_EXT_RECOVMP )
	{
		statusValue = 0.0f;
	}
	else
	{
		/// 자신의 직업과 스텟 구분이랑 같은 것이 있는지 체크
		statusValue = STATUSSCRIPT->GetStatusExtBaseInfo( statusExt, job );
		//if( FloatToInt(statusValue) == 0 )
		//{
		//	if( playerJobParent != 0 )
		//	{
		//		/// 상위 직업이 있으면 
		//		for( int i = playerJobParent->GetSize() - 1 ; 0 <= i  ; --i )
		//		{
		//			/// 상위 직업코드로 스크립트 값을 읽어옴
		//			statusValue = STATUSSCRIPT->GetStatusExtBaseInfo( statusExt, (*playerJobParent)[i] );

		//			if( FloatToInt(statusValue) != 0 )
		//			{
		//				break;
		//			}
		//		}
		//	}
		//	else
		//	{
		//		statusValue = 0.0f;
		//	}
		//}
	}


	/// 플레이어의 최상위(1차) 직업 기록
	tArray<unsigned long>* playerJobParent = STATUSSCRIPT->GetParentJob( job );
	unsigned long firstJob = job;
	if( playerJobParent != NULL )
		firstJob = (*playerJobParent)[0];

	/// 1차 직업에 따라 공격력 스텟 비중치 읽어올 항목값 설정
	unsigned long plusStatus = 0;
	switch( firstJob )
	{		
	case ePLAYER_FIGHTER:	/// 파이터
	case ePLAYER_WARRIOR:	/// 워리어
		plusStatus = 0;
		break;
	case ePLAYER_RANGER:	/// 레인져
	case ePLAYER_ROGUE:		/// 로그
		plusStatus = 1;
		break;
	case ePLAYER_MAGE:		/// 메이지
	case ePLAYER_SHAMAN:	/// 샤먼
		plusStatus = 2;
		break;
	}

	float importance = 0.0f;
	for( unsigned char i = ePLAYER_STATUS_STR ; i < ePLAYER_STATUS_LEVEL_MAX ; ++i )
	{
        eEXT_MULTIPL chgStatusExt = eEXT_MULTIPL_MAX;
		switch( statusExt )
		{
		case ePLAYER_STATUS_EXT_ATK:
            chgStatusExt = (eEXT_MULTIPL)( eEXT_MULTIPL_ATK_FIGHTER + plusStatus );	/// 근거리, 원거리, 마법 직업에 따른 공격력 항목 설정
			break;
		case ePLAYER_STATUS_EXT_RATK:
			chgStatusExt = (eEXT_MULTIPL)( eEXT_MULTIPL_RATK_FIGHTER + plusStatus );
			break;
		case ePLAYER_STATUS_EXT_MATK:
			chgStatusExt = (eEXT_MULTIPL)( eEXT_MULTIPL_MATK_FIGHTER + plusStatus );
			break;
		case ePLAYER_STATUS_EXT_DEF:		chgStatusExt = eEXT_MULTIPL_DEF;		break;	/// 물리 방어력
		case ePLAYER_STATUS_EXT_MDEF:		chgStatusExt = eEXT_MULTIPL_MDEF;		break;	/// 마법 방어력
		case ePLAYER_STATUS_EXT_CRI:		chgStatusExt = eEXT_MULTIPL_CRI;		break;	/// 물리 크리티컬 수치
		case ePLAYER_STATUS_EXT_MCRI:		chgStatusExt = eEXT_MULTIPL_MCRI;		break;	/// 마법 크리티컬 수치
		case ePLAYER_STATUS_EXT_MAXHP:		chgStatusExt = eEXT_MULTIPL_MAXHP;		break;	/// HP 
		case ePLAYER_STATUS_EXT_MAXMP:		chgStatusExt = eEXT_MULTIPL_MAXMP;		break;	/// MP
		case ePLAYER_STATUS_EXT_RECOVHP:	chgStatusExt = eEXT_MULTIPL_RECOVHP;	break;	/// HP 회복 속도
		case ePLAYER_STATUS_EXT_RECOVMP:	chgStatusExt = eEXT_MULTIPL_RECOVMP;	break;	/// MP 회복 속도
		case ePLAYER_STATUS_EXT_HIT:		chgStatusExt = eEXT_MULTIPL_HIT;		break;	/// 명중 수치
		case ePLAYER_STATUS_EXT_MHIT:		chgStatusExt = eEXT_MULTIPL_MHIT;		break;	/// 마법 명중
		case ePLAYER_STATUS_EXT_AVOID:		chgStatusExt = eEXT_MULTIPL_AVOID;		break;	/// 회피 수치
		case ePLAYER_STATUS_EXT_WEIGHT:		chgStatusExt = eEXT_MULTIPL_WEIGHT;		break;	/// 무게 
		case ePLAYER_STATUS_EXT_RANGE:		chgStatusExt = eEXT_MULTIPL_RANGE;		break;	/// 사정거리
		case ePLAYER_STATUS_EXT_RRANGE:		chgStatusExt = eEXT_MULTIPL_RRANGE;		break;	/// 방출혈사정거리
		case ePLAYER_STATUS_EXT_COOLTIME:	chgStatusExt = eEXT_MULTIPL_COOLTIME;	break;	/// 쿨타임
		case ePLAYER_STATUS_EXT_SPD:		chgStatusExt = eEXT_MULTIPL_SPD;		break;	/// 이동속도
		case ePLAYER_STATUS_EXT_ASPD:		chgStatusExt = eEXT_MULTIPL_ASPD;		break;	/// 공격 속도	
		}

		/// 베이스 스텟을 비중과 곱한다.
		importance = STATUSSCRIPT->GetStatusExtMultiplInfo( chgStatusExt, (ePLAYER_STATUS)i );
		statusValue += baseStatus[i] * importance;
	}

	/// 기준 이동 속도가 이미 설정돼있는 경우(몬스터변신) 처리
	if( statusExt == ePLAYER_STATUS_EXT_SPD && pPlayer->GetMoveSpeed() != 0 )
		mExtStatus[statusExt] = (float)pPlayer->GetMoveSpeed();
	else	/// 결과 값을 기록
		mExtStatus[statusExt] = mExtStatus[statusExt] + statusValue;

	if( mExtStatus[statusExt] < 0.0f ) 
		mExtStatus[statusExt] = 0.0f;
}



void cStatusCalc_Server::CalcPlayerInit( sObject object )
{
	cPlayer* pPlayer = OBJECTMANAGER->GetPlayer( object.index );
	if( pPlayer != NULL )
	{
		mMoveSpeed = pPlayer->GetMoveSpeed();
		mAttackSpeed = pPlayer->GetStatus2()->mAttackSpeed;
		mSizePer = pPlayer->GetFixedObjectSizePer();
		mMaxHP = pPlayer->GetMaxHP();
		mMaxMP = pPlayer->GetMaxMP();

		pPlayer->InitStatus2();

		CalcPlayerBaseOnly( pPlayer );
		CalcPlayerExtensionOnly( pPlayer );
		CalcPlayerGlobal( pPlayer );
	}
}



void cStatusCalc_Server::CalcPlayerBaseGlobal( sObject object )
{
	cPlayer* pPlayer = OBJECTMANAGER->GetPlayer( object.index );
	if( pPlayer != NULL )
	{
		mMoveSpeed = pPlayer->GetMoveSpeed();
		mAttackSpeed = pPlayer->GetStatus2()->mAttackSpeed;
		mSizePer = pPlayer->GetFixedObjectSizePer();
		mMaxHP = pPlayer->GetMaxHP();
		mMaxMP = pPlayer->GetMaxMP();

		pPlayer->InitStatus2();

		mBaseStatusPlus[ePLAYER_STATUS_STR] = (float)pPlayer->GetStatus1Plus()->mStr;
		mBaseStatusPlus[ePLAYER_STATUS_DEX] = (float)pPlayer->GetStatus1Plus()->mDex;
		mBaseStatusPlus[ePLAYER_STATUS_CON] = (float)pPlayer->GetStatus1Plus()->mCon;
		mBaseStatusPlus[ePLAYER_STATUS_INT] = (float)pPlayer->GetStatus1Plus()->mInt;
		mBaseStatusPlus[ePLAYER_STATUS_WIS] = (float)pPlayer->GetStatus1Plus()->mWis;

		memcpy( mExtStatusPlus, pPlayer->GetPointerStatus2Plus(), sizeof(mExtStatusPlus) );
		memcpy( mExtStatusPer, pPlayer->GetPointerStatus2Per(), sizeof(mExtStatusPer) );

		mExtAddStatus[ePLAYER_STATUS_EXT_ADD_EXPADDPER] = (float)pPlayer->GetStatusEtc()->mExpAddPer;
		mExtAddStatus[ePLAYER_STATUS_EXT_ADD_EXPADDPLUS] = (float)pPlayer->GetStatusEtc()->mExpAddPlus; 
		mExtAddStatus[ePLAYER_STATUS_EXT_ADD_SXPADDPER] = (float)pPlayer->GetStatusEtc()->mSxpAddPer;
		mExtAddStatus[ePLAYER_STATUS_EXT_ADD_SXPADDPLUS] = (float)pPlayer->GetStatusEtc()->mSxpAddPlus;

		CalcPlayerBaseOnly( pPlayer );
		CalcPlayerGlobal( pPlayer );
	}
}



void cStatusCalc_Server::CalcPlayerExtensionGlobal( sObject object )
{
	cPlayer* pPlayer = OBJECTMANAGER->GetPlayer( object.index );
	if( pPlayer != NULL )
	{
		mMoveSpeed = pPlayer->GetMoveSpeed();
		mAttackSpeed = pPlayer->GetStatus2()->mAttackSpeed;
		mSizePer = pPlayer->GetFixedObjectSizePer();
		mMaxHP = pPlayer->GetMaxHP();
		mMaxMP = pPlayer->GetMaxMP();
		
		pPlayer->InitStatus2();

		mBaseStatus[ePLAYER_STATUS_STR] = (float)pPlayer->GetStatus1()->mStr;
		mBaseStatus[ePLAYER_STATUS_DEX] = (float)pPlayer->GetStatus1()->mDex;
		mBaseStatus[ePLAYER_STATUS_CON] = (float)pPlayer->GetStatus1()->mCon;
		mBaseStatus[ePLAYER_STATUS_INT] = (float)pPlayer->GetStatus1()->mInt;
		mBaseStatus[ePLAYER_STATUS_WIS] = (float)pPlayer->GetStatus1()->mWis;

		CalcPlayerExtensionOnly( pPlayer );
		CalcPlayerGlobal( pPlayer );
	}
}



void  cStatusCalc_Server::CalcPlayerGlobal( cPlayer* pPlayer )
{

	/// 스텟2 베이스 초기화
	for( int i = ePLAYER_STATUS_EXT_ATK ; ePLAYER_STATUS_EXT_MAX > i ; ++i )
	{
		mExtStatus[i] = 0;
	}

	/// 스텟2 최종값
	for( int i = ePLAYER_STATUS_EXT_ATK ; ePLAYER_STATUS_EXT_MAX > i ; ++i )
	{
		mExtStatusLast[i] = 0;
	}

	float num1, num2, num3, num4;
	float valueA, valueB, valueC;

	sEquipAbility* pItem = pPlayer->GetEquipAbility();

	for( unsigned char i = ePLAYER_STATUS_EXT_ATK ; i < ePLAYER_STATUS_EXT_MAX ; ++i )
	{
		/// 스크립트에서 읽어들인 순수한 스텟2 값 계산
		CalcPlayerExtStatus( pPlayer , (ePLAYER_STATUS_EXT)i );

		/// 최종 스텟 계산전 가감산 할 항목 적용
		switch( (ePLAYER_STATUS_EXT)i )
		{
			/// 근거리 물리 공격
		case ePLAYER_STATUS_EXT_ATK:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 301 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 302 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 303 );

				/// A =	캐릭터 스탯 근거리 공격력
				/// B =	아이템 물리 공격력 + ( [캐릭터] Lv * {num301} )
				/// C =	(({num302} + ( 버프성 근거리 공격력[%]값 + 아이템옵션값[%]값 ) / {num303})
				///	((A + B ) * C) + ( 버프성 근거리 공격력[+-]값 + 아이템옵션값[+-]값 )
				valueA = mExtStatus[i];
				valueB = pItem->mPhysicMinAttack + ( pPlayer->GetLevel() * num1 );
				valueC = ( ( num2 + mExtStatusPer[i] + pItem->mOptPercentPhysicAttack ) / num3 );
				mExtStatusLast[i] = ( ( valueA + valueB ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusPhysicAttack;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_ATKMin, mExtStatusLast[i] );

				valueB = pItem->mPhysicMaxAttack + ( pPlayer->GetLevel() * num1 );
				valueC = ( ( num2 + mExtStatusPer[i] + pItem->mOptPercentPhysicAttack ) / num3 );
				mExtStatusLast[i] = ( ( valueA + valueB ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusPhysicAttack;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;
					
				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_ATKMax, mExtStatusLast[i] );

				mExtStatusLast[i] = 0.0f;
			}
			break;
			/// 원거리 물리 공격
		case ePLAYER_STATUS_EXT_RATK:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 331 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 332 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 333 );

				/// A =	캐릭터 스탯 원거리 공격력
				/// B =	아이템 물리 공격력 + ( [캐릭터] Lv * {num331} )
				/// C =	(({num332} + ( 버프성 근거리 공격력[%]값 + 아이템옵션값[%]값 ) / {num333})
				///	((A + B ) * C) + ( 버프성 근거리 공격력[+-]값 + 아이템옵션값[+-]값 )
				valueA = mExtStatus[i];
				valueB = pItem->mPhysicMinAttack + ( pPlayer->GetLevel() * num1 );
				valueC = ( ( num2 + mExtStatusPer[i] + pItem->mOptPercentPhysicAttack ) / num3 );
				mExtStatusLast[i] = ( ( valueA + valueB ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusPhysicAttack;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_RATKMin, mExtStatusLast[i] );

				valueB = pItem->mPhysicMaxAttack + ( pPlayer->GetLevel() * num1 );
				valueC = ( ( num2 + mExtStatusPer[i] + pItem->mOptPercentPhysicAttack ) / num3 );
				mExtStatusLast[i] = ( ( valueA + valueB ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusPhysicAttack;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_RATKMax, mExtStatusLast[i] );

				mExtStatusLast[i] = 0.0f;
			}
			break;
			/// 물리 방어력
		case ePLAYER_STATUS_EXT_DEF:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 401 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 402 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 403 );
				num4 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 404 );

				// A =	캐릭터 스탯물리 방어력
				// B =	아이템 물리 방어력
				// C =	(( {num401} + ( 버프성 물리 방어력[%]값 + 아이템옵션[%]값 ) ) / {num402} )
				// D =	((A /{num403}  + B /{num404}) * C) + ( 버프성 물리 방어력[+-]값 + 아이템옵션[+-]값 )
				valueA = mExtStatus[i];
				valueB = pItem->mPhysicDefense;
				valueC = ( ( num1 + ( mExtStatusPer[i] + pItem->mOptPercentPhysicDefense ) ) / num2 );
				mExtStatusLast[i] = ( ( valueA / num3 + valueB / num4 ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusPhysicDefense;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// 마법 공격
		case ePLAYER_STATUS_EXT_MATK:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 361 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 362 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 363 );
				num4 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 364 );

				/// A =	캐릭터 스탯 마법 공격력 / {num361}
				/// B =	아이템 마법 공격력 + ( [캐릭터] Lv * {num362} )
				/// C =	(({num363} + ( 버프성 마법 공격력[%]값 + 아이템옵션[%]값 ) ) / {num364})
				///	((A + B ) * C) + ( 버프성 마법 공격력[+-]값 + 아이템옵션[+-]값 )
				valueA = mExtStatus[i] / num1;
				valueB = pItem->mMagicMinAttack + ( pPlayer->GetLevel() * num2 );
				valueC = ( ( num3 + ( mExtStatusPer[i] + pItem->mOptPercentMagicAttack ) ) / num4 );
				mExtStatusLast[i] = ( ( valueA + valueB ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusMagicAttack;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_MATKMin, mExtStatusLast[i] );

				valueB = pItem->mMagicMaxAttack + ( pPlayer->GetLevel() * num2 );
				valueC = ( ( num3 + ( mExtStatusPer[i] + pItem->mOptPercentMagicAttack ) ) / num4 );
				mExtStatusLast[i] = ( ( valueA + valueB ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusMagicAttack;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_MATKMax, mExtStatusLast[i] );

				mExtStatusLast[i] = 0.0f;
			}
			break;
			/// 마법 방어력
		case ePLAYER_STATUS_EXT_MDEF:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 431 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 432 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 433 );
				num4 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 434 );

				// A =	캐릭터 스탯마법 방어력
				// B =	아이템 마법 방어력
				// C =	(( {num431} + ( 버프성 마법 방어력[%]값 + 아이템옵션[%]값 ) ) / {num432} )
				// D =	((A /{num433}  + B /{num434}) * C) + ( 버프성 물리 방어력[+-]값 + 아이템옵션[+-]값 )
				valueA = mExtStatus[i];
				valueB = pItem->mMagicDefense;
				valueC = ( ( num1 + ( mExtStatusPer[i] + pItem->mOptPercentMagicDefense ) ) / num2 );
				mExtStatusLast[i] = ( ( valueA / num3 + valueB / num4 ) * valueC ) + mExtStatusPlus[i] + pItem->mOptPlusMagicDefense;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// 물리 공격 크리
		case ePLAYER_STATUS_EXT_CRI:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 201 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 202 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 203 );
				num4 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 204 );

				/// A =	[공격자] 캐릭터 스탯 물리 크리티컬 수치 + 아이템 물리 크리티컬 수치
				///	( A * {num201} ) * (({num202} + ( 버프성 물리 크리티컬 [%]값 + 아이템옵션[%]값 ) ) /
				///	{num203}) + ( 버프성 물리 크리티컬 수치[+-]값 + 아이템옵션[+-]값 ) * {num204} )
				valueA = mExtStatus[i] + pItem->mPhysicCritical;
				mExtStatusLast[i] = ( valueA * num1 ) * ( ( num2 + ( mExtStatusPer[i] + pItem->mOptPercentPhysicCritical ) ) / num3 ) +
					( ( mExtStatusPlus[i] + pItem->mOptPlusPhysicCritical ) * num4 );

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}break;
			///	마법 공격 크리
		case ePLAYER_STATUS_EXT_MCRI:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 231 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 232 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 233 );
				num4 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 234 );

				/// A =	[공격자] 캐릭터 스탯 마법 크리티컬 수치 + 아이템 마법 크리티컬 수치
				///	( A * {num231} ) * (({num232} + ( 버프성 마법 크리티컬 [%]값 + 아이템옵션[%]값 ) ) / 
				/// {num233}) + ( ( 버프성 마법 크리티컬 수치[+-]값 + 아이템옵션[+-]값 ) * {num234} )
				valueA = mExtStatus[i] + pItem->mMagicCritical;
				mExtStatusLast[i] = ( valueA * num1 ) * ( ( num2 + ( mExtStatusPer[i] + pItem->mOptPercentMagicCritical ) ) / num3 ) + 
					( ( mExtStatusPlus[i] + pItem->mOptPlusMagicCritical ) * num4 );

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}break;
			/// 최대 HP
		case ePLAYER_STATUS_EXT_MAXHP:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 20 );

				/// 기본 hp량 + ( 레벨 * num1 )
				mExtStatus[i] = mExtStatus[i] + ( pPlayer->GetLevel() * num1 );                
				mExtStatusLast[i] = mExtStatus[i] * ( ( 100 + ( mExtStatusPer[i] + pItem->mOptPercentMaxHp ) ) / 100 )
					+ mExtStatusPlus[i] + pItem->mOptPlusMaxHp;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// 최대 MP
		case ePLAYER_STATUS_EXT_MAXMP:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 20 );

				mExtStatusLast[i] = mExtStatus[i] * ( ( 100 + ( mExtStatusPer[i] + pItem->mOptPercentMaxMp ) ) / 100 ) + 
					mExtStatusPlus[i] + pItem->mOptPlusMaxMp;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// HP 자연 회복량
		case ePLAYER_STATUS_EXT_RECOVHP:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 1001 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 1002 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 1003 );

				float defaultValueHP = STATUSSCRIPT->GetStatusExtBaseInfo( (ePLAYER_STATUS_EXT)i, pPlayer->GetJob() );

				/// A =	클래스 기본값
				///	B =	( 캐릭터 스탯 회복 수치 * {num1001} ) * (( {num1002} + 버프성 HP회복 [%]값 + 아이템옵션[%]값 ) / {num1003} ) + 아이템에 의한 HP 회복 수치 총합
				///	A + B + 버프성 HP회복 [+-]값
				valueA = defaultValueHP;
				valueB = ( mExtStatus[i] * num1 ) * ( ( num2 + ( mExtStatusPer[i] + pItem->mOptPercentHp ) ) / num3 ) + pItem->mOptPlusHp;
				mExtStatusLast[i] = valueA + valueB + mExtStatusPlus[i];

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// MP 자연 회복량
		case ePLAYER_STATUS_EXT_RECOVMP:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 1101 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 1102 );
				num3 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 1103 );

				float defaultValueMP = STATUSSCRIPT->GetStatusExtBaseInfo( (ePLAYER_STATUS_EXT)i, pPlayer->GetJob() );

				/// A =	클래스 기본값
				///	B =	( 캐릭터 스탯 회복 수치 * {num1101} ) * (( {num1102} + ( 버프성 MP회복 [%]값 + 아이템옵션[%]값 ) ) / {num1103} ) + 아이템에 의한 MP 회복 수치 총합
				///	A + B + 버프성 MP회복 [+-]값
				valueA = defaultValueMP;
				valueB = ( mExtStatus[i] * num1 ) * ( ( num2 + mExtStatusPer[i] + pItem->mOptPercentMp ) / num3 ) + pItem->mOptPlusMp;
				mExtStatusLast[i] = valueA + valueB + mExtStatusPlus[i];

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// 물리 공격 성공
		case ePLAYER_STATUS_EXT_HIT:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 101 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 102 );

				/// ( [공격자] 캐릭터 스탯 명중 수치 + 아이템 명중 수치 ) * (({num101} + ( 버프성 명중 수치[%]값 + + 아이템옵션[%]값 ) ) / {num102}) + 
				/// 버프성 명중 수치[+-]값 + 아이템옵션[+-]값 
				mExtStatusLast[i] = ( mExtStatus[i] + pItem->mPhysicAttackRate ) * 
					( ( num1 + ( mExtStatusPer[i] + pItem->mOptPercentPhysicAttackRate ) ) / num2 ) + mExtStatusPlus[i] + pItem->mOptPlusPhysicAttackRate;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// 마법 공격 성공
		case ePLAYER_STATUS_EXT_MHIT:
			{
				/// 마법은 스텟사용 안함
				mExtStatusLast[i] = 0.0f;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// 회피율
		case  ePLAYER_STATUS_EXT_AVOID:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 105 );
				num2 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 106 );

				///	( [방어자] 캐릭터 스탯 회피 수치 + 아이템 회피 수치 ) * (({num105} + ( 버프성 회피 수치[%]값 + 아이템옵션[%]값 ) ) / {num106}) + 
				/// 버프성 회피 수치[+-]값 + 아이템옵션[+-]값 )
				mExtStatusLast[i] = ( mExtStatus[i] + pItem->mOptPlusDod ) *
					( ( num1 + ( mExtStatusPer[i] + pItem->mOptPercentDod ) ) / num2 ) + mExtStatusPlus[i] + pItem->mOptPlusDod;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
			/// 일반공격 속도
		case ePLAYER_STATUS_EXT_ASPD:
			{
				mExtStatusLast[i] = ( 100 + pItem->mAttackSpeed + mExtStatusPer[i] ) / 100;

				if( mExtStatusLast[i] < 0.1f )
					mExtStatusLast[i] = 0.1f;

				if( mExtStatusLast[i] > 2.0f )
					mExtStatusLast[i] = 2.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );                
			}break;
			/// 이동 속도 계산
		case ePLAYER_STATUS_EXT_SPD:
			{
				/// 몬스터 변신중일 경우 이동속도 기본값을 몬스터에서 읽어온다.
				if( pPlayer->GetChgMonsterIdx() != 0 )
				{
					sMonsterScript* pMScript = MONSTERSCRIPT->GetMonsterListInfo( pPlayer->GetChgMonsterIdx() );
					if( pMScript != NULL )
						mExtStatus[i] = (float)pMScript->mActionRunSpd;

					mExtStatusLast[i] = mExtStatus[i] * ( ( 100 + mExtStatusPer[i] + pItem->mOptPercentMov ) / 100 ) + 
						mExtStatusPlus[i] + pItem->mOptPlusMov;

					if( mExtStatusLast[i] < 0.0f )
						mExtStatusLast[i] = 0.0f;

					pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
				}
				else
				{
					mExtStatusLast[i] = mExtStatus[i] * ( ( 100 + mExtStatusPer[i] + pItem->mOptPercentMov  ) / 100 ) + 
						mExtStatusPlus[i] + pItem->mOptPlusMov;

					if( mExtStatusLast[i] < 0.0f )
						mExtStatusLast[i] = 0.0f;

					pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
				}

			}
			break;
			/// 힐 회복량
		case ePLAYER_STATUS_EXT_HEAL:
			{
				num1 = STATUSSCRIPT->GetDamageCalcNumericalInfo( 1203 );

				///	( 캐릭터 스탯 마법 공격력 * 0.5 ) + 아이템에 의한 스킬회복량 증가 수치 총합
				mExtStatusLast[i] = pPlayer->GetStatus2()->mMagicMinAttack * num1;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_HEALMin, mExtStatusLast[i] );

				mExtStatusLast[i] = pPlayer->GetStatus2()->mMagicMaxAttack * num1;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( ePLAYER_STATUS_EXT_HEALMax, mExtStatusLast[i] );

				mExtStatusLast[i] = 0.0f;
			}
			break;
			/// 무게
		case ePLAYER_STATUS_EXT_WEIGHT:
			{
				mExtStatusLast[i] = mExtStatus[i] * ( ( 100.0f + ( mExtStatusPer[i] + pItem->mOptPercentWei ) ) / 100.0f ) + 
					mExtStatusPlus[i] + pItem->mOptPlusWei;

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
		default:
			{
				mExtStatusLast[i] = mExtStatus[i] * ( ( 100.0f + mExtStatusPer[i] ) / 100.0f ) + mExtStatusPlus[i];

				if( mExtStatusLast[i] < 0.0f )
					mExtStatusLast[i] = 0.0f;

				pPlayer->SetStatus2( (ePLAYER_STATUS_EXT)i, mExtStatusLast[i] );
			}
			break;
		}
	}

	if( pPlayer->GetGameIn() == true )
	{

		/// hp수치를 hp최대치로 넘어가지 않게 셋팅
		if( pPlayer->GetHP() > pPlayer->GetStatus2()->mMaxHP )
			pPlayer->HPHeal( 0, true, 0, 0, eTAKEDAMAGETYPE_NONE );
		
		/// mp수치를 mp최대치로 넘어가지 않게 셋팅
		if( pPlayer->GetMP() > pPlayer->GetStatus2()->mMaxMP )
			pPlayer->MPHeal( 0, true );

		if( pPlayer->GetStateDie() != true )
		{
			if( mMaxHP != pPlayer->GetStatus2()->mMaxHP )
				pPlayer->SendHPHeal();

			if( mMaxMP != pPlayer->GetStatus2()->mMaxMP )
				pPlayer->SendMPHeal();
		}
		else
		{
			if( mMaxHP != pPlayer->GetStatus2()->mMaxHP )
				pPlayer->SendMaxHP();

			if( mMaxMP != pPlayer->GetStatus2()->mMaxMP )
				pPlayer->SendMaxMP();
		}

		/// 치트 이동속도 증가
		pPlayer->CalcCheatSpeedUp();

		/// 공격속도 변경시 발송
		if( pPlayer->GetStatus2()->mAttackSpeed != mAttackSpeed )
			pPlayer->SendAttackSpeed();

		/// 이동 속도가 변한경우 클라이언트에 전송
		if( pPlayer->GetMoveSpeed() != mMoveSpeed )
			pPlayer->SendMoveSpeed();

		/// 캐릭터 크기가 변한경우
		if( pPlayer->GetFixedObjectSizePer() != mSizePer )
		{
			pPlayer->SetFixedObjectSizePer( (short)mSizePer );
			pPlayer->SendPlayerSize();
		}
	}
}
