#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 #include "SChatType.h" #include "SDebug_Util.h" #include #include 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; //전투 불능 상태입니다.
마을로 귀환하시려면 확인 버튼을 누르세요. 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], , \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; }