Files
2026-06-01 12:46:52 +02:00

254 lines
5.5 KiB
C++

#include "StructNPC.h"
#include "StructPlayer.h"
#include "StructSummon.h"
#include "GameAllocator.h"
#include "SendMessage.h"
StructNPC::StructNPC( const NPCBase * npc_base )
{
m_nArObjectType = ArObject::MOVABLE_OBJECT;
m_hHandle = AllocMiscHandle( this );
m_bHasQuest = false;
m_pBaseInfo = npc_base;
std::string name = GameContent::GetString( m_pBaseInfo->text_id );
name += " ";
name += GameContent::GetString( m_pBaseInfo->name_text_id );
s_strcpy( m_szName, _countof( m_szName ), name.c_str() );
m_pRoamer = NULL;
m_pDeadHandler = NULL;
m_nStatus = STATUS_NORMAL;
m_bComeBackHome = false;
m_nLastEnemyDistance = 0;
m_nLastTrackTime = 0;
m_RespawnX = npc_base->x;
m_RespawnY = npc_base->y;
if( !IsAttackableNPC() )
SetInvincible( true );
#ifdef _MEM_USAGE_DEBUG
XSEH::IncreaseAllocCount( "StructNPC" );
#endif
}
AR_UNIT StructNPC::GetChaseRange() const
{
return m_pBaseInfo->chase_range * GameRule::DEFAULT_UNIT_SIZE;
}
StructNPC::NPC_STATUS StructNPC::GetStatus() const
{
return static_cast< NPC_STATUS >( m_nStatus );
}
void StructNPC::SetStatus( NPC_STATUS status )
{
if( m_nStatus != status && status != STATUS_DEAD )
{
BroadcastStatusMessage( this );
}
m_nStatus = status;
}
StructNPC::~StructNPC()
{
FreeMiscHandle( m_hHandle );
#ifdef _MEM_USAGE_DEBUG
XSEH::DecreaseAllocCount( "StructNPC" );
#endif
}
const CreatureStat & StructNPC::GetBaseStat() const
{
return GameContent::GetStatInfo( m_pBaseInfo->stat_id );
}
void StructNPC::LinkQuest( const QuestLink & quest_link_info )
{
if( quest_link_info.bLF_Start ) m_vQuestLink_Start.push_back( quest_link_info );
if( quest_link_info.bLF_Progress ) m_vQuestLink_Progress.push_back( quest_link_info );
if( quest_link_info.bLF_End ) m_vQuestLink_End.push_back( quest_link_info );
}
void StructNPC::DoEachStartableQuest( struct StructPlayer * pPlayer,QuestFunctor & _fo )
{
std::vector< QuestLink >::iterator it;
for( it = m_vQuestLink_Start.begin(); it != m_vQuestLink_Start.end(); ++it )
{
if( pPlayer->IsStartableQuest( (*it).code ) )
{
_fo( pPlayer, (*it) );
}
}
}
void StructNPC::DoEachInProgressQuest( struct StructPlayer * pPlayer,QuestFunctor & _fo )
{
std::vector< QuestLink >::iterator it;
for( it = m_vQuestLink_Progress.begin(); it != m_vQuestLink_Progress.end(); ++it )
{
if( pPlayer->IsInProgressQuest( (*it).code ) )
{
_fo( pPlayer, (*it) );
}
}
}
void StructNPC::DoEachFinishableQuest( struct StructPlayer * pPlayer, QuestFunctor & _fo )
{
std::vector< QuestLink >::iterator it;
for( it = m_vQuestLink_End.begin(); it != m_vQuestLink_End.end(); ++it )
{
if( pPlayer->IsFinishableQuest( (*it).code ) )
{
_fo( pPlayer, (*it) );
}
}
}
bool StructNPC::HasStartableQuest( const struct StructPlayer * pPlayer )
{
std::vector< QuestLink >::iterator it;
bool bHasStartableRandom = false;
bool bHasProgressRandom = false;
for( it = m_vQuestLink_Start.begin(); it != m_vQuestLink_Start.end(); ++it )
{
const QuestBase & rQuestBase = StructQuest::GetQuestBase( (*it).code );
if( rQuestBase.nType == QuestBase::QUEST_RANDOM_KILL_INDIVIDUAL || rQuestBase.nType == QuestBase::QUEST_RANDOM_COLLECT )
{
if( pPlayer->IsInProgressQuest( (*it).code ) || pPlayer->IsFinishableQuest( (*it).code ) )
{
bHasProgressRandom = true;
}
else if( pPlayer->IsStartableQuest( (*it).code ) ) bHasStartableRandom = true;
continue;
}
if( pPlayer->IsStartableQuest( (*it).code ) ) return true;
}
if( bHasStartableRandom && !bHasProgressRandom )
return true;
return false;
}
bool StructNPC::HasInProgressQuest( const struct StructPlayer * pPlayer )
{
std::vector< QuestLink >::iterator it;
for( it = m_vQuestLink_Progress.begin(); it != m_vQuestLink_Progress.end(); ++it )
{
if( pPlayer->IsInProgressQuest( (*it).code ) ) return true;
}
return false;
}
bool StructNPC::HasFinishableQuest( const struct StructPlayer * pPlayer )
{
std::vector< QuestLink >::iterator it;
for( it = m_vQuestLink_End.begin(); it != m_vQuestLink_End.end(); ++it )
{
if( pPlayer->IsFinishableQuest( (*it).code ) ) return true;
}
return false;
}
int StructNPC::GetQuestTextId( QuestBase::QuestCode code, int progress )
{
std::vector< QuestLink >::iterator it;
if( progress == ::QUEST_IS_FINISHABLE )
{
for( it = m_vQuestLink_End.begin(); it != m_vQuestLink_End.end(); ++it )
{
if( (*it).code != code ) continue;
return (*it).nEndTextId;
}
}
else if( progress == ::QUEST_IS_IN_PROGRESS )
{
for( it = m_vQuestLink_Progress.begin(); it != m_vQuestLink_Progress.end(); ++it )
{
if( (*it).code != code ) continue;
return (*it).nInProgressTextId;
}
}
else
{
for( it = m_vQuestLink_Start.begin(); it != m_vQuestLink_Start.end(); ++it )
{
if( (*it).code != code ) continue;
return (*it).nStartTextId;
}
}
return 0;
}
bool StructNPC::IsEnemy( StructCreature* pTarget, bool bIncludeHiding )
{
if( !StructCreature::IsEnemy( pTarget, bIncludeHiding ) )
return false;
if( pTarget->IsMonster() )
return false;
StructPlayer * pPlayer = NULL;
if( pTarget->IsPlayer() )
{
pPlayer = static_cast< StructPlayer * >( pTarget );
}
else if( pTarget->IsSummon() )
{
pPlayer = static_cast< StructSummon * >( pTarget )->GetMaster();
}
if( pPlayer && ( pPlayer->IsPKOn() || pPlayer->IsDemoniacCharacter() || pPlayer->IsBloodyCharacter() ) )
{
return true;
}
return false;
}
bool StructNPC::IsAttackableNPC() const
{
return m_pBaseInfo->attackable;
}
void StructNPC::onDead( StructCreature *pFrom, bool decreaseEXPOnDead )
{
StructCreature::onDead( pFrom, decreaseEXPOnDead );
SetStatus( STATUS_DEAD );
}