868 lines
26 KiB
C++
868 lines
26 KiB
C++
|
|
#include <cmath>
|
|
|
|
#include <mmo/ArcadiaServer.h>
|
|
#include <toolkit/XEnv.h>
|
|
#include <dump/XExceptionHandler.h>
|
|
|
|
#include "GameProc.h"
|
|
#include "StructRoamer.h"
|
|
#include "RoamingManager.h"
|
|
#include "ThreadPlayerHelper.h"
|
|
|
|
|
|
StructRoamer::StructRoamer( const int nID, const StructRoamer::ROAMING_TYPE eRoamingType, const int nMoveSpeed, const HATE_TYPE eHateType, const AR_TIME nRespawnInterval, const int nAttributeFlag, const bool bIsRaidDungeonRoamer )
|
|
: ArObject( MOVABLE_OBJECT )
|
|
, m_nID( nID )
|
|
, m_nMoveSpeed( nMoveSpeed )
|
|
, m_eRoamingType( eRoamingType )
|
|
, m_eHateType( eHateType )
|
|
, m_nRespawnInterval( nRespawnInterval )
|
|
, m_bIsRaidDungeonRoamer( bIsRaidDungeonRoamer )
|
|
, m_eCurrentRoamingDirection( ROAMING_DIRECTION_FORWARD )
|
|
, m_nCurrentRoamingPointIndex( 0 )
|
|
, m_eRoamingStatus( ROAMING_STATUS_IDLE )
|
|
, m_nNextRespawnProcTime( 0 )
|
|
, m_nLastRegenCount( 0 )
|
|
{
|
|
m_AttributeFlag.CopyFrom( &nAttributeFlag );
|
|
}
|
|
|
|
StructRoamer::~StructRoamer()
|
|
{
|
|
DeInit( true );
|
|
}
|
|
|
|
void StructRoamer::AddCreatureRespawnInfo( const GameContent::ROAMING_CREATURE_RESPAWN_INFO & info )
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
m_vRoamingCreatureRespawnInfo.push_back( info );
|
|
}
|
|
|
|
void StructRoamer::AddRoamingPoint( const ArPosition & pos )
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
if( IsInitialized() || IsInWorld() )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
m_vRoamingPoint.push_back( pos );
|
|
|
|
switch( m_vRoamingPoint.size() )
|
|
{
|
|
case 1:
|
|
// 최초 등록이면 현재 위치로 설정
|
|
SetCurrentXY( pos.x, pos.y );
|
|
break;
|
|
case 2:
|
|
// 2번째 등록이면 방향 설정
|
|
mv.SetDirection( pos );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StructRoamer::DeleteRespawnedCreature()
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
m_nNextRespawnProcTime = 0;
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
StructCreature *pCreature = static_cast< StructCreature * >( (*it).m_pCreature );
|
|
if( !pCreature )
|
|
continue;
|
|
|
|
(*it).m_nNextRespawnTime = 0;
|
|
(*it).m_pCreature = NULL;
|
|
|
|
if( pCreature->IsInWorld() )
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pCreature ) );
|
|
|
|
if( pCreature->IsEnable() )
|
|
{
|
|
pCreature->Disable();
|
|
|
|
switch( (*it).m_eCreatureType )
|
|
{
|
|
case ROAMING_CREATURE_MONSTER:
|
|
{
|
|
StructMonster *pMonster = static_cast< StructMonster * >( pCreature );
|
|
|
|
// 락 걸려고 대기하는 사이에 없어졌을 수 있음
|
|
if( pMonster->IsInWorld() )
|
|
RemoveMonsterFromWorld( pMonster );
|
|
|
|
pMonster->SetDeleteHandler( NULL );
|
|
}
|
|
break;
|
|
case ROAMING_CREATURE_NPC:
|
|
{
|
|
StructNPC *pNPC = static_cast< StructNPC * >( pCreature );
|
|
|
|
// 락 걸려고 대기하는 사이에 없어졌을 수 있음
|
|
if( pNPC->IsInWorld() )
|
|
RemoveNPCFromWorld( pNPC );
|
|
|
|
pNPC->SetDeadHandler( NULL );
|
|
}
|
|
break;
|
|
default:
|
|
// 뉘시오 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
ArcadiaServer::Instance().DeleteObject( pCreature );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool StructRoamer::Init()
|
|
{
|
|
if( IsInitialized() )
|
|
return false;
|
|
|
|
m_nCurrentRoamingPointIndex = 0;
|
|
m_eCurrentRoamingDirection = ROAMING_DIRECTION_FORWARD;
|
|
SetCurrentXY( m_vRoamingPoint.front().GetX(), m_vRoamingPoint.front().GetY() );
|
|
|
|
m_eRoamingStatus = ROAMING_STATUS_ROAMING;
|
|
|
|
// 리젠 일체화 적용 그룹의 경우 최초에 리젠 처리가 발생하도록 리젠 처리 시각을 1회 세팅(안 하면 일체화 그룹에는 몹이 리젠되지 않음)
|
|
if( m_nRespawnInterval )
|
|
{
|
|
m_nNextRespawnProcTime = GetArTime();
|
|
m_nLastRegenCount = 0;
|
|
}
|
|
// 리젠 일체화 비적용 그룹의 경우 최초에 리젠 처리가 발생하도록 각각의 리젠 처리 시각을 1회 세팅(안 하면 비일체화 그룹에는 몹이 리젠되지 않음)
|
|
else
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
(*it).m_nNextRespawnTime = GetArTime();
|
|
}
|
|
}
|
|
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( this ) );
|
|
|
|
ArcadiaServer::Instance().AddObject( this );
|
|
}
|
|
ArcadiaServer::Instance().SetObjectPriority( this, UPDATE_PRIORITY_HIGH );
|
|
|
|
return true;
|
|
}
|
|
|
|
const bool StructRoamer::DeInit( const bool bForceToDeleteEverlastingRoamer )
|
|
{
|
|
if( !IsInitialized() )
|
|
return false;
|
|
|
|
ArcadiaServer::Instance().SetObjectPriority( this, UPDATE_PRIORITY_IDLE );
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( m_csHate );
|
|
|
|
for( std::vector< PENDING_HATE_SHARE_INFO * >::const_iterator itHate = m_vPendingHateInfo.begin() ; itHate != m_vPendingHateInfo.end() ; ++itHate )
|
|
delete (*itHate);
|
|
|
|
m_vPendingHateInfo.clear();
|
|
}
|
|
|
|
DeleteRespawnedCreature();
|
|
|
|
if( IsInWorld() )
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( this ) );
|
|
|
|
ArcadiaServer::Instance().RemoveObject( this );
|
|
}
|
|
|
|
// 던전 레이드 로밍 그룹은 DeInit 되어도 유지되어야 하므로 객체를 삭제하지 않음(월드에서는 제거해서 추후 Init 시 AddObject가 가능하도록 유지)
|
|
if( m_bIsRaidDungeonRoamer && !bForceToDeleteEverlastingRoamer )
|
|
return true;
|
|
|
|
RoamingManager::Instance().UnregisterRoamingInfo( m_nID );
|
|
|
|
ArcadiaServer::Instance().DeleteObject( this );
|
|
|
|
return true;
|
|
}
|
|
|
|
void StructRoamer::onProcess( int nThreadIdx )
|
|
{
|
|
char buf[255];
|
|
s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx );
|
|
ENV().Set( buf, "StructRoamer" );
|
|
|
|
AR_TIME t = GetArTime();
|
|
|
|
extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo;
|
|
s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "StructRoamer(0x%08X)", (UINT_PTR)this );
|
|
s_ThreadInfo.last_execute_time = t;
|
|
ThreadPlayerHelper TPHelper( NULL );
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
if( GetPriority() != ArSchedulerObject::UPDATE_PRIORITY_HIGH )
|
|
{
|
|
assert( 0 );
|
|
ArcadiaServer::Instance().SetObjectPriority( this, UPDATE_PRIORITY_HIGH );
|
|
}
|
|
|
|
ArPosition currentPos;
|
|
float fFace;
|
|
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObject( this ) );
|
|
|
|
currentPos = GetCurrentPosition( t );
|
|
fFace = GetFace();
|
|
}
|
|
|
|
// 리젠 처리
|
|
if( !m_nRespawnInterval || ( m_eRoamingStatus == ROAMING_STATUS_ROAMING && m_nNextRespawnProcTime && m_nNextRespawnProcTime <= t ) )
|
|
{
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; /* 루프에서 ++it 처리 */ )
|
|
{
|
|
StructCreature *pCreature = static_cast< StructCreature * >( (*it).m_pCreature );
|
|
if( pCreature )
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
// 개별 리젠인데 몹 리젠해야할 시간이 지정되어 있지 않거나 아직 시간이 안 됐으면 패스
|
|
if( ( !m_nRespawnInterval && ( !(*it).m_nNextRespawnTime || (*it).m_nNextRespawnTime > t ) ) )
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
switch( (*it).m_eCreatureType )
|
|
{
|
|
case ROAMING_CREATURE_MONSTER:
|
|
{
|
|
StructMonster *pMonster = StructMonster::AllocMonster( (*it).m_nCreatureID );
|
|
|
|
if( !pMonster )
|
|
{
|
|
assert( 0 );
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
(*it).m_pCreature = pMonster;
|
|
|
|
pMonster->SetGenerateCode( StructMonster::BY_RESPAWN );
|
|
ArPosition pos = getCurrentRespawnObjectPosition( currentPos, fFace, (*it).m_nAngle, (*it).m_nDistance );
|
|
pMonster->SetCurrentXY( pos.x, pos.y );
|
|
pMonster->SetCurrentLayer( GetLayer() );
|
|
pMonster->SetWandering( false );
|
|
|
|
pMonster->SetRoamer( this );
|
|
pMonster->SetDeleteHandler( this );
|
|
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pMonster ) );
|
|
AddMonsterToWorld( pMonster );
|
|
|
|
// 이동 중이었어야 했다면 이동 처리
|
|
if( IsMoving() )
|
|
{
|
|
ArPosition targetPos = getCurrentRespawnObjectPosition( GetCurrentRoamingTargetPosition(), fFace, (*it).m_nAngle, (*it).m_nDistance );
|
|
ArcadiaServer::Instance().SetMove( pMonster, pos, targetPos, m_nMoveSpeed, false, 0 );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ROAMING_CREATURE_NPC:
|
|
{
|
|
NPCBase & npcBase( GameContent::GetNPCInfo( (*it).m_nCreatureID ) );
|
|
|
|
if( !npcBase.id )
|
|
{
|
|
assert( 0 );
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
if( npcBase.is_periodic )
|
|
{
|
|
time_t tCurrent = time( NULL );
|
|
// 리젠 기간이 만료된 NPC가 리젠 대기 중이라면 삭제
|
|
if( npcBase.end_of_period <= tCurrent )
|
|
{
|
|
it = m_vRoamingCreatureRespawnInfo.erase( it );
|
|
continue;
|
|
}
|
|
|
|
// 리젠 기간이 아니면 패스
|
|
if( npcBase.begin_of_period > tCurrent )
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
StructNPC * pNPC = new StructNPC( &npcBase );
|
|
(*it).m_pCreature = pNPC;
|
|
|
|
ArPosition pos = getCurrentRespawnObjectPosition( currentPos, fFace, (*it).m_nAngle, (*it).m_nDistance );
|
|
pNPC->SetCurrentXY( pos.x, pos.y );
|
|
pNPC->SetCurrentLayer( GetLayer() );
|
|
|
|
pNPC->SetRoamer( this );
|
|
pNPC->SetDeadHandler( this );
|
|
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pNPC ) );
|
|
AddNPCToWorld( pNPC );
|
|
|
|
// 이동 중이었어야 했다면 이동 처리
|
|
if( IsMoving() )
|
|
{
|
|
ArPosition targetPos = getCurrentRespawnObjectPosition( GetCurrentRoamingTargetPosition(), fFace, (*it).m_nAngle, (*it).m_nDistance );
|
|
ArcadiaServer::Instance().SetMove( pNPC, pos, targetPos, m_nMoveSpeed, false, 0 );
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// 뉘시오 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
|
|
++it;
|
|
}
|
|
|
|
m_nNextRespawnProcTime = 0;
|
|
m_nLastRegenCount = 0;
|
|
}
|
|
|
|
// 이동 처리
|
|
if( IsMoving() )
|
|
processWalk( t );
|
|
else
|
|
processRoaming( t, currentPos, fFace );
|
|
}
|
|
|
|
// Hate 공유 처리(m_csHate를 사용하므로 m_CS를 걸고 호출하면 안 됨)
|
|
processHateSharing();
|
|
}
|
|
|
|
void StructRoamer::onMonsterDelete( struct StructMonster * pMonster )
|
|
{
|
|
const bool bDeleteByTimer = pMonster->IsLifeTimeOver();
|
|
bool bNeedToDeleteRoamer = false;
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
if( (*it).m_pCreature == pMonster )
|
|
{
|
|
// 몬스터는 processDead에서 ArcadiaServer::Instance().DeleteObject를 호출해 줌(NPC와의 일관성 따위... -_ -;)
|
|
(*it).m_pCreature = NULL;
|
|
// 레이드 던전 내에서 로밍 몬스터가 죽은 경우에는 리젠될 시각을 0으로 세팅하여 리젠 처리가 발생하지 않게 함(개별 리젠 설정의 그룹 로밍 레이드 몹 리젠 처리 방지)
|
|
(*it).m_nNextRespawnTime = ( m_bIsRaidDungeonRoamer ) ? 0 : ( ( m_nRespawnInterval || !(*it).m_nRespawnInterval ) ? 0 : GetArTime() + (*it).m_nRespawnInterval );
|
|
|
|
// 개별 리젠 처리 로밍 그룹에서 리젠 간격이 0으로 되어 있거나 타이머에 의해 삭제된 몬스터는 리젠 안 시킴
|
|
if( ( !m_nRespawnInterval && !(*it).m_nRespawnInterval ) || bDeleteByTimer )
|
|
{
|
|
m_vRoamingCreatureRespawnInfo.erase( it );
|
|
|
|
if( m_vRoamingCreatureRespawnInfo.empty() )
|
|
bNeedToDeleteRoamer = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bNeedToDeleteRoamer )
|
|
DeInit( false );
|
|
}
|
|
|
|
void StructRoamer::onNPCDead( struct StructNPC * pNPC )
|
|
{
|
|
bool bNeedToDeleteRoamer = false;
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
if( (*it).m_pCreature == pNPC )
|
|
{
|
|
ArcadiaServer::Instance().DeleteObject( (*it).m_pCreature );
|
|
(*it).m_pCreature = NULL;
|
|
(*it).m_nNextRespawnTime = ( m_nRespawnInterval || !(*it).m_nRespawnInterval ) ? 0 : GetArTime() + (*it).m_nRespawnInterval;
|
|
|
|
// 개별 리젠 처리 로밍 그룹에서 리젠 간격이 0으로 되어 있으면 죽어도 리젠 안 시킴
|
|
if( !m_nRespawnInterval && !(*it).m_nRespawnInterval )
|
|
{
|
|
m_vRoamingCreatureRespawnInfo.erase( it );
|
|
|
|
if( m_vRoamingCreatureRespawnInfo.empty() )
|
|
bNeedToDeleteRoamer = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bNeedToDeleteRoamer )
|
|
DeInit( false );
|
|
}
|
|
|
|
bool StructRoamer::IsMovable()
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
if( m_eRoamingStatus == ROAMING_STATUS_IDLE )
|
|
return false;
|
|
|
|
return isMovable();
|
|
}
|
|
|
|
void StructRoamer::PauseRoaming()
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
if( m_eRoamingStatus == ROAMING_STATUS_PAUSED )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
AR_TIME t = GetArTime();
|
|
ArPosition pos = GetCurrentPosition( t );
|
|
float fFace = GetFace();
|
|
|
|
m_eRoamingStatus = ROAMING_STATUS_PAUSED;
|
|
|
|
StopMove();
|
|
ArcadiaServer::Instance().SetMove( this, pos, pos, 0, false, 0, false );
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
StructCreature *pCreature = (*it).m_pCreature;
|
|
|
|
if( !pCreature || !pCreature->IsInWorld() )
|
|
continue;
|
|
|
|
ArPosition returnPos = getCurrentRespawnObjectPosition( pos, fFace, (*it).m_nAngle, (*it).m_nDistance );
|
|
|
|
if( pCreature->IsMonster() )
|
|
static_cast< StructMonster * >( pCreature )->SetReturnPosition( returnPos.x, returnPos.y );
|
|
else if( pCreature->IsNPC() )
|
|
static_cast< StructNPC * >( pCreature )->SetReturnPosition( returnPos.x, returnPos.y );
|
|
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pCreature ) );
|
|
pCreature->StopMove();
|
|
ArcadiaServer::Instance().SetMove( pCreature, returnPos, returnPos, 0, false, 0 );
|
|
}
|
|
}
|
|
|
|
const ArPosition StructRoamer::GetCurrentRoamingTargetPosition()
|
|
{
|
|
return m_vRoamingPoint[ m_nCurrentRoamingPointIndex ];
|
|
}
|
|
|
|
const ArPosition StructRoamer::GetNextRoamingTargetPosition()
|
|
{
|
|
size_t nNextRoamingTargetIndex = getNextRoamingTargetIndex();
|
|
|
|
return m_vRoamingPoint[ nNextRoamingTargetIndex ];
|
|
}
|
|
|
|
void StructRoamer::PendHateShare( const AR_HANDLE hRequester, const AR_HANDLE hHateTarget, const int nHate, const int eApplyHateType )
|
|
{
|
|
if( !( eApplyHateType & m_eHateType ) )
|
|
return;
|
|
|
|
THREAD_SYNCHRONIZE( m_csHate );
|
|
|
|
m_vPendingHateInfo.push_back( new PENDING_HATE_SHARE_INFO( hRequester, hHateTarget, nHate ) );
|
|
}
|
|
|
|
const bool StructRoamer::isMovable()
|
|
{
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
StructCreature *pCreature = (*it).m_pCreature;
|
|
|
|
if( !pCreature || !pCreature->IsInWorld() )
|
|
continue;
|
|
|
|
// 전투 중이거나, 이동 불가면 이동 불가
|
|
if( pCreature->GetEnemyHandle() || !pCreature->IsMovable() )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void StructRoamer::processWalk( AR_TIME t )
|
|
{
|
|
ArMoveVector tmp_mv;
|
|
// 시간만큼 이동해 본다.
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( this ) );
|
|
tmp_mv = GetMv();
|
|
}
|
|
tmp_mv.Step( t );
|
|
|
|
// 만약 이동했는데 Region 변경이 일어났거나 혹은 이동이 멈추었다면
|
|
// 그것을 ArcadiaServer 에게 통지해준다.
|
|
if( tmp_mv.GetRX() != GetRX() || tmp_mv.GetRY() != GetRY() || !tmp_mv.IsMoving() )
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithSpecificRegion( this, tmp_mv.GetRX(), tmp_mv.GetRY() ) );
|
|
|
|
ArcadiaServer::Instance().onRegionChange( this,
|
|
t - lastStepTime,
|
|
!tmp_mv.IsMoving() );
|
|
}
|
|
}
|
|
|
|
void StructRoamer::processRoaming( AR_TIME t, const ArPosition & currentPos, const float & fFace )
|
|
{
|
|
// 소속 객체가 전투 중이거나 제 위치에 멈춰 있지 않으면 아무 것도 안 함
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
StructCreature *pCreature = (*it).m_pCreature;
|
|
|
|
if( pCreature && pCreature->IsInWorld() )
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObject( pCreature ) );
|
|
|
|
if( pCreature->IsMoving() || pCreature->GetEnemyHandle() )
|
|
return;
|
|
|
|
// 이동 중도 전투 중도 아닌 놈이 좌표가 있어야 할 곳과 다르다면 이동하라고 시킴
|
|
ArPosition posTarget = getCurrentRespawnObjectPosition( currentPos, fFace, (*it).m_nAngle, (*it).m_nDistance );
|
|
ArPosition posCurrent = pCreature->GetCurrentPosition( t );
|
|
if( !( posCurrent == posTarget ) )
|
|
{
|
|
ArcadiaServer::Instance().SetMove( pCreature, posCurrent, posTarget, m_nMoveSpeed, false, 0 );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 일단 이동 가능한 상태이므로 이동 재개 처리만 하면 됨
|
|
switch( m_eRoamingStatus )
|
|
{
|
|
case ROAMING_STATUS_ROAMING:
|
|
{
|
|
// 모두 현재 목표 지점으로 이동을 마친 상태이므로 다음 목표 지점을 향해 대형 방향 전환
|
|
SetDirection( GetNextRoamingTargetPosition() );
|
|
|
|
ArPosition pos = GetCurrentRoamingTargetPosition();
|
|
float fNewFace = GetFace();
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
StructCreature *pCreature = (*it).m_pCreature;
|
|
|
|
if( !pCreature || !pCreature->IsInWorld() )
|
|
continue;
|
|
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pCreature ) );
|
|
|
|
// 락 거는 동안 월드에서 사라졌을 수 있음
|
|
if( !pCreature->IsInWorld() )
|
|
continue;
|
|
|
|
ArcadiaServer::Instance().SetMove( pCreature, pCreature->GetPos(), getCurrentRespawnObjectPosition( pos, fNewFace, (*it).m_nAngle, (*it).m_nDistance ), m_nMoveSpeed * GameRule::ROAMING_ROATING_MOVE_SPEED_RATE, false, 0 );
|
|
}
|
|
|
|
m_eRoamingStatus = ROAMING_STATUS_ROTATING;
|
|
}
|
|
break;
|
|
|
|
case ROAMING_STATUS_ROTATING:
|
|
{
|
|
// 대형 전환이 완료되었으므로 다음 목표 지점으로 이동 시작
|
|
proceedRoamingTargetIndex();
|
|
}
|
|
// break 없이 이어서 일시 정지 상태에서 로밍 재시작과 동일한 처리
|
|
|
|
case ROAMING_STATUS_PAUSED:
|
|
{
|
|
// 모두 이동 가능한 상태이므로 로밍 재시작
|
|
ArPosition pos = GetCurrentRoamingTargetPosition();
|
|
int nNeedToProcRespawnCount = 0;
|
|
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( GetPos().GetRX(), GetPos().GetRY(), pos.GetRX(), pos.GetRY() ) );
|
|
|
|
ArcadiaServer::Instance().SetMove( this, GetPos(), pos, m_nMoveSpeed, false, 0, false );
|
|
|
|
float fFace = GetFace();
|
|
bool bNeedToRegenFullHPMP = m_AttributeFlag.IsOn( ATTRIBUTE_HEAL_HP_ON_COMEBACKHOME ) && m_nRespawnInterval;
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
StructCreature *pCreature = (*it).m_pCreature;
|
|
|
|
if( !pCreature )
|
|
{
|
|
++nNeedToProcRespawnCount;
|
|
continue;
|
|
}
|
|
|
|
// 등장 기간이 만료된 NPC가 월드에서만 삭제되고 개체 자체는 삭제 처리 대기 중일 수 있으므로 그냥 건너 뜀
|
|
if( !pCreature->IsInWorld() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int nMaxHP = pCreature->GetMaxHP();
|
|
int nCurrentHP = pCreature->GetHP();
|
|
if( bNeedToRegenFullHPMP && nCurrentHP < nMaxHP )
|
|
{
|
|
pCreature->RegenFullHPMP();
|
|
}
|
|
|
|
ArcadiaServer::Instance().SetMove( pCreature, pCreature->GetPos(), getCurrentRespawnObjectPosition( pos, fFace, (*it).m_nAngle, (*it).m_nDistance ), m_nMoveSpeed, false, 0 );
|
|
}
|
|
}
|
|
|
|
// 레이드 던전 로밍 몬스터는 다시 리젠되지 않는다.
|
|
if( m_nRespawnInterval && ( m_nLastRegenCount != nNeedToProcRespawnCount ) && !m_bIsRaidDungeonRoamer )
|
|
{
|
|
// 그룹형 리젠이면 마지막에 죽는 녀석의 리스폰 시각에 맞춰 리젠타임이 조정된다.
|
|
// (예: 리젠 타임이 1시간 일 경우, 첫 번째 녀석이 0시에 죽고 두번째 녀석이 0시 30분에 죽었으면 다음 리젠 시각 1시 30분에 두마리 다 리젠.)
|
|
m_nNextRespawnProcTime = t + m_nRespawnInterval;
|
|
m_nLastRegenCount = nNeedToProcRespawnCount;
|
|
}
|
|
|
|
m_eRoamingStatus = ROAMING_STATUS_ROAMING;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StructRoamer::processHateSharing()
|
|
{
|
|
// m_csHate와 지역 락, m_CS 와의 데드락 발생 가능성을 배제하기 위해서 버퍼를 두고 복제된 내용만 락 안 걸고 처리해야 함
|
|
// 어그로 공유 메시지 큐와 같은 개념이므로 처리 로직에는 문제 없음
|
|
std::vector< PENDING_HATE_SHARE_INFO * > vHateProcBuffer;
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( m_csHate );
|
|
|
|
if( m_vPendingHateInfo.empty() )
|
|
return;
|
|
|
|
vHateProcBuffer = m_vPendingHateInfo;
|
|
m_vPendingHateInfo.clear();
|
|
}
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( m_CS );
|
|
|
|
for( std::vector< PENDING_HATE_SHARE_INFO * >::iterator itHate = vHateProcBuffer.begin() ; itHate != vHateProcBuffer.end() ; ++itHate )
|
|
{
|
|
PENDING_HATE_SHARE_INFO * pPendingHate = (*itHate);
|
|
|
|
for( std::vector< ROAMING_CREATURE_INFO >::iterator it = m_vRoamingCreatureRespawnInfo.begin() ; it != m_vRoamingCreatureRespawnInfo.end() ; ++it )
|
|
{
|
|
if( !(*it).m_pCreature || (*it).m_pCreature->GetHandle() == pPendingHate->m_hRequester || (*it).m_pCreature->GetHandle() == pPendingHate->m_hHateTarget || !(*it).m_pCreature->IsMonster() || !(*it).m_pCreature->IsInWorld() || (*it).m_pCreature->IsDead() )
|
|
continue;
|
|
|
|
StructMonster *pMonster = static_cast< StructMonster * >( (*it).m_pCreature );
|
|
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pMonster ) );
|
|
|
|
if( !pMonster->IsInWorld() || pMonster->IsDead() )
|
|
continue;
|
|
|
|
// 멍하니 있던 애면 Hate주고 적을 인식시켜야 함
|
|
bool bNeedToFindEnemy = pMonster->GetStatus() == StructMonster::STATUS_NORMAL;
|
|
|
|
pMonster->AddHate( pPendingHate->m_hHateTarget, pPendingHate->m_nHate, true, false );
|
|
|
|
if( bNeedToFindEnemy )
|
|
pMonster->SetNeedToFindEnemy();
|
|
}
|
|
|
|
delete pPendingHate;
|
|
}
|
|
}
|
|
}
|
|
|
|
const size_t StructRoamer::getNextRoamingTargetIndex() const
|
|
{
|
|
size_t nNextRoamingTargetIndex = 0;
|
|
size_t nRoaminPointCount = m_vRoamingPoint.size();
|
|
|
|
// 일단 로밍 타겟 지점이 1개 이하면 이동이 안 일어남 -_ -
|
|
if( nRoaminPointCount <= 1 )
|
|
return 0;
|
|
|
|
// 전방 진행 중
|
|
if( m_eCurrentRoamingDirection == ROAMING_DIRECTION_FORWARD )
|
|
{
|
|
// 최종 점을 초과하려는 경우
|
|
if( m_nCurrentRoamingPointIndex >= nRoaminPointCount - 1 )
|
|
{
|
|
switch( m_eRoamingType )
|
|
{
|
|
case ROAMING_TYPE_ROUND:
|
|
// 순환 타입(ROAMING_TYPE_ROUND)이면 0번 위치가 다음 목표 지점
|
|
nNextRoamingTargetIndex = 0;
|
|
break;
|
|
|
|
case ROAMING_TYPE_GO_BACK:
|
|
// 왕복 타입(ROAMING_TYPE_GO_BACK)이면 역방향의 다음 지점 지정
|
|
nNextRoamingTargetIndex = nRoaminPointCount - 2;
|
|
break;
|
|
|
|
default:
|
|
// 당췌 어떤 로밍을 하고 계신게요 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
// 문제 없으면 걍 다음 지점으로
|
|
else
|
|
{
|
|
nNextRoamingTargetIndex = m_nCurrentRoamingPointIndex + 1;
|
|
}
|
|
}
|
|
else if( m_eCurrentRoamingDirection == ROAMING_DIRECTION_BACKWARD )
|
|
{
|
|
// 시작 점을 초과하려는 경우
|
|
if( m_nCurrentRoamingPointIndex == 0 )
|
|
{
|
|
switch( m_eRoamingType )
|
|
{
|
|
case ROAMING_TYPE_ROUND:
|
|
// 순환 타입(ROAMING_TYPE_ROUND)인데 어떻게 BACKWARD로 진행하셨수 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
|
|
case ROAMING_TYPE_GO_BACK:
|
|
// 왕복 타입(ROAMING_TYPE_GO_BACK)이면 순방향의 다음 지점 지정
|
|
nNextRoamingTargetIndex = 1;
|
|
break;
|
|
|
|
default:
|
|
// 당췌 어떤 로밍을 하고 계신게요 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
// 문제 없으면 걍 다음 지점으로
|
|
else
|
|
{
|
|
nNextRoamingTargetIndex = m_nCurrentRoamingPointIndex - 1;
|
|
}
|
|
}
|
|
|
|
assert( nNextRoamingTargetIndex >= 0 && nNextRoamingTargetIndex < nRoaminPointCount );
|
|
|
|
return nNextRoamingTargetIndex;
|
|
}
|
|
|
|
void StructRoamer::proceedRoamingTargetIndex()
|
|
{
|
|
size_t nNextRoamingTargetIndex = 0;
|
|
size_t nRoaminPointCount = m_vRoamingPoint.size();
|
|
|
|
// 일단 로밍 타겟 지점이 1개 이하면 이동이 안 일어남 -_ -
|
|
if( nRoaminPointCount <= 1 )
|
|
return;
|
|
|
|
// 전방 진행 중
|
|
if( m_eCurrentRoamingDirection == ROAMING_DIRECTION_FORWARD )
|
|
{
|
|
// 최종 점을 초과하려는 경우
|
|
if( m_nCurrentRoamingPointIndex >= nRoaminPointCount - 1 )
|
|
{
|
|
switch( m_eRoamingType )
|
|
{
|
|
case ROAMING_TYPE_ROUND:
|
|
// 순환 타입(ROAMING_TYPE_ROUND)이면 0번 위치가 다음 목표 지점
|
|
nNextRoamingTargetIndex = 0;
|
|
break;
|
|
|
|
case ROAMING_TYPE_GO_BACK:
|
|
// 왕복 타입(ROAMING_TYPE_GO_BACK)이면 역순 진행으로 변경
|
|
m_eCurrentRoamingDirection = ROAMING_DIRECTION_BACKWARD;
|
|
nNextRoamingTargetIndex = nRoaminPointCount - 2;
|
|
break;
|
|
|
|
default:
|
|
// 당췌 어떤 로밍을 하고 계신게요 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
// 문제 없으면 걍 다음 지점으로
|
|
else
|
|
{
|
|
nNextRoamingTargetIndex = m_nCurrentRoamingPointIndex + 1;
|
|
}
|
|
}
|
|
else if( m_eCurrentRoamingDirection == ROAMING_DIRECTION_BACKWARD )
|
|
{
|
|
// 시작 점을 초과하려는 경우
|
|
if( m_nCurrentRoamingPointIndex == 0 )
|
|
{
|
|
switch( m_eRoamingType )
|
|
{
|
|
case ROAMING_TYPE_ROUND:
|
|
// 순환 타입(ROAMING_TYPE_ROUND)인데 어떻게 BACKWARD로 진행하셨수 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
|
|
case ROAMING_TYPE_GO_BACK:
|
|
// 왕복 타입(ROAMING_TYPE_GO_BACK)이면 순 진행으로 변경
|
|
m_eCurrentRoamingDirection = ROAMING_DIRECTION_FORWARD;
|
|
nNextRoamingTargetIndex = 1;
|
|
break;
|
|
|
|
default:
|
|
// 당췌 어떤 로밍을 하고 계신게요 -_ -?
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
// 문제 없으면 걍 다음 지점으로
|
|
else
|
|
{
|
|
nNextRoamingTargetIndex = m_nCurrentRoamingPointIndex - 1;
|
|
}
|
|
}
|
|
|
|
assert( nNextRoamingTargetIndex >= 0 && nNextRoamingTargetIndex < nRoaminPointCount );
|
|
|
|
m_nCurrentRoamingPointIndex = nNextRoamingTargetIndex;
|
|
}
|
|
|
|
const ArPosition StructRoamer::getCurrentRespawnObjectPosition( const ArPosition & currentPos, const float & fFace, const int & nAngle, const AR_UNIT & nDistance )
|
|
{
|
|
float fCreatureFace = fFace + static_cast< float >( nAngle ) * 3.141592f / 180;
|
|
|
|
return ArPosition( currentPos.x - ( cos( fCreatureFace ) * nDistance ), currentPos.y - ( sin( fCreatureFace ) * nDistance ) );
|
|
}
|