Files
Leviathan/Client/Game/game/Player/SStateMachine.cpp
T
2026-06-01 12:46:52 +02:00

1705 lines
49 KiB
C++

#include "stdafx.h"
#include "SStateMachine.h"
#include "SGameAvatarEx.h"
//#include "Smove_filter.h"
#include "TerrainMapEngine.h"
#include "SGameWorld.h"
#include "SkillBaseFile.h"
#include "SSkillDB.h"
#include "SSKillStageType.h"
#include "SDebug_Util.h"
#include "SLog.h"
//namespace PF
//{
// SGameObject *s_pGameObj;
// move_filter_history history;
//
// struct intf_move_filter
// {
// static bool is_empty_at( float x, float y )
// {
// return !s_pGameObj->IsBlockedAttribute( x, y );
// }
// };
// typedef move_filter<intf_move_filter, 8, 8> SPATHFINDER;
//
//
// SPATHFINDER finder;
// void ResetHistory()
// {
// history.reset();
// }
// bool GetNextPos( SGameObject *pObj, const K3DVector &vCur, const K3DVector &vTarget, K3DVector *vNext )
// {
// s_pGameObj = pObj;
// return finder.get_direct_dest( history, vCur.x, vCur.y, vTarget.x, vTarget.y, vNext->x, vNext->y );
// }
//};
const float c_fRangeAcceptable = 1.f; //100% 정도로 거리를 줄인다
SObjectStateMachine::SObjectStateMachine()
: m_pCurrentState( NULL )
, m_idPendingState( SObjectState::STATE_NONE )
, m_idDefaultState( SObjectState::STATE_NONE )
, m_idNextState( SObjectState::STATE_NONE )
, m_idPrevState( SObjectState::STATE_NONE )
, m_hTargetHandle( 0 )
, m_pReceiver( NULL )
, m_dwTime( 0 )
, m_dwCurrentStateStartTime( 0 )
, m_idCurState( SObjectState::STATE_IDLE )
, m_idCreatureState( SObjectState::STATE_IDLE )
, m_nNeedStopPos( NEED_STOP_POS )
, m_dwFindDetour( 0 )
, m_hAttackTarget( 0 )
, m_bAutoAttack( false )
, m_dwAttackDelayTime( 0 )
, m_bInputFreezing(false)
{
SetMode( MODE_NORMAL ); //전투대기 상태 , 공격을 받거나, 내가 공격 시작하면 설정 된다.
}
void SObjectStateMachine::ClearInfo()
{
m_pCurrentState = NULL;
m_idPendingState = SObjectState::STATE_NONE;
m_idDefaultState = SObjectState::STATE_IDLE;
m_idNextState = SObjectState::STATE_NONE;
m_idPrevState = SObjectState::STATE_NONE;
m_hTargetHandle = 0;
m_dwCurrentStateStartTime = 0;
m_idCurState = SObjectState::STATE_IDLE;
m_idCreatureState = SObjectState::STATE_IDLE;
m_nNeedStopPos = NEED_STOP_POS;
m_dwFindDetour = 0;
m_hAttackTarget = 0;
// 2010.06.15 - prodongi
//m_bAutoAttack = false;
setAutoAttack(false);
m_dwAttackDelayTime = 0;
m_bInputFreezing = false;
SetMode( MODE_NORMAL ); //전투대기 상태 , 공격을 받거나, 내가 공격 시작하면 설정 된다.
while( !m_queInputState.empty() )
m_queInputState.pop();
m_infoCurrent.Reset();
m_infoPending.Reset();
m_infoDefault.Reset();
m_infoNext.Reset();
}
SObjectStateMachine::~SObjectStateMachine()
{
for( OBJSTATE_MAP::iterator it = m_StateMap.begin(); it != m_StateMap.end(); it++ )
delete it->second;
while( !m_queInputState.empty() )
m_queInputState.pop();
}
SObjectState::REQCOMMAND_PERMIT SObjectStateMachine::CheckNextPermission( SObjectState::ID state )
{
if( NULL != m_pCurrentState )
{
//Mode 허용 처리
SObjectState * pReqState = GetObjState( state );
if( pReqState )
{
if( pReqState->CheckModePermission( GetMode() ) == false )
return SObjectState::PERMIT_FORBIDDEN;
}
// 현재 상태의 퍼미션 체크
return m_pCurrentState->GetRequestPermission( state );
}
return SObjectState::PERMIT_IMMEDIATELY;
}
bool SObjectStateMachine::RequestNewState( SObjectState::ID NewState, const SStateInfo &infoState )
{
// _oprint( "[상태머신 : RequestNewState : %d %d]\n", m_idCurState, NewState );
bool bNextMove = false;
K3DVertex vtNextMove, vtMyself;
vtMyself = *m_pReceiver->GetPosition();
if( NewState == SObjectState::STATE_MOVE )
m_nNeedStopPos = NEED_STOP_POS;
/*
else if( m_nNeedStopPos == INSPECTED_STOP_POS )
m_nNeedStopPos = NOT_NEED_STOP_POS;
*/
if( ( NewState != SObjectState::STATE_IDLE /*&&m_pReceiver->GetCurrState() != NewState*/ ) || m_pReceiver->GetCurrTarget() != infoState.hTarget )
{
m_pReceiver->EraseDetour();
if( NewState != SObjectState::STATE_IDLE ) m_pReceiver->SetCurrState( NewState );
m_pReceiver->SetCurrTarget( infoState.hTarget );
// 2008. 6. 18 floyd #2.3.1.23
// http://bug.nflavor.com/view.php?id=2868
// 아바타가 대상을 타겟하지 않으면, 크리쳐는 스킬 자동공격을 하지 않는 문제 관련 수정
// 한 동작(state?)가 끝나고 StopCurrentState()가 호출될때 m_infoDefault내용을 복사하며 크리쳐에 설정된 타겟이 NULL로 세팅되는 문제때문에
// m_infoDefault.hTarget 에 마지막으로 설정된 타겟을 기억하고 있도록 수정
m_infoDefault.hTarget = infoState.hTarget;
}
//추적중에 공격형 명령이 들어왔다면
if( m_idCurState == SObjectState::STATE_CHASE && IsCombatState( NewState ) )
{
//현재 타겟과 새로운 타겟을 비교한다
if( m_infoCurrent.hTarget != infoState.hTarget || m_idNextState == SObjectState::STATE_NONE )
{
//현재 타겟과 새로운 타겟이 다르다면 명령 실행
m_nNeedStopPos = NEED_STOP_POS;
}
}
// 현재 상태의 퍼미션 체크
switch( CheckNextPermission( NewState ) )
{
case SObjectState::PERMIT_IMMEDIATELY: // 즉시 허용일 경우 바로 현재 상태로 설정
// _oprint( "[상태머신 : RequestNewState - SObjectState::PERMIT_IMMEDIATELY]\n" );
if ( infoState.fRange > 0.f )
{
/*
//정지할 위치가 필요없다면
if( m_nNeedStopPos == NOT_NEED_STOP_POS )
{
return true;
}
*/
SStateInfo newinfo = infoState;
/// 2010.10.21 - prodongi
//if ( newinfo.hTarget != 0 ) //타겟이 있다.
if ( newinfo.hTarget != 0 || newinfo.isRegionTarget ) //타겟이 있다.
{
/// 2010.10.21 - prodongi
SGameAvatarEx* pTarget = NULL;
if (!newinfo.isRegionTarget)
{
pTarget = (SGameAvatarEx*)m_pReceiver->GetGameObject( newinfo.hTarget );
if (!pTarget) return false;
}
//SGameAvatarEx* pTarget = (SGameAvatarEx*)m_pReceiver->GetGameObject( newinfo.hTarget );
//if ( pTarget == NULL ) return false; //없어 지면 끝
//else
{
// 2010.05.24 - prodongi
if( m_pReceiver->GetObjType() == TS_ENTER::GAME_SUMMON && !infoState.isValidToCorpse )
//if( m_pReceiver->GetObjType() == TS_ENTER::GAME_SUMMON )
{
//if( IsCombatState( NewState ) )
if( IsCombatState( NewState ) && !newinfo.isRegionTarget )
{
switch( pTarget->GetObjType() )
{
case TS_ENTER::GAME_MOB:
{
if(pTarget->GetHP() <= 0 ) return false;
}
break;
case TS_ENTER::GAME_PLAYER:
{
if( ( pTarget->GetStatus() & TS_ENTER::PlayerInfo::FLAG_PK_ON ) ||
( pTarget->GetStatus() & TS_ENTER::PlayerInfo::FLAG_DEMONIAC ) ||
( pTarget->GetStatus() & TS_ENTER::PlayerInfo::FLAG_BLOODY ) )
{
if(pTarget->GetHP() <= 0 ) return false;
}
}
break;
case TS_ENTER::GAME_SUMMON:
{
SGameAvatarEx* pMaster = (SGameAvatarEx*)m_pReceiver->GetGameObject( pTarget->GetMaster() );
if( pMaster )
{
if( ( pMaster->GetStatus() & TS_ENTER::PlayerInfo::FLAG_PK_ON ) ||
( pMaster->GetStatus() & TS_ENTER::PlayerInfo::FLAG_DEMONIAC ) ||
( pMaster->GetStatus() & TS_ENTER::PlayerInfo::FLAG_BLOODY ) )
{
if(pTarget->GetHP() <= 0 ) return false;
}
}
}
break;
}
}
}
}
if (newinfo.isRegionTarget)
{
K3DVector regionTargetPos = newinfo.vRegionTargetPos;
vtNextMove = GetChasePos( NULL, &regionTargetPos, 1, newinfo, NewState );
}
else
{
/*if( m_pReceiver->GetObjType() == TS_ENTER::GAME_SUMMON )
vtNextMove = GetCreatureChasePos( m_pReceiver, pTarget, pTarget->GetPosition(), newinfo, NewState );
else*/
vtNextMove = GetChasePos( pTarget, pTarget->GetPosition(), GetSize(pTarget), newinfo, NewState );
}
if( vtNextMove.x < 0 || vtNextMove.y < 0 )
return false; //임시
int nReusultLen = 0;
//공격 가능 한가 검사 한다.
bool isInRange = false;
if (newinfo.isRegionTarget)
{
isInRange = IsRange( *m_pReceiver->GetPosition(), GetSize( m_pReceiver ), newinfo.vRegionTargetPos, 1, infoState.fRange, true, nReusultLen );
}
else
{
AR_UNIT fTargetSize = GetSize( pTarget );
AR_UNIT fMySize = GetSize( m_pReceiver );
bool bCollisionToLine = true;
if( NewState == SObjectState::STATE_TAKEITEM )
{
fTargetSize = 0;
fMySize = 0;
// 펫은 충돌 검사를 하지 않는다.
if( m_pReceiver->GetObjType() == TS_ENTER::GAME_PET )
{
bCollisionToLine = false;
}
}
isInRange = IsRange( *m_pReceiver->GetPosition(), fMySize, *pTarget->GetPosition(), fTargetSize, infoState.fRange, bCollisionToLine, nReusultLen );
}
if (isInRange)
{
//내가 이동 중이 아니면
if ( m_pReceiver->IsMoving() == false )
{
//일반 공격
if( m_idCurState == SObjectState::STATE_ATTACK )
{
//공격 상태 중에 이미 공격 중인 것은 다시 때리지 않는다.
if( m_infoCurrent.hTarget == newinfo.hTarget )
return true;
if( m_infoCurrent.hTarget != newinfo.hTarget )
{
m_pReceiver->ReqCancelAction();
}
}
//TODO : SObjectState::STATE_AIMING 일때, 다른 타겟의 공격 요청시, 활을 발사 완료 후 Attack이 되어야 하는데,
// 현재 Pending 시스템은 상태 전이가 한다계 뿐이 안 되므로, 아직 지원이 안된다.
if( m_idCurState == SObjectState::STATE_SHOOTING )
{
if( m_infoCurrent.hTarget != newinfo.hTarget )
m_pReceiver->ReqCancelAction();
}
if( m_idCurState == SObjectState::STATE_ATTACK && NewState == SObjectState::STATE_CAST )
{
m_pReceiver->ReqCancelAction();
}
//바로 공격~
SendServerReq( NewState, newinfo );
return true;
}
else
{
if( newinfo.hTarget != m_pReceiver->GetArID())
{
//범위 안 이면서, 이동 중이면, 거리는 -1m 처리 스킵
if( nReusultLen <= 0 && nReusultLen > -GameRule::DEFAULT_UNIT_SIZE ) //(-)이면 스킵
{
return true;
}
}
}
m_pReceiver->FindDetour( vtNextMove, true, true);
//추적 위치 검사
newinfo.vTargetPos = vtNextMove;
newinfo.bIsMovePos = false; //그냥 이동이 아니다.
newinfo.SpeedSync = false; //크리처일 경우 자기 속도로 움직인다
//새로운 정보의 타겟이 존재한다면 현재 정보 갱신
/// 2010.10.21 - prodongi
if (newinfo.isRegionTarget)
{
m_infoCurrent.isRegionTarget = true;
m_infoCurrent.vRegionTargetPos = newinfo.vRegionTargetPos;
m_infoCurrent.vTargetPos = newinfo.vTargetPos;
m_infoCurrent.fRange = newinfo.fRange;
m_infoCurrent.nSkillID = newinfo.nSkillID;
}
else
{
if( newinfo.hTarget != 0 )
{
m_infoCurrent.hTarget = newinfo.hTarget;
m_infoCurrent.fRange = newinfo.fRange;
m_infoCurrent.nSkillID = newinfo.nSkillID;
}
else
m_infoCurrent.vTargetPos = newinfo.vTargetPos;
}
//다음 상태 설정
SetNextState( NewState, newinfo );
//추적이다. 서버에 알림
if (!newinfo.isRegionTarget) // 논 타겟팅일 경우 추적하지 않습니다.
//SendServerReq( SObjectState::STATE_CHASE, newinfo );
SendServerReq( SObjectState::STATE_ATTACK, newinfo );
return true;
}
newinfo.vTargetPos = vtNextMove;
newinfo.bIsMovePos = false;
m_pReceiver->FindDetour( vtNextMove, true, true);
}
else
{
newinfo.vTargetPos = GetChasePos();
m_pReceiver->FindDetour( newinfo.vTargetPos, true, true );
size_t numDetourPoints = m_pReceiver->GetDetourSize();
if(numDetourPoints >= 2)
{
int x, y;
if(m_pReceiver->GetDetourPoint(1, x, y))
{
vtNextMove.x = (float) x;
vtNextMove.y = (float) y;
vtNextMove.z = 0.0f;
bNextMove = true;
}
}
}
newinfo.vTargetPos = vtNextMove;
newinfo.SpeedSync = false; //크리처일 경우 자기 속도로 움직인다
//새로운 정보의 타겟이 존재한다면 현재 정보 갱신
if( newinfo.hTarget != 0 )
{
m_infoCurrent.hTarget = newinfo.hTarget;
m_infoCurrent.fRange = newinfo.fRange;
m_infoCurrent.nSkillID = newinfo.nSkillID;
}
else
m_infoCurrent.vTargetPos = newinfo.vTargetPos;
if ( m_pReceiver->CollisionToLine( *m_pReceiver->GetPosition(), vtNextMove ) ) // 앞에 장애물이 있으면..
{
newinfo.vTargetPos = *m_pReceiver->GetPosition();
SetNextState( NewState, newinfo );
SendServerReq( SObjectState::STATE_MOVE, newinfo );
}
else
{
SetNextState( NewState, newinfo );
SendServerReq( SObjectState::STATE_CHASE, newinfo );
}
return true;
}
//이동중인 상태가 아니면서, 내가 이동 중이면
if ( CheckMovingState( NewState ) == false && m_pReceiver->IsMoving() == true )
{
if( NewState != SObjectState::STATE_IDLE && NewState != SObjectState::STATE_NONE )
{
//정지할 위치가 필요없다면
if( m_nNeedStopPos == NOT_NEED_STOP_POS )
return true;
//추적하라???
SStateInfo newinfo = infoState;
newinfo.vTargetPos = GetChasePos();
m_pReceiver->FindDetour( newinfo.vTargetPos, true, true );
size_t numDetourPoints = m_pReceiver->GetDetourSize();
if(numDetourPoints >= 2)
{
int x, y;
if(m_pReceiver->GetDetourPoint(1, x, y))
{
vtNextMove.x = (float) x;
vtNextMove.y = (float) y;
vtNextMove.z = 0.0f;
bNextMove = true;
}
}
if(!bNextMove) newinfo.vTargetPos = vtMyself;
//새로운 정보의 타겟이 존재한다면 현재 정보 갱신
if( newinfo.hTarget != 0 )
{
m_infoCurrent.hTarget = newinfo.hTarget;
m_infoCurrent.fRange = newinfo.fRange;
m_infoCurrent.nSkillID = newinfo.nSkillID;
}
else
m_infoCurrent.vTargetPos = newinfo.vTargetPos;
SetNextState( NewState, newinfo );
SendServerReq( SObjectState::STATE_CHASE, newinfo );
}
}
else
{
if( NewState == SObjectState::STATE_MOVE )
{
if(SObjectState::STATE_SIT == m_idCurState)
{
m_pReceiver->ReqNormal();
}
int nDetourSize = m_pReceiver->GetAllDetourPointSize();
if( nDetourSize <= 0 )
{
m_pReceiver->FindDetour( infoState.vTargetPos, true, false );
}
}
SendServerReq( NewState, infoState );
}
break;
case SObjectState::PERMIT_FORBIDDEN: // 불가
// _oprint( "[상태머신 : RequestNewState - SObjectState::PERMIT_FORBIDDEN]\n" );
{
/*
if( m_idCurState == SObjectState::STATE_ATTACK && NewState == SObjectState::STATE_CAST )
{
int nBoneCnt = 0;
DWORD dwMinTime = 0, dwMaxTime = 0;
DWORD dwCurAniTime = 0;
m_pReceiver->GetCurrentAnimationInfo( nBoneCnt, dwMinTime, dwMaxTime, dwCurAniTime );
dwMaxTime = dwMaxTime / 160;
dwCurAniTime = (dwCurAniTime / 160) * 4.8f;
float fPlayProgress = (dwCurAniTime / (float)dwMaxTime);
//80프로 애니메이션 진행되면 모션 캔슬 가능
if( fPlayProgress > 0.8f )
{
int x = 0;
x = 0;
}
}*/
}
return false;
case SObjectState::PERMIT_PENDING: // 대기시킨다.
// _oprint( "[상태머신 : RequestNewState - SObjectState::PERMIT_PENDING]\n" );
if( !infoState.chase )
{
if( NewState == SObjectState::STATE_ATTACK )
{
//공격 중인 상태에서 같은 타겟에 공격 명령이 들어 오면 Input 안됨
if( SObjectState::STATE_ATTACK == m_idCurState || SObjectState::STATE_AIMING == m_idCurState )
{
if( m_infoCurrent.hTarget == infoState.hTarget )
return false;
}
}
}
/// 2010.12.06 파이어 상태일 때는 캐스팅과 아이템 사용을 못 하게 한다,(연속 스킬일 경우에 스킬이 취소되는 것을 방지하기 위해서) - prodongi
if ((SObjectState::STATE_CAST == NewState || SObjectState::STATE_USEITEM == NewState) &&
SObjectState::STATE_FIRE == m_idCurState)
{
return false;
}
//모션 캔슬 가능 상태
bool bMotionCancel = true;
if( m_pCurrentState->GetMotionCancelInfo() )
bMotionCancel = m_pReceiver->MotionCancel();
SetPendingState( NewState, infoState );
//개선 해야함
switch( NewState )
{
case SObjectState::STATE_MOVE:
case SObjectState::STATE_TAKEITEM:
case SObjectState::STATE_SIT:
case SObjectState::STATE_USEITEM:
case SObjectState::STATE_CAST:
{
//공격 및 슈팅 중에 이동이나 아이템 줍기 및 아이템 사용을 요청할때 바로 반응 할수 있도록 먼저 캐스팅 캔슬을 요청한다
if( SObjectState::STATE_SHOOTING == m_idCurState )
{
m_pReceiver->ReqCancelAction();
}
else if( SObjectState::STATE_ATTACK == m_idCurState )
{
if( bMotionCancel )
{
m_pReceiver->ReqCancelAction();
}
}
}
break;
case SObjectState::STATE_CAST_CANCEL:
{
//공격 및 조준중일때 와 캐스팅 일때 캐스팅 캔슬이 가능하다
if( SObjectState::STATE_ATTACK == m_idCurState ||
SObjectState::STATE_AIMING == m_idCurState ||
SObjectState::STATE_SHOOTING == m_idCurState ||
SObjectState::STATE_CAST == m_idCurState )
{
m_pReceiver->ReqCancelAction();
}
}
break;
case SObjectState::STATE_ATTACK:
case SObjectState::STATE_AIMING:
{
//공격중이고 다시 공격 명령이 들어왔다면 현재 타겟과 새로운 타겟이 다른지 검사한다
if( SObjectState::STATE_ATTACK == m_idCurState || SObjectState::STATE_AIMING == m_idCurState )
{
if( m_infoCurrent.hTarget != infoState.hTarget )
m_pReceiver->ReqCancelAction();
}
}
break;
}
}
return true;
}
//현재 상태 정지
void SObjectStateMachine::StopCurrentState()
{
//SDEBUGLOG( "[상태머신 : StopCurrentState]\n" );
if( NULL != m_pCurrentState )
{
if ( SObjectState::STATE_NONE == m_idNextState )
{
// 다음 상태가 없는 경우
// 대기 상태가 있으면 현재 상태로 돌린다.
if( m_idPendingState != SObjectState::STATE_NONE )
{
//SDEBUGLOG( "[상태머신 : StopCurrentState 01]\n" );
bool bRequestNewState = true;
if( m_pCurrentState->GetInfo().nSkillID == SKILL_NAME::SKILL_WARP )
{
bRequestNewState = false;
}
m_pReceiver->ReqCancelAction();
m_pCurrentState = NULL;
m_idCurState = SObjectState::STATE_NONE;
m_idCreatureState = SObjectState::STATE_IDLE;
if( bRequestNewState ) RequestNewState( m_idPendingState, m_infoPending );
ClearPending();
}
// 대기 상태가 없으면 디폴트 상태로 돌린다.
else
{
//SDEBUGLOG( "[상태머신 : StopCurrentState 02]\n" );
m_pCurrentState = NULL;
m_idCurState = SObjectState::STATE_NONE;
m_idCreatureState = SObjectState::STATE_IDLE;
RequestNewState( m_idDefaultState, m_infoDefault );
m_pCurrentState = NULL;
}
}
// 다음 상태가 있으면 설정
else
{
//SDEBUGLOG( "[상태머신 : StopCurrentState 03]\n" );
// m_pCurrentState = NULL;
// m_idCurState = SObjectState::STATE_NONE; //현재 상태도 None
// m_idCreatureState = SObjectState::STATE_IDLE;
// m_infoNext.vTargetPos = *m_pReceiver->GetPosition();
// SendServerReq( m_idNextState, m_infoNext );
bool bRequestNewState = true;
if( m_pCurrentState->GetInfo().nSkillID == SKILL_NAME::SKILL_WARP )
{
bRequestNewState = false;
}
m_pCurrentState = NULL;
SObjectState::ID NextState = m_idNextState;
m_idNextState = SObjectState::STATE_NONE; //다음 상태도 None
if( RequestNewState( NextState, m_infoNext ) == false && bRequestNewState )
{
ClearAllState();
ClearPendingNextState();
}
}
}
/// 2010.12.21 상태가 초기화 되지 않아서 펫이 가만히 있는 것 같음 - prodongi
else
{
m_pCurrentState = NULL;
m_idCurState = SObjectState::STATE_NONE;
m_idCreatureState = SObjectState::STATE_IDLE;
RequestNewState( m_idDefaultState, m_infoDefault );
m_pCurrentState = NULL;
}
}
void SObjectStateMachine::SetNextState( SObjectState::ID State, const SStateInfo &info )
{
SDEBUGLOG( "[상태머신 : SetNextState]\n" );
m_idNextState = State;
m_infoNext = info;
}
float SObjectStateMachine::GetTargetDist( SGameObject *pTarget )
{
K3DVector spos, tpos;
spos = *m_pReceiver->GetPosition();
tpos = *pTarget->GetPosition();
spos.z = 0.f;
tpos.z = 0.f;
m_pReceiver->CorrectivePosition( spos, spos );
m_pReceiver->CorrectivePosition( tpos, tpos );
return GetDistance( spos, tpos );
}
//추적 거리 구하기
K3DVector SObjectStateMachine::GetChasePos( SGameAvatarEx* pTarget, K3DVector* pTargetPos, float fTargetSize, const SStateInfo &infoState, SObjectState::ID NewState )
{
//길찾기 땜에 int로 변환한다 안그럼 오차생김
K3DVector pos = K3DVector( (int)pTargetPos->x, (int)pTargetPos->y, (int)pTargetPos->z );
//정지할 위치를 구했다면 더이상 정지할 위치를 필요로 하지 않는다
if( m_nNeedStopPos != NOT_NEED_STOP_POS )
m_nNeedStopPos = INSPECTED_STOP_POS;
return pos;
/*
K3DVector pos, newpos;
float fDistance;
bool bIsNeedRange = false;
if ( infoState.fRange > 0 )
{
K3DVector vTarget = K3DVector( pTargetPos->x, pTargetPos->y, 0.0f );
K3DVector vSelf = *m_pReceiver->GetPosition();
vSelf.z = 0.f;
K3DVector attackview;
K3DVector vDist = vTarget - vSelf;
float tar_range = fTargetSize;
float my_range = infoState.fRange + GetSize( m_pReceiver );
K3DVectorNormalize( attackview, vDist );
//원거리 공격 이라면
if( NotProximityAttack( NewState ) )
{
//이동 중이다
if( m_pReceiver->IsMoving() )
{
//회전 중이다
if( m_pReceiver->IsRotation() )
{
float fTargetRoll = m_pReceiver->GetTargetRoll();
K3DMatrix zRollMat;
K3DMatrixIdentity( zRollMat );
K3DMatrixRotationZ( zRollMat, fTargetRoll + 3.1415926f/2 );
attackview = K3DVector( 0.0f, 1.0f, 0.0f );
K3DVectorTransform( attackview, attackview, zRollMat );
attackview = -attackview;
}
else
attackview = m_pReceiver->GetViewVector();
}
//활을 들고 있다면 활 전용 스킬
if( IsBowAttack() )
{
bIsNeedRange = true;
}
else
{
if( infoState.nSkillID != 0 )
{
SkillBaseEx* s_data = GetSkillDB().GetSkillData( infoState.nSkillID );
if( s_data )
{
//근접이 아니라면
if( s_data->GetCastRange() != -1 )
{
bIsNeedRange = true;
}
}
}
}
}
//근접 공격이라면
else if( ProximityAttack( NewState ) )
{
//활 공격이냐
if( IsBowAttack() )
{
if( m_pReceiver->IsMoving() )
{
if( m_pReceiver->IsRotation() )
{
float fTargetRoll = m_pReceiver->GetTargetRoll();
K3DMatrix zRollMat;
K3DMatrixIdentity( zRollMat );
K3DMatrixRotationZ( zRollMat, fTargetRoll + 3.1415926f/2 );
attackview = K3DVector( 0.0f, 1.0f, 0.0f );
K3DVectorTransform( attackview, attackview, zRollMat );
attackview = -attackview;
}
else
attackview = m_pReceiver->GetViewVector();
}
bIsNeedRange = true;
}
else
{
if( m_pReceiver->GetDBAttackRange() > 1.f )
bIsNeedRange = true;
}
}
fDistance = GetDistance( vTarget, vSelf ); //전체 거리
/// 2010.10.14 줍기 거리와 구분 해줘야 된다 - prodongi
bool isInRange = false;
if (SObjectState::STATE_TAKEITEM != NewState)
{
fDistance = fDistance - (my_range + tar_range );
}
//fDistance = fDistance - (my_range + tar_range );
if ( fDistance < 0.f ) // 레인지 범위 안쪽
{
if( m_pReceiver->IsMoving() )
{
//이동 중일때는 가는 방향에서 약간 앞으로 이동..
pos = vSelf + ( attackview * (GameRule::DEFAULT_UNIT_SIZE)); // 1m 더 간다.
}
else
{ //정지 중... 지금 자리 준다.
pos = vSelf; // 일단은 그대로 쓴다...
// 나중에 너무 가까울때를 처리 해야 할 수도 있다.
}
}
else
{
K3DVector vRadius;
if( bIsNeedRange ) //원거리 공격
{
/// 2010.10.21 - prodongi
//if( pTarget->IsMoving() )
if(pTarget && pTarget->IsMoving() )
{
// ArMoveVector _mv = pTarget->GetArObject().mv;
// DWORD dwEndTime = pTarget->GetArObject().mv.GetTargetPosTime();
// DWORD dwProcTime = pTarget->GetArObject().mv.GetProcTime();
// DWORD dwCurTime = GetArTime();
// dwCurTime += (dwEndTime - dwProcTime)/2;
// _mv.Step( dwCurTime );
// K3DVector vTargetFinalPos( _mv.x, _mv.y, 0.0f );
// vRadius = vSelf - vTargetFinalPos;
vRadius = vSelf - vTarget;
vRadius.Normalize();
float needdist = tar_range + my_range;
vRadius = vRadius * (needdist-(float)GameRule::DEFAULT_UNIT_SIZE);
pos = vTarget + vRadius;
}
else
{
vRadius = vSelf - vTarget;
vRadius.Normalize();
float needdist = tar_range + my_range;
vRadius = vRadius * (needdist-(float)GameRule::DEFAULT_UNIT_SIZE);
pos = vTarget + vRadius;
}
}
/// 2010.10.21 타겟이 없는 것은 타겟이 제자리에 있다고 생각해도 될 것 같다 - prodongi
//else if( !pTarget->IsMoving() ) //근접이고 타겟이 제자리에 있다면
else if( !pTarget || !pTarget->IsMoving() ) //근접이고 타겟이 제자리에 있다면
{
vRadius = vSelf - vTarget;
vRadius.Normalize();
/// 2010.10.14 - prodongi
if (SObjectState::STATE_TAKEITEM == NewState)
{
vRadius = vRadius * (GameRule::MAX_TAKE_ITEM_RANGE*c_fRangeAcceptable); //80% 앞으로 땡겨주자
}
else
{
float needdist = tar_range + GetSize( m_pReceiver );
vRadius = vRadius * (needdist*c_fRangeAcceptable); //80% 앞으로 땡겨주자
}
//float needdist = tar_range + GetSize( m_pReceiver );
//vRadius = vRadius * (needdist*c_fRangeAcceptable); //80% 앞으로 땡겨주자
pos = vTarget + vRadius;
}
/// 2010.10.21 - prodongi
//else //근접이고 타겟이 이동중이면
else //근접이고 타겟이 이동중이면
{
//예측 이동
ArMoveVector _mv = pTarget->GetArObject().mv;
DWORD dwEndTime = pTarget->GetArObject().mv.GetTargetPosTime();
DWORD dwProcTime = pTarget->GetArObject().mv.GetProcTime();
DWORD dwCurTime = GetArTime();
if( (int)(dwEndTime - dwProcTime) <= 60 ) dwCurTime = dwEndTime - 30;
else dwCurTime = dwCurTime + 50;
_mv.Step( dwCurTime );
pos.x = _mv.x;
pos.y = _mv.y;
}
}
}
else
pos = GetChasePos();
//보정 해야 한다.
m_pReceiver->CorrectivePosition( pos, newpos );
//정지할 위치를 구했다면 더이상 정지할 위치를 필요로 하지 않는다
if( m_nNeedStopPos != NOT_NEED_STOP_POS )
m_nNeedStopPos = INSPECTED_STOP_POS;
newpos.x = (int)newpos.x;
newpos.y = (int)newpos.y;
newpos.z = (int)newpos.z;
return newpos;
*/
}
K3DVector SObjectStateMachine::GetChasePos()
{
K3DVector vSelf = *m_pReceiver->GetPosition();
K3DVector attackview = m_pReceiver->GetViewVector();
//이동 중이라면 내가 클릭한 이동 위치로 방향을 구하자
if( m_pReceiver->IsMoving() )
{
if( m_pReceiver->IsRotation() )
{
float fTargetRoll = m_pReceiver->GetTargetRoll();
K3DMatrix zRollMat;
K3DMatrixIdentity( zRollMat );
K3DMatrixRotationZ( zRollMat, fTargetRoll + 3.1415926f/2 );
attackview = K3DVector( 0.0f, 1.0f, 0.0f );
K3DVectorTransform( attackview, attackview, zRollMat );
attackview = -attackview;
}
else
{
attackview = *m_pReceiver->GetTerrainPickPos() - vSelf;
attackview.Normalize();
}
// float needdist = 4.f;
float needdist = (float)GameRule::DEFAULT_UNIT_SIZE;
vSelf = vSelf + ( attackview * needdist );
}
//길찾기 땜에 int로 변환한다 안그럼 오차생김
K3DVector pos = K3DVector( (int)vSelf.x, (int)vSelf.y, (int)vSelf.z );
//정지할 위치를 구했다면 더이상 정지할 위치를 필요로 하지 않는다
if( m_nNeedStopPos != NOT_NEED_STOP_POS )
m_nNeedStopPos = INSPECTED_STOP_POS;
return pos;
// return vSelf;
}
K3DVector SObjectStateMachine::GetCreatureChasePos( SGameAvatarEx* pReceiver, SGameAvatarEx* pTarget, K3DVector* pTargetPos, const SStateInfo &infoState, SObjectState::ID NewState )
{
//위에서 pReceiver, pTarget 검사하기 때문에 NULL 체크 하지 않는다
float fDistance;
bool bIsNeedRange = false;
K3DVector pos;
K3DVector vTarget = *pTargetPos;
K3DVector vSelf = *pReceiver->GetPosition();
vTarget.z = 0.f;
vSelf.z = 0.f;
K3DVector attackview;
K3DVector vDist = vTarget - vSelf;
float tar_range = GetSize( pTarget );
float my_range = infoState.fRange + GetSize( m_pReceiver );
K3DVectorNormalize( attackview, vDist );
//원거리 공격 이라면
if( NotProximityAttack( NewState ) )
{
//이동 중이다
if( m_pReceiver->IsMoving() )
{
//회전 중이다
if( m_pReceiver->IsRotation() )
{
float fTargetRoll = m_pReceiver->GetTargetRoll();
K3DMatrix zRollMat;
K3DMatrixIdentity( zRollMat );
K3DMatrixRotationZ( zRollMat, fTargetRoll + 3.1415926f/2 );
attackview = K3DVector( 0.0f, 1.0f, 0.0f );
K3DVectorTransform( attackview, attackview, zRollMat );
attackview = -attackview;
}
else
attackview = m_pReceiver->GetViewVector();
}
//활을 들고 있다면 활 전용 스킬
if( IsBowAttack() )
{
bIsNeedRange = true;
}
else
{
if( infoState.nSkillID != 0 )
{
SkillBaseEx* s_data = GetSkillDB().GetSkillData( infoState.nSkillID );
if( s_data )
{
//근접이 아니라면
if( s_data->GetCastRange() != -1 )
{
bIsNeedRange = true;
}
}
}
}
}
//근접 공격이라면
else if( ProximityAttack( NewState ) )
{
//활 공격이냐
if( IsBowAttack() )
{
if( m_pReceiver->IsMoving() )
{
if( m_pReceiver->IsRotation() )
{
float fTargetRoll = m_pReceiver->GetTargetRoll();
K3DMatrix zRollMat;
K3DMatrixIdentity( zRollMat );
K3DMatrixRotationZ( zRollMat, fTargetRoll + 3.1415926f/2 );
attackview = K3DVector( 0.0f, 1.0f, 0.0f );
K3DVectorTransform( attackview, attackview, zRollMat );
attackview = -attackview;
}
else
attackview = m_pReceiver->GetViewVector();
}
bIsNeedRange = true;
}
else
{
if( m_pReceiver->GetDBAttackRange() > 1.f )
bIsNeedRange = true;
}
}
fDistance = GetDistance( vTarget, vSelf ); //전체 거리
fDistance = fDistance - (my_range + tar_range );
if ( fDistance < 0.f ) // 레인지 범위 안쪽
{
if( m_pReceiver->IsMoving() )
{
//이동 중일때는 가는 방향에서 약간 앞으로 이동..
pos = vSelf + ( attackview * (GameRule::DEFAULT_UNIT_SIZE)); // 1m 더 간다.
}
else
{ //정지 중... 지금 자리 준다.
pos = vSelf; // 일단은 그대로 쓴다...
// 나중에 너무 가까울때를 처리 해야 할 수도 있다.
}
}
else // 범위 밖 레인지 만큼 다가섬
{
K3DVector vRadius;
if( bIsNeedRange ) //원거리 공격
{
if( pTarget->IsMoving() )
{
vRadius = vSelf - vTarget;
vRadius.Normalize();
float needdist = tar_range + my_range;
vRadius = vRadius * (needdist-(float)GameRule::DEFAULT_UNIT_SIZE);
pos = vTarget + vRadius;
}
else
{
vRadius = vSelf - vTarget;
vRadius.Normalize();
float needdist = tar_range + my_range;
vRadius = vRadius * (needdist-(float)GameRule::DEFAULT_UNIT_SIZE);
pos = vTarget + vRadius;
}
}
else if( !pTarget->IsMoving() ) //근접이고 타겟이 제자리에 있다면
{
vRadius = vSelf - vTarget;
vRadius.Normalize();
float needdist = tar_range + GetSize( m_pReceiver );
vRadius = vRadius * (needdist*c_fRangeAcceptable); //80% 앞으로 땡겨주자
pos = vTarget + vRadius;
}
else //근접이고 타겟이 이동중이면
{
//예측 이동
ArMoveVector _mv = pTarget->GetArObject().mv;
DWORD dwEndTime = pTarget->GetArObject().mv.GetTargetPosTime();
DWORD dwProcTime = pTarget->GetArObject().mv.GetProcTime();
DWORD dwCurTime = GetArTime();
if( (int)(dwEndTime - dwProcTime) <= 60 ) dwCurTime = dwEndTime - 30;
else dwCurTime = dwCurTime + 50;
_mv.Step( dwCurTime );
pos.x = _mv.x;
pos.y = _mv.y;
}
}
return pos;
}
// 2010.06.15 - prodongi
void SObjectStateMachine::setAutoAttack(bool autoAttack)
{
m_bAutoAttack = autoAttack;
}
AR_UNIT SObjectStateMachine::GetSize( SGameAvatarEx *pObject )
{
AR_UNIT size = pObject->GetSize()/2.f; // 캐릭터 사이즈는 지름이라 2 나눈다.
AR_UNIT scale = pObject->GetScale(); // 캐릭터 사이즈 비율
if( pObject->GetObjType() == TS_ENTER::GAME_PLAYER )
{
// 플레이어의 경우 서버에서 금지된 융합, 변신등으로 인한 사이즈가 반영되지 않는다.
size = 1.f / 2.f;
scale = 1.f;
}
AR_UNIT allsize = size*scale * GameRule::DEFAULT_UNIT_SIZE; // 실제 좌표로 변환
return allsize;
}
//현재 타겟과의 유효거리와 사거리 검사
bool SObjectStateMachine::IsRange( K3DVector & my_pos, float fmy_size, const K3DVector & pos, float ftarget_size, float fRange, bool bCollisionToLine, int & nResultLength )
{
K3DVector vSelf = my_pos; // *m_pReceiver->GetPosition();
K3DVector vTarget = pos;
vSelf.z = 0.f;
vTarget.z = 0.f;
float target_size = ftarget_size;
float my_size = fmy_size; // GetSize( m_pReceiver );
float fDistance = GetDistance( vTarget, vSelf );
if( fDistance > 0.0f )
{ //거리가 양수 이면, 사거리와 크기를 뺀다.
fDistance = fDistance - (fRange + my_size + target_size );
nResultLength = (int)fDistance;
if( 0 <= fDistance && fDistance < 1 ) // 정확도 오차가 있을 수 있어 정확히 맞는 범위(fDistance == 0)는 사거리에 들었다고 판단하지 않는다.
{
nResultLength = 1;
}
if( nResultLength <= 0 )
{
// 사거리 안에 들었다면 앞에 무언가로 막혀있는지 확인한다.
if( bCollisionToLine == false || m_pReceiver->CollisionToLine( vSelf, vTarget ) == false )
{
return true;
}
}
return false;
}
else
{
nResultLength = (int)fDistance;
return true;
}
nResultLength = (int)fDistance;
return false;
}
void SObjectStateMachine::SetAttackLock( bool bAttackLock )
{
m_bLockAttack = bAttackLock;
#ifndef NDEBUG
if( m_bLockAttack )
_oprint( "SetAttackLock - Lock\n" );
else
_oprint( "SetAttackLock - UnLock\n" );
#endif
}
void SObjectStateMachine::Process( DWORD dwTime )
{
m_dwTime = dwTime;
if( !m_queInputState.empty() )
{
PENDING_INPUT_STATE* prevstate = &m_queInputState.front();
if( prevstate->nInputID != RQ_MOVE )
{
PendingInput( prevstate );
m_queInputState.pop();
return;
}
else if( m_queInputState.size() >= 3 ) //큐에 쌓인 입력 정보가 3개를 넘었다면 버그 다 날려버리자
{
while( !m_queInputState.empty() )
m_queInputState.pop();
}
}
if( !m_pReceiver->IsMoving() )
{
m_nNeedStopPos = NEED_STOP_POS;
}
//전투 대기 상태
if( GetMode() == MODE_ATTACK )
{
if( m_dwAttackTime == 0 )
m_dwAttackTime = m_dwTime;
bool bCurState = (GetCurrentState() == SObjectState::STATE_SKILL_END );
bool bPrevState = (GetPrevState() == SObjectState::STATE_ATTACK || GetPrevState() == SObjectState::STATE_SHOOTING);
bool bPendingState = (GetPendingState() == SObjectState::STATE_NONE);
//현재 상태는 IDLE이고 Pending된 명령이 없어야 하며
//이전 상태가 공격이였거나 || 자동 공격을 해야 할경우
if( bCurState && bPendingState && ( bPrevState || m_bAutoAttack ) )
{
// sonador 1.8.13 아바타 IDLE 모션 동작 구현
if( IsMoving() == false &&
( m_pReceiver->GetCurrAnimationID() == ANI_DEFAULT01 ||
m_pReceiver->GetCurrAnimationID() == ANI_DEFAULT02 ||
m_pReceiver->GetCurrAnimationID() == ANI_IDLE ) )
{
m_pReceiver->CheckPrevState();
SetDefaultPrevState();
// 2010.06.15 - prodongi
//m_bAutoAttack = false;
setAutoAttack(false);
m_dwAttackTime = m_dwTime;
}
return;
}
if( (m_dwTime - m_dwAttackTime) > ATTACK_WAIT_TIME )
{
//Attack Casting Fire중일때 시간 재설정
if( m_pReceiver->IsAttacking() || m_pReceiver->IsUsingSkill() )
{
m_dwAttackTime = m_dwTime;
}
else
{
SetMode( MODE_NORMAL );
m_pReceiver->ReqNormal(); //기본 상태로
//안 움직이고 있는 상태 일때만, 통지~
if( IsMoving() == false && m_pReceiver->IsMoving() == false )
{
//앉아 있으면, 안함.
if( m_pReceiver->GetStatus() & TS_ENTER::PlayerInfo::FLAG_SITDOWN )
return;
StopCurrentState();
return;
}
}
}
}
if( GetMode() == MODE_MOUNT )
{
if( m_idCurState == SObjectState::STATE_MOUNT )
{
//플레이어 인지 체크, 이동중인지 체크
if( /*m_pReceiver->GetObjType() == TS_ENTER::PLAYER &&*/ !m_pReceiver->IsMoving() )
{
//이동이 끝났다면 IDLE상태로
StopCurrentState();
}
}
}
// 탑승모드가 아니고 내리는 상태라면
else if( m_idCurState == SObjectState::STATE_UNMOUNT )
{
StopCurrentState();
}
if( NULL != m_pCurrentState )
{
//스킬 완료는 들어 오자 마자 Stop 시킨다.
if ( m_idCurState == SObjectState::STATE_FIRE ||
m_idCurState == SObjectState::STATE_SKILL_END )
{
if( m_pReceiver->IsUseSkill_complete() && !m_pReceiver->IsUsingSkill() )
{
StopCurrentState();
}
return;
}
//공격 검사
if( m_idCurState == SObjectState::STATE_ATTACK ||
m_idCurState == SObjectState::STATE_SHOOTING )
{
if( m_dwAttackDelayTime == 0 )
m_dwAttackDelayTime = m_dwTime;
if( !m_pReceiver->IsAttacking() && !m_pReceiver->IsUsingSkill() ) //Fixed Follow Problem // ZONE
{
StopCurrentState();
return;
}
//예약된게 있다면
else if( m_idPendingState != SObjectState::STATE_NONE )
{
//휴지기가 없다면 바로 캔슬을 날리자
if( m_infoCurrent.dwDuration <= 0 )
m_pReceiver->ReqCancelAction();
else
{
if( ( m_dwTime - m_dwAttackDelayTime ) >= (m_infoCurrent.dwDuration / 2) )
m_pReceiver->ReqCancelAction();
}
}
return;
}
if( m_idCurState == SObjectState::STATE_ATTACK_END )
{
if( !m_pReceiver->IsAttacking() && !m_pReceiver->IsUsingSkill() )
{
StopCurrentState();
}
return;
}
/* if( m_idCurState == SObjectState::STATE_CAST_CANCEL )
{
ClearAllState();
return;
}*/
if( m_idCurState == SObjectState::STATE_IDLE || m_idCurState == SObjectState::STATE_CAST_CANCEL )
{
if( m_pReceiver->IsUseSkill_complete() && !m_pReceiver->IsUsingSkill() ) // floyd #2.3.1.15
{
StopCurrentState();
}
return;
}
// TODO : ATTACK_END가 오면 state 중지
// 지속 시간
/* DWORD dwDurationTime = m_pCurrentState->GetInfo().dwDuration;
// 지속시간이 정해져 있고, 시간이 초과 되었다면 현재 state를 중지
if( 0 < dwDurationTime && (m_dwTime - m_dwCurrentStateStartTime) >= dwDurationTime )
{
StopCurrentState();
return;
}*/
// 타겟의 유효성 검사
SGameAvatarEx* pTarget = NULL;
if ( m_infoCurrent.hTarget != 0 )
{
pTarget = (SGameAvatarEx*)m_pReceiver->GetGameObject( m_infoCurrent.hTarget );
if ( pTarget == NULL ) // 타겟이 없어지면
{
StopCurrentState();
return;
}
}
//추적이나 이동 상태이고, 이동이 완료된 경우
if( IsMoving() == true && m_pReceiver->IsMoving() == false )
{
// 목적지 도착인 경우
StopCurrentState();
return;
}
if( ( m_idCurState == SObjectState::STATE_CHASE && m_pReceiver->IsChasing() ) ||
( m_idCurState == SObjectState::STATE_MOVE && m_pReceiver->IsChasing() ) ) // 이동 상태이고 길찾기는 안끝났고 추적 모드면
{
if( pTarget == NULL && !m_infoCurrent.isRegionTarget )
return;
static DWORD dwFindDetourTick = 100;
if( (dwTime - m_dwFindDetour) > dwFindDetourTick )
{
m_dwFindDetour = dwTime;
K3DVertex vtMyPos = *m_pReceiver->GetPosition();
vtMyPos.z = 0.f;
// 아이템을 줍는다면 그냥 목적지까지 간다.
if( m_idNextState == SObjectState::STATE_TAKEITEM )
{
return;
}
int nReusultLen = 0;
if ( m_infoCurrent.isRegionTarget && IsRange(vtMyPos, GetSize( m_pReceiver ), m_infoCurrent.vRegionTargetPos, 1, m_infoCurrent.fRange, true, nReusultLen ) )
{
m_pReceiver->EraseDetour();
m_pReceiver->SetArObjectStop();
m_infoCurrent.vTargetPos = vtMyPos;
SendServerReq( m_idCurState, m_infoCurrent );
}else
if( pTarget && IsRange( vtMyPos, GetSize( m_pReceiver ), *pTarget->GetPosition(), GetSize( pTarget ), m_infoCurrent.fRange*c_fRangeAcceptable, true, nReusultLen ) )
{
m_pReceiver->EraseDetour();
m_pReceiver->SetArObjectStop();
m_infoCurrent.vTargetPos = vtMyPos;
SendServerReq( m_idCurState, m_infoCurrent );
}
else
{
K3DVertex vtTargetPos;
if ( m_infoCurrent.isRegionTarget ) vtTargetPos = m_infoCurrent.vRegionTargetPos;
else vtTargetPos = *pTarget->GetPosition();
size_t numDetourPoints = m_pReceiver->GetDetourSize();
int x = 0;
int y = 0;
//길찾기로 찾은 경로의 마지막 위치를 가져온다
if( m_pReceiver->GetDetourPoint(numDetourPoints - 1, x, y) )
{
K3DVector vFinalPos = K3DVector( x, y, 0.f );
K3DVector vTargetTemp = vtTargetPos;
vTargetTemp.z = 0;
int nFinalLen = static_cast< int >( GetDistance( vFinalPos, vtMyPos ) );
int nTargetLen = static_cast< int >( GetDistance( vTargetTemp, vtMyPos ) );
// 목적지 보다 타겟이 더 가까이 있는가?
if( nTargetLen < nFinalLen )
{
K3DVertex vtNextMove;
/*if( m_pReceiver->GetObjType() == TS_ENTER::GAME_SUMMON )
vtNextMove = GetCreatureChasePos( m_pReceiver, pTarget, pTarget->GetPosition(), m_infoCurrent, m_idNextState );
else*/
if ( m_infoCurrent.isRegionTarget ) vtNextMove = GetChasePos( NULL, &m_infoCurrent.vRegionTargetPos, 1, m_infoCurrent, m_idNextState );
else vtNextMove = GetChasePos( pTarget, pTarget->GetPosition(), GetSize(pTarget), m_infoCurrent, m_idNextState );
m_pReceiver->FindDetour(vtNextMove, true, true);
m_pReceiver->MoveDetour(vtNextMove);
m_infoCurrent.vTargetPos = vtNextMove;
SendServerReq( m_idCurState, m_infoCurrent );
}
// 목적지에서 타겟의 공격이 가능한가?
else if( pTarget && !IsRange( vFinalPos, GetSize( m_pReceiver ), *pTarget->GetPosition(), GetSize( pTarget ), m_infoCurrent.fRange*c_fRangeAcceptable, true, nReusultLen ) )
{
// 마지막 위치가 공격 가능하지 않다면 다시 이동
K3DVertex vtNextMove;
/*if( m_pReceiver->GetObjType() == TS_ENTER::GAME_SUMMON )
vtNextMove = GetCreatureChasePos( m_pReceiver, pTarget, pTarget->GetPosition(), m_infoCurrent, m_idNextState );
else*/
if ( m_infoCurrent.isRegionTarget ) vtNextMove = GetChasePos( NULL, &m_infoCurrent.vRegionTargetPos, 1, m_infoCurrent, m_idNextState );
else vtNextMove = GetChasePos( pTarget, pTarget->GetPosition(), GetSize(pTarget), m_infoCurrent, m_idNextState );
m_pReceiver->FindDetour(vtNextMove, true, true);
m_pReceiver->MoveDetour(vtNextMove);
m_infoCurrent.vTargetPos = vtNextMove;
SendServerReq( m_idCurState, m_infoCurrent );
}
}
}
}
return;
}
if ( m_idCurState == SObjectState::STATE_ATTACK )
{
if ( ((SGameAvatarEx*)pTarget)->IsLive() == false && m_pReceiver->IsAniLock() == false )
{
// 죽었고, 공격 모션이 완료 되었다.
StopCurrentState();
}
// 레인지 체크 - 타겟과 레인지가 존재할때
else if ( m_infoCurrent.bIsMovePos == false && m_infoCurrent.fRange > 0.0f )
{
K3DVertex vtMyPos, vtTargetPos;
K3DVertex vtNextMove;
vtMyPos = *m_pReceiver->GetPosition();
vtTargetPos = *pTarget->GetPosition();
int nReusultLen = 0;
if( !IsRange( vtMyPos, GetSize( m_pReceiver ), vtTargetPos, GetSize( pTarget ), m_infoCurrent.fRange*c_fRangeAcceptable, true, nReusultLen ) )
{
//타겟의 위치가 바뀌었을때 위치 갱신
/*if( m_pReceiver->GetObjType() == TS_ENTER::GAME_SUMMON )
vtNextMove = GetCreatureChasePos( m_pReceiver, pTarget, pTarget->GetPosition(), m_infoCurrent, m_idNextState );
else*/
vtNextMove = GetChasePos( pTarget, pTarget->GetPosition(), GetSize(pTarget), m_infoCurrent, m_idNextState );
m_pReceiver->FindDetour(vtNextMove, true, true);
m_pReceiver->MoveDetour(vtNextMove);
m_infoCurrent.vTargetPos = vtNextMove;
SObjectState::ID id = m_idCurState;
SStateInfo info = m_infoCurrent;
// 서버에 공격취소 요청
m_pReceiver->ReqCancelAction();
// RequestNewState( SObjectState::STATE_CHASE, info );
SendServerReq( SObjectState::STATE_CHASE, info );
SetNextState( id, info );
}
}
}
}
}
SObjectState* SObjectStateMachine::GetObjState( const SObjectState::ID State ) const
{
OBJSTATE_MAP::const_iterator it = m_StateMap.find( State );
if( it != m_StateMap.end() ) return it->second;
assert( false );
return NULL;
}
void SObjectStateMachine::StartNewState( SObjectState::ID state, const SStateInfo &info )
{
m_pCurrentState = GetObjState( state );
if( m_pCurrentState )
{
m_pCurrentState->SetInfo( info );
m_idCurState = state;
m_idCreatureState = state;
m_infoCurrent = info;
m_dwCurrentStateStartTime = m_dwTime;
m_pCurrentState->m_infoState.dwStartTime = m_infoCurrent.dwStartTime = m_dwTime;
m_dwAttackDelayTime = 0;
if ( m_pReceiver )
{
m_pReceiver->OnChangeState( m_idCurState, m_infoCurrent );
}
}
else
{
ClearInfo();
}
}
void SObjectStateMachine::SetPendingState( SObjectState::ID state, const SStateInfo &infoState )
{
// _oprint( "[상태머신 : SetPendingState]\n" );
m_idPendingState = state;
m_infoPending = infoState;
}
void SObjectStateMachine::ClearPending()
{
// _oprint( "[상태머신 : ClearPending]\n" );
m_idPendingState = SObjectState::STATE_NONE;
}
void SObjectStateMachine::AddState( SObjectState* pObjState )
{
// _oprint( "[상태머신 : AddState]\n" );
if( m_StateMap.find( pObjState->GetID() ) == m_StateMap.end() )
m_StateMap.insert( std::make_pair( pObjState->GetID(), pObjState ) );
}
void SObjectStateMachine::RemoveState( const SObjectState::ID State )
{
// _oprint( "[상태머신 : RemoveState]\n" );
OBJSTATE_MAP::iterator it = m_StateMap.find( State );
if( it != m_StateMap.end() )
{
delete it->second;
m_StateMap.erase( it );
}
}
void SObjectStateMachine::SetDefaultState( const SObjectState::ID State, const SStateInfo &info )
{
// _oprint( "[상태머신 : SetDefaultState]\n" );
m_idDefaultState = State;
m_infoDefault = info;
}
//전투형 상태인가
bool SObjectStateMachine::IsCombatState( SObjectState::ID newstate )
{
return ( newstate == SObjectState::STATE_AIMING ||
newstate == SObjectState::STATE_SHOOTING ||
newstate == SObjectState::STATE_ATTACK ||
newstate == SObjectState::STATE_CAST ||
newstate == SObjectState::STATE_FIRE ||
newstate == SObjectState::STATE_USEITEM );
}
//일반 공격인지 검사
bool SObjectStateMachine::ProximityAttack( SObjectState::ID stateId )
{
if( stateId == SObjectState::STATE_ATTACK || stateId == SObjectState::STATE_TAKEITEM )
return true;
return false;
}
//원거리 공격인지 검사
bool SObjectStateMachine::NotProximityAttack( SObjectState::ID stateId )
{
if( stateId == SObjectState::STATE_CAST || stateId == SObjectState::STATE_AIMING ||
stateId == SObjectState::STATE_FIRE || stateId == SObjectState::STATE_SHOOTING ||
stateId == SObjectState::STATE_USEITEM )
return true;
return false;
}
//활 공격 인지 검사
// AziaMafia Double Crossbow
//bool SObjectStateMachine::IsBowAttack()
//{
// ItemBase::ItemClass nLeftClass = m_pReceiver->GetLeftWeaponInfo();
// ItemBase::ItemClass nRightClass = m_pReceiver->GetRightWeaponInfo();
// if ((nLeftClass == ItemBase::CLASS_LIGHT_BOW || nLeftClass == ItemBase::CLASS_HEAVY_BOW) ||
// (nRightClass == ItemBase::CLASS_LIGHT_BOW || nRightClass == ItemBase::CLASS_HEAVY_BOW))
// {
// return true;
// }
//
// return false;
//}
// From ZONE source; dual crossbows
bool SObjectStateMachine::IsBowAttack()
{
ItemBase::ItemClass nLeftClass = m_pReceiver->GetLeftWeaponInfo();
ItemBase::ItemClass nRightClass = m_pReceiver->GetRightWeaponInfo();
if( ( nLeftClass == ItemBase::CLASS_LIGHT_BOW || nLeftClass == ItemBase::CLASS_HEAVY_BOW || nLeftClass == ItemBase::CLASS_CROSSBOW ) ||
( nRightClass == ItemBase::CLASS_LIGHT_BOW || nRightClass == ItemBase::CLASS_HEAVY_BOW || nRightClass == ItemBase::CLASS_CROSSBOW ) )
{
return true;
}
return false;
}
void SObjectStateMachine::ClearAllState()
{
m_pCurrentState = NULL;
m_idCurState = SObjectState::STATE_NONE;
m_idCreatureState = SObjectState::STATE_IDLE;
}
void SObjectStateMachine::Dead()
{
m_pCurrentState = NULL;
m_idCurState = SObjectState::STATE_NONE;
m_idCreatureState = SObjectState::STATE_IDLE;
ClearPending();
ClearNextState();
SetDefaultPrevState();
}
void SObjectStateMachine::ClearPendingNextState()
{
m_idPendingState = SObjectState::STATE_NONE;
m_idNextState = SObjectState::STATE_NONE;
while( !m_queInputState.empty() )
m_queInputState.pop();
}
void SObjectStateMachine::InputFreezing()
{
m_bInputFreezing = true;
}
void SObjectStateMachine::InputThawing()
{
m_bInputFreezing = false;
}