1044 lines
26 KiB
C++
1044 lines
26 KiB
C++
#include "stdafx.h"
|
|
#include ".\SGameLocalCreature.h"
|
|
|
|
#include "GameDefine.h"
|
|
#include "KSeqAvatarEx.h"
|
|
|
|
#include "SBasicStat.h"
|
|
#include "SMonsterDB.h"
|
|
#include "SCreatureDB.h"
|
|
#include "SGameAniType.h"
|
|
#include "SAvatarProperty.h"
|
|
#include "SCreatureStateMachine.h"
|
|
#include "SSkillDB.h"
|
|
#include "SSKillStageType.h"
|
|
|
|
#include "SGameInput.h"
|
|
#include "SGameMessage.h"
|
|
#include "SStringDB.h"
|
|
|
|
#include "SGameMessage.h"
|
|
//#include "SGameMessageUI.h"
|
|
|
|
#include "LuaVM.h"
|
|
#include <toolkit/XStringUtil.h>
|
|
#include "SChatType.h"
|
|
|
|
#include "SDebug_Util.h"
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
|
|
bool SGameLocalCreature::s_bIdleProc = true;
|
|
|
|
SGameLocalCreature::SGameLocalCreature( int nCreatureID, unsigned char ucEnhance ) : SGameCreature( nCreatureID, ucEnhance )
|
|
, m_dwTryMoveTime( 0 )
|
|
, m_pMasterPlayer( NULL )
|
|
, m_hMaster( NULL )
|
|
, m_dwUpdateTime( 0 ) //서버 업데이트 시간
|
|
, m_dwBeginScriptTime( 0 )
|
|
, m_dwCallScriptTime( 1000 )
|
|
, m_vMasterLastPos( 0, 0, 0 )
|
|
, m_hTargetMonster( NULL )
|
|
, m_fLeftNRight( -1.0f )
|
|
, m_nCreatureState( STATE_NSTANDBY )
|
|
, m_nCreatureMode( BATTLE_MODE_OFF )
|
|
, m_dwFindDetour( 0 )
|
|
, m_dwFindDetourTick( 200 )
|
|
, m_arMasterSize( 0 )
|
|
, m_dwEscHitTime( 0 )
|
|
, m_EscHitCount( 0 )
|
|
, m_bDeadDialog( false )
|
|
, m_dwDeadDialogTime( 0 )
|
|
{
|
|
m_pStateVM = new SLocalCreatureStateMachine;
|
|
m_pStateVM->SetReceiver( this );
|
|
|
|
m_bLocalCreature = true;
|
|
m_arMySize = GetSize( this );
|
|
m_bFootsteps = true;
|
|
}
|
|
|
|
SGameLocalCreature::~SGameLocalCreature()
|
|
{
|
|
if( m_pStateVM ) m_pStateVM->SetReceiver( NULL );
|
|
|
|
if( m_bDeadDialog && IsDead() )
|
|
{
|
|
bool bClose = true;
|
|
if( m_pMasterPlayer )
|
|
{
|
|
//주인이 죽었다면 닫지 말자
|
|
if( m_pMasterPlayer->IsDead() )
|
|
bClose = false;
|
|
}
|
|
|
|
if( bClose )
|
|
{
|
|
m_bDeadDialog = false;
|
|
m_dwDeadDialogTime = 0;
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_all_message_box" ) );
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_rebirth_message_box" ) );
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_rebirth_PVP_message_box" ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SGameLocalCreature::Deactivate()
|
|
{
|
|
if( m_bDeadDialog && IsDead() )
|
|
{
|
|
bool bClose = true;
|
|
if( m_pMasterPlayer )
|
|
{
|
|
//주인이 죽었다면 닫지 말자
|
|
if( m_pMasterPlayer->IsDead() )
|
|
bClose = false;
|
|
}
|
|
|
|
if( bClose )
|
|
{
|
|
m_bDeadDialog = false;
|
|
m_dwDeadDialogTime = 0;
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_all_message_box" ) );
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_rebirth_message_box" ) );
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_rebirth_PVP_message_box" ) );
|
|
}
|
|
}
|
|
|
|
return SGameAvatarEx::Deactivate();
|
|
}
|
|
|
|
//IDLE 상태일때 테이머의 방향 벡터로 크리처의 방향을 구한다
|
|
void SGameLocalCreature::SetViewVectorStateIdle( const K3DVector &tpos )
|
|
{
|
|
float fTargetRoll = atan2( tpos.y, tpos.x );
|
|
if( fTargetRoll != m_fTargetRoll )
|
|
{
|
|
m_fTargetRoll = fTargetRoll;
|
|
m_dwPrevTime = m_dwTime;
|
|
m_bUseRot = true;
|
|
|
|
StandingDegree();
|
|
}
|
|
}
|
|
|
|
void SGameLocalCreature::SetMaster( AR_HANDLE hMaster, SGameAvatarEx * pMaster )
|
|
{
|
|
m_hMaster = hMaster;
|
|
m_pMasterPlayer = pMaster;
|
|
|
|
if( m_pMasterPlayer )
|
|
m_arMasterSize = GetSize( m_pMasterPlayer );
|
|
}
|
|
void SGameLocalCreature::SetTargetMonster( AR_HANDLE hMonster )
|
|
{
|
|
m_hTargetMonster = hMonster;
|
|
}
|
|
|
|
void SGameLocalCreature::CheckPrevState()
|
|
{
|
|
if( m_pStateVM == NULL ) return;
|
|
|
|
TS_ATTACK_REQUEST msg;
|
|
msg.handle = GetArID();
|
|
msg.target_handle = m_pStateVM->GetAttackTarget();
|
|
SendMsg( &msg );
|
|
}
|
|
|
|
bool SGameLocalCreature::Process( DWORD dwTime, unsigned long uProcessBitVector )
|
|
{
|
|
//시야에서 사라졌을때에도 지속적으로 서버에 TS_CS_UPDATE를 보내준다
|
|
if( m_dwUpdateTime == 0 )
|
|
{
|
|
m_dwUpdateTime = dwTime;
|
|
}
|
|
else
|
|
{
|
|
if( (dwTime - m_dwUpdateTime)>=UPDATE_TIME )
|
|
{
|
|
m_dwUpdateTime = dwTime; // sonador 7.0.27 로컬 크리쳐 업데이트 주기 오류 수정
|
|
//서버에 메세지 보냄
|
|
TS_CS_UPDATE msg;
|
|
msg.handle = GetArID();
|
|
msg.ar_time = GetArTime();
|
|
time_t nowtime;
|
|
time(&nowtime);
|
|
msg.rtc = nowtime;
|
|
|
|
|
|
SendMsg( &msg );
|
|
}
|
|
}
|
|
|
|
if( m_bIsActivated && m_bIsInit )
|
|
{
|
|
if( IsDead() )
|
|
{
|
|
if( !m_bDeadDialog )
|
|
{
|
|
m_bDeadDialog = true;
|
|
m_dwDeadDialogTime = GetSafeTickCount() + 5000; // 5초후에 죽었다고 다이얼로그 띄움
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_bDeadDialog && !IsDead() )
|
|
{
|
|
// 다이얼로그 닫자
|
|
m_bDeadDialog = false;
|
|
m_dwDeadDialogTime = 0;
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_all_message_box" ) );
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_rebirth_message_box" ) );
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_rebirth_PVP_message_box" ) );
|
|
}
|
|
}
|
|
|
|
if( IsDead() && m_dwDeadDialogTime && m_dwDeadDialogTime < GetSafeTickCount() )
|
|
{
|
|
bool bShowDlg = true;
|
|
if( m_pMasterPlayer )
|
|
{
|
|
//주인이 죽어있다면 다이얼로그 안띄우기
|
|
if( m_pMasterPlayer->IsDead() )
|
|
{
|
|
m_bDeadDialog = false;
|
|
bShowDlg = false;
|
|
}
|
|
}
|
|
|
|
m_dwDeadDialogTime = 0;
|
|
|
|
if( bShowDlg )
|
|
{
|
|
int nStringID = 809; //전투 불능 상태입니다. <br> 마을로 귀환하시려면 확인 버튼을 누르세요.
|
|
if( GetStateFlag() & FLAG_STATE::FLAG_STATE_RESURRECTION )
|
|
{
|
|
SendGameMsg( &SMSG_SEND_DATA( "close_all_message_box" ) );
|
|
|
|
StateInfoEx* pStateInfo = GetStateDBInfo( STATE_TYPE::STATE_RESURRECTION );
|
|
if( pStateInfo )
|
|
SendGameMsg( &SIMSG_REQ_OPEN_MSGBOX( SIMSG_REQ_OPEN_MSGBOX::MSGBOX_DEAD, GetArID(), false, S(nStringID), false, GetStringDB().GetString( pStateInfo->name_id ) ) );
|
|
else
|
|
SendGameMsg( &SIMSG_REQ_OPEN_MSGBOX( SIMSG_REQ_OPEN_MSGBOX::MSGBOX_DEAD, GetArID(), false, S(nStringID), false ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SGameCreature::Process( dwTime, uProcessBitVector ); // sonador 1.8.15 다른 플레이어의 크리처 스킬 연출 오류 수정
|
|
|
|
if( dwTime - m_dwBeginScriptTime >= m_dwCallScriptTime)
|
|
{
|
|
CheckCurrentState();
|
|
m_dwBeginScriptTime = dwTime;
|
|
}
|
|
|
|
if( m_pStateVM )
|
|
{
|
|
m_pStateVM->Process( m_dwTime );
|
|
}
|
|
|
|
m_dwEscHitTime = dwTime;
|
|
|
|
return true;
|
|
}
|
|
|
|
void SGameLocalCreature::Dead()
|
|
{
|
|
m_hTargetMonster = 0;
|
|
SetTargetMonster( m_hTargetMonster );
|
|
SetCreatureBattleModeOff();
|
|
|
|
SGameCreature::Dead();
|
|
}
|
|
|
|
void SGameLocalCreature::CheckCurrentState()
|
|
{
|
|
if( m_pStateVM == NULL ) return;
|
|
|
|
if( IsMountMode() || IsIng() || IsReservation() ) return;
|
|
|
|
std::string strState;
|
|
if( IsStandbyState() )
|
|
XStringUtil::Format( strState, "%s( %u )", "call_creature_standby", GetArID() );
|
|
else
|
|
{
|
|
switch( m_pStateVM->GetCreatureState() )
|
|
{
|
|
case SObjectState::STATE_ATTACK :
|
|
XStringUtil::Format( strState, "%s( %u, %u, %u )", "call_creature_attack", m_pMasterPlayer->GetArID(), GetArID(), m_hTargetMonster );
|
|
break;
|
|
case SObjectState::STATE_MOVE :
|
|
{
|
|
if( m_nCreatureMode == BATTLE_MODE_OFF )
|
|
XStringUtil::Format( strState, "%s( %u )", "call_creature_move", GetArID() );
|
|
}
|
|
break;
|
|
case SObjectState::STATE_IDLE :
|
|
{
|
|
if( m_nCreatureMode == BATTLE_MODE_OFF )
|
|
XStringUtil::Format( strState, "%s( %u, %u )", "call_creature_idle", m_pMasterPlayer->GetArID(), GetArID() );
|
|
else
|
|
{
|
|
SGameAvatarEx* pTargetMonster = (SGameAvatarEx*)GetGameObject(m_hTargetMonster);
|
|
if( pTargetMonster )
|
|
ReqAttack( m_hTargetMonster );
|
|
else
|
|
{
|
|
SetTargetMonster( m_hTargetMonster = 0 );
|
|
SetCreatureBattleModeOff();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( strState.length() )
|
|
LUA()->RunString( strState.c_str() );
|
|
}
|
|
void SGameLocalCreature::OnInput( class SGameInput * pInput )
|
|
{
|
|
if( !m_pStateVM ) return;
|
|
m_pStateVM->OnInput( pInput );
|
|
|
|
//공격 명령 또는 스킬명령 이 왔는가?
|
|
//다른 명령일 경우, 테이머를 쫓아 가도록 설정 한다.
|
|
static DWORD dwEscHitTime = 0;
|
|
|
|
int nInputID = pInput->GetInputID();
|
|
if( nInputID == RQ_CAST_CANCEL )
|
|
{
|
|
if( dwEscHitTime == 0 ) dwEscHitTime = m_dwEscHitTime;
|
|
if( (m_dwEscHitTime - dwEscHitTime) <= 1000 )
|
|
{
|
|
++m_EscHitCount;
|
|
if( m_EscHitCount >= 3 )
|
|
{
|
|
CreatureBackDownSpeedOfCreature();
|
|
SetUnHold();
|
|
|
|
dwEscHitTime = 0;
|
|
m_EscHitCount = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwEscHitTime = m_dwEscHitTime;
|
|
m_EscHitCount = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwEscHitTime = 0;
|
|
m_EscHitCount = 0;
|
|
}
|
|
}
|
|
|
|
void SGameLocalCreature::ReqTakeItem( AR_HANDLE handle )
|
|
{
|
|
TS_CS_TAKE_ITEM msg;
|
|
msg.item_handle = handle;
|
|
msg.taker_handle = GetArID(); // sonador #2.1.2.4.3 팻 조작 UI 연동
|
|
|
|
SendMsg( &msg );
|
|
}
|
|
|
|
void SGameLocalCreature::ReqMove( AR_HANDLE handle, const ArPosition & target, bool bSpeedSync, bool bChase )
|
|
{
|
|
// _oprint( "ReqMove : %f %f %f\n", target.x, target.y, target.z );
|
|
|
|
//전에 요청한 위치보다 FREQ_MOVE_LIMIT 만큼은 커야 한다.
|
|
float fLen = GetDistance( K3DVector(m_vTargetPos.x, m_vTargetPos.y, 0.f), K3DVector(target.x, target.y, 0.f) );
|
|
if( fLen < FREQ_MOVE_LIMIT )
|
|
{
|
|
// return;
|
|
}
|
|
|
|
//사용 중인 스킬이 있어서 이동 불가
|
|
if( IsUsingSkill() )
|
|
return;
|
|
|
|
if(IsMount()||IsIng()) return;
|
|
|
|
m_vTargetPos.x = target.x;
|
|
m_vTargetPos.y = target.y;
|
|
m_vTargetPos.z = target.z;
|
|
|
|
int nDetourSize = GetAllDetourPointSize();
|
|
if( nDetourSize > 0 )
|
|
SGameObject::ReqMove( handle, target, GetAllDetourPoint(), nDetourSize, bSpeedSync );
|
|
else
|
|
SGameObject::ReqMove( handle, target, &target, 1, bSpeedSync );
|
|
|
|
DeleteReqPoint();
|
|
}
|
|
|
|
void SGameLocalCreature::ReqAttack( AR_HANDLE target )
|
|
{
|
|
assert( target && "ReqAttack : Target ID Invalid!!!" );
|
|
|
|
//#ifndef NDEBUG
|
|
// SGameObject * pTarget = GetGameObject( target );
|
|
// if( pTarget )
|
|
// {
|
|
// float fDistance;
|
|
// K3DVector tpos = *pTarget->GetPosition();
|
|
// K3DVector spos = *GetPositionh();
|
|
// fDistance = K3DVectorGetLength( tpos, spos ); //전체 거리
|
|
//
|
|
// _oprint( "[s크기 : %f %f], [t크기 %f %f]\n", GetSize()/2.f, GetScale(), pTarget->GetSize()/2.f, pTarget->GetScale() );
|
|
// _oprint( "[사거리 : %f], [거리 %f], <tpos %f %f %f >, <spos %f %f %f > \n", GetAttackRange(), fDistance, tpos.x, tpos.y, tpos.z, spos.x, spos.y, spos.z );
|
|
// }
|
|
//#endif
|
|
|
|
if(IsMount()||IsIng()) return;
|
|
|
|
TS_ATTACK_REQUEST msg;
|
|
msg.handle = GetArID();
|
|
msg.target_handle = target;
|
|
SendMsg( &msg );
|
|
}
|
|
|
|
void SGameLocalCreature::ReqCast( AR_HANDLE target, int nSkillID, int nSkillLv, K3DVector const& targetPos )
|
|
{
|
|
// _oprint( "====>스킬 요청 %d %d\n", nSkillID, nSkillLv );
|
|
|
|
//소환, 역소환 예외~
|
|
//if( nSkillID==4001 || nSkillID==4002 )
|
|
//{
|
|
// TS_CS_SUMMON msg;
|
|
// msg.card_handle = target;
|
|
// msg.is_summon = (nSkillID==4001) ? 1 : 0;
|
|
// SendMsg( &msg );
|
|
// return;
|
|
//}
|
|
|
|
if(IsMount()||IsIng()) return;
|
|
|
|
_SKILL_FX* pSkillFX = GetSkillStageDB().GetSkillStageData( nSkillID );
|
|
if( !pSkillFX )
|
|
{
|
|
assert(0 && "스킬 ID 확인 : 스킬 유형이 없다!!!" );
|
|
SetCreatureBattleModeOff();
|
|
return;
|
|
}
|
|
|
|
if( pSkillFX->szType == 0 || pSkillFX->szDeal_Damage == 1 ) //전투 기술이면 또는 해로운 기술 이면, 전투 모드로 전환
|
|
SetAttackMode();
|
|
|
|
|
|
TS_CS_SKILL msg;
|
|
msg.caster = GetArID();
|
|
msg.skill_id = nSkillID;
|
|
msg.target = target;
|
|
msg.skill_level= nSkillLv;
|
|
msg.x = targetPos.x;
|
|
msg.y = targetPos.y;
|
|
msg.z = targetPos.z;
|
|
SendMsg( &msg );
|
|
|
|
//사용 여부를 인터페이스에 알리자~
|
|
}
|
|
void SGameLocalCreature::ReqCancelAction()
|
|
{
|
|
TS_CS_CANCEL_ACTION msg;
|
|
msg.handle = GetArID(); //자신이 취소
|
|
SendMsg( &msg );
|
|
}
|
|
void SGameLocalCreature::ReqThrow( AR_HANDLE target, AR_HANDLE itemhandle )
|
|
{
|
|
//던지는게 가능한 아이템인지 검사는 Input에서 필터링 하는 것이 맞음.
|
|
//TS_CS_USE_ITEM use_msg;
|
|
//use_msg.item_handle = itemhandle;
|
|
//use_msg.target_handle = target;
|
|
//SendMsg( &use_msg );
|
|
}
|
|
void SGameLocalCreature::ReqSit()
|
|
{
|
|
if(!CheckState()) return;
|
|
if( IsMount() ) return;
|
|
|
|
RqChatting( CHAT_NORMAL, m_pProperty->Name, "/앉기" );
|
|
}
|
|
|
|
void SGameLocalCreature::ReqStandUp()
|
|
{
|
|
if(!CheckState()) return;
|
|
if( IsMount() ) return;
|
|
|
|
RqChatting( CHAT_NORMAL, m_pProperty->Name, "/서기" );
|
|
}
|
|
|
|
void SGameLocalCreature::ReqNormal()
|
|
{
|
|
if(!CheckState()) return;
|
|
if( IsMount() ) return;
|
|
|
|
std::string str;
|
|
XStringUtil::Format(str, "%u", GetArID() );
|
|
RqChatting( CHAT_NORMAL, str.c_str(), "/normal" );
|
|
}
|
|
void SGameLocalCreature::ReqBattle()
|
|
{
|
|
if(!CheckState()) return;
|
|
if( IsMount() ) return;
|
|
|
|
std::string str;
|
|
XStringUtil::Format(str, "%u", GetArID() );
|
|
RqChatting( CHAT_NORMAL, str.c_str(), "/battle" );
|
|
}
|
|
|
|
bool SGameLocalCreature::CheckDistance( CREATE_DISTANCE_TYPE nType, SGameAvatarEx* pTarget )
|
|
{
|
|
float fDistance = GetDistance( GetCurPosWithChangeDir(), pTarget->GetCurPosWithChangeDir() );
|
|
float fRange = (float)nType + m_arMySize /*+ GetSize( pTarget )*/;
|
|
if( fDistance > fRange )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AR_UNIT SGameLocalCreature::GetSize( SGameObject *pObject )
|
|
{
|
|
AR_UNIT size = pObject->GetSize()/2.f; // 캐릭터 사이즈는 지름이라 2 나눈다.
|
|
AR_UNIT scale = pObject->GetScale(); // 캐릭터 사이즈 비율
|
|
|
|
return size*scale * GameRule::DEFAULT_UNIT_SIZE; // 실제 좌표로 변환
|
|
}
|
|
|
|
//대기 상태
|
|
//테이머가 명령을 내릴때까지 대기상태를 유지한다
|
|
void SGameLocalCreature::CmdStandBy()
|
|
{
|
|
if(!CheckState()) return;
|
|
|
|
if( m_pMasterPlayer )
|
|
{
|
|
if( m_nCreatureState == STATE_RECALL_COUNTDOWN )
|
|
{
|
|
//1분이 경과 되기 전에 테이머와이 거리가 150M 이하로 거리가 좁혀졌다면 따르기로 전환
|
|
if( !CheckDistance( DISTANCE_150, m_pMasterPlayer ) )
|
|
{
|
|
m_nCreatureState = STATE_NSTANDBY;
|
|
CmdMove();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//대기 상태중 플레이어와 150M 이상 거리가 발생한다면 역소환 준비 한다
|
|
if( CheckDistance( DISTANCE_150, m_pMasterPlayer ) )
|
|
m_nCreatureState = STATE_RECALL_COUNTDOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
//준비 상태
|
|
//테이머가 이동하면 거리에 따라 같이 이동한다
|
|
void SGameLocalCreature::CmdIdle()
|
|
{
|
|
if( !s_bIdleProc ) return;
|
|
|
|
if(!CheckState()) return;
|
|
|
|
if( m_pMasterPlayer && !IsStandbyState() )
|
|
{
|
|
//테이머와 3M 이상 벌어질경우
|
|
|
|
if( CheckDistance( DISTANCE_3, m_pMasterPlayer ) || m_vMasterLastPos != *m_pMasterPlayer->GetPosition() )
|
|
{
|
|
CmdMove();
|
|
}
|
|
//테이머와 150M 이상 벌어질 경우 대기 상태로 전환
|
|
else if( CheckDistance( DISTANCE_150, m_pMasterPlayer ) )
|
|
{
|
|
m_nCreatureState = STATE_RECALL_COUNTDOWN;
|
|
}
|
|
else
|
|
{
|
|
//테이머가 이동이 완료된 후에 크리처 방향 설정
|
|
// if( !m_pMasterPlayer->IsMoving() )
|
|
SetViewVectorStateIdle( m_pMasterPlayer->GetViewVector() );
|
|
}
|
|
}
|
|
}
|
|
|
|
//공격 중
|
|
void SGameLocalCreature::CmdAttack()
|
|
{
|
|
if(!CheckState()) return;
|
|
|
|
float fDistance = 0.f;
|
|
SGameAvatarEx* pTargetMonster = (SGameAvatarEx*)GetGameObject(m_hTargetMonster);
|
|
if( pTargetMonster )
|
|
{
|
|
//대상몬스터와 크리처가 50M이상 떨어져 있다면 따르기 상태로 전환
|
|
if( CheckDistance( DISTANCE_50, pTargetMonster ) )
|
|
{
|
|
CmdMove();
|
|
}
|
|
}
|
|
if( m_pMasterPlayer )
|
|
{
|
|
//테이머와 크리처가 150M이상 떨어져 있다면 대기 상태로 전환후 1분후 자동 리콜
|
|
if( CheckDistance( DISTANCE_150, m_pMasterPlayer ) )
|
|
{
|
|
m_nCreatureState = STATE_STANDBY;
|
|
}
|
|
}
|
|
}
|
|
//거리를 체크한후 이동명령을 보낼지 결정한다
|
|
void SGameLocalCreature::CmdMove()
|
|
{
|
|
if(!CheckState()) return;
|
|
|
|
if( m_pMasterPlayer && !IsStandbyState() )
|
|
{
|
|
if( CheckDistance( DISTANCE_15, m_pMasterPlayer ) )
|
|
{
|
|
//플레이어 속도로 이동 하도록 수정됨
|
|
SetBestPosition();
|
|
// SetBestPositionEx();
|
|
}
|
|
else
|
|
{
|
|
SetBestPosition();
|
|
}
|
|
|
|
m_vMasterLastPos = *m_pMasterPlayer->GetPosition();
|
|
}
|
|
}
|
|
//소환수의 가장 적절한 포지션을 구한다 플레이어 속도로 이동
|
|
void SGameLocalCreature::SetBestPosition( bool bImmediately )
|
|
{
|
|
K3DVector vOut, vNextPos;
|
|
if( GetCreaturePosition( &vOut ) == false ) return;
|
|
|
|
if( !bImmediately )
|
|
{
|
|
// if( NeedNewDetourPoint() )
|
|
{
|
|
if( m_dwTime - m_dwFindDetour > m_dwFindDetourTick )
|
|
{
|
|
//TODO : 크리처 길찾기 무시
|
|
// FindDetour( vOut, false, true );
|
|
// MoveDetour( vNextPos );
|
|
|
|
SInputMove input( vOut );
|
|
OnInput( &input );
|
|
|
|
m_dwFindDetour = m_dwTime;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//TODO : 크리처 길찾기 무시
|
|
//즉시 반응 해야 할경우
|
|
// FindDetour( vOut, false, true );
|
|
// MoveDetour( vNextPos );
|
|
|
|
SInputMove input( vOut );
|
|
OnInput( &input );
|
|
}
|
|
}
|
|
//소환수의 가장 적절한 포지션을 구한다, 크리처 속도로 이동
|
|
void SGameLocalCreature::SetBestPositionEx( bool bImmediately )
|
|
{
|
|
K3DVector vOut, vNextPos;
|
|
if( GetCreaturePosition( &vOut ) == false ) return;
|
|
|
|
if( !bImmediately )
|
|
{
|
|
// if( NeedNewDetourPoint() )
|
|
{
|
|
if( m_dwTime - m_dwFindDetour > m_dwFindDetourTick )
|
|
{
|
|
//TODO : 크리처 길찾기 무시
|
|
// FindDetour( vOut, false, true );
|
|
// MoveDetour( vNextPos );
|
|
|
|
SInputMove input( vOut, false );
|
|
OnInput( &input );
|
|
|
|
m_dwFindDetour = m_dwTime;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//TODO : 크리처 길찾기 무시
|
|
// //즉시 반응 해야 할경우
|
|
// FindDetour( vOut, false, true );
|
|
// MoveDetour( vNextPos );
|
|
|
|
SInputMove input( vOut, false );
|
|
OnInput( &input );
|
|
}
|
|
}
|
|
bool SGameLocalCreature::GetCreaturePosition( K3DVector* pOut )
|
|
{
|
|
// float fDistance = K3DVectorGetLength( vOut, GetCurPos() );
|
|
//크리처와 테이머의 거리가 7M이하 가까워 졌다면 최종 위치를 구한다
|
|
// if( fDistance <= (float)GameRule::DEFAULT_UNIT_SIZE * 7 ) //매번 호출하는게 아니여서 오차가 생긴다
|
|
// {
|
|
//테이머가 바라보는 방향을 기준으로 좌측 이나 우측으로 1M, 후방 1M 이동
|
|
*pOut = m_pMasterPlayer->GetCurPosWithChangeDir();
|
|
K3DVector vUpVector = K3DVector( 0.f, 0.f, m_fLeftNRight );
|
|
K3DVector vViewVector = -m_pMasterPlayer->GetViewVector();
|
|
K3DVectorCross( vUpVector, vViewVector, vUpVector );
|
|
*pOut += ( vUpVector + vViewVector ) * m_arMySize;
|
|
// }
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SGameLocalCreature::NeedNewDetourPoint()
|
|
{
|
|
size_t numDetourPoints = GetDetourSize();
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
//길찾기로 찾은 경로의 마지막 위치을 가져온다
|
|
if( GetDetourPoint(numDetourPoints - 1, x, y) )
|
|
{
|
|
K3DVector vFinalPos = K3DVector( x, y, 0.f );
|
|
float fDistance = GetDistance( m_pMasterPlayer->GetCurPosWithChangeDir(), vFinalPos );
|
|
if( fDistance < (float)DISTANCE_3 )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SGameLocalCreature::SetHold()
|
|
{
|
|
SetCreatureBattleModeOff();
|
|
|
|
//대기 상태로 전환
|
|
if(!CheckState()) return;
|
|
|
|
m_nCreatureState = STATE_STANDBY;
|
|
}
|
|
void SGameLocalCreature::SetUnHold()
|
|
{
|
|
SetCreatureBattleModeOff();
|
|
|
|
//대기 상태가 해지되면 따르기로 전환
|
|
if( IsStandbyState() )
|
|
m_nCreatureState = STATE_NSTANDBY;
|
|
}
|
|
void SGameLocalCreature::AddNLeaveSummon()
|
|
{
|
|
if( m_pStateVM == NULL ) return;
|
|
|
|
if(!CheckState()) return;
|
|
|
|
if( m_nCreatureMode == BATTLE_MODE_ON ) return;
|
|
|
|
//IDLE상태 이거나 MOVE상태일때 바로 자기 자리를 찾아간다
|
|
if( m_pStateVM->GetCurrentState() == SObjectState::STATE_IDLE ||
|
|
m_pStateVM->GetCurrentState() == SObjectState::STATE_MOVE )
|
|
SetBestPosition();
|
|
}
|
|
void SGameLocalCreature::CreatureBackDownSpeedOfPlayer()
|
|
{
|
|
//플레이어 속도로 퇴각하자
|
|
if(!CheckState()) return;
|
|
|
|
SetBestPosition( true );
|
|
}
|
|
void SGameLocalCreature::CreatureBackDownSpeedOfCreature()
|
|
{
|
|
if( m_pStateVM == NULL ) return;
|
|
|
|
//크리처 속도로 퇴각하자
|
|
if(!CheckState()) return;
|
|
|
|
if(m_pMasterPlayer)
|
|
{
|
|
if( !CheckDistance( DISTANCE_3, m_pMasterPlayer ) )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( IsStandbyState() ||
|
|
m_pStateVM->GetCurrentState() == SObjectState::STATE_CHASE ||
|
|
m_pStateVM->GetCurrentState() == SObjectState::STATE_ATTACK )
|
|
{
|
|
//플레이어 속도로 이동 하도록 수정됨
|
|
SetBestPosition( true );
|
|
// SetBestPositionEx( true );
|
|
}
|
|
}
|
|
|
|
bool SGameLocalCreature::CheckState()
|
|
{
|
|
//마운트 상태이거나 마운트중이거나 Ing중이거나 삭제 대기 중이거나
|
|
if( IsMount() || IsMountMode() || IsIng() || IsReservation() ) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SGameLocalCreature::IsStandbyState()
|
|
{
|
|
//대기중이거나 역소환 하려고 카운트 다운중이거나
|
|
if( m_nCreatureState == STATE_STANDBY || m_nCreatureState == STATE_RECALL_COUNTDOWN )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void SGameLocalCreature::SetCreatureBattleModeOff()
|
|
{
|
|
m_nCreatureMode = BATTLE_MODE_OFF;
|
|
}
|
|
void SGameLocalCreature::SetCreatureBattleModeOn()
|
|
{
|
|
m_nCreatureMode = BATTLE_MODE_ON;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
/////운송 수단용 크리처
|
|
SGameCreatureForTransport::SGameCreatureForTransport( int nCreatureID, unsigned char ucEnhance ) : SGameCreature( nCreatureID, ucEnhance )
|
|
, m_pMasterPlayer( NULL )
|
|
, m_hMaster( 0 )
|
|
{
|
|
m_bFootsteps = true;
|
|
|
|
m_pStateVM = new SLocalCreatureStateMachine;
|
|
m_pStateVM->SetReceiver( this );
|
|
}
|
|
|
|
SGameCreatureForTransport::~SGameCreatureForTransport()
|
|
{
|
|
}
|
|
|
|
void SGameCreatureForTransport::SetMaster( AR_HANDLE hMaster, SGameAvatarEx * pMaster )
|
|
{
|
|
m_hMaster = hMaster;
|
|
m_pMasterPlayer = pMaster;
|
|
|
|
if( m_pMasterPlayer )
|
|
m_bLocalCreature = m_pMasterPlayer->IsLocalPlayer();
|
|
else
|
|
m_bLocalCreature = false;
|
|
}
|
|
|
|
void SGameCreatureForTransport::SetViewVectorStateIdle( const K3DVector &tpos )
|
|
{
|
|
m_pMasterPlayer = (SGameAvatarEx*)GetGameObject(m_hMaster);
|
|
|
|
if( m_pMasterPlayer )
|
|
{
|
|
if( m_pMasterPlayer->IsMoving() )
|
|
{
|
|
struct ArcadiaClient* pArClient = m_pMasterPlayer->GetArClient();
|
|
if( pArClient )
|
|
{
|
|
const ArPosition& cursteppos = pArClient->GetMyPos().GetDirection();
|
|
m_vNextPos = K3DVector( cursteppos.x, cursteppos.y, 0.f );
|
|
SetViewVector( m_vNextPos );
|
|
}
|
|
else
|
|
{
|
|
_Object& arObj = m_pMasterPlayer->GetArObject();
|
|
const ArPosition& cursteppos = arObj.mv.GetDirection();
|
|
m_vNextPos = K3DVector( cursteppos.x, cursteppos.y, 0.f );
|
|
SetViewVector( m_vNextPos );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
K3DVector vView = m_pMasterPlayer->GetViewVector();
|
|
m_fTargetRoll = atan2( vView.y, vView.x );
|
|
m_dwPrevTime = m_dwTime;
|
|
m_bUseRot = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SGameCreatureForTransport::Process( DWORD time, unsigned long uProcessBitVector )
|
|
{
|
|
m_pMasterPlayer = (SGameAvatarEx*)GetGameObject(m_hMaster);
|
|
|
|
if( m_pMasterPlayer )
|
|
{
|
|
struct ArcadiaClient* pArClient = m_pMasterPlayer->GetArClient();
|
|
if( pArClient )
|
|
{
|
|
const ArMoveVector& pos = pArClient->GetMyPos();
|
|
SetArObjectPos( pos.x, pos.y, 0.0f, pArClient->GetLayer() );
|
|
if( pArClient->GetMyPos().HasDirectionChanged() )
|
|
{
|
|
const ArPosition& cursteppos = pArClient->GetMyPos().GetDirection();
|
|
m_vNextPos = K3DVector( cursteppos.x, cursteppos.y, 0.f );
|
|
SetViewVector( m_vNextPos );
|
|
}
|
|
|
|
GetArObject().mv.SetMoving( pArClient->GetMyPos().IsMoving() );
|
|
}
|
|
else
|
|
{
|
|
_Object& arObj = m_pMasterPlayer->GetArObject();
|
|
SetArObjectPos( arObj.mv.x, arObj.mv.y, 0.0f, arObj.layer );
|
|
if( arObj.mv.HasDirectionChanged() )
|
|
{
|
|
const ArPosition& cursteppos = arObj.mv.GetDirection();
|
|
m_vNextPos = K3DVector( cursteppos.x, cursteppos.y, 0.f );
|
|
SetViewVector( m_vNextPos );
|
|
}
|
|
|
|
GetArObject().mv.SetMoving( arObj.mv.IsMoving() );
|
|
}
|
|
}
|
|
|
|
SGameAvatarEx::Process( time );
|
|
|
|
return true;
|
|
}
|
|
|
|
void SGameCreatureForTransport::OnChangeState( SObjectState::ID stateid, const SStateInfo &info )
|
|
{
|
|
switch( stateid )
|
|
{
|
|
case SObjectState::STATE_IDLE:
|
|
{
|
|
if( GetCurrAnimationID() == ANI_RUN ||
|
|
GetCurrAnimationID() == ANI_WALK )
|
|
{
|
|
Default();
|
|
}
|
|
}
|
|
break;
|
|
case SObjectState::STATE_MOVE:
|
|
{
|
|
if( info.dwSpeed == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetAniLock( false );
|
|
if(info.dwSpeed < m_fWalkSpeed)
|
|
{
|
|
CalcCreatureWalkPlayRate( (float)info.dwSpeed );
|
|
Walk(true);
|
|
}
|
|
else
|
|
{
|
|
CalcCreatureRunPlayRate( (float)info.dwSpeed );
|
|
Run();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//Dummy Creature
|
|
SGameDummyCreature::SGameDummyCreature( int nCreatureID, unsigned char ucEnhance ) : SGameCreature( nCreatureID, ucEnhance )
|
|
{
|
|
m_pStateVM = new SOtherCreatureStateMachine; //상태 머신
|
|
m_pStateVM->SetReceiver( this );
|
|
|
|
m_nMode = 0;
|
|
|
|
SetScale( 1.0f );
|
|
}
|
|
|
|
SGameDummyCreature::~SGameDummyCreature()
|
|
{
|
|
}
|
|
|
|
void SGameDummyCreature::SetDBData()
|
|
{
|
|
m_fPlayRate = DEFAULT_ANI_PLAY_RATE;
|
|
m_fSpeed = DEFAULT_MOVE_SPEED;
|
|
}
|
|
|
|
void SGameDummyCreature::OnChangeState( SObjectState::ID stateid, const SStateInfo &info )
|
|
{
|
|
switch( stateid )
|
|
{
|
|
case SObjectState::STATE_MOVE :
|
|
{
|
|
if( info.dwSpeed == 0 )
|
|
{
|
|
//달리다 죽은 것이므로 Ani 처리는 스킵한다.
|
|
return;
|
|
}
|
|
|
|
//SetViewVector( info.vTargetPos );
|
|
SetAniLock( false );
|
|
switch( m_nMode )
|
|
{
|
|
case 0 :
|
|
{
|
|
if( info.dwSpeed >= 10 )
|
|
{
|
|
Run();
|
|
}
|
|
else
|
|
{
|
|
Walk(true);
|
|
}
|
|
}
|
|
break;
|
|
case 1 :
|
|
Walk(true);
|
|
break;
|
|
case 2 :
|
|
Run();
|
|
break;
|
|
}
|
|
}
|
|
break; // 이동
|
|
default:
|
|
{
|
|
SGameCreature::OnChangeState( stateid, info );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SGameDummyCreature::SetDummyCreatureSpeed( float fPerSpeed )
|
|
{
|
|
m_fSpeed = GetCalcMoveSpeed( fPerSpeed );
|
|
}
|
|
|
|
void SGameDummyCreature::SetDummyCreaturePlayRate( float fPerPlayRate )
|
|
{
|
|
m_fPlayRate = GetCalcPlayRate( fPerPlayRate );
|
|
}
|
|
|
|
void SGameDummyCreature::SetDummyCreatureSpeedWithPlayRate( float fPerSpeed )
|
|
{
|
|
m_fSpeed = GetCalcMoveSpeed( fPerSpeed );
|
|
|
|
m_fPlayRate = DEFAULT_ANI_PLAY_RATE * (m_fSpeed / DEFAULT_MOVE_SPEED);
|
|
}
|
|
|
|
void SGameDummyCreature::SetDummyCreatureMode( int nMode )
|
|
{
|
|
m_nMode = nMode;
|
|
}
|
|
|
|
float SGameDummyCreature::GetCalcMoveSpeed( float fPerSpeed )
|
|
{
|
|
return (DEFAULT_MOVE_SPEED * fPerSpeed) / 100.0f;
|
|
}
|
|
|
|
float SGameDummyCreature::GetCalcPlayRate( float fPerPlayRate )
|
|
{
|
|
return (DEFAULT_ANI_PLAY_RATE * fPerPlayRate) / 100.0f;
|
|
}
|
|
|
|
float SGameDummyCreature::GetPlayRatePercentage( float fSpeed )
|
|
{
|
|
return (fSpeed / DEFAULT_MOVE_SPEED) * 100.0f;
|
|
} |