1705 lines
49 KiB
C++
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, ®ionTargetPos, 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;
|
|
}
|
|
|