#include "stdafx.h"
#include "actionmove.h"
#include "gamesrv.h"

#include "../Common/Protocol.h"
#include "Monster_Common.h"

#include "MonsterScript.h"

#include "Monster.h"
#include "Player.h"
#include "AIManager.h"
#include "GridManager.h"
#include "ObjectManager.h"


cActionMove::cActionMove(void): cAction( eACT_MOVE )
{
}



cActionMove::~cActionMove(void)
{
}



void cActionMove::ActionInit( cMonster* pMonster, unsigned long accumTime )
{

	cAction::ActionInit( pMonster, accumTime );

	if( pMonster->IsFirstMove() == true )
	{
		/// 리젠 후 일정 시간동안 선공 안하는 시간
		mRegenCantMoveEndTime = accumTime + FIRSTMOVE_WAIT_TIME;

		/// 이동 정지 & 리젠 정지 시간이 중복 되므로 큰값이 적용 되게 계산
		if( accumTime + MOVE_WAIT_TIME > accumTime + FIRSTMOVE_WAIT_TIME )
			mWaitEndTime = accumTime + MOVE_WAIT_TIME;
		else
			mWaitEndTime = accumTime + FIRSTMOVE_WAIT_TIME;

		/// 처음이동 플래그 사용 끝 - 이동,선공 일정 시간동안 안함
		pMonster->FirstMoveEnd();
	}
	else
		mWaitEndTime = accumTime + MOVE_WAIT_TIME;

	mDelayTime = accumTime;
	mDelayTime2 = accumTime;

	mSetPathErrCnd = 0;

	mIsIdle2Send = false;
	mIdle2SendTime = 0;

	mFirstMoveEndCheck = true;
}



eACTION_CHANGE cActionMove::Action( cMonster* pMonster, unsigned long deltaTime, unsigned long accumTime )
{
	const sMonsterScript* pMonsterScript = pMonster->GetMonsterInfo();
	if( pMonsterScript == NULL )
	{
		assert(NULL);
		NETWORK2->PostServerEvent("cActionRegen::Action pRegenGroupScript == NULL");
		return eACTCH_ERROR;
	}

	/// 몬스터가 선공몹인경우 타겟 검사
	if( pMonsterScript->mIsPrecedeAttack == true && pMonster->GetFollowEndMove() == false )
	{
		/// 0.5초후에 재검사 - 부하방지
		if( mDelayTime <= accumTime )
		{
			mDelayTime = accumTime + 500;

			/// 리젠 후 일정시간 동안 선공 안함
			if( mRegenCantMoveEndTime < accumTime )
			{
				/// 범위안에 들어온 플레이어 선택
				cPlayer* pPlayer = TargetSelect( pMonster, pMonsterScript->mSeekRange );
				if( pPlayer != NULL )
				{
					pMonster->AddTakeDamage( pPlayer->GetObject(), 0, 0, eTAKEDAMAGETYPE_ADDPLAYER );
					return eACTCH_MOVE_END;

				}
			}
		}
	}

	/// 몬스터 이동
	if( pMonster->MoveUpdate( deltaTime ) == true )
	{
		mFirstMoveEndCheck = false;

		/// 마지막 이동까지 대기시간 3초 설정
		mWaitEndTime = accumTime + MOVE_WAIT_TIME;
		return eACTCH_CONTINUE;
	}

	/// 이동 끝나자마자 한번 체크
	if( mFirstMoveEndCheck == false )	
	{
		mFirstMoveEndCheck = true;
		pMonster->SendSpeech( eMONSTERTALK_MAX );
	}

	/// 이동이 끝나면 추적이동 회귀는 완료
	pMonster->SetFollowEndMove( false );


	/// 대기동작2 발송 체크
	if( mIsIdle2Send == true && accumTime < mIdle2SendTime )
	{
		/// 메세지 발송
		MSG_SYN_MONSTER_IDLE2ANI synMsg;
		synMsg.Category = NM_MONSTER;
		synMsg.Protocol = NM_MONSTER_IDLE2_ANI_SYN;
		synMsg.mMonsterIdx = pMonster->GetObjectID();
		NETWORK2->QuickSend( (cBaseObject*)pMonster, (char*)&synMsg, sizeof(MSG_SYN_MONSTER_IDLE2ANI) );

		mIdle2SendTime = false;
		mIdle2SendTime = 0;
	}


	/// 대기시간 비교
	if( mWaitEndTime > accumTime )
	{
		return eACTCH_CONTINUE;
	}


	/// 경로 설정시 실패 한경우면 다른 이동방식 선정 안하고 기존 이동방법으로 선정
	if( mSetPathErrCnd == 0 )
	{
		/// 이동이 끝난경우 자유이동 방식 선정
		unsigned long moveSpeed = 0;
		eMOVE_SPEED_TYPE moveSpeedType = RandMoveSpeed( pMonster, &moveSpeed );
		/// 행동 정지 선택
		if( moveSpeed == 0 )
		{
			/// 대기시간 추가
			mWaitEndTime = accumTime + pMonsterScript->mActionStopTime;

			/// 대기시간 중 일정시간에 대기동작2 메세지 발송 결정
			mIsIdle2Send = (rand() % 100) + 1 <= IDLE2_ANI_SEND_PER;
			if( mIsIdle2Send == true )
				mIdle2SendTime = accumTime + ( pMonsterScript->mActionStopTime / 3 );

			pMonster->SendSpeech( eMONSTERTALK_STAND );

			return eACTCH_CONTINUE;
		}

		/// 몬스터의 이동 속도 결정
		pMonster->SetMoveSpeed( moveSpeed, moveSpeedType );

		if( moveSpeedType == eMOVESPEED_WALK )
			pMonster->SendSpeech( eMONSTERTALK_WORK );
		else if( moveSpeedType == eMOVESPEED_RUN )
			pMonster->SendSpeech( eMONSTERTALK_RUN );
	}

	/// 범위안에서 랜덤 좌표의 리젠 위치 
	unsigned long freeRange = pMonsterScript->mFreeMoveRange;
	float targetPosX = pMonster->GetRegenPos().x + ( rand() % (int)freeRange ) - ( rand() % (int)freeRange );
	float targetPosY = pMonster->GetRegenPos().y + ( rand() % (int)freeRange ) - ( rand() % (int)freeRange );
	if( targetPosX < 0 )
		targetPosX = 0;
	if( targetPosY < 0 )
		targetPosY = 0;

	/// 몬스터가 이동할 경로 셋팅
	if( pMonster->SetPath( targetPosX, targetPosY ) == 0 )
		++mSetPathErrCnd;
	else
		mSetPathErrCnd = 0;

	/// 갈수없는 경로 연속실패 횟수가 너무 많은 경우 몬스터가 길찾기 허용 범위까지 나갔을 가능성이 크다.
	if( mSetPathErrCnd > 100 )
	{
		/// 강제 이동
		pMonster->SetPos( targetPosX, targetPosY );
		mSetPathErrCnd = 0;
	}

	/// 특별한 이벤트에 걸리지 않는 한 계속 이동
	return eACTCH_CONTINUE;
}



eMOVE_SPEED_TYPE cActionMove::RandMoveSpeed( cMonster* pMonster, unsigned long *moveSpeed )
{

	const sMonsterScript* pMonsterScript = pMonster->GetMonsterInfo();
	if( pMonsterScript == NULL )
	{
		assert(NULL);
		*moveSpeed = 0;
		return eMOVESPEED_RUN;
	}

	/// 비율 합계
	unsigned short totalRatio = pMonsterScript->mActionStopRatio + pMonsterScript->mActionWalkRatio + pMonsterScript->mActionRunRatio;

	/// 랜덤 선택값
	unsigned short randSelect = (unsigned short)( rand() % totalRatio ) + 1;
	if( randSelect <= pMonsterScript->mActionStopRatio )
	{
		*moveSpeed = 0;
		/// 정지 선택
		return eMOVESPEED_STOP;
	}
	else if( randSelect <= pMonsterScript->mActionStopRatio + pMonsterScript->mActionWalkRatio  )
	{
		*moveSpeed = pMonsterScript->mActionWalkSpd;
		/// 걷기 선택
		return eMOVESPEED_WALK;
	}
	else
	{
		*moveSpeed = pMonsterScript->mActionRunSpd;
		/// 뛰기 선택
		return eMOVESPEED_RUN;
	}
}



cPlayer* cActionMove::TargetSelect( cMonster* pMonster, unsigned long seekRange )
{

	float tempRange;

	/// 주변 그리드에 플레이어가 있는지 먼저 체크 - 부하방지
	if( GRIDMANAGER->IsPlayer( pMonster ) == 0 )
		return NULL;

	cPlayer* pPlayer = GRIDMANAGER->FindFirstPlayer( pMonster );
	/// 각각의 플레이어를 검색함
	while( pPlayer != NULL )
	{
		/// 플레이어가 있는데 죽은경우는 통과
		if( pPlayer->GetStateDie() != true ) 
		{
			tempRange = OBJECTMANAGER->ObjectSizeRange( pMonster, pPlayer, (float)seekRange );

			/// 몬스터 범위 설정
			mRangechk.SetRadius( tempRange );

			/// 몬스터 위치
			NiPoint3 monsterpoint( pMonster->GetXPos(), pMonster->GetYPos(), pMonster->Height() );
			/// 플레이어 위치
			NiPoint3 playerpoint( pPlayer->GetXPos(), pPlayer->GetYPos(), pPlayer->Height() );

			/// 못가는 지역인경우 통과
			if( AIMANAGER->IsPassible( pPlayer->GetMapNumber(), playerpoint.x, playerpoint.y, pPlayer->GetObject() ) == false )
			{
				pPlayer = GRIDMANAGER->FindNextPlayer();
				continue;
			}

			/// 몬스터와 플레이어 충돌 체크
			if( mRangechk.IsRange( monsterpoint, playerpoint ) )
			{
				return pPlayer;
			}
		}

		pPlayer = GRIDMANAGER->FindNextPlayer();
	}

	return NULL;
}