#include "stdafx.h" // sonador 10.2.1 팻 시스템 구현 #include "SGameLocalPet.h" #include "SPetStateMachine.h" #include "GameDefine.h" #include "KSeqAvatarEx.h" #include "SGameAniType.h" #include "SSkillDB.h" #include "SGameInput.h" #include "SStringDB.h" #include "SGameMessage.h" //#include "SGameMessageUI.h" #include "LuaVM.h" #include #include "SChatType.h" #include "SGameItem.h" #include "SGameUIMgr.h" #include "SMessengerMgr.h" #include "SDebug_Util.h" #include "SLog.h" #include "SGameScript.h" #include "SGame.h" //#include "SAvatarProperty.h" SGameLocalPet::SGameLocalPet( int nPetID ) : SGamePet ( nPetID ) , m_dwUpdateTime ( 0 ) , m_dwFindDetour ( 0 ) , m_dwFindDetourTick ( 200 ) , m_dwBeginScriptTime ( 0 ) , m_dwCallScriptTime ( 500 ) // 게임 로컬 펫 스크립트 호출 주기 1초에서 0.5초로 변경 (줍기 속도 개선. 2013-12-12) , m_bPickItemMode ( false ) // sonador #2.1.2.4.3 팻 조작 UI 연동 , m_fPickItemDistance ( ITEM_TAKE_RANGE ) // , m_vMasterLastPos( 0, 0, 0 ) { m_bLocalPet = true; m_pStateVM = new SLocalPetStateMachine(); m_pStateVM->SetReceiver( this ); m_arMySize = GetSize( this ); m_dwItemPickCommandTime = 0; } SGameLocalPet::~SGameLocalPet() { } /// 2010.10.13 - prodongi void SGameLocalPet::Destroy() { SGamePet::Destroy(); m_queueIgnoredItems.clear(); } bool SGameLocalPet::Process( DWORD time, unsigned long procBitSet ) { m_dwCurrentTime = time; /* /* 펫은 보낼 필요없더라. // udpate rarely if( m_dwUpdateTime == 0 ) m_dwUpdateTime = time; else if( ( time - m_dwUpdateTime ) >= UPDATE_TIME ) { m_dwUpdateTime = time; // send udpate message to server TS_CS_UPDATE msg; msg.handle = GetArID(); SendMsg( &msg ); } */ SGamePet::Process( time, procBitSet ); if( time - m_dwBeginScriptTime > m_dwCallScriptTime ) { UpdateStateByScript(); m_dwBeginScriptTime = time; // sonador #2.1.2.4.3 팻 조작 UI 연동 } if( m_pStateVM ) m_pStateVM->Process( time ); if ((m_pStateVM->GetCurrentState() != SObjectState::STATE_IDLE) && m_dwItemPickCommandTime != 0) { if (m_dwCurrentTime - m_dwItemPickCommandTime > 7000) { SDEBUGLOG("펫이 아이템을 회수 못하고 있음, 강제로 상태 리셋 \n"); m_dwItemPickCommandTime = 0; m_pStateVM->StopCurrentState(); } } if (!m_queueIgnoredItems.empty()) { if (m_dwCurrentTime - m_queueIgnoredItems.front().m_dwIgnoredTime > 5 * 60 * 1000) // 10분 { SDEBUGLOG("무시 리스트에서 1개제거\n"); m_queueIgnoredItems.pop_front(); // 1프레임에 1개씩만 제거한다. 어차피 1프레임에 1개이상 제거될 일 없으니 코드 단순하게. } } return true; } void SGameLocalPet::UpdateStateByScript() { if( m_pStateVM == 0 ) return; if( IsIng() || IsReservation() ) return; // Fraun performance tweak std::string Function; switch( m_pStateVM->GetCurrentState() ) { case SObjectState::STATE_NONE: case SObjectState::STATE_IDLE: { STRUCT_CREATURE_CMD_SCRIPT Idle; Idle.nCmdScipt = STRUCT_CREATURE_CMD_SCRIPT::CMD_IDLE; Idle.cHandle = GetArID(); SGame* pGame = GetActGame(); if (pGame) pGame->SendCreatureCmdScript(Idle); break; } case SObjectState::STATE_MOVE: { STRUCT_CREATURE_CMD_SCRIPT Move; Move.nCmdScipt = STRUCT_CREATURE_CMD_SCRIPT::CMD_MOVE; Move.cHandle = GetArID(); Move.nCreaturePosition = 1; SGame* pGame = GetActGame(); if (pGame) pGame->SendCreatureCmdScript(Move); break; } } } bool SGameLocalPet::HasStateHeld() { if( IsIng() || IsReservation() ) return true; return false; } bool SGameLocalPet::IsFarFrom( SGameAvatarEx* Target, DISTANCE_TYPE Type ) { float fDistance = GetDistance( GetCurPosWithChangeDir(), Target->GetCurPosWithChangeDir() ); float fRange = (float)Type + m_arMySize; if( fDistance > fRange ) return true; return false; } void SGameLocalPet::CmdIdle() { if( HasStateHeld() ) return; if( m_pMasterPlayer ) { if( m_bPickItemMode && !m_pMasterPlayer->IsDead() ) // #2.1.2.4.2 { const int PET_MAX_TAKE_ITEM_SEARCH_COUNT = INT_MAX; // 검색최대 갯수 float fDistance = 0.f; vector vecItems; vector vecDist; m_pMsgHandler->GetItemObjects_For_PetAutoGather( *m_pMasterPlayer->GetPosition(), m_fPickItemDistance, vecItems, *GetPosition(), vecDist, PET_MAX_TAKE_ITEM_SEARCH_COUNT ); if (!vecItems.empty()) { float maxDist = FLT_MAX; int index = -1; /// 2010.10.13 - prodongi bool isInIgnoredList = false; for (size_t i=0; iGetArID()) { // if (m_dwCurrentTime - m_queueIgnoredItems[j].m_dwLastAttemptedTime > 30 * 1000) // 30초 { someBigDist = 100000; m_queueIgnoredItems[j].m_nTryCount++; SDEBUGLOG("Considering ignored item: %d / trycount: %d \n", m_queueIgnoredItems[j].m_hItem, m_queueIgnoredItems[j].m_nTryCount); if (m_queueIgnoredItems[j].m_nTryCount >= 2) // 3번까지는 시도해본다. { m_queueIgnoredItems[j].m_nTryCount = 0; m_queueIgnoredItems[j].m_dwLastAttemptedTime = m_dwCurrentTime; } } // else // bIgnore = true; /// 2010.10.13 - prodongi bIgnore = true; break; } } // if (bIgnore) continue; if (vecDist[i] + someBigDist < maxDist ) // 리스트에 있는 아이템을 무조건 무시가 아니라 최하순위로 고려 { /// 2010.10.13 - prodongi isInIgnoredList = bIgnore; index = i; maxDist = vecDist[i] + someBigDist; } } if (index != -1) { SGameItem* pItemObj = static_cast< SGameItem* >( vecItems[index] ); // 2011.10.18 : servantes : SPartyMgr 관리 클래스 int nPartyID = static_cast< CPartyListMgr* >( GameUIMgrInstance.GetUIMgr( SGameUIInstance::PARTY_MGR ) )->GetPartyID(); if( pItemObj->IsPickable( m_pMasterPlayer->GetArID(), nPartyID ) ) { /// /// 2010.10.13 왜 m_queueIgnoredItems에 들어 있는지 체크를 안 하는가???? - prodongi /// SInputTakeItem inputTakeItem( pItemObj->GetArID(), fDistance ); OnInput( &inputTakeItem ); /// 2010.10.13 무시 리스트에 없는 것만 새로 추가한다 - prodongi if (!isInIgnoredList) { IgnoredItem item; item.m_nTryCount = 0; item.m_dwIgnoredTime = m_dwCurrentTime; item.m_dwLastAttemptedTime = 0; item.m_hItem = pItemObj->GetArID(); m_queueIgnoredItems.push_back(item); } m_dwItemPickCommandTime = m_dwCurrentTime; return; } } } else { // 주변에 아이템이 없다면 무시 리스트를 초기화 한다. m_queueIgnoredItems.clear(); } //SGameObject* pItem = m_pMsgHandler->GetItemObject( *m_pMasterPlayer->GetPosition(), fDistance, -1, m_fPickItemDistance ); //if( pItem ) //{ // SGameItem* pItemObj = static_cast< SGameItem* >( pItem ); // int nPartyID = static_cast< SPartyMgr* >( GameUIMgrInstance.GetUIMgr( SGameUIInstance::PARTY_MGR ) )->GetPartyID(); // if( pItemObj->IsPickable( m_pMasterPlayer->GetArID(), nPartyID ) ) // { // SInputTakeItem inputTakeItem( pItem->GetArID(), fDistance ); // OnInput( &inputTakeItem ); // return; // } //} } if( IsFarFrom( m_pMasterPlayer, DISTANCE_3 ) || m_vMasterLastPos != *m_pMasterPlayer->GetPosition() ) { CmdMove(); } else { SetViewVectorStateIdle( m_pMasterPlayer->GetViewVector() ); } } } void SGameLocalPet::CmdMove() { if( HasStateHeld() ) return; if( m_pMasterPlayer ) { if( IsFarFrom( m_pMasterPlayer, DISTANCE_15 ) ) InputCmd_MoveToFitPosition( true ); else InputCmd_MoveToFitPosition( false ); m_vMasterLastPos = *m_pMasterPlayer->GetPosition(); } } void SGameLocalPet::OnInput( class SGameInput* input ) { if( !m_pStateVM ) return; m_pStateVM->OnInput( input ); // TODO: processing input by id int inputID = input->GetInputID(); } bool SGameLocalPet::GetFitPosition( K3DVector& out ) { // TODO: locate pet behind the master player K3DVector vUp( 0, 0, -1.f ); K3DVectorCross( out, m_pMasterPlayer->GetViewVector(), vUp ); out = m_pMasterPlayer->GetCurPosWithChangeDir() + ( out * m_arMySize ); return true; } AR_UNIT SGameLocalPet::GetSize( SGameObject *object ) { AR_UNIT size = object->GetSize() / 2.f; AR_UNIT scale = object->GetScale(); return size * scale * GameRule::DEFAULT_UNIT_SIZE; } void SGameLocalPet::ReqTakeItem( AR_HANDLE handle ) { SDEBUGLOG("ReqTakeItem \n"); { // 목표로한 아이템을 줍는다. TS_CS_TAKE_ITEM msg; msg.item_handle = handle; msg.taker_handle = GetArID(); // sonador #2.1.2.4.3 팻 조작 UI 연동 SendMsg( &msg ); } // 현재 위치에서 바로 주울 수 있는 n개의 아이템을 검색하여 줍는다. const int PET_ADDITIONAL_MAX_TAKE_ITEM_COUNT = INT_MAX; // 팻이 한번에 아이템을 추가로 주울수 있는 갯수 if( PET_ADDITIONAL_MAX_TAKE_ITEM_COUNT > 0 ) { vector vecItems; vector vecDist; m_pMsgHandler->GetItemObjects_For_PetAutoGather( *GetPosition(), GameRule::MAX_TAKE_ITEM_RANGE, vecItems, *GetPosition(), vecDist, PET_ADDITIONAL_MAX_TAKE_ITEM_COUNT ); if (!vecItems.empty()) { auto pos = vecItems.cbegin(); auto end = vecItems.cend(); for( ; pos != end; ++pos ) { SGameItem* pItemObj = static_cast< SGameItem* >( *pos ); // 무시된 아이템 제외 auto ignored_pos = m_queueIgnoredItems.cbegin(); auto ignored_end = m_queueIgnoredItems.cend(); bool ignored_flag = false; for( ; ignored_pos != ignored_end; ++ignored_pos ) { const IgnoredItem& ignored_item = (*ignored_pos); if (ignored_item.m_hItem == pItemObj->GetArID()) { ignored_flag = true; break; } } if( ignored_flag == false ) { // 2011.10.18 : servantes : SPartyMgr 관리 클래스 int nPartyID = static_cast< CPartyListMgr* >( GameUIMgrInstance.GetUIMgr( SGameUIInstance::PARTY_MGR ) )->GetPartyID(); if( pItemObj->GetArID() != handle && pItemObj->IsPickable( m_pMasterPlayer->GetArID(), nPartyID ) ) { IgnoredItem item; item.m_nTryCount = 0; item.m_dwIgnoredTime = m_dwCurrentTime; item.m_dwLastAttemptedTime = 0; item.m_hItem = pItemObj->GetArID(); m_queueIgnoredItems.push_back(item); TS_CS_TAKE_ITEM msg; msg.item_handle = pItemObj->GetArID(); msg.taker_handle = GetArID(); SendMsg( &msg ); } } } } } } void SGameLocalPet::ReqMove( AR_HANDLE handle, const ArPosition& target, bool speedSync, bool chase ) { SDEBUGLOG("ReqMove \n"); if( IsUsingSkill() || 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, speedSync ); else SGameObject::ReqMove( handle, target, &target, 1, speedSync ); DeleteReqPoint(); } void SGameLocalPet::ReqCast( AR_HANDLE target, int skillID, int skillLv, K3DVector const& targetPos ) { if( IsIng() ) return; _SKILL_FX* skillFx = GetSkillStageDB().GetSkillStageData( skillID ); if( !skillFx ) { assert( 0 && "스킬 ID 확인 : 스킬 유형이 없다!!!" ); return; } TS_CS_SKILL msg; msg.caster = GetArID(); msg.target = 0; msg.skill_id = skillID; msg.skill_level = skillLv; msg.x = targetPos.x; msg.y = targetPos.y; msg.z = targetPos.z; SendMsg( &msg ); } void SGameLocalPet::ReqCancelAction() { TS_CS_CANCEL_ACTION msg; msg.handle = GetArID(); SendMsg( &msg ); } void SGameLocalPet::InputCmd_MoveToFitPosition( bool syncWithMasterPlayer, bool immediately ) { K3DVector out, nextPos; if( GetFitPosition( out ) == false ) return; if( !immediately ) { if( m_dwTime - m_dwFindDetour > m_dwFindDetourTick ) { SInputMove input( out, syncWithMasterPlayer ); OnInput( &input ); m_dwFindDetour = m_dwTime; } } else { SInputMove input( out, syncWithMasterPlayer ); OnInput( &input ); } } //void SGameLocalPet::SetBestPositionEx( bool immediately ) //{ // K3DVector out, nextPos; // if( GetFitPosition( out ) == false ) return; // // if( !immediately ) // { // if( m_dwTime - m_dwFindDetour > m_dwFindDetourTick ) // { // SInputMove input( out, false ); // OnInput( &input ); // // m_dwFindDetour = m_dwTime; // } // } // else // { // SInputMove input( out, false ); // OnInput( &input ); // } //} //팻 자동 줍기 가능 거리 설정 // sonador #2.1.2.4.3 팻 조작 UI 연동 void SGameLocalPet::SetAutoPickItemDistanceByMeter( float meter ) { m_fPickItemDistance = meter * GameRule::DEFAULT_UNIT_SIZE; } void SGameLocalPet::DeleteItemFromIgnoreQueue(AR_HANDLE id) { for (std::deque::iterator i=m_queueIgnoredItems.begin(); i!=m_queueIgnoredItems.end(); ++i) { if ((*i).m_hItem == id) { SDEBUGLOG("Removed from ignore list: %d", id); m_queueIgnoredItems.erase(i); SDEBUGLOG("Item ignore list: "); for (size_t i=0; i