#include "stdafx.h"
#include "Party.h"
#include "PartyManager.h"
#include "RangeCheck.h"
#include "ObjectManager.h"
#include "Player.h"
#include "Monster.h"
#include "Drop.h"
#include "DropScript.h"

cParty::cParty()
: mIndex(0)
, mDivideType( PARTY_DIVIDE_NONE )
, mLevel(0)
, mCount(0)
, mLeaderIndex(0)
, mOwnUserNum(-1)
, mDropMoney(0)
, mDropExp(0)
, mDropSxp(0)
, mLastAccumTime(0)
{
	/// 유저 인덱스 초기화
	memset( mUserArr, 0, sizeof(unsigned long)*MAX_PARTY );
}

cParty::~cParty()
{}

bool cParty::IsUser( unsigned long userIdx )
{
	unsigned long* ptr = mUserArr;
	for ( unsigned int i = 0; i < mCount; ++i, ++ptr )
	{
		if ( (*ptr) == userIdx )
			return true;
	}
	return false;
}

bool cParty::AddUser( unsigned long userIdx )
{
	if ( !(mCount > MAX_PARTY) )
	{
		mUserArr[mCount] = userIdx;	/// 삽입
		mCount++;					/// 증가
		return true;				/// 파티레벨 다시 갱신하기.
	}
	return false;
}

bool cParty::DeleteUser( unsigned long userIdx )
{
	if ( mCount > 0 )
	{
		int index = -1;
		for ( unsigned int i = 0; i < mCount; ++i )
		{
			if ( mUserArr[i] == userIdx )
			{
				index = i;
				break;
			}
		}
		if ( index > -1 )
		{
			mCount--;	/// 감소
			for ( unsigned int i = index; i < mCount; ++i )
			{
				mUserArr[i] = mUserArr[i+1];
			}
			mUserArr[mCount] = 0;
			return true;
		}
	}
	return false;
}

unsigned long cParty::GetTurnUser( unsigned short mapNumber, float x, float y )
{
	NiPoint2 monPos( x, y );

	for( unsigned int i = 0; i < mCount; ++i )
	{
		/// 그다음 유저로 증가
		mOwnUserNum++;	

		if( mOwnUserNum >= (int)mCount )
			mOwnUserNum = 0;
	
		/// 해당유저 찾기
		cPlayer* user = OBJECTMANAGER->GetPlayer( mUserArr[ mOwnUserNum ] );
		if( user )	
		{
			if( mapNumber != user->GetMapNumber() )
			{
				continue;
			}

			NiPoint2 playerPos( user->GetXPos(), user->GetYPos() );

			cRangeCheck rangeCheck( PARTY_RANGE );
			bool check = rangeCheck.IsRange( monPos, playerPos );
			if( check )
				return mUserArr[ mOwnUserNum ];
		}
	}

	return 0;
}

unsigned long cParty::GetRandomUser( unsigned short mapNumber, float x, float y )
{
	cPlayer* user = 0;
	unsigned long tempArr[MAX_PARTY] = {0,};
	int count = 0;

	NiPoint2 monPos( x, y );

	/// 사정거리안에 있는 유저만 따로 모은다
	for( unsigned int i = 0; i < mCount; ++i )
	{
		user = OBJECTMANAGER->GetPlayer( mUserArr[i] );
		if( user )
		{
			if( mapNumber != user->GetMapNumber() )
			{
				continue;
			}

			NiPoint2 playerPos( user->GetXPos(), user->GetYPos() );

			cRangeCheck rangeCheck( PARTY_RANGE );
			bool check = rangeCheck.IsRange( monPos, playerPos );
			if( check == true )
			{
				tempArr[count] = mUserArr[i];
				count++;
			}
		}
	}

	if( count == 0 )
	{
		assert(0);
		return 0;
	}

	/// 랜덤으로 뽑기
	int random = rand()%count;
	mOwnUserNum = tempArr[random];
	return mOwnUserNum;
}
///
void* cParty::operator new( size_t n )
{
	if( n != sizeof(cParty) )
	{
		assert(0);
		return NULL;
	}

	/// 할당
	return PARTYMAN->AllocParty();
}

///
void cParty::operator delete( void* ptr, size_t n )
{
	if( ptr == NULL )
	{
		assert(0);
		return;
	}

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

	/// 해제
    PARTYMAN->FreeParty( static_cast<cParty*>(ptr) );
	return;
}

void cParty::DivideDropValue( unsigned long monsterIdx, unsigned char monsterLevel )
{

	unsigned long dropMoney = FloatToInt( mDropMoney );
	unsigned long dropExp = FloatToInt( mDropExp );
	unsigned long dropSxp = FloatToInt( mDropSxp );
	unsigned long damage = mDamage;
	unsigned long monsterMaxHP = mMonsterMaxHP;

	mDropMoney = 0;
	mDropExp = 0;
	mDropSxp = 0;
	mMonsterMaxHP = 0;
	mDamage = 0;

	cMonster* pMonster = OBJECTMANAGER->GetMonster( monsterIdx );
	if( pMonster == NULL )
	{
		assert(NULL);
		return;
	}

	/// 몬스터에 파티전체가 공격한 데미지가 hp의 30%미만이면 보상 없음
	if( monsterMaxHP * 0.3f > damage )
		return;

	unsigned short mapNumber = pMonster->GetMapNumber();
	NiPoint2 monPos = pMonster->GetPos();

	/// 경험치를 분배해줄 파티원 목록
	unsigned long dropUserIdx[MAX_PARTY] = {0,};
	unsigned char dropUserCnt = 0;

	unsigned char maxLevel = 0;		/// 파티 레벨
	float averageLevel = 0;			/// 파티 평균 레벨

	NiPoint2 playerPos = NiPoint2::ZERO;
	cRangeCheck rangeCheck( PARTY_RANGE );

	cPlayer* pPlayer = NULL;
	/// 전체 파티원 검색 - 위치, 몬스터 공격 해당 파티원 리스트만 추려냄
	for( unsigned int i = 0; i < mCount; ++i )
	{
		pPlayer = OBJECTMANAGER->GetPlayer( mUserArr[i] );
		if( pPlayer == NULL )
			continue;

		playerPos = pPlayer->GetPos();

		/// 위치 확인
		if( mapNumber != pPlayer->GetMapNumber() || rangeCheck.IsRange( monPos, playerPos ) != true )
		{
			/// 위치가 틀리고 공격도 안했다면 드롭대상에서 빼버림
			if( pMonster->GetTakeDamage( mUserArr[i] ) == 0 )
				continue;
		}

		/// 드롭 해당 유저 카운트와 파티 레벨 설정
		dropUserIdx[dropUserCnt] = mUserArr[i];
		++dropUserCnt;
		averageLevel = averageLevel + pPlayer->GetLevel();
		if( maxLevel < pPlayer->GetLevel() )
			maxLevel = pPlayer->GetLevel();
	}

	/// 몬스터를 공격 했던 파티가 몬스터가 죽기전에 다 떠남
	if( dropUserCnt == 0 )
		return;

	if( averageLevel == 0 )
	{
		assert(NULL);
		return;
	}

	averageLevel = averageLevel / dropUserCnt;

	/// 레벨차 증감치 계산
	float calcExp = DROP->MonsterDropExpIncrease( (float)dropExp, maxLevel, monsterLevel );
	float calcSxp = DROP->MonsterDropExpIncrease( (float)dropSxp, maxLevel, monsterLevel );

	/// 파티 증감치 계산
	float partyIncrease = DROPSCRIPT->GetPartyExpIncrease( dropUserCnt );
	calcExp = calcExp * partyIncrease;
	partyIncrease = DROPSCRIPT->GetPartySxpIncrease( dropUserCnt );
	calcSxp = calcSxp * partyIncrease;

	/// 개개인의 경험치
	unsigned long eachExp;
	unsigned long eachSxp;
	/// 선정된 플레이어에게 드롭
	for( unsigned char i = 0 ; dropUserCnt > i ; ++i )
	{
		pPlayer = OBJECTMANAGER->GetPlayer( dropUserIdx[i] );
		if( pPlayer == NULL || pPlayer->GetStateDie() == true )
			continue;

		playerPos.x = pPlayer->GetXPos();
		playerPos.y = pPlayer->GetYPos();

		/// 드롭 대상 이지만 드롭 분배 당시 멀리 있으면 대상에서 제외
		if( mapNumber != pPlayer->GetMapNumber() || rangeCheck.IsRange( monPos, playerPos ) != true )
			continue;

		/// [개인별 획득 경험치] = [파티에 분배되는 전체 경험치] * [자신의 레벨 / 파티 평균 레벨] / [파티원수]
		eachExp = FloatToInt( calcExp * ( pPlayer->GetLevel() / averageLevel ) / dropUserCnt );
		eachSxp = FloatToInt( calcSxp * ( pPlayer->GetLevel() / averageLevel ) / dropUserCnt );

		pPlayer->AddExpSxp( eachExp, eachSxp );

		unsigned long money = dropMoney;
		money = money / dropUserCnt;
		/// money는 균등 분배
		if( money != 0 )
			pPlayer->AddMoney( pMonster->GetObject(), money );
	}
}

void cParty::AddDropValue( unsigned long maxHP, unsigned long damage, float money, float exp, float sxp )
{
	mDropMoney = mDropMoney + money;
	mDropExp = mDropExp + exp;
	mDropSxp = mDropSxp + sxp;
	mMonsterMaxHP = maxHP;
	mDamage = mDamage + damage;
}
