1021 lines
30 KiB
C++
1021 lines
30 KiB
C++
|
|
#include <memory.h>
|
|
|
|
#include <toolkit/XConsole.h>
|
|
#include <logging/FileLog.h>
|
|
|
|
#include "StructQuestManager.h"
|
|
#include "GameContent.h"
|
|
#include "SendMessage.h"
|
|
#include "StructPlayer.h"
|
|
#include "DB_Commands.h"
|
|
|
|
|
|
StructQuestManager::StructQuestManager()
|
|
{
|
|
m_QuestIdx = 0;
|
|
|
|
#ifdef _MEM_USAGE_DEBUG
|
|
XSEH::IncreaseAllocCount( "StructQuestManager" );
|
|
#endif
|
|
};
|
|
|
|
StructQuestManager::~StructQuestManager()
|
|
{
|
|
m_hsFinishedQuest.clear();
|
|
m_hsQuestCoolTime.clear();
|
|
|
|
for( std::vector< StructQuest * >::iterator it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
// 메모리 풀 사용하도록 변경
|
|
//delete (*it);
|
|
(*it)->FreeQuest();
|
|
}
|
|
m_vActiveQuest.clear();
|
|
|
|
#ifdef _MEM_USAGE_DEBUG
|
|
XSEH::DecreaseAllocCount( "StructQuestManager" );
|
|
#endif
|
|
};
|
|
|
|
const size_t StructQuestManager::DoEachActiveQuest( struct QuestFunctor & _fo )
|
|
{
|
|
size_t nCount = 0;
|
|
|
|
for( std::vector< StructQuest * >::iterator it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
// 전체 퀘스트 목록 클라이언트에 송신 및 캐릭터 정보 저장에만 사용되므로 QuestInstance::FAIL 상태의 퀘스트도 포함하여 처리 함
|
|
if( (*it)->GetProgress() != QuestInstance::FINISHED && (*it)->GetProgress() != QuestInstance::NOT_STARTED )
|
|
{
|
|
++nCount;
|
|
_fo( *it );
|
|
}
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
const size_t StructQuestManager::DoEachPendingQuest( struct QuestFunctor & _fo )
|
|
{
|
|
size_t nCount = 0;
|
|
|
|
for( std::vector< StructQuest * >::iterator it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
if( (*it)->GetProgress() == QuestInstance::NOT_STARTED )
|
|
{
|
|
++nCount;
|
|
_fo( *it );
|
|
}
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
bool StructQuestManager::AddQuest( struct StructQuest* pQuest )
|
|
{
|
|
QuestBase::QuestCode nQuestCode = pQuest->GetQuestCode();
|
|
|
|
if( nQuestCode == 0 )
|
|
{
|
|
FILELOG( "Quest code is zero!\n" );
|
|
_cprint( "Quest code is zero!\n" );
|
|
return false;
|
|
}
|
|
|
|
for( std::vector< StructQuest * >::iterator it = m_vActiveQuest.begin() ; it != m_vActiveQuest.end() ; ++it )
|
|
{
|
|
if( nQuestCode == (*it)->GetQuestCode() )
|
|
{
|
|
FILELOG( "Duplicated active quest detected. PlayerUID(%d), QuestCode(%d)", static_cast< StructPlayer * >( m_pHandler )->GetSID(), nQuestCode );
|
|
_cprint( "Duplicated active quest detected. PlayerUID(%d), QuestCode(%d)\n", static_cast< StructPlayer * >( m_pHandler )->GetSID(), nQuestCode );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( pQuest->GetProgress() == QuestInstance::FINISHED )
|
|
{
|
|
if ( m_hsFinishedQuest.has( nQuestCode ) && !pQuest->IsRepeatable() )
|
|
{
|
|
FILELOG( "Duplicated completed quest detected. PlayerUID(%d), QuestCode(%d)", static_cast< StructPlayer * >( m_pHandler )->GetSID(), nQuestCode );
|
|
_cprint( "Duplicated completed quest detected. PlayerUID(%d), QuestCode(%d)\n", static_cast< StructPlayer * >( m_pHandler )->GetSID(), nQuestCode );
|
|
}
|
|
else
|
|
m_hsFinishedQuest.add( nQuestCode, pQuest->GetQuestID() );
|
|
|
|
return false;
|
|
}
|
|
|
|
if( pQuest->IsRandomQuest() )
|
|
{
|
|
std::vector< RandomQuestInfo >::iterator it;
|
|
|
|
for( it = m_vRandomQuestInfo.begin(); it != m_vRandomQuestInfo.end(); ++it )
|
|
{
|
|
if( (*it).code == nQuestCode )
|
|
{
|
|
for( int i = 0; i < QuestInstance::MAX_RANDOM_VALUE; ++i )
|
|
{
|
|
pQuest->SetRandomKey( i, (*it).key[i] );
|
|
pQuest->SetRandomValue( i, (*it).value[i] );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_vActiveQuest.push_back( pQuest );
|
|
|
|
return true;
|
|
}
|
|
|
|
const bool StructQuestManager::AddPendingQuest( const QuestBase::QuestCode code, const int nStartID )
|
|
{
|
|
// 이미 시작 대기 중인 퀘스트인지 체크
|
|
if( FindQuest( code ) )
|
|
return false;
|
|
|
|
// 시작 가능 퀘스트인지 체크
|
|
if( !IsStartableQuest( code ) )
|
|
return false;
|
|
|
|
int nStatus[QuestInstance::MAX_STATUS];
|
|
memset( &nStatus, 0, sizeof( nStatus ) );
|
|
|
|
// 시작 대기 퀘스트 정보는 제한 시간 정보를 사용하지 않고 StartQuest에서 세팅함
|
|
StructQuest * pQuest = StructQuest::AllocQuest( m_pHandler, allocQuestID(), code, nStatus, 0, QuestInstance::NOT_STARTED, nStartID );
|
|
m_vActiveQuest.push_back( pQuest );
|
|
|
|
// 랜덤 퀘스트일 경우 미리 랜덤 정보를 결정해 둬야 한다면 여기서 결정하면 되지만 현재는 미리 세팅하지 않고 StartQuest에서 세팅함
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StructQuestManager::StartQuest( QuestBase::QuestCode code, int nStartID )
|
|
{
|
|
if( !code || !nStartID )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
// 시작 대기 상태가 아닌 퀘스트 정보가 있는지 외부에서 체크한 것과 별도로 다시 한 번 확인
|
|
StructQuest * pQuest = FindQuest( code );
|
|
if( pQuest && pQuest->GetProgress() != QuestInstance::NOT_STARTED )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
AR_TIME nTimeLimit = ( StructQuest::GetQuestBase( code ).eTimeLimitType != QuestBase::TIME_LIMIT_TYPE_PERMANENT ) ? GetArTime() + StructQuest::GetQuestBase( code ).nTimeLimit * 100 : 0;
|
|
|
|
// 시작 대기 상태의 퀘스트일 경우 기존에 추가되어 있는 퀘스트 정보 사용
|
|
if( pQuest )
|
|
pQuest->SetTimeLimit( nTimeLimit );
|
|
else
|
|
{
|
|
int nStatus[QuestInstance::MAX_STATUS];
|
|
memset( &nStatus, 0, sizeof( nStatus ) );
|
|
|
|
pQuest = StructQuest::AllocQuest( m_pHandler, allocQuestID(), code, nStatus, nTimeLimit, QuestInstance::IN_PROGRESS, nStartID );
|
|
}
|
|
|
|
if( pQuest->IsRandomQuest() )
|
|
{
|
|
RandomQuestInfo * pQuestInfo = NULL;
|
|
bool bNeedToUpdate = true;
|
|
|
|
std::vector< RandomQuestInfo >::iterator it;
|
|
|
|
for( it = m_vRandomQuestInfo.begin(); it != m_vRandomQuestInfo.end(); ++it )
|
|
{
|
|
if( (*it).code == code )
|
|
{
|
|
pQuestInfo = &(*it);
|
|
bNeedToUpdate = !pQuestInfo->is_dropped;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !pQuestInfo )
|
|
{
|
|
m_vRandomQuestInfo.push_back( RandomQuestInfo( code ) );
|
|
pQuestInfo = &( m_vRandomQuestInfo.back() );
|
|
}
|
|
|
|
if( bNeedToUpdate )
|
|
{
|
|
int group_id = 0;
|
|
int prev_group_id = 0;
|
|
|
|
std::vector< GameContent::RANDOM_POOL_INFO > vRandomPoolInfo;
|
|
|
|
for( int i = 0; i < QuestInstance::MAX_RANDOM_VALUE; ++i )
|
|
pQuestInfo->key[i] = pQuestInfo->value[i] = 0;
|
|
|
|
for( int i = 0; i < QuestInstance::MAX_RANDOM_VALUE; ++i )
|
|
{
|
|
group_id = pQuest->GetValue( i * 4 );
|
|
|
|
if( !group_id )
|
|
break;
|
|
|
|
if( group_id != prev_group_id && !GameContent::GetRandomPoolInfo( group_id, vRandomPoolInfo, pQuest->GetQuestBase().nLimitLevel ) )
|
|
assert( 0 );
|
|
|
|
if( !vRandomPoolInfo.size() )
|
|
{
|
|
pQuest->FreeQuest();
|
|
return false;
|
|
}
|
|
|
|
prev_group_id = group_id;
|
|
|
|
size_t idx = 0;
|
|
|
|
idx = XRandom() % vRandomPoolInfo.size();
|
|
|
|
pQuestInfo->key[i] = vRandomPoolInfo[idx].quest_target_id;
|
|
pQuestInfo->value[i] = XRandom( pQuest->GetValue( i * 4 + 1 ), pQuest->GetValue( i * 4 + 2 ) );
|
|
|
|
vRandomPoolInfo.erase( vRandomPoolInfo.begin() + idx );
|
|
}
|
|
}
|
|
|
|
pQuestInfo->is_dropped = false;
|
|
|
|
for( int i = 0; i < QuestInstance::MAX_RANDOM_VALUE; ++i )
|
|
{
|
|
pQuest->SetRandomKey( i, pQuestInfo->key[i] );
|
|
pQuest->SetRandomValue( i, pQuestInfo->value[i] );
|
|
}
|
|
}
|
|
|
|
// 수락/포기 쿨타임이 있는 퀘스트에 한해서만 쿨타임 설정
|
|
if( pQuest->GetQuestBase().nDropCoolTime )
|
|
{
|
|
time_t tCoolTime = time( NULL ) + pQuest->GetQuestBase().nDropCoolTime;
|
|
|
|
AddQuestCoolTime( pQuest->GetQuestCode(), tCoolTime, QuestInstance::IN_PROGRESS );
|
|
}
|
|
|
|
// 시작 대기 상태의 퀘스트였을 경우 진행 상태만 변경, 아니라면 새로 목록에 추가
|
|
if( pQuest->GetProgress() == QuestInstance::NOT_STARTED )
|
|
pQuest->SetProgress( QuestInstance::IN_PROGRESS );
|
|
else
|
|
{
|
|
m_vActiveQuest.push_back( pQuest );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StructQuestManager::EndQuest( struct StructQuest* pQuest )
|
|
{
|
|
pQuest->SetProgress( QuestInstance::FINISHED );
|
|
|
|
if( !m_hsFinishedQuest.has( pQuest->GetQuestCode() ) )
|
|
{
|
|
m_hsFinishedQuest.add( pQuest->GetQuestCode(), pQuest->GetQuestID() );
|
|
}
|
|
|
|
// 반복퀘가 아니더라도 퀘스트를 다시 받을 방법이 존재하므로, 쿨타임을 사용하게 되어있다면 의도된 것으로 판단하여 쿨타임 설정을 해준다.
|
|
if( pQuest->GetQuestBase().nCoolTime )
|
|
{
|
|
time_t tCoolTime = time( NULL ) + pQuest->GetQuestBase().nCoolTime;
|
|
|
|
// 완료 후 재수락 쿨타임(단위: 초, 0 : 사용 안 함, 음수 : 일일 단위 초기화)
|
|
if( pQuest->GetQuestBase().nCoolTime < 0 )
|
|
{
|
|
tCoolTime = static_cast< StructPlayer * >( m_pHandler )->GetNextHuntaholicEnterableCountRefill();
|
|
}
|
|
|
|
AddQuestCoolTime( pQuest->GetQuestCode(), tCoolTime, QuestInstance::FINISHED );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const bool StructQuestManager::ProcExpiredQuest( const AR_TIME t )
|
|
{
|
|
bool bExpired = false;
|
|
|
|
for( std::vector< StructQuest * >::iterator it = m_vActiveQuest.begin() ; it != m_vActiveQuest.end() ; ++it )
|
|
{
|
|
// 진행 중인 퀘스트가 아니면 패스
|
|
if( (*it)->GetProgress() != QuestInstance::IN_PROGRESS )
|
|
continue;
|
|
|
|
// 시간 무제한 퀘스트 제외
|
|
if( (*it)->GetTimeLimitType() == QuestBase::TIME_LIMIT_TYPE_PERMANENT )
|
|
continue;
|
|
|
|
// 시간 경과 타입에 관계 없이 GetTimeLimit으로 나온 값이 제한 시간
|
|
if( (*it)->GetTimeLimit() >= t )
|
|
continue;
|
|
|
|
// 만료된 퀘스트 처리
|
|
bExpired = true;
|
|
(*it)->SetProgress( QuestInstance::FAIL );
|
|
}
|
|
|
|
if( bExpired )
|
|
SendNPCStatusInVisibleRange( static_cast< StructPlayer * >( m_pHandler ) );
|
|
|
|
return bExpired;
|
|
}
|
|
|
|
|
|
struct StructQuest* StructQuestManager::FindQuest( QuestBase::QuestCode code ) const
|
|
{
|
|
std::vector< StructQuest * >::const_iterator it;
|
|
|
|
for( it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
if( (*it)->GetQuestCode() == code && (*it)->GetProgress() != QuestInstance::FINISHED )
|
|
{
|
|
return (*it);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const bool StructQuestManager::IsPendingQuest( const QuestBase::QuestCode code ) const
|
|
{
|
|
StructQuest * pQuest = FindQuest( code );
|
|
|
|
return ( pQuest && pQuest->GetProgress() == QuestInstance::NOT_STARTED );
|
|
}
|
|
|
|
bool StructQuestManager::IsFinishedQuest( QuestBase::QuestCode code ) const
|
|
{
|
|
return m_hsFinishedQuest.has( code );
|
|
}
|
|
|
|
QuestInstance::QuestID StructQuestManager::GetFinishedQuestID( QuestBase::QuestCode code ) const
|
|
{
|
|
QuestInstance::QuestID id = 0;
|
|
|
|
m_hsFinishedQuest.lookup( code, id );
|
|
|
|
return id;
|
|
}
|
|
|
|
const bool StructQuestManager::ResetFinishedQuest( QuestBase::QuestCode code )
|
|
{
|
|
return m_hsFinishedQuest.erase( code );
|
|
}
|
|
|
|
static bool checkQuestTypeFlag( QuestBase::QuestType type, int flag )
|
|
{
|
|
if( flag == QuestBase::TYPE_FLAG_ALL ) return true;
|
|
|
|
{
|
|
switch( type )
|
|
{
|
|
case QuestBase::QUEST_MISC:
|
|
case QuestBase::QUEST_EXTERNAL_CONTROL: return ( QuestBase::TYPE_FLAG_MISC & flag );
|
|
case QuestBase::QUEST_KILL_TOTAL: return ( QuestBase::TYPE_FLAG_KILL_TOTAL & flag );
|
|
case QuestBase::QUEST_KILL_INDIVIDUAL: return ( QuestBase::TYPE_FLAG_KILL_INDIVIDUAL & flag );
|
|
case QuestBase::QUEST_COLLECT: return ( QuestBase::TYPE_FLAG_COLLECT & flag );
|
|
case QuestBase::QUEST_LEARN_SKILL: return ( QuestBase::TYPE_FLAG_LEARN_SKILL & flag );
|
|
case QuestBase::QUEST_HUNT_ITEM: return ( QuestBase::TYPE_FLAG_HUNT_ITEM & flag );
|
|
case QuestBase::QUEST_HUNT_ITEM_FROM_ANY_MONSTERS: return ( QuestBase::TYPE_FLAG_HUNT_ITEM_FROM_ANY_MONSTSERS & flag );
|
|
case QuestBase::QUEST_HUNT_ITEM_DROP_PENALTY: return ( QuestBase::TYPE_FLAG_HUNT_ITEM_FROM_ANY_MONSTSERS & flag );
|
|
case QuestBase::QUEST_KILL_PLAYER: return ( QuestBase::TYPE_FLAG_KILL_PLAYER & flag );
|
|
case QuestBase::QUEST_UPGRADE_ITEM: return ( QuestBase::TYPE_FLAG_UPGRADE_ITEM & flag );
|
|
case QuestBase::QUEST_CONTACT: return ( QuestBase::TYPE_FLAG_CONTACT & flag );
|
|
case QuestBase::QUEST_JOB_LEVEL: return ( QuestBase::TYPE_FLAG_JOB_LEVEL & flag );
|
|
case QuestBase::QUEST_PARAMETER: return ( QuestBase::TYPE_FLAG_PARAMETER & flag );
|
|
case QuestBase::QUEST_RANDOM_COLLECT: return ( QuestBase::TYPE_FLAG_RANDOM_COLLECT & flag );
|
|
case QuestBase::QUEST_RANDOM_KILL_INDIVIDUAL: return ( QuestBase::TYPE_FLAG_RANDOM_KILL_INDIVIDUAL & flag );
|
|
|
|
// by Testors
|
|
//default: assert( 0 ); return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool StructQuestManager::IsTakeableQuestItem( QuestBase::QuestCode code ) const
|
|
{
|
|
std::vector< StructQuest * >::const_iterator it;
|
|
|
|
for( it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
if( (*it)->GetProgress() == QuestInstance::FINISHED || (*it)->GetProgress() == QuestInstance::FAIL ) continue;
|
|
|
|
if( (*it)->GetQuestType() != QuestBase::QUEST_COLLECT &&
|
|
(*it)->GetQuestType() != QuestBase::QUEST_HUNT_ITEM &&
|
|
(*it)->GetQuestType() != QuestBase::QUEST_HUNT_ITEM_FROM_ANY_MONSTERS &&
|
|
(*it)->GetQuestType() != QuestBase::QUEST_HUNT_ITEM_DROP_PENALTY &&
|
|
(*it)->GetQuestType() != QuestBase::QUEST_RANDOM_COLLECT ) continue;
|
|
|
|
if( (*it)->IsFinishable() ) continue;
|
|
|
|
if( (*it)->IsRandomQuest() )
|
|
{
|
|
if( (*it)->GetRandomKey( 0 ) == code ||
|
|
(*it)->GetRandomKey( 1 ) == code ||
|
|
(*it)->GetRandomKey( 2 ) == code ) return true;
|
|
}
|
|
else if( (*it)->GetQuestType() == QuestBase::QUEST_COLLECT )
|
|
{
|
|
if( (*it)->GetValue( 0 ) == code ||
|
|
(*it)->GetValue( 2 ) == code ||
|
|
(*it)->GetValue( 4 ) == code ||
|
|
(*it)->GetValue( 6 ) == code ||
|
|
(*it)->GetValue( 8 ) == code ||
|
|
(*it)->GetValue( 10 ) == code ) return true;
|
|
}
|
|
else
|
|
{
|
|
if( (*it)->GetValue( 0 ) == code ||
|
|
(*it)->GetValue( 2 ) == code ||
|
|
(*it)->GetValue( 4 ) == code ) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void StructQuestManager::GetRelatedQuest( std::vector< StructQuest* > & vQuestList, int flag )
|
|
{
|
|
std::vector< StructQuest * >::iterator it;
|
|
|
|
for( it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
if( (*it)->GetProgress() == QuestInstance::FINISHED || (*it)->GetProgress() == QuestInstance::FAIL ) continue;
|
|
|
|
// 조건검사
|
|
if( !checkQuestTypeFlag( (*it)->GetQuestType(), flag ) ) continue;
|
|
|
|
vQuestList.push_back( *it );
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::GetRelatedQuestByItem( ItemBase::ItemCode code, std::vector< StructQuest* > & vQuestList, int flag )
|
|
{
|
|
std::vector< StructQuest * >::iterator it;
|
|
|
|
for( it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
if( (*it)->GetProgress() == QuestInstance::FINISHED || (*it)->GetProgress() == QuestInstance::FAIL ) continue;
|
|
|
|
// 조건검사
|
|
if( !checkQuestTypeFlag( (*it)->GetQuestType(), flag ) ) continue;
|
|
|
|
// 사냥 수집퀘스트
|
|
if( (*it)->GetQuestType() == QuestBase::QUEST_HUNT_ITEM ||
|
|
(*it)->GetQuestType() == QuestBase::QUEST_HUNT_ITEM_FROM_ANY_MONSTERS ||
|
|
(*it)->GetQuestType() == QuestBase::QUEST_HUNT_ITEM_DROP_PENALTY )
|
|
{
|
|
if( (*it)->GetValue( 0 ) == code || (*it)->GetValue( 2 ) == code || (*it)->GetValue( 4 ) == code ) vQuestList.push_back( *it );
|
|
continue;
|
|
}
|
|
|
|
// 수집퀘스트
|
|
if( (*it)->GetQuestType() == QuestBase::QUEST_COLLECT )
|
|
{
|
|
if( (*it)->GetValue( 0 ) == code || (*it)->GetValue( 2 ) == code || (*it)->GetValue( 4 ) == code ||
|
|
(*it)->GetValue( 6 ) == code || (*it)->GetValue( 8 ) == code || (*it)->GetValue( 10 ) == code ) vQuestList.push_back( *it );
|
|
continue;
|
|
}
|
|
|
|
// 랜덤 수집 퀘스트
|
|
if( (*it)->GetQuestType() == QuestBase::QUEST_RANDOM_COLLECT )
|
|
{
|
|
if( (*it)->GetRandomKey( 0 ) == code || (*it)->GetRandomKey( 1 ) == code || (*it)->GetRandomKey( 2 ) == code ) vQuestList.push_back( *it );
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::UpdateQuestStatusByItemCount( ItemBase::ItemCode code, const __int64 & count )
|
|
{
|
|
std::vector< StructQuest* > vQuestList;
|
|
|
|
GetRelatedQuestByItem( code, vQuestList, QuestBase::TYPE_FLAG_COLLECT | QuestBase::TYPE_FLAG_HUNT_ITEM | QuestBase::TYPE_FLAG_HUNT_ITEM_FROM_ANY_MONSTSERS | QuestBase::TYPE_FLAG_RANDOM_COLLECT );
|
|
|
|
for( std::vector< StructQuest* >::iterator it = vQuestList.begin(); it != vQuestList.end(); ++it )
|
|
{
|
|
StructQuest * pQuest = (*it);
|
|
|
|
bool bFinishable = pQuest->IsFinishable();
|
|
|
|
for( int i = 0; i < QuestInstance::MAX_STATUS; ++i )
|
|
{
|
|
if( pQuest->IsRandomQuest() )
|
|
{
|
|
if( pQuest->GetRandomKey( i ) == code )
|
|
{
|
|
pQuest->UpdateStatus( i, std::min( pQuest->GetRandomValue( i ), static_cast< int >( count ) ) );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pQuest->GetValue( i * 2 ) == code )
|
|
{
|
|
pQuest->UpdateStatus( i, std::min( (int) pQuest->GetValue( i * 2 + 1), static_cast< int >( count ) ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bFinishable != pQuest->IsFinishable() )
|
|
{
|
|
// 완료 가능하던 퀘스트가 완료 불가능하게 변경되었으면 실패 처리
|
|
if( bFinishable )
|
|
{
|
|
pQuest->SetProgress( QuestInstance::FAIL );
|
|
}
|
|
// 완료 불가능하던 퀘스트가 완료 가능하게 되었으면 종료 가능 상태로 변경(시간제 퀘스트 및 불필요한 체크 중지)
|
|
else
|
|
{
|
|
pQuest->SetProgress( QuestInstance::FINISHABLE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::UpdateQuestStatusByItemEnhance( StructInventory* inventory, const ItemBase::ItemCode code )
|
|
{
|
|
// 어차피 퀘스트 전체를 순회할텐데 굳이 GetRelatedQuest이나 flag 등의 불필요한 추상화를 도입한 의도를 모르겠음
|
|
std::vector< StructItem* > itemList;
|
|
|
|
for( std::vector< StructQuest* >::iterator questIt = m_vActiveQuest.begin(); questIt != m_vActiveQuest.end(); ++questIt )
|
|
{
|
|
StructQuest* quest = *questIt;
|
|
|
|
if( quest->GetQuestType() != QuestBase::QUEST_ENHANCE_ITEM ||
|
|
quest->GetProgress() == QuestInstance::FINISHED ||
|
|
quest->GetProgress() == QuestInstance::FAIL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool bFinishable = quest->IsFinishable();
|
|
|
|
for( int i = 0 ; i < 3 ; i++ )
|
|
{
|
|
ItemBase::ItemCode targetItemCode = quest->GetValue( i*2 );
|
|
|
|
// 인자로 주어진 code가 0인 경우는 예외 없이 체크
|
|
if( targetItemCode != 0 && ( targetItemCode == code || code == 0 ) )
|
|
{
|
|
itemList.clear();
|
|
inventory->Find( targetItemCode, itemList );
|
|
|
|
int enhance = 0;
|
|
|
|
for( std::vector< StructItem* >::iterator itemIt = itemList.begin(); itemIt != itemList.end(); itemIt++ )
|
|
{
|
|
StructItem* item = *itemIt;
|
|
enhance = std::max( item->GetItemEnhance(), enhance );
|
|
}
|
|
|
|
quest->UpdateStatus( i, std::min( enhance, quest->GetValue( i*2 + 1 ) ) );
|
|
}
|
|
}
|
|
|
|
if( bFinishable != quest->IsFinishable() )
|
|
{
|
|
// 완료 가능하던 퀘스트가 완료 불가능하게 변경되었으면 실패 처리
|
|
if( bFinishable )
|
|
{
|
|
quest->SetProgress( QuestInstance::FAIL );
|
|
}
|
|
// 완료 불가능하던 퀘스트가 완료 가능하게 되었으면 종료 가능 상태로 변경(시간제 퀘스트 및 불필요한 체크 중지)
|
|
else
|
|
{
|
|
quest->SetProgress( QuestInstance::FINISHABLE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::UpdateQuestStatusByParameter( int parameter_id, int value )
|
|
{
|
|
std::vector< StructQuest* > vQuestList;
|
|
|
|
GetRelatedQuest( vQuestList, QuestBase::TYPE_FLAG_PARAMETER );
|
|
|
|
for( std::vector< StructQuest * >::iterator it = vQuestList.begin(); it != vQuestList.end(); ++it )
|
|
{
|
|
bool bFinishable = (*it)->IsFinishable();
|
|
|
|
for( int nIndex = 0 ; nIndex <= 6 ; nIndex += 3 )
|
|
{
|
|
if( (*it)->GetValue( nIndex ) == parameter_id )
|
|
{
|
|
(*it)->UpdateStatus( nIndex / 3, value );
|
|
}
|
|
}
|
|
|
|
if( bFinishable != (*it)->IsFinishable() )
|
|
{
|
|
// 완료 가능하던 퀘스트가 완료 불가능하게 변경되었으면 실패 처리
|
|
if( bFinishable )
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FAIL );
|
|
}
|
|
// 완료 불가능하던 퀘스트가 완료 가능하게 되었으면 종료 가능 상태로 변경(시간제 퀘스트 및 불필요한 체크 중지)
|
|
else
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FINISHABLE );
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::UpdateQuestStatusByJobLevel( int nJobDepth, int nJobLevel )
|
|
{
|
|
std::vector< StructQuest* > vQuestList;
|
|
|
|
GetRelatedQuest( vQuestList, QuestBase::TYPE_FLAG_JOB_LEVEL );
|
|
|
|
for( std::vector< StructQuest * >::iterator it = vQuestList.begin(); it != vQuestList.end(); ++it )
|
|
{
|
|
bool bFinishable = (*it)->IsFinishable();
|
|
|
|
(*it)->UpdateStatus( 0, std::min( (int) (*it)->GetValue( 0 ), nJobDepth ) );
|
|
(*it)->UpdateStatus( 1, std::min( (int) (*it)->GetValue( 1 ), nJobLevel ) );
|
|
|
|
if( bFinishable != (*it)->IsFinishable() )
|
|
{
|
|
// 완료 가능하던 퀘스트가 완료 불가능하게 변경되었으면 실패 처리
|
|
if( bFinishable )
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FAIL );
|
|
}
|
|
// 완료 불가능하던 퀘스트가 완료 가능하게 되었으면 종료 가능 상태로 변경(시간제 퀘스트 및 불필요한 체크 중지)
|
|
else
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FINISHABLE );
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::UpdateQuestStatusBySkillLevel( int nSkillId, int nSkillLevel )
|
|
{
|
|
std::vector< StructQuest* > vQuestList;
|
|
|
|
GetRelatedQuest( vQuestList, QuestBase::TYPE_FLAG_LEARN_SKILL );
|
|
|
|
for( std::vector< StructQuest * >::iterator it = vQuestList.begin(); it != vQuestList.end(); ++it )
|
|
{
|
|
bool bFinishable = (*it)->IsFinishable();
|
|
|
|
for( int nIndex = 0 ; nIndex < QuestBase::MAX_VALUE_NUMBER ; nIndex += 2 )
|
|
{
|
|
if( (*it)->GetValue( nIndex ) == nSkillId )
|
|
{
|
|
(*it)->UpdateStatus( nIndex / 2, std::min( (int) (*it)->GetValue( nIndex + 1 ), nSkillLevel ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bFinishable != (*it)->IsFinishable() )
|
|
{
|
|
// 완료 가능하던 퀘스트가 완료 불가능하게 변경되었으면 실패 처리
|
|
if( bFinishable )
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FAIL );
|
|
}
|
|
// 완료 불가능하던 퀘스트가 완료 가능하게 되었으면 종료 가능 상태로 변경(시간제 퀘스트 및 불필요한 체크 중지)
|
|
else
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FINISHABLE );
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::UpdateQuestStatusByPlayerKill( const struct StructPlayer * pKilledPlayer )
|
|
{
|
|
std::vector< StructQuest * > vQuestList;
|
|
GetRelatedQuest( vQuestList, QuestBase::TYPE_FLAG_KILL_PLAYER );
|
|
|
|
for( std::vector< StructQuest * >::iterator it = vQuestList.begin() ; it != vQuestList.end() ; ++it )
|
|
{
|
|
// 이미 조건 완료면 KIN
|
|
if( (*it)->IsFinishable() )
|
|
continue;
|
|
|
|
if( (*it)->GetValue( 0 ) == pKilledPlayer->GetRace() && (*it)->GetStatus( 0 ) < (*it)->GetValue( 1 ) )
|
|
(*it)->IncStatus( 0 );
|
|
if( (*it)->GetValue( 2 ) == pKilledPlayer->GetJobId() && (*it)->GetStatus( 1 ) < (*it)->GetValue( 3 ) )
|
|
(*it)->IncStatus( 1 );
|
|
|
|
// 완료 불가능하던 퀘스트가 완료 가능하게 되었으면 종료 가능 상태로 변경(시간제 퀘스트 및 불필요한 체크 중지)
|
|
if( (*it)->IsFinishable() )
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FINISHABLE );
|
|
}
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::UpdateQuestStatusByMonsterKill( int nMonsterId )
|
|
{
|
|
std::vector< StructQuest* > vQuestList;
|
|
GetRelatedQuest( vQuestList, QuestBase::TYPE_FLAG_KILL );
|
|
|
|
for( std::vector< StructQuest * >::iterator it = vQuestList.begin(); it != vQuestList.end(); ++it )
|
|
{
|
|
// 이미 조건 완료면 KIN
|
|
if( (*it)->IsFinishable() ) continue;
|
|
|
|
switch( (*it)->GetQuestType() )
|
|
{
|
|
case QuestBase::QUEST_KILL_TOTAL:
|
|
if( GameContent::IsInRandomPoolMonster( (*it)->GetValue( 0 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 2 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 4 ), nMonsterId ) )
|
|
{
|
|
if( (*it)->GetStatus( 0 ) < (*it)->GetValue( 1 ) )
|
|
(*it)->IncStatus( 0 );
|
|
}
|
|
break;
|
|
|
|
case QuestBase::QUEST_KILL_INDIVIDUAL:
|
|
if( GameContent::IsInRandomPoolMonster( (*it)->GetValue( 0 ), nMonsterId ) && (*it)->GetStatus( 0 ) < (*it)->GetValue( 1 ) )
|
|
(*it)->IncStatus( 0 );
|
|
else if( GameContent::IsInRandomPoolMonster( (*it)->GetValue( 2 ), nMonsterId ) && (*it)->GetStatus( 1 ) < (*it)->GetValue( 3 ) )
|
|
(*it)->IncStatus( 1 );
|
|
else if( GameContent::IsInRandomPoolMonster( (*it)->GetValue( 4 ), nMonsterId ) && (*it)->GetStatus( 2 ) < (*it)->GetValue( 5 ) )
|
|
(*it)->IncStatus( 2 );
|
|
break;
|
|
|
|
case QuestBase::QUEST_RANDOM_KILL_INDIVIDUAL:
|
|
if( (*it)->GetRandomKey( 0 ) == nMonsterId && (*it)->GetStatus( 0 ) < (*it)->GetRandomValue( 0 ) )
|
|
(*it)->IncStatus( 0 );
|
|
else if( (*it)->GetRandomKey( 1 ) == nMonsterId && (*it)->GetStatus( 1 ) < (*it)->GetRandomValue( 1 ) )
|
|
(*it)->IncStatus( 1 );
|
|
else if( (*it)->GetRandomKey( 2 ) == nMonsterId && (*it)->GetStatus( 2 ) < (*it)->GetRandomValue( 2 ) )
|
|
(*it)->IncStatus( 2 );
|
|
break;
|
|
}
|
|
|
|
// 완료 불가능하던 퀘스트가 완료 가능하게 되었으면 종료 가능 상태로 변경(시간제 퀘스트 및 불필요한 체크 중지)
|
|
if( (*it)->IsFinishable() )
|
|
{
|
|
(*it)->SetProgress( QuestInstance::FINISHABLE );
|
|
}
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::GetRelatedQuestByMonster( int nMonsterId, std::vector< StructQuest* > & vQuestList, int flag )
|
|
{
|
|
std::vector< StructQuest * >::iterator it;
|
|
|
|
for( it = m_vActiveQuest.begin(); it != m_vActiveQuest.end(); ++it )
|
|
{
|
|
if( (*it)->GetProgress() == QuestInstance::FINISHED || (*it)->GetProgress() == QuestInstance::FAIL ) continue;
|
|
|
|
// 조건검사
|
|
if( !checkQuestTypeFlag( (*it)->GetQuestType(), flag ) ) continue;
|
|
|
|
// 학살 퀘스트
|
|
if( (*it)->GetQuestType() == QuestBase::QUEST_KILL_TOTAL || (*it)->GetQuestType() == QuestBase::QUEST_KILL_INDIVIDUAL )
|
|
{
|
|
if( GameContent::IsInRandomPoolMonster( (*it)->GetValue( 0 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 2 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 4 ), nMonsterId ) )
|
|
{
|
|
vQuestList.push_back( *it );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// 사냥 수집 퀘스트
|
|
if( (*it)->GetQuestType() == QuestBase::QUEST_HUNT_ITEM )
|
|
{
|
|
if( GameContent::IsInRandomPoolMonster( (*it)->GetValue( 6 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 7 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 8 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 9 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 10 ), nMonsterId ) ||
|
|
GameContent::IsInRandomPoolMonster( (*it)->GetValue( 11 ), nMonsterId ) )
|
|
{
|
|
vQuestList.push_back( *it );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// 사냥 수집 퀘스트(어떠한 몬스터든)
|
|
if( (*it)->GetQuestType() == QuestBase::QUEST_HUNT_ITEM_FROM_ANY_MONSTERS ||
|
|
(*it)->GetQuestType() == QuestBase::QUEST_HUNT_ITEM_DROP_PENALTY )
|
|
{
|
|
vQuestList.push_back( *it );
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::SetMaxQuestID( QuestInstance::QuestID id )
|
|
{
|
|
if( m_QuestIdx )
|
|
{
|
|
throw XException( "SetMaxQuestID가 한 매니저에 대해 두번 불렸음." );
|
|
}
|
|
|
|
m_QuestIdx = id;
|
|
}
|
|
|
|
QuestInstance::QuestID StructQuestManager::allocQuestID()
|
|
{
|
|
return InterlockedIncrement( &m_QuestIdx );
|
|
}
|
|
|
|
bool StructQuestManager::IsStartableQuest( const QuestBase::QuestCode code ) const
|
|
{
|
|
// 시작 대기 상태가 아닌 퀘스트가 이미 있다면 시작 불가
|
|
StructQuest * pQuest = FindQuest( code );
|
|
if( pQuest && pQuest->GetProgress() != QuestInstance::NOT_STARTED )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const QuestBase & rQuestBase = StructQuest::GetQuestBase( code );
|
|
|
|
// 이미 끝냈고 반복가능퀘스트가 아니면 KIN
|
|
if( !rQuestBase.bIsRepeatable && IsFinishedQuest( code ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 선행조건
|
|
bool bRtn = true;
|
|
if( rQuestBase.bForeCheckType ) bRtn = false;
|
|
for( int i = 0; i < QuestBase::MAX_FOREQUEST; ++i )
|
|
{
|
|
// or 타입
|
|
if( rQuestBase.bForeCheckType )
|
|
{
|
|
if( rQuestBase.nForeQuest[i] && IsFinishedQuest( rQuestBase.nForeQuest[i] ) ) bRtn = true;
|
|
|
|
}
|
|
// and 타입
|
|
else
|
|
{
|
|
if( rQuestBase.nForeQuest[i] && !IsFinishedQuest( rQuestBase.nForeQuest[i] ) ) return false;
|
|
}
|
|
}
|
|
|
|
return bRtn;
|
|
}
|
|
|
|
void StructQuestManager::PopFromActiveQuest( struct StructQuest * pQuest )
|
|
{
|
|
std::vector< StructQuest * >::iterator it = std::find( m_vActiveQuest.begin(), m_vActiveQuest.end(), pQuest );
|
|
|
|
if( it != m_vActiveQuest.end() )
|
|
{
|
|
m_vActiveQuest.erase( it );
|
|
|
|
pQuest->FreeQuest();
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::SetDropFlagToRandomQuestInfo( int code )
|
|
{
|
|
std::vector< RandomQuestInfo >::iterator it;
|
|
|
|
for( it = m_vRandomQuestInfo.begin(); it != m_vRandomQuestInfo.end(); ++it )
|
|
{
|
|
if( (*it).code == code )
|
|
{
|
|
(*it).is_dropped = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool StructQuestManager::HasRandomQuestInfo( int code ) const
|
|
{
|
|
std::vector< RandomQuestInfo >::const_iterator it;
|
|
|
|
for( it = m_vRandomQuestInfo.begin(); it != m_vRandomQuestInfo.end(); ++it )
|
|
{
|
|
if( (*it).code == code )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool StructQuestManager::NeedToUpdateRandomQuestInfo( int code ) const
|
|
{
|
|
std::vector< RandomQuestInfo >::const_iterator it;
|
|
|
|
for( it = m_vRandomQuestInfo.begin(); it != m_vRandomQuestInfo.end(); ++it )
|
|
{
|
|
if( (*it).code == code )
|
|
{
|
|
if( (*it).is_dropped )
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void StructQuestManager::AddRandomQuestInfo( int code, int key[], int value[], bool is_dropped )
|
|
{
|
|
m_vRandomQuestInfo.push_back( RandomQuestInfo( code ) );
|
|
|
|
RandomQuestInfo * pQuestInfo = &(m_vRandomQuestInfo.back());
|
|
|
|
for( int i = 0; i < QuestInstance::MAX_RANDOM_VALUE; ++i )
|
|
{
|
|
pQuestInfo->key[i] = key[i];
|
|
pQuestInfo->value[i] = value[i];
|
|
}
|
|
|
|
pQuestInfo->is_dropped = is_dropped;
|
|
}
|
|
|
|
void StructQuestManager::ReadQuestCoolTime( int code, AR_TIME cool_time, QuestInstance::QUEST_PROGRESS progress )
|
|
{
|
|
m_hsQuestCoolTime.add( code, new QuestCoolTimeInfo( cool_time, progress ) );
|
|
}
|
|
|
|
void StructQuestManager::AddQuestCoolTime( int code, AR_TIME cool_time, QuestInstance::QUEST_PROGRESS progress )
|
|
{
|
|
QuestCoolTimeInfo *pQuestCoolTime = NULL;
|
|
|
|
StructPlayer *pPlayer = static_cast< StructPlayer * >( m_pHandler );
|
|
|
|
// 존재한다면 업데이트
|
|
if( m_hsQuestCoolTime.lookup( code, pQuestCoolTime ) )
|
|
{
|
|
// 가장 긴 쿨타임 하나만 유지하며 progress를 이용하여 완료 쿨타임과 재수락 쿨타임의 툴팁을 다르게 표현할 수 있도록 기록
|
|
if( pQuestCoolTime->cool_time < cool_time )
|
|
{
|
|
pQuestCoolTime->cool_time = cool_time;
|
|
pQuestCoolTime->progress = progress;
|
|
|
|
pPlayer->DBQuery( new DB_UpdateQuestCoolTime( pPlayer, pPlayer->GetSID(), code, cool_time, progress ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pQuestCoolTime = new QuestCoolTimeInfo( cool_time, progress );
|
|
m_hsQuestCoolTime.add( code, pQuestCoolTime );
|
|
|
|
pPlayer->DBQuery( new DB_InsertQuestCoolTime( pPlayer, pPlayer->GetSID(), code, cool_time, progress ) );
|
|
}
|
|
}
|
|
|
|
void StructQuestManager::RemoveQuestCoolTime( int code )
|
|
{
|
|
QuestCoolTimeInfo *pQuestCoolTime = NULL;
|
|
|
|
StructPlayer *pPlayer = static_cast< StructPlayer * >( m_pHandler );
|
|
|
|
if( m_hsQuestCoolTime.lookup( code, pQuestCoolTime ) )
|
|
{
|
|
delete pQuestCoolTime;
|
|
m_hsQuestCoolTime.erase( code );
|
|
|
|
pPlayer->DBQuery( new DB_DeleteQuestCoolTime( pPlayer, pPlayer->GetSID(), code ) );
|
|
}
|
|
}
|
|
|
|
const time_t StructQuestManager::GetQuestCoolTime( int code ) const
|
|
{
|
|
time_t tCoolTime = 0;
|
|
|
|
QuestCoolTimeInfo *pQuestCoolTime = NULL;
|
|
if( m_hsQuestCoolTime.lookup( code, pQuestCoolTime ) )
|
|
tCoolTime = pQuestCoolTime->cool_time;
|
|
|
|
return tCoolTime;
|
|
}
|
|
|
|
const time_t StructQuestManager::GetRemainQuestCoolTime( int code, time_t tCurrent ) const
|
|
{
|
|
time_t tCoolTime = 0;
|
|
|
|
QuestCoolTimeInfo *pQuestCoolTime = NULL;
|
|
if( m_hsQuestCoolTime.lookup( code, pQuestCoolTime ) )
|
|
tCoolTime = pQuestCoolTime->cool_time;
|
|
|
|
if( tCurrent < tCoolTime )
|
|
return tCoolTime - tCurrent;
|
|
|
|
return 0;
|
|
} |