1868 lines
69 KiB
C++
1868 lines
69 KiB
C++
|
|
#include <toolkit/XEnv.h>
|
|
#include <dump/XExceptionHandler.h>
|
|
#include <mmo/ArcadiaServer.h>
|
|
|
|
#include "LogClient/LogClient.h"
|
|
#include "ErrorCode/ErrorCode.h"
|
|
|
|
#include "HuntaholicManager.h"
|
|
#include "SendMessage.h"
|
|
#include "PartyManager.h"
|
|
#include "RankingManager.h"
|
|
#include "NPCProc.h"
|
|
#include "GameProc.h"
|
|
#include "StructFieldProp.h"
|
|
#include "DB_Commands.h"
|
|
|
|
extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo;
|
|
|
|
|
|
struct WarpFunctor : public PartyManager::PartyFunctor
|
|
{
|
|
WarpFunctor( const AR_UNIT _x, const AR_UNIT _y, const unsigned char _layer, const AR_TIME _begin_time = 0 )
|
|
: x( _x )
|
|
, y( _y )
|
|
, layer( _layer )
|
|
, begin_time( _begin_time )
|
|
{}
|
|
|
|
virtual bool operator()( AR_HANDLE handle )
|
|
{
|
|
StructPlayer::iterator itPlayer = StructPlayer::get( handle );
|
|
StructPlayer * pPlayer = (*itPlayer);
|
|
|
|
if( !pPlayer )
|
|
return false;
|
|
|
|
PendWarpPlayer( pPlayer );
|
|
|
|
if( begin_time )
|
|
{
|
|
TS_SC_HUNTAHOLIC_BEGIN_HUNTING msg;
|
|
msg.begin_time = begin_time;
|
|
PendMessage( pPlayer, &msg );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PendWarpPlayer( StructPlayer * pPlayer )
|
|
{
|
|
// 랜덤 위치 설정
|
|
AR_UNIT targetX, targetY;
|
|
bool bValidPos = true;
|
|
unsigned short try_cnt = 0;
|
|
do
|
|
{
|
|
targetX = XRandom( x - 60, x + 60 );
|
|
targetY = XRandom( y - 60, y + 60 );
|
|
|
|
if( ++try_cnt > 300 )
|
|
{
|
|
bValidPos = false;
|
|
break;
|
|
}
|
|
} while( GameContent::CollisionToLine( x, y, targetX, targetY ) );
|
|
|
|
if( !bValidPos )
|
|
{
|
|
targetX = x;
|
|
targetY = y;
|
|
|
|
if( GameContent::IsBlocked( targetX, targetY ) )
|
|
{
|
|
FILELOG( "Unable to PendWarpPlayer (%f, %f)", targetX, targetY );
|
|
_cprint( "Unable to PendWarpPlayer (%f, %f)\n", targetX, targetY );
|
|
}
|
|
}
|
|
|
|
// 파티 락 걸린 상태로 호출되기도 하는 곳이므로 지역락 걸지 않음
|
|
pPlayer->PendWarp( targetX, targetY, layer );
|
|
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
|
|
}
|
|
|
|
AR_UNIT x;
|
|
AR_UNIT y;
|
|
unsigned char layer;
|
|
AR_TIME begin_time;
|
|
};
|
|
|
|
|
|
// HuntaholicManager::InstanceDungeon
|
|
void HuntaholicManager::InstanceDungeon::onMonsterDelete( StructMonster * pMonster )
|
|
{
|
|
// 사냥이 종료된 이후면 아무것도 하지 않음
|
|
if( bEnd || nScore >= pHuntaholicInfo->pHuntaholicBase->nMaxPoint )
|
|
return;
|
|
|
|
THREAD_SYNCHRONIZE( csInstance );
|
|
|
|
// 사냥이 종료된 이후면 아무것도 하지 않음(락 걸고 나서 한 번 더 검사)
|
|
if( bEnd || nScore >= pHuntaholicInfo->pHuntaholicBase->nMaxPoint )
|
|
return;
|
|
|
|
AR_TIME t = GetArTime();
|
|
|
|
int nRespawnID = pMonster->GetInstanceRespawnID();
|
|
|
|
std::vector< StructMonster * >::iterator it = std::find( vRespawnedMonster.begin(), vRespawnedMonster.end(), pMonster );
|
|
// 이미 삭제 처리 된 몬스터면 점수 카운트 및 리젠 진행시키지 않음(사냥 진행 중인데 사망 처리가 2번 들어왔거나 리젠만 되고 vRespawnedMonster에 추가되지 않았거나... -_ -?)
|
|
if( it == vRespawnedMonster.end() )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
vRespawnedMonster.erase( it );
|
|
|
|
// 시간제로 소환되었던 몬스터가 시간 제한에 의해 사라지는 경우에는 점수 가산, 리젠 예약 처리를 하지 않음
|
|
// * 스킬에 의해 소환되었어도 영구 몹이면 포인트 가산 처리 함(단, 리젠은 안 됨. 현재 함수 아래쪽의 리젠 처리 부분 참고)
|
|
if( pMonster->IsLifeTimeOver() )
|
|
return;
|
|
|
|
int nMonsterScore = 0;
|
|
|
|
switch( pMonster->GetMonsterType() )
|
|
{
|
|
case MonsterBase::MONSTER_TYPE_HUNTAHOLIC_1LV:
|
|
nMonsterScore = GameRule::HUNTAHOLIC_SCORE_FOR_LV1_MONSTER;
|
|
break;
|
|
case MonsterBase::MONSTER_TYPE_HUNTAHOLIC_2LV:
|
|
nMonsterScore = GameRule::HUNTAHOLIC_SCORE_FOR_LV2_MONSTER;
|
|
break;
|
|
case MonsterBase::MONSTER_TYPE_HUNTAHOLIC_3LV:
|
|
nMonsterScore = GameRule::HUNTAHOLIC_SCORE_FOR_LV3_MONSTER;
|
|
break;
|
|
case MonsterBase::MONSTER_TYPE_HUNTAHOLIC_BOSS:
|
|
nMonsterScore = GameRule::HUNTAHOLIC_SCORE_FOR_BOSS_MONSTER;
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
// 개인별 점수 추가
|
|
{
|
|
AR_HANDLE hKiller = NULL;
|
|
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObject( pMonster ) );
|
|
hKiller = pMonster->GetMaxDamageDealer();
|
|
}
|
|
|
|
if( hKiller )
|
|
{
|
|
_SCORE_TAG * pScoreTag = getScoreTag( hKiller );
|
|
if( !pScoreTag )
|
|
{
|
|
vScoreTag.push_back( _SCORE_TAG( hKiller ) );
|
|
pScoreTag = &vScoreTag.back();
|
|
}
|
|
|
|
++( pScoreTag->nKillCount );
|
|
pScoreTag->nScore += nMonsterScore;
|
|
}
|
|
}
|
|
|
|
// 방 전체 점수 추가
|
|
++nKillCount;
|
|
nScore += nMonsterScore;
|
|
|
|
// 최대 점수 도달 시 처리
|
|
bool bMaxPointReached = nScore >= pHuntaholicInfo->pHuntaholicBase->nMaxPoint;
|
|
if( bMaxPointReached )
|
|
{
|
|
nScore = pHuntaholicInfo->pHuntaholicBase->nMaxPoint;
|
|
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( pHuntaholicInfo->pHuntaholicBase->nDungeonRegionLeft, pHuntaholicInfo->pHuntaholicBase->nDungeonRegionTop,
|
|
pHuntaholicInfo->pHuntaholicBase->nDungeonRegionRight, pHuntaholicInfo->pHuntaholicBase->nDungeonRegionBottom, nInstanceNo ) );
|
|
|
|
clearMonsters();
|
|
clearHealingProps();
|
|
}
|
|
|
|
struct MaxPointAchievementBroadcaster : public PartyManager::PartyFunctor
|
|
{
|
|
virtual bool operator ()( AR_HANDLE handle )
|
|
{
|
|
StructPlayer::iterator itPlayer = StructPlayer::get( handle );
|
|
StructPlayer * pPlayer = (*itPlayer);
|
|
|
|
if( pPlayer )
|
|
PendMessage( pPlayer, &msg );
|
|
|
|
return true;
|
|
}
|
|
|
|
TS_SC_HUNTAHOLIC_MAX_POINT_ACHIEVED msg;
|
|
} fo;
|
|
|
|
PartyManager::GetInstance().DoEachMember( nPartyID, fo );
|
|
}
|
|
|
|
// 점수 방송
|
|
{
|
|
TS_SC_HUNTAHOLIC_UPDATE_SCORE msg;
|
|
|
|
msg.score = nScore;
|
|
|
|
struct ScoreBroadcaster : public PartyManager::PartyFunctor
|
|
{
|
|
ScoreBroadcaster( const HuntaholicManager::InstanceDungeon * pInstance, TS_SC_HUNTAHOLIC_UPDATE_SCORE * pMsg )
|
|
: m_pInstance( pInstance )
|
|
, m_pMsg( pMsg )
|
|
{}
|
|
|
|
virtual bool operator ()( AR_HANDLE handle )
|
|
{
|
|
StructPlayer::iterator itPlayer = StructPlayer::get( handle );
|
|
StructPlayer * pPlayer = (*itPlayer);
|
|
|
|
if( !pPlayer )
|
|
return false;
|
|
|
|
const _SCORE_TAG * pScoreTag = m_pInstance->getScoreTag( handle );
|
|
m_pMsg->kill_count = ( pScoreTag ) ? pScoreTag->nKillCount : 0;
|
|
|
|
PendMessage( pPlayer, m_pMsg );
|
|
|
|
return true;
|
|
}
|
|
|
|
const HuntaholicManager::InstanceDungeon * m_pInstance;
|
|
TS_SC_HUNTAHOLIC_UPDATE_SCORE * m_pMsg;
|
|
} fo( this, &msg );
|
|
|
|
PartyManager::GetInstance().DoEachMember( nPartyID, fo );
|
|
}
|
|
|
|
// 몬스터 리젠 예약(최대 점수 도달 시 또는 스킬에 의해 리젠되어 리젠 정보 ID 값이 설정되지 않은 몬스터는 리젠 예약하지 않음)
|
|
if( !bMaxPointReached && nRespawnID )
|
|
{
|
|
std::vector< const GameContent::HUNTAHOLIC_MONSTER_RESPAWN_INFO * >::const_iterator itRespawn = pInstanceBase->vRespawnInfo.begin();
|
|
for( ; itRespawn != pInstanceBase->vRespawnInfo.end() ; ++itRespawn )
|
|
{
|
|
if( (*itRespawn)->nID != nRespawnID )
|
|
continue;
|
|
|
|
vPendedMonsterRespawn.push_back( PendedMonsterRespawnInfo( (*itRespawn), t + (*itRespawn)->nPeriod ) );
|
|
|
|
break;
|
|
}
|
|
|
|
assert( itRespawn != pInstanceBase->vRespawnInfo.end() );
|
|
}
|
|
}
|
|
|
|
void HuntaholicManager::InstanceDungeon::clearMonsters()
|
|
{
|
|
// 리젠 대기 중인 몬스터 목록 제거
|
|
vPendedMonsterRespawn.clear();
|
|
|
|
// 몬스터 제거 처리(지역 락 필요)
|
|
for( std::vector< StructMonster * >::iterator itMonster = vRespawnedMonster.begin() ; itMonster != vRespawnedMonster.end() ; /* 루프에서 ++itMonster 처리 */ )
|
|
{
|
|
if( (*itMonster)->IsEnable() )
|
|
{
|
|
(*itMonster)->SetDeleteHandler( NULL );
|
|
|
|
// 더이상의 스케쥴러 요청을 무시
|
|
(*itMonster)->Disable();
|
|
|
|
// 월드에서 제거한다.
|
|
if( (*itMonster)->IsInWorld() )
|
|
{
|
|
RemoveMonsterFromWorld( (*itMonster) );
|
|
}
|
|
|
|
// object delete 요청
|
|
ArcadiaServer::Instance().DeleteObject( (*itMonster) );
|
|
|
|
itMonster = vRespawnedMonster.erase( itMonster );
|
|
}
|
|
else
|
|
{
|
|
++itMonster;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HuntaholicManager::InstanceDungeon::onFieldPropDelete( StructFieldProp * pProp )
|
|
{
|
|
// 사냥이 종료된 이후면 아무것도 하지 않음
|
|
if( bEnd )
|
|
return;
|
|
|
|
THREAD_SYNCHRONIZE( csInstance );
|
|
|
|
// 사냥이 종료된 이후면 아무것도 하지 않음(락 걸고 나서 한 번 더 검사)
|
|
if( bEnd )
|
|
return;
|
|
|
|
std::vector< StructFieldProp * >::iterator itErase = std::find( vRespawnedHealingProp.begin(), vRespawnedHealingProp.end(), pProp );
|
|
// 이미 삭제된 프랍이면 아무 처리 안 함(삭제처리가 2번 일어났거나 리젠되기만 하고 vRespawnedHealingProp에는 추가되지 않았거나 -_ -?)
|
|
if( itErase == vRespawnedHealingProp.end() )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
vRespawnedHealingProp.erase( itErase );
|
|
vPendedHealingPropRespawn.push_back( PendedHealingPropRespawnInfo( pProp->GetRespawnInfo(), GetArTime() + pProp->GetFieldPropBase()->nRegenTime ) );
|
|
}
|
|
|
|
void HuntaholicManager::InstanceDungeon::clearHealingProps()
|
|
{
|
|
// 리젠 대기 중인 힐링 프랍 제거
|
|
vPendedHealingPropRespawn.clear();
|
|
|
|
// 힐링 프랍 제거 처리(지역 락 필요)
|
|
for( std::vector< StructFieldProp * >::iterator itHealingProp = vRespawnedHealingProp.begin() ; itHealingProp != vRespawnedHealingProp.end() ; ++itHealingProp )
|
|
{
|
|
// 해당 프랍이 ProcDelete에서 다시 리젠되도록 vPendedHealingPropRespawn에 추가되지 않도록 DeleteHandler를 제거하고 삭제함
|
|
(*itHealingProp)->SetDeleteHandler( NULL );
|
|
|
|
// 유저가 사용함으로 인해서 RemoveObject/PendFreeFieldProp은 걸려 있지만 아직 onFieldPropDelete가 호출되지 않은 경우에는 아무것도 하지 않음
|
|
if( !(*itHealingProp)->IsInWorld() )
|
|
continue;
|
|
|
|
ArcadiaServer::Instance().RemoveObject( (*itHealingProp) );
|
|
StructFieldProp::PendFreeFieldProp( (*itHealingProp) );
|
|
}
|
|
|
|
vRespawnedHealingProp.clear();
|
|
}
|
|
|
|
void HuntaholicManager::InstanceDungeon::broadcastInstanceDungeonInfoToMembers() const
|
|
{
|
|
TS_HUNTAHOLIC_INSTANCE_INFO info;
|
|
HuntaholicManager::assembleInstanceInfo( this, &info );
|
|
|
|
struct InstanceDungeonInfoBroadcaster : public PartyManager::PartyFunctor
|
|
{
|
|
InstanceDungeonInfoBroadcaster( const TS_HUNTAHOLIC_INSTANCE_INFO & info )
|
|
: m_InstanceInfo( info )
|
|
{}
|
|
|
|
virtual bool operator ()( AR_HANDLE handle )
|
|
{
|
|
StructPlayer::iterator itPlayer = StructPlayer::get( handle );
|
|
StructPlayer * pPlayer = (*itPlayer);
|
|
|
|
if( pPlayer )
|
|
SendHuntaholicInstanceInfo( pPlayer, m_InstanceInfo );
|
|
|
|
return true;
|
|
}
|
|
|
|
const TS_HUNTAHOLIC_INSTANCE_INFO & m_InstanceInfo;
|
|
} fo( info );
|
|
|
|
PartyManager::GetInstance().DoEachMember( nPartyID, fo );
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::InstanceDungeon::joinInstanceDungeon( StructPlayer * pPlayer, const char * pszPassword )
|
|
{
|
|
// 이미 시작된 방이면 조인 불가
|
|
if( tBeginTime )
|
|
return RESULT_COOL_TIME;
|
|
|
|
// 최대 멤버 수 제한 걸렸으면 조인 불가
|
|
if( PartyManager::GetInstance().GetMemberCount( nPartyID ) >= nMaxMemberCount )
|
|
return RESULT_LIMIT_MAX;
|
|
|
|
// 레벨대가 맞지 않는 방에는 조인 불가
|
|
if( !pInstanceBase->IsProperLevel( pPlayer->GetLevel() ) )
|
|
return RESULT_LIMIT_TARGET;
|
|
|
|
// 비번이 지정되어있는데 맞지 않으면 조인 불가
|
|
if( strlen( szPassword ) && strcmp( szPassword, pszPassword ) )
|
|
return RESULT_INVALID_PASSWORD;
|
|
|
|
// 파티에 조인 실패하면 방에도 조인 불가
|
|
if( !PartyManager::GetInstance().JoinParty( nPartyID, pPlayer ) )
|
|
return RESULT_UNKNOWN;
|
|
|
|
broadcastInstanceDungeonInfoToMembers();
|
|
|
|
SendPartyInfo( pPlayer );
|
|
|
|
BroadcastPartyMemberInfo( nPartyID, pPlayer );
|
|
|
|
SendTimeSync( pPlayer );
|
|
|
|
LOG::Log11N4S( LM_PARTY_JOIN, pPlayer->GetAccountID(), pPlayer->GetSID(), nPartyID, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), 1, 0, 0, 0, 0,
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_JOIN, pPlayer->GetAccountID(), pPlayer->GetSID(), pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, nInstanceNo, nMaxMemberCount, !!szPassword[ 0 ], PartyManager::GetInstance().GetMemberCount( nPartyID ), pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::InstanceDungeon::leaveInstanceDungeon( StructPlayer * pPlayer )
|
|
{
|
|
// 파티가 이미 해산된 후에 leaveInstanceDungeon이 호출되는 경우라면(endHunting 처리) 걍 패스
|
|
if( !pPlayer->IsInParty() )
|
|
return RESULT_SUCCESS;
|
|
|
|
// 현재 인스턴스에 소속되지 않은 플레이어가 나가겠다고 하면 탈퇴 불가
|
|
if( !PartyManager::GetInstance().IsMember( nPartyID, pPlayer->GetPlayerUID() ) )
|
|
{
|
|
assert( 0 );
|
|
return RESULT_NOT_OWN;
|
|
}
|
|
|
|
// 파장이 나가려고 하면
|
|
if( PartyManager::GetInstance().IsLeader( nPartyID, pPlayer->GetPlayerUID() ) )
|
|
{
|
|
// 다음 파장 될 사람이 없거나, 다른 파티원이 남아 있긴 한데
|
|
// 죄다 오프라인이면 파티 해산(베어로드 파티에는 오프라인 멤버가 있으면 안되지만...)
|
|
if( PartyManager::GetInstance().GetMemberCount( nPartyID ) == 1 || !PartyManager::GetInstance().AutoPromote( nPartyID, true ) )
|
|
{
|
|
LOG::Log11N4S( LM_PARTY_DESTROY, pPlayer->GetAccountID(), pPlayer->GetSID(), nPartyID, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), 1, 0, 0, 0, 0,
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
BroadcastPartyDestroy( nPartyID );
|
|
|
|
PartyManager::GetInstance().DestroyParty( nPartyID );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
// 새 파장 정보 얻기
|
|
StructPlayer::iterator itNewLeader = StructPlayer::get( StructPlayer::FindPlayer( PartyManager::GetInstance().GetLeaderName( nPartyID ).c_str() ) );
|
|
StructPlayer * pNewLeader = (*itNewLeader);
|
|
|
|
// 인계는 됐는데 어쩐 일인지 새 파장이 없다... 오프라인이거나... 해산해버리자 -_ -;
|
|
if( !pNewLeader )
|
|
{
|
|
LOG::Log11N4S( LM_PARTY_DESTROY, pPlayer->GetAccountID(), pPlayer->GetSID(), nPartyID, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), 1, 0, 0, 0, 0,
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
BroadcastPartyDestroy( nPartyID );
|
|
|
|
PartyManager::GetInstance().DestroyParty( nPartyID );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
// 다른 사람에게 자동 인계가 처리됐으면 파장 변경 방송 처리
|
|
PrintfPartyChatMessage( CHAT_PARTY_SYSTEM, nPartyID, "PROMOTE|%d|%s|", nPartyID, pNewLeader->GetName() );
|
|
|
|
LOG::Log11N4S( LM_PARTY_PROMOTE, pPlayer->GetAccountID(), pPlayer->GetSID(), nPartyID, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(),
|
|
pNewLeader->GetAccountID(), pNewLeader->GetSID(), pNewLeader->GetX(), pNewLeader->GetY(), pNewLeader->GetLayer(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, pNewLeader->GetAccountName(), LOG::STR_NTS, pNewLeader->GetName(), LOG::STR_NTS );
|
|
}
|
|
|
|
BroadcastPartyLeave( pPlayer );
|
|
|
|
PartyManager::GetInstance().LeaveParty( nPartyID, pPlayer->GetPlayerUID() );
|
|
|
|
LOG::Log11N4S( LM_PARTY_LEAVE, pPlayer->GetAccountID(), pPlayer->GetSID(), nPartyID, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), 1, 0, 0, 0, 0,
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
broadcastInstanceDungeonInfoToMembers();
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::InstanceDungeon::beginHunting()
|
|
{
|
|
if( bBegin )
|
|
return RESULT_ALREADY_EXIST;
|
|
|
|
bBegin = true;
|
|
|
|
// 몬스터 리젠 시키고~
|
|
for( std::vector< const GameContent::HUNTAHOLIC_MONSTER_RESPAWN_INFO * >::const_iterator itRespawn = pInstanceBase->vRespawnInfo.begin() ; itRespawn != pInstanceBase->vRespawnInfo.end() ; ++itRespawn )
|
|
{
|
|
for( int nIndex = 0 ; nIndex < (*itRespawn)->nCount ; ++nIndex )
|
|
{
|
|
int nRespawnX;
|
|
int nRespawnY;
|
|
|
|
int nRespawnTryCount = 0;
|
|
|
|
do
|
|
{
|
|
nRespawnX = XRandom( static_cast< int >( (*itRespawn)->bxArea.GetLeft() ), static_cast< int >( (*itRespawn)->bxArea.GetRight() ) );
|
|
nRespawnY = XRandom( static_cast< int >( (*itRespawn)->bxArea.GetTop() ), static_cast< int >( (*itRespawn)->bxArea.GetBottom() ) );
|
|
|
|
if( ++nRespawnTryCount > 10 )
|
|
{
|
|
FILELOG( "Unable to respawn monster in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn)->nID );
|
|
_cprint( "Unable to respawn monster in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)\n", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn)->nID );
|
|
|
|
nRespawnTryCount = 0;
|
|
break;
|
|
}
|
|
}
|
|
while( GameContent::IsBlocked( nRespawnX, nRespawnY ) );
|
|
|
|
if( !nRespawnTryCount )
|
|
continue;
|
|
|
|
StructMonster * pMob = respawnMonster( nRespawnX, nRespawnY, nInstanceNo, (*itRespawn)->nMonsterID, (*itRespawn)->bWandering, 0, this );
|
|
if( !pMob )
|
|
{
|
|
FILELOG( "Unknown monster is to be respawned in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn)->nID );
|
|
_cprint( "Unknown monster is to be respawned in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)\n", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn)->nID );
|
|
|
|
continue;
|
|
}
|
|
|
|
pMob->SetInstanceRespawnID( (*itRespawn)->nID );
|
|
|
|
vRespawnedMonster.push_back( pMob );
|
|
}
|
|
}
|
|
|
|
// 프랍 리젠 시키고~
|
|
for( std::vector< const GameContent::FIELD_PROP_RESPAWN_INFO * >::const_iterator itHealingProp = pInstanceBase->vHealingPropInfo.begin() ; itHealingProp != pInstanceBase->vHealingPropInfo.end() ; ++itHealingProp )
|
|
{
|
|
StructFieldProp * pProp = NULL;
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockWithVisibleRange( GetRegionX( (*itHealingProp)->x ), GetRegionY( (*itHealingProp)->y ), nInstanceNo ) );
|
|
|
|
pProp = StructFieldProp::Create( this, (*itHealingProp), (*itHealingProp)->x, (*itHealingProp)->y, nInstanceNo );
|
|
}
|
|
|
|
if( pProp )
|
|
{
|
|
vRespawnedHealingProp.push_back( pProp );
|
|
}
|
|
}
|
|
|
|
// 모두 입장~
|
|
WarpFunctor beginWarpFo( pHuntaholicInfo->pHuntaholicBase->posDungeon.x, pHuntaholicInfo->pHuntaholicBase->posDungeon.y, nInstanceNo, GetArTime() );
|
|
PartyManager::GetInstance().DoEachMember( nPartyID, beginWarpFo );
|
|
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_BEGIN, 0, 0, pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, nInstanceNo, nMaxMemberCount, !!szPassword[ 0 ], PartyManager::GetInstance().GetMemberCount( nPartyID ), 0, 0, 0,
|
|
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::InstanceDungeon::quitHunting( StructPlayer * pPlayer, const bool bNeedToReward, const HuntaholicManager::_HUNTING_RESULT eResult, int * pnSuppliedRewardPoint )
|
|
{
|
|
unsigned short nErrorCode = leaveInstanceDungeon( pPlayer );
|
|
if( nErrorCode != RESULT_SUCCESS )
|
|
return nErrorCode;
|
|
|
|
if( bNeedToReward )
|
|
{
|
|
int nPrevPoint = pPlayer->GetHuntaholicPoint();
|
|
bool bSuccess = ( eResult == HUNTING_RESULT_SUCCESS );
|
|
int nGainPoint = ( bSuccess ) ? ceil( pInstanceBase->fPointAdvantage * nScore ) : 0;
|
|
|
|
// 게임 중독 방지 시스템에 의한 획득 보상 페널티 적용
|
|
if( pPlayer->IsGameTimeLimited() )
|
|
{
|
|
nGainPoint = GameRule::GetGameTimeLimitPenalty( pPlayer->GetContinuousPlayTime() ) * nGainPoint;
|
|
}
|
|
|
|
InstanceDungeon::_SCORE_TAG * pScoreTag = getScoreTag( pPlayer->GetHandle() );
|
|
|
|
// 성공 시 성공 보상 및 포인트 지급
|
|
if( bSuccess )
|
|
{
|
|
// AddExp는 onExpChange에서 레벨업 방송이 있으므로 지역 락 걸어야 함
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
|
|
|
|
pPlayer->SetHuntaholicPoint( nPrevPoint + nGainPoint );
|
|
pPlayer->AddExp( pInstanceBase->nRewardExp, pInstanceBase->nRewardJp, false );
|
|
|
|
if( pInstanceBase->nRewardSuccessItemCode )
|
|
{
|
|
StructItem * pRewardSuccessItem = StructItem::AllocItem( 0, static_cast< ItemBase::ItemCode >( pInstanceBase->nRewardSuccessItemCode ), pInstanceBase->nRewardSuccessItemCount, ItemInstance::BY_HUNTAHOLIC );
|
|
|
|
if( pRewardSuccessItem->IsJoinable() )
|
|
{
|
|
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@254\v#@item_name@#\v@%d\v#@item_num@#\v%d", pRewardSuccessItem->GetItemBase().nNameId, pRewardSuccessItem->GetCount() );
|
|
}
|
|
else
|
|
{
|
|
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@253\v#@item_name@#\v@%d", pRewardSuccessItem->GetItemBase().nNameId );
|
|
}
|
|
|
|
StructItem * pNewItem = pPlayer->PushItem( pRewardSuccessItem, pRewardSuccessItem->GetCount() );
|
|
if( pNewItem )
|
|
{
|
|
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pRewardSuccessItem->GetItemEnhance() * 100 + pRewardSuccessItem->GetItemLevel(), pRewardSuccessItem->GetItemCode(), pRewardSuccessItem->GetCount(), pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "HUNTAHOLIC", LOG::STR_NTS );
|
|
}
|
|
|
|
if( pNewItem != pRewardSuccessItem )
|
|
{
|
|
StructItem::PendFreeItem( pRewardSuccessItem );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( pnSuppliedRewardPoint )
|
|
*pnSuppliedRewardPoint = nGainPoint;
|
|
}
|
|
// 사망에 의한 실패 및 중동 방지에 의한 실패일 경우에는 실패 보상 지급
|
|
else if( ( eResult == HUNTING_RESULT_FAILED_BY_DEATH || eResult == HUNTING_RESULT_FAILED_BY_GAMETIME_LIMIT ) && pInstanceBase->nRewardFailItemCode )
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
|
|
|
|
StructItem * pRewardFailItem = StructItem::AllocItem( 0, static_cast< ItemBase::ItemCode >( pInstanceBase->nRewardFailItemCode ), pInstanceBase->nRewardFailItemCount, ItemInstance::BY_HUNTAHOLIC );
|
|
|
|
if( pRewardFailItem->IsJoinable() )
|
|
{
|
|
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@254\v#@item_name@#\v@%d\v#@item_num@#\v%d", pRewardFailItem->GetItemBase().nNameId, pRewardFailItem->GetCount() );
|
|
}
|
|
else
|
|
{
|
|
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@253\v#@item_name@#\v@%d", pRewardFailItem->GetItemBase().nNameId );
|
|
}
|
|
|
|
StructItem * pNewItem = pPlayer->PushItem( pRewardFailItem, pRewardFailItem->GetCount() );
|
|
if( pNewItem )
|
|
{
|
|
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pRewardFailItem->GetItemEnhance() * 100 + pRewardFailItem->GetItemLevel(), pRewardFailItem->GetItemCode(), pRewardFailItem->GetCount(), pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "HUNTAHOLIC", LOG::STR_NTS );
|
|
}
|
|
|
|
if( pNewItem != pRewardFailItem )
|
|
{
|
|
StructItem::PendFreeItem( pRewardFailItem );
|
|
}
|
|
}
|
|
// 성공도 아니고 사망에 의한 실패도 아니라면 보상을 전혀 지급하지 않고 페널티로 디버프 부여 및 잔여 입장 횟수를 1회 추가 차감
|
|
else
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
|
|
|
|
pPlayer->AddHuntaholicEnterableCount( -1 );
|
|
AR_TIME t = GetArTime();
|
|
pPlayer->AddState( StructState::MOVE_SPEED_SLOWDOWN, pPlayer->GetHandle(), 1, t, t + GameRule::HUNTAHOLIC_QUITTING_PENALTY_DEBUFF_TIME );
|
|
}
|
|
|
|
SendHuntaholicHuntingScore( pPlayer, pHuntaholicInfo->pHuntaholicBase->nID, ( pScoreTag ) ? pScoreTag->nKillCount : 0, ( pScoreTag ) ? pScoreTag->nScore : 0,
|
|
nKillCount, nScore, pInstanceBase->fPointAdvantage, nGainPoint, eResult );
|
|
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_QUIT, pPlayer->GetAccountID(), pPlayer->GetSID(), pHuntaholicInfo->pHuntaholicBase->nID, nInstanceNo, eResult, nGainPoint, pPlayer->GetHuntaholicPoint(), pInstanceBase->nRewardExp, pPlayer->GetEXP(), pInstanceBase->nRewardJp, pPlayer->GetJobPoint(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
}
|
|
else
|
|
{
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_QUIT, pPlayer->GetAccountID(), pPlayer->GetSID(), pHuntaholicInfo->pHuntaholicBase->nID, nInstanceNo, HUNTING_RESULT_NOREWARD, 0, pPlayer->GetHuntaholicPoint(), 0, pPlayer->GetEXP(), 0, pPlayer->GetJobPoint(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
}
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::InstanceDungeon::endHunting()
|
|
{
|
|
if( bEnd )
|
|
return RESULT_NOT_EXIST;
|
|
|
|
bEnd = true;
|
|
|
|
// 리젠되어 있는 몹/프랍 모두 제거
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( pHuntaholicInfo->pHuntaholicBase->nDungeonRegionLeft, pHuntaholicInfo->pHuntaholicBase->nDungeonRegionTop,
|
|
pHuntaholicInfo->pHuntaholicBase->nDungeonRegionRight, pHuntaholicInfo->pHuntaholicBase->nDungeonRegionBottom, nInstanceNo ) );
|
|
|
|
// 리젠 대기 중인 몬스터 데이터 제거
|
|
{
|
|
THREAD_SYNCHRONIZE( pHuntaholicInfo->csPendedRespawn );
|
|
|
|
for( std::vector< GameContent::PENDED_RESPAWN_INFO * >::iterator itRespawn = pHuntaholicInfo->vPendedRespawn.begin() ; itRespawn != pHuntaholicInfo->vPendedRespawn.end() ; /* 루프에서 ++itRespawn 처리 */ )
|
|
{
|
|
if( (*itRespawn)->layer == nInstanceNo )
|
|
{
|
|
delete (*itRespawn);
|
|
itRespawn = pHuntaholicInfo->vPendedRespawn.erase( itRespawn );
|
|
}
|
|
else
|
|
++itRespawn;
|
|
}
|
|
}
|
|
|
|
clearMonsters();
|
|
clearHealingProps();
|
|
}
|
|
|
|
struct MemberHandleCollector : public PartyManager::PartyFunctor
|
|
{
|
|
virtual bool operator()( AR_HANDLE handle )
|
|
{
|
|
vMember.push_back( handle );
|
|
return true;
|
|
}
|
|
|
|
std::vector< AR_HANDLE > vMember;
|
|
} fo;
|
|
|
|
PartyManager::GetInstance().DoEachMember( nPartyID, fo );
|
|
|
|
BroadcastPartyDestroy( nPartyID );
|
|
|
|
LOG::Log11N4S( LM_PARTY_DESTROY, 0, 0, nPartyID, 0, 0, 0, 2, 0, 0, 0, 0,
|
|
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
PartyManager::GetInstance().DestroyParty( nPartyID );
|
|
|
|
// endHunting 함수는 모든 유저가 나가거나 타임아웃으로 동시에 모두 쫓겨나야 할 때만 호출되므로(개별 탈퇴는 quitHunting) 실패 시에도 페널티 없음(사망 실패와 동일하게 처리)
|
|
_HUNTING_RESULT eResult = ( pHuntaholicInfo->pHuntaholicBase->nObjectivePoint > nScore ) ? HUNTING_RESULT_FAILED_BY_DEATH : HUNTING_RESULT_SUCCESS;
|
|
WarpFunctor endWarpFo( pHuntaholicInfo->pHuntaholicBase->posLobby.x, pHuntaholicInfo->pHuntaholicBase->posLobby.y, pInstanceBase->nID );
|
|
for( std::vector< AR_HANDLE >::const_iterator itPlayer = fo.vMember.begin() ; itPlayer != fo.vMember.end() ; ++itPlayer )
|
|
{
|
|
StructPlayer::iterator it = StructPlayer::get( (*itPlayer) );
|
|
StructPlayer * pPlayer = (*it);
|
|
|
|
if( !pPlayer )
|
|
continue;
|
|
|
|
// 파티 해산 이후에 호출되는 quitHunting 안에서 호출되는 leaveInstanceDungeon은 파티 관련 처리는 아무것도 하지 않으므로 보상 지급&결과 방송 처리만 됨
|
|
int nSuppliedRewardPoint = 0;
|
|
quitHunting( pPlayer, true, ( pPlayer->IsDead() ) ? HUNTING_RESULT_FAILED_BY_DEATH : eResult, &nSuppliedRewardPoint );
|
|
|
|
// 성공해서 보상을 지급받았다면 지급된 보상을 랭킹에 적용
|
|
if( nSuppliedRewardPoint )
|
|
{
|
|
RankingManager::Instance().AddRankingScore( RankingManager::RANKING_TYPE_HUNTAHOLIC_THIS_MONTH, pPlayer->GetPlayerUID(), pPlayer->GetName(), nSuppliedRewardPoint, true );
|
|
RankingManager::Instance().AddRankingScore( RankingManager::RANKING_TYPE_HUNTAHOLIC_TOTAL, pPlayer->GetPlayerUID(), pPlayer->GetName(), nSuppliedRewardPoint, true );
|
|
}
|
|
|
|
// 로비로 돌려보냄
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
|
|
|
|
endWarpFo.PendWarpPlayer( pPlayer );
|
|
}
|
|
}
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
void HuntaholicManager::InstanceDungeon::procMonsterRespawn( const AR_TIME & tCurrent )
|
|
{
|
|
for( std::vector< PendedMonsterRespawnInfo >::iterator itRespawn = vPendedMonsterRespawn.begin() ; itRespawn != vPendedMonsterRespawn.end() ; /* 루프에서 ++itRespawn 처리 */ )
|
|
{
|
|
if( (*itRespawn).second > tCurrent )
|
|
{
|
|
++itRespawn;
|
|
continue;
|
|
}
|
|
|
|
int nRespawnX;
|
|
int nRespawnY;
|
|
|
|
int nRespawnTryCount = 0;
|
|
|
|
do
|
|
{
|
|
nRespawnX = XRandom( static_cast< int >( (*itRespawn).first->bxArea.GetLeft() ), static_cast< int >( (*itRespawn).first->bxArea.GetRight() ) );
|
|
nRespawnY = XRandom( static_cast< int >( (*itRespawn).first->bxArea.GetTop() ), static_cast< int >( (*itRespawn).first->bxArea.GetBottom() ) );
|
|
|
|
if( ++nRespawnTryCount > 10 )
|
|
{
|
|
FILELOG( "Unable to respawn monster in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn).first->nID );
|
|
_cprint( "Unable to respawn monster in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)\n", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn).first->nID );
|
|
|
|
nRespawnTryCount = 0;
|
|
break;
|
|
}
|
|
}
|
|
while( GameContent::IsBlocked( nRespawnX, nRespawnY ) );
|
|
|
|
if( !nRespawnTryCount )
|
|
continue;
|
|
|
|
StructMonster * pMob = respawnMonster( nRespawnX, nRespawnY, nInstanceNo, (*itRespawn).first->nMonsterID, (*itRespawn).first->bWandering, 0, this );
|
|
if( !pMob )
|
|
{
|
|
FILELOG( "Unknown monster is to be respawned in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn).first->nID );
|
|
_cprint( "Unknown monster is to be respawned in huntaholic dungeon. HuntaholicID(%d), InstanceID(%d), RespawnID(%d)\n", pHuntaholicInfo->pHuntaholicBase->nID, pInstanceBase->nID, (*itRespawn).first->nID );
|
|
|
|
continue;
|
|
}
|
|
|
|
pMob->SetInstanceRespawnID( (*itRespawn).first->nID );
|
|
|
|
vRespawnedMonster.push_back( pMob );
|
|
|
|
itRespawn = vPendedMonsterRespawn.erase( itRespawn );
|
|
}
|
|
}
|
|
|
|
void HuntaholicManager::InstanceDungeon::procHealingPropRespawn( const AR_TIME & tCurrent )
|
|
{
|
|
for( std::vector< PendedHealingPropRespawnInfo >::iterator itRespawn = vPendedHealingPropRespawn.begin() ; itRespawn != vPendedHealingPropRespawn.end() ; /* 루프에서 ++itRespawn 처리 */ )
|
|
{
|
|
if( (*itRespawn).second > tCurrent )
|
|
{
|
|
++itRespawn;
|
|
continue;
|
|
}
|
|
|
|
StructFieldProp * pProp = NULL;
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockWithVisibleRange( GetRegionX( (*itRespawn).first->x ), GetRegionY( (*itRespawn).first->y ), nInstanceNo ) );
|
|
|
|
pProp = StructFieldProp::Create( this, (*itRespawn).first, (*itRespawn).first->x, (*itRespawn).first->y, nInstanceNo );
|
|
}
|
|
|
|
if( pProp )
|
|
{
|
|
vRespawnedHealingProp.push_back( pProp );
|
|
}
|
|
|
|
itRespawn = vPendedHealingPropRespawn.erase( itRespawn );
|
|
}
|
|
}
|
|
|
|
HuntaholicManager::InstanceDungeon::_SCORE_TAG * HuntaholicManager::InstanceDungeon::getScoreTag( const AR_HANDLE & hPlayer )
|
|
{
|
|
for( std::vector< _SCORE_TAG >::iterator it = vScoreTag.begin() ; it != vScoreTag.end() ; ++it )
|
|
{
|
|
if( (*it).hOwner != hPlayer )
|
|
continue;
|
|
|
|
return &(*it);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const HuntaholicManager::InstanceDungeon::_SCORE_TAG * HuntaholicManager::InstanceDungeon::getScoreTag( const AR_HANDLE & hPlayer ) const
|
|
{
|
|
for( std::vector< _SCORE_TAG >::const_iterator it = vScoreTag.begin() ; it != vScoreTag.end() ; ++it )
|
|
{
|
|
if( (*it).hOwner != hPlayer )
|
|
continue;
|
|
|
|
return &(*it);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// HuntaholicManager::HuntaholicInfo
|
|
const unsigned char HuntaholicManager::HuntaholicInfo::GetNewInstanceNo() const
|
|
{
|
|
unsigned char nInstanceNo = 1;
|
|
|
|
while( stInstanceNo.find( nInstanceNo ) != stInstanceNo.end() && nInstanceNo < 0xFF )
|
|
++nInstanceNo;
|
|
|
|
return nInstanceNo;
|
|
}
|
|
|
|
void HuntaholicManager::HuntaholicInfo::ProcPendedMonsterRespawn()
|
|
{
|
|
// [지역 락 -> csPEndedRespawn / csInstanceDungeon -> HuntaholicInfo::csInstance -> 지역 락] 지역 락으로 인한 데드락 발생 방지용 락 전략
|
|
std::vector< GameContent::PENDED_RESPAWN_INFO * > _vPendedRespawn;
|
|
{
|
|
THREAD_SYNCHRONIZE( csPendedRespawn );
|
|
|
|
if( vPendedRespawn.empty() )
|
|
return;
|
|
|
|
_vPendedRespawn.swap( vPendedRespawn );
|
|
}
|
|
|
|
THREAD_SYNCHRONIZE( csInstanceDungeon );
|
|
|
|
for( std::vector< GameContent::PENDED_RESPAWN_INFO * >::const_iterator itRespawn = _vPendedRespawn.begin() ; itRespawn != _vPendedRespawn.end() ; ++itRespawn )
|
|
{
|
|
GameContent::PENDED_RESPAWN_INFO * pRespawn = (*itRespawn);
|
|
|
|
for( std::vector< InstanceDungeon * >::iterator itInstance = vInstanceDungeon.begin() ; itInstance != vInstanceDungeon.end() ; ++itInstance )
|
|
{
|
|
InstanceDungeon * pInstance = (*itInstance);
|
|
|
|
if( pInstance->nInstanceNo != pRespawn->layer )
|
|
continue;
|
|
|
|
// 몹이 리젠되어야 할 방인데 아직 시작되지 않은 상태이거나 끝난 이후면 리젠시키지 않음
|
|
if( !pInstance->bBegin || pInstance->bEnd || pInstance->nScore >= pHuntaholicBase->nMaxPoint )
|
|
break;
|
|
|
|
THREAD_SYNCHRONIZE( pInstance->csInstance );
|
|
|
|
// 몹이 리젠되어야 할 방인데 아직 시작되지 않은 상태이거나 끝난 이후면 리젠시키지 않음(락 걸고 나서 한 번 더 검사)
|
|
if( !pInstance->bBegin || pInstance->bEnd || pInstance->nScore >= pHuntaholicBase->nMaxPoint )
|
|
break;
|
|
|
|
for( int nCountIdx = 0 ; nCountIdx < pRespawn->respawnCount ; ++nCountIdx )
|
|
{
|
|
// 랜덤 위치 설정
|
|
AR_UNIT x, y;
|
|
bool bValidPos = true;
|
|
unsigned short try_cnt = 0;
|
|
do
|
|
{
|
|
x = XRandom( pRespawn->x - 60, pRespawn->x + 60 );
|
|
y = XRandom( pRespawn->y - 60, pRespawn->y + 60 );
|
|
|
|
if( ++try_cnt > 300 )
|
|
{
|
|
bValidPos = false;
|
|
break;
|
|
}
|
|
} while( GameContent::CollisionToLine( pRespawn->x, pRespawn->y, x, y ) );
|
|
|
|
if( !bValidPos )
|
|
{
|
|
x = pRespawn->x;
|
|
y = pRespawn->y;
|
|
|
|
if( GameContent::IsBlocked( x, y ) == true )
|
|
{
|
|
FILELOG( "Unable to respawn monster in huntaholic dungeon. (respawn pos) HuntaholicID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)", pHuntaholicBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId );
|
|
_cprint( "Unable to respawn monster in huntaholic dungeon. (respawn pos) HuntaholicID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)", pHuntaholicBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId );
|
|
}
|
|
}
|
|
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockWithVisibleRange( ::GetRegionX( x ), ::GetRegionY( y ), pRespawn->layer ) );
|
|
|
|
StructMonster * pMob = respawnMonster( x, y, pRespawn->layer, pRespawn->monsterId, true, 0, pInstance );
|
|
if( !pMob )
|
|
{
|
|
FILELOG( "Unknown monster is to be respawned in huntaholic dungeon by pending. HuntaholicID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)", pHuntaholicBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId );
|
|
_cprint( "Unknown monster is to be respawned in huntaholic dungeon by pending. HuntaholicID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)\n", pHuntaholicBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId );
|
|
|
|
break;
|
|
}
|
|
|
|
if( pRespawn->initialEnemy )
|
|
pMob->AddHate( pRespawn->initialEnemy, 1 );
|
|
|
|
pMob->SetLifeTime( pRespawn->lifeTime );
|
|
|
|
pInstance->vRespawnedMonster.push_back( pMob );
|
|
}
|
|
}
|
|
}
|
|
|
|
delete pRespawn;
|
|
}
|
|
}
|
|
|
|
// HuntaholicManager
|
|
HuntaholicManager::~HuntaholicManager()
|
|
{
|
|
if( IsInitialized() )
|
|
DeInit();
|
|
|
|
ClearHuntaholicInfo();
|
|
}
|
|
|
|
HuntaholicManager & HuntaholicManager::Instance()
|
|
{
|
|
static HuntaholicManager _instance;
|
|
|
|
return _instance;
|
|
}
|
|
|
|
const bool HuntaholicManager::Init()
|
|
{
|
|
if( GameRule::bDisableHuntaholic )
|
|
return false;
|
|
|
|
if( IsInitialized() )
|
|
return false;
|
|
|
|
ArcadiaServer::Instance().SetObjectPriority( &HuntaholicManager::Instance(), ArSchedulerObject::UPDATE_PRIORITY_HIGH );
|
|
|
|
return true;
|
|
}
|
|
|
|
const bool HuntaholicManager::DeInit()
|
|
{
|
|
if( !IsInitialized() )
|
|
return false;
|
|
|
|
ArcadiaServer::Instance().SetObjectPriority( &HuntaholicManager::Instance(), ArSchedulerObject::UPDATE_PRIORITY_IDLE );
|
|
|
|
// 진행 중이던 헌터홀릭 던전이 있는 상태에서 종료 또는 뒷 처리를 해야할 경우 여기서 하면 됨
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
// 리젠 대기 중인 몬스터 데이터 제거
|
|
{
|
|
THREAD_SYNCHRONIZE( (*it)->csPendedRespawn );
|
|
|
|
for( std::vector< GameContent::PENDED_RESPAWN_INFO * >::const_iterator itRespawn = (*it)->vPendedRespawn.begin() ; itRespawn != (*it)->vPendedRespawn.end() ; ++itRespawn )
|
|
{
|
|
delete (*itRespawn);
|
|
}
|
|
|
|
(*it)->vPendedRespawn.clear();
|
|
}
|
|
|
|
// 삭제되어야 할 던전 정보를 즉시 삭제하기 위해서 모으는 벡터
|
|
std::vector< InstanceDungeon * > vPendedInstanceDungeonToBeDeleted;
|
|
{
|
|
THREAD_SYNCHRONIZE( (*it)->csInstanceDungeon );
|
|
|
|
// 삭제 대기 중인 던전들을 즉시 삭제할 목록에 추가 후 대기 리스트 초기화
|
|
vPendedInstanceDungeonToBeDeleted.swap( (*it)->vPendedInstanceDungeonToBeDeleted );
|
|
(*it)->stInstanceNo.clear();
|
|
|
|
for( std::vector< InstanceDungeon * >::iterator itInstance = (*it)->vInstanceDungeon.begin() ; itInstance != (*it)->vInstanceDungeon.end() ; /* 루프에서 ++itInstance 처리 */ )
|
|
{
|
|
InstanceDungeon * pInstance = (*itInstance);
|
|
|
|
THREAD_SYNCHRONIZE( pInstance->csInstance );
|
|
|
|
if( pInstance->bBegin && !pInstance->bEnd )
|
|
{
|
|
pInstance->endHunting();
|
|
|
|
itInstance = (*it)->vInstanceDungeon.erase( itInstance );
|
|
}
|
|
else
|
|
{
|
|
// 사냥 시작한 던전 아니면 파티 박살내고 방 정보 바로 삭제해버림(이쯤 되면 메모리 누수만 안 생기면 어떻게 되도 별 상관없다 -_ -; 어차피 서버 종료되는 중...)
|
|
PartyManager::GetInstance().DestroyParty( (*itInstance)->nPartyID );
|
|
|
|
++itInstance;
|
|
}
|
|
|
|
vPendedInstanceDungeonToBeDeleted.push_back( pInstance );
|
|
}
|
|
}
|
|
|
|
for( std::vector< InstanceDungeon * >::const_iterator itInstance = vPendedInstanceDungeonToBeDeleted.begin() ; itInstance != vPendedInstanceDungeonToBeDeleted.end() ; ++itInstance )
|
|
{
|
|
// 삭제 전에 락을 한 번 걸었다 풀어서 다른 프로세스가 인스턴스 정보를 사용 중일 가능성을 낮춘 후에 삭제
|
|
// 그래봐야 여기서 거는 락 때문에 기다리고 있는 쓰레드가 생기면 말짱 꽝이지만,
|
|
// 어차피 서버 내려가고 있는데 그딴 거 알 게 뭐삼! 걍 지워!(서버 종료되면서 불필요한 덤프가 남는 주 원인이 될 수도 있음... ``;)
|
|
{
|
|
THREAD_SYNCHRONIZE( (*itInstance)->csInstance );
|
|
}
|
|
delete (*itInstance);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const bool HuntaholicManager::IsInitialized() const
|
|
{
|
|
return GetFinalPriority() != ArSchedulerObject::UPDATE_PRIORITY_IDLE;
|
|
}
|
|
|
|
const bool HuntaholicManager::ClearHuntaholicInfo()
|
|
{
|
|
if( IsInitialized() )
|
|
return false;
|
|
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
// beginHunting에서 몬스터/프랍 리젠 정보를 사용하지만 ClearHuntaholicInfo가 호출되는 상황이면 beginHunting이 처리 중일 수는 없으므로 락 안 걸고 삭제
|
|
for( std::vector< const GameContent::HUNTAHOLIC_INSTANCE_BASE * >::const_iterator itInstance = (*it)->pHuntaholicBase->vInstanceBase.begin() ; itInstance != (*it)->pHuntaholicBase->vInstanceBase.end() ; ++itInstance )
|
|
{
|
|
for( std::vector< const GameContent::HUNTAHOLIC_MONSTER_RESPAWN_INFO * >::const_iterator itRespawn = (*itInstance)->vRespawnInfo.begin() ; itRespawn != (*itInstance)->vRespawnInfo.end() ; ++itRespawn )
|
|
{
|
|
delete (*itRespawn);
|
|
}
|
|
|
|
for( std::vector< const GameContent::FIELD_PROP_RESPAWN_INFO * >::const_iterator itHealingProp = (*itInstance)->vHealingPropInfo.begin() ; itHealingProp != (*itInstance)->vHealingPropInfo.end() ; ++itHealingProp )
|
|
{
|
|
delete (*itHealingProp);
|
|
}
|
|
|
|
delete (*itInstance);
|
|
}
|
|
|
|
delete (*it);
|
|
}
|
|
m_vHuntaholicInfo.clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
const bool HuntaholicManager::RegisterHuntaholicBase( const GameContent::HUNTAHOLIC_BASE * pHuntaholicBase )
|
|
{
|
|
assert( !IsInitialized() );
|
|
|
|
#ifdef _DEBUG
|
|
// 디버그 모드일 때만 이미 등록된 헌터홀릭 정보인지 확인
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID == pHuntaholicBase->nID )
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
m_vHuntaholicInfo.push_back( new HuntaholicInfo( pHuntaholicBase ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
const int HuntaholicManager::GetHuntaholicID( const ArPosition & pos ) const
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
unsigned int nRegionX = pos.GetRX();
|
|
unsigned int nRegionY = pos.GetRY();
|
|
|
|
if( ( nRegionX >= (*it)->pHuntaholicBase->nDungeonRegionLeft && nRegionX <= (*it)->pHuntaholicBase->nDungeonRegionRight &&
|
|
nRegionY >= (*it)->pHuntaholicBase->nDungeonRegionTop && nRegionY <= (*it)->pHuntaholicBase->nDungeonRegionBottom ) ||
|
|
( nRegionX >= (*it)->pHuntaholicBase->nLobbyRegionLeft && nRegionX <= (*it)->pHuntaholicBase->nLobbyRegionRight &&
|
|
nRegionY >= (*it)->pHuntaholicBase->nLobbyRegionTop && nRegionY <= (*it)->pHuntaholicBase->nLobbyRegionBottom ) )
|
|
return (*it)->pHuntaholicBase->nID;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const bool HuntaholicManager::IsHuntaholicLobby( const ArPosition & pos ) const
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
unsigned int nRegionX = pos.GetRX();
|
|
unsigned int nRegionY = pos.GetRY();
|
|
|
|
if( nRegionX >= (*it)->pHuntaholicBase->nLobbyRegionLeft && nRegionX <= (*it)->pHuntaholicBase->nLobbyRegionRight &&
|
|
nRegionY >= (*it)->pHuntaholicBase->nLobbyRegionTop && nRegionY <= (*it)->pHuntaholicBase->nLobbyRegionBottom )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const bool HuntaholicManager::IsHuntaholicDungeon( const ArPosition & pos ) const
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
unsigned int nRegionX = pos.GetRX();
|
|
unsigned int nRegionY = pos.GetRY();
|
|
|
|
if( nRegionX >= (*it)->pHuntaholicBase->nDungeonRegionLeft && nRegionX <= (*it)->pHuntaholicBase->nDungeonRegionRight &&
|
|
nRegionY >= (*it)->pHuntaholicBase->nDungeonRegionTop && nRegionY <= (*it)->pHuntaholicBase->nDungeonRegionBottom )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const ArPosition HuntaholicManager::GetLobbyPosition( const int nHuntaholicID ) const
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
return (*it)->pHuntaholicBase->posLobby;
|
|
}
|
|
|
|
assert( 0 );
|
|
return ArPosition( 0, 0 );
|
|
}
|
|
|
|
const unsigned char HuntaholicManager::GetProperLobbyLayer( const int nHuntaholicID, const int nLevel ) const
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
return (*it)->pHuntaholicBase->GetProperLobbyLayer( nLevel );
|
|
}
|
|
|
|
assert( 0 );
|
|
return GameRule::HUNTAHOLIC_UNUSABLE_LOBBY_LAYER;
|
|
}
|
|
|
|
const size_t HuntaholicManager::GetFreeInstanceCount( const int nHuntaholicID ) const
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( (*it)->csInstanceDungeon );
|
|
|
|
return (*it)->GetFreeInstanceCount();
|
|
}
|
|
|
|
assert( 0 );
|
|
return 0;
|
|
}
|
|
|
|
const size_t HuntaholicManager::GetUsedInstanceCount( const int nHuntaholicID, const int nLevel ) const
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( (*it)->csInstanceDungeon );
|
|
|
|
if( (*it)->vInstanceDungeon.empty() )
|
|
return 0;
|
|
|
|
size_t nCount = 0;
|
|
|
|
for( std::vector< InstanceDungeon * >::const_iterator itInstance = (*it)->vInstanceDungeon.begin() ; itInstance != (*it)->vInstanceDungeon.end() ; ++itInstance )
|
|
{
|
|
if( !nLevel || (*itInstance)->pInstanceBase->IsProperLevel( nLevel ) )
|
|
++nCount;
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
assert( 0 );
|
|
return 0;
|
|
}
|
|
|
|
const size_t HuntaholicManager::GetInstanceList( const int nHuntaholicID, const int nLevel, const int nPage, TS_HUNTAHOLIC_INSTANCE_INFO * pBuffer )
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::const_iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( (*it)->csInstanceDungeon );
|
|
|
|
if( (*it)->vInstanceDungeon.size() <= GameRule::HUNTAHOLIC_MAX_INSTANCE_COUNT_PER_PAGE * ( nPage - 1 ) )
|
|
return 0;
|
|
|
|
size_t nValidCount = 0;
|
|
size_t nTotalCount = 0;
|
|
|
|
for( std::vector< InstanceDungeon * >::const_iterator itInstance = (*it)->vInstanceDungeon.begin() ; itInstance != (*it)->vInstanceDungeon.end() ; ++itInstance )
|
|
{
|
|
// 적정 레벨에 해당되지 않는 방은 카운트도 안 하고 건너 뜀
|
|
if( !(*itInstance)->pInstanceBase->IsProperLevel( nLevel ) )
|
|
continue;
|
|
|
|
// 이미 사냥을 시작한 방은 없는셈 쳐버림(시작 버튼을 누르자마자 목록에서는 사라지는 걸로 처리)
|
|
if( (*itInstance)->tBeginTime )
|
|
continue;
|
|
|
|
// 선택한 페이지 수 만큼의 개수는 건너 뛰기
|
|
if( ++nTotalCount <= GameRule::HUNTAHOLIC_MAX_INSTANCE_COUNT_PER_PAGE * ( nPage - 1 ) )
|
|
continue;
|
|
|
|
assembleInstanceInfo( (*itInstance), pBuffer + nValidCount );
|
|
|
|
if( ++nValidCount >= GameRule::HUNTAHOLIC_MAX_INSTANCE_COUNT_PER_PAGE )
|
|
break;
|
|
}
|
|
|
|
return nValidCount;
|
|
}
|
|
|
|
assert( 0 );
|
|
return 0;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::CreateInstanceDungeon( const int nHuntaholicID, struct StructPlayer * pPlayer, const char * szName, const int nMaxMember, const char * szPassword )
|
|
{
|
|
if( !IsInitialized() )
|
|
return RESULT_ACCESS_DENIED;
|
|
|
|
if( !IsHuntaholicLobby( pPlayer->GetPos() ) )
|
|
return RESULT_ACCESS_DENIED;
|
|
|
|
if( pPlayer->IsInParty() )
|
|
return RESULT_NOT_ACTABLE;
|
|
|
|
if( pPlayer->IsGameTimeLimited() && pPlayer->GetContinuousPlayTime() >= GameRule::nMaxTiredGameTime )
|
|
return RESULT_GAMETIME_LIMITED;
|
|
|
|
// 나쁜 파티 이름
|
|
int code_page = ENV().GetInt( "CodePage", CP_ACP );
|
|
if( !GameRule::IsValidName( code_page, szName, static_cast< int >( strlen( szName ) + 1 ), 1, GameRule::HUNTAHOLIC_MAX_INSTANCE_NAME_LENGTH ) ||
|
|
GameContent::IsBannedWord( code_page, szName ) )
|
|
return RESULT_INVALID_TEXT;
|
|
|
|
// 최대 인원 수가 비정상적인 값인 경우
|
|
if( nMaxMember != 4 && nMaxMember != 6 && nMaxMember != 8 )
|
|
return RESULT_INVALID_ARGUMENT;
|
|
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
HuntaholicInfo * pHuntaholicInfo = (*it);
|
|
|
|
if( pHuntaholicInfo->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( pHuntaholicInfo->csInstanceDungeon );
|
|
|
|
if( pHuntaholicInfo->IsInstanceFull() )
|
|
return RESULT_LIMIT_MAX;
|
|
|
|
const GameContent::HUNTAHOLIC_INSTANCE_BASE * pInstanceBase = NULL;
|
|
for( std::vector< const GameContent::HUNTAHOLIC_INSTANCE_BASE * >::const_iterator itInstance = pHuntaholicInfo->pHuntaholicBase->vInstanceBase.begin() ; itInstance != pHuntaholicInfo->pHuntaholicBase->vInstanceBase.end() ; ++itInstance )
|
|
{
|
|
if( (*itInstance)->IsProperLevel( pPlayer->GetLevel() ) )
|
|
{
|
|
pInstanceBase = (*itInstance);
|
|
break;
|
|
}
|
|
}
|
|
if( !pInstanceBase )
|
|
return RESULT_NOT_ACTABLE;
|
|
|
|
// 헌터홀릭 시스템용 파티 이름 생성(외부의 파티 이름과 중첩되지 않도록 유저가 지정한 이름 뒤에 추가 문자를 붙임)
|
|
std::string strHuntaholicPartyName;
|
|
int nSuffix;
|
|
int nPartyID = 0;
|
|
for( nSuffix = 0 ; nSuffix < GameRule::HUNTAHOLIC_MAX_INSTANCE_COUNT ; ++nSuffix )
|
|
{
|
|
XStringUtil::Format( strHuntaholicPartyName, "%s<%d_h>", szName, nSuffix );
|
|
|
|
nPartyID = PartyManager::GetInstance().MakeParty( strHuntaholicPartyName.c_str(), pPlayer->GetPlayerUID(), PartyManager::TYPE_HUNTAHOLIC_PARTY );
|
|
if( nPartyID > 0 )
|
|
break;
|
|
}
|
|
|
|
if( nSuffix >= GameRule::HUNTAHOLIC_MAX_INSTANCE_COUNT )
|
|
return RESULT_ALREADY_EXIST;
|
|
|
|
if( !PartyManager::GetInstance().JoinParty( nPartyID, pPlayer ) )
|
|
{
|
|
PartyManager::GetInstance().DestroyParty( nPartyID );
|
|
return RESULT_UNKNOWN;
|
|
}
|
|
|
|
// TS_SC_HUNTAHOLIC_BEGIN_HUNTING 메시지에서 사용되는 헌터홀릭 시작 시간
|
|
SendTimeSync( pPlayer );
|
|
SendPartyInfo( pPlayer );
|
|
|
|
LOG::Log11N4S( LM_PARTY_CREATE, pPlayer->GetAccountID(), pPlayer->GetSID(), pPlayer->GetPartyID(), pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), 4, PartyManager::TYPE_HUNTAHOLIC_PARTY, 0, 0, 0,
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, strHuntaholicPartyName.c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
unsigned char nInstanceNo = pHuntaholicInfo->GetNewInstanceNo();
|
|
InstanceDungeon * pInstance = new InstanceDungeon( pHuntaholicInfo, pInstanceBase, nInstanceNo, pPlayer->GetPartyID(), nMaxMember, szPassword );
|
|
|
|
pHuntaholicInfo->stInstanceNo.insert( nInstanceNo );
|
|
pHuntaholicInfo->vInstanceDungeon.push_back( pInstance );
|
|
|
|
pInstance->broadcastInstanceDungeonInfoToMembers();
|
|
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_CREATE, pPlayer->GetAccountID(), pPlayer->GetSID(), nHuntaholicID, pInstance->pInstanceBase->nID, pInstance->nInstanceNo, pInstance->nMaxMemberCount, !!pInstance->szPassword[ 0 ], (*it)->vInstanceDungeon.size(), pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, strHuntaholicPartyName.c_str(), LOG::STR_NTS, szPassword, LOG::STR_NTS );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
assert( 0 );
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::JoinInstanceDungeon( const int nHuntaholicID, struct StructPlayer * pPlayer, const int nInstanceNo, const char * szPassword )
|
|
{
|
|
if( !IsInitialized() )
|
|
return RESULT_ACCESS_DENIED;
|
|
|
|
if( !IsHuntaholicLobby( pPlayer->GetPos() ) )
|
|
return RESULT_ACCESS_DENIED;
|
|
|
|
if( pPlayer->IsInParty() )
|
|
return RESULT_ALREADY_EXIST;
|
|
|
|
if( pPlayer->IsGameTimeLimited() && pPlayer->GetContinuousPlayTime() >= GameRule::nMaxTiredGameTime )
|
|
return RESULT_GAMETIME_LIMITED;
|
|
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( (*it)->csInstanceDungeon );
|
|
|
|
for( std::vector< InstanceDungeon * >::iterator itInstance = (*it)->vInstanceDungeon.begin() ; itInstance != (*it)->vInstanceDungeon.end() ; ++itInstance )
|
|
{
|
|
InstanceDungeon * pInstance = (*itInstance);
|
|
|
|
if( pInstance->nInstanceNo != nInstanceNo )
|
|
continue;
|
|
|
|
// 이미 시작된 방이면 조인 불가(시작 처리가 아닌 시작 버튼을 누른 시점부터 입장 불가)
|
|
if( pInstance->tBeginTime )
|
|
return RESULT_COOL_TIME;
|
|
|
|
THREAD_SYNCHRONIZE( pInstance->csInstance );
|
|
|
|
return pInstance->joinInstanceDungeon( pPlayer, szPassword );
|
|
}
|
|
|
|
// 유저가 가지고 있는 방 목록이 갱신되지 않은 상태에서 방이 없어지면
|
|
// 그 방에 조인을 시도할 경우 여기까지 오게 됨.(정상적인 경우이므로 assert 없음)
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
assert( 0 );
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::LeaveInstanceDungeon( const int nHuntaholicID, struct StructPlayer * pPlayer )
|
|
{
|
|
if( !IsHuntaholicLobby( pPlayer->GetPos() ) )
|
|
return RESULT_ACCESS_DENIED;
|
|
|
|
int nPartyID = pPlayer->GetPartyID();
|
|
if( !nPartyID )
|
|
return RESULT_NOT_EXIST;
|
|
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
HuntaholicInfo * pHuntaholicInfo = (*it);
|
|
|
|
if( pHuntaholicInfo->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( pHuntaholicInfo->csInstanceDungeon );
|
|
|
|
for( std::vector< InstanceDungeon * >::iterator itInstance = pHuntaholicInfo->vInstanceDungeon.begin() ; itInstance != pHuntaholicInfo->vInstanceDungeon.end() ; ++itInstance )
|
|
{
|
|
InstanceDungeon * pInstance = (*itInstance);
|
|
|
|
if( pInstance->nPartyID != nPartyID )
|
|
continue;
|
|
|
|
std::string strPartyName( PartyManager::GetInstance().GetPartyName( pInstance->nPartyID ) );
|
|
|
|
// 사냥 시작 후일 경우 탈퇴 불가(QuitHunting 함수를 통해 사냥을 종료하고 나가야 함)
|
|
if( pInstance->bBegin )
|
|
return RESULT_COOL_TIME;
|
|
|
|
THREAD_SYNCHRONIZE( pInstance->csInstance );
|
|
|
|
// 사냥 시작 후일 경우 탈퇴 불가(락 걸고 한 번 더 검사)
|
|
if( pInstance->bBegin )
|
|
return RESULT_COOL_TIME;
|
|
|
|
unsigned short nErrorCode = pInstance->leaveInstanceDungeon( pPlayer );
|
|
if( nErrorCode != RESULT_SUCCESS )
|
|
return nErrorCode;
|
|
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_LEAVE, pPlayer->GetAccountID(), pPlayer->GetSID(), nHuntaholicID, pInstance->pInstanceBase->nID, pInstance->nInstanceNo, pInstance->nMaxMemberCount, !!pInstance->szPassword[ 0 ], PartyManager::GetInstance().GetMemberCount( pInstance->nPartyID ), pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, strPartyName.c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
// 아무도 남지 않은 경우 방 정보 삭제 대기 처리(아직 시작되지도 않았던 방이므로 몹/힐링프랍 없으므로 그냥 방 정보만 삭제하면 됨)
|
|
if( !PartyManager::GetInstance().GetMemberCount( pInstance->nPartyID ) )
|
|
{
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_DESTROY, pPlayer->GetAccountID(), pPlayer->GetSID(), nHuntaholicID, pInstance->pInstanceBase->nID, pInstance->nInstanceNo, pInstance->nMaxMemberCount, !!pInstance->szPassword[ 0 ], 0, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, strPartyName.c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
pHuntaholicInfo->vInstanceDungeon.erase( itInstance );
|
|
|
|
pHuntaholicInfo->vPendedInstanceDungeonToBeDeleted.push_back( pInstance );
|
|
}
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
// 여기 걸리는 건 방은 예전에 폭파됐는 데 사람은 남았다가 사람이 방에서 나가려고 하는 경우(로비에서 방 가입된 상태로 서버 다운 후 로그인하는 경우)
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
assert( 0 );
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::BeginHunting( const int nHuntaholicID, struct StructPlayer * pPlayer )
|
|
{
|
|
if( !IsHuntaholicLobby( pPlayer->GetPos() ) )
|
|
return RESULT_ACCESS_DENIED;
|
|
|
|
int nPartyID = pPlayer->GetPartyID();
|
|
if( !nPartyID )
|
|
return RESULT_NOT_EXIST;
|
|
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
HuntaholicInfo * pHuntaholicInfo = (*it);
|
|
|
|
if( pHuntaholicInfo->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( pHuntaholicInfo->csInstanceDungeon );
|
|
|
|
for( std::vector< InstanceDungeon * >::iterator itInstance = pHuntaholicInfo->vInstanceDungeon.begin() ; itInstance != pHuntaholicInfo->vInstanceDungeon.end() ; ++itInstance )
|
|
{
|
|
InstanceDungeon * pInstance = (*itInstance);
|
|
|
|
if( pInstance->nPartyID != nPartyID )
|
|
continue;
|
|
|
|
if( !PartyManager::GetInstance().IsLeader( pInstance->nPartyID, pPlayer->GetPlayerUID() ) )
|
|
return RESULT_NOT_OWN;
|
|
|
|
// 이미 시작 버튼이 눌린 던전은 시작 요청 불가
|
|
if( pInstance->tBeginTime )
|
|
return RESULT_ALREADY_EXIST;
|
|
|
|
THREAD_SYNCHRONIZE( pInstance->csInstance );
|
|
|
|
// 이미 시작 버튼이 눌린 던전은 시작 요청 불가
|
|
if( pInstance->tBeginTime )
|
|
return RESULT_ALREADY_EXIST;
|
|
|
|
// 파티원 중 입장 불가 대상이 있는지 체크
|
|
struct AdmissionChecker : public PartyManager::PartyFunctor
|
|
{
|
|
AdmissionChecker()
|
|
: bAllAdmittable( true )
|
|
{}
|
|
|
|
virtual bool operator()( AR_HANDLE handle )
|
|
{
|
|
if( !bAllAdmittable )
|
|
return false;
|
|
|
|
StructPlayer::iterator itPlayer = StructPlayer::get( handle );
|
|
StructPlayer * pPlayer = (*itPlayer);
|
|
|
|
if( !pPlayer || !HuntaholicManager::Instance().IsHuntaholicLobby( pPlayer->GetPos() ) || !pPlayer->GetHuntaholicEnterableCount() )
|
|
{
|
|
bAllAdmittable = false;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool bAllAdmittable;
|
|
} foAdmissionChecker;
|
|
|
|
PartyManager::GetInstance().DoEachMember( pInstance->nPartyID, foAdmissionChecker );
|
|
|
|
if( !foAdmissionChecker.bAllAdmittable )
|
|
return RESULT_NOT_ACTABLE;
|
|
|
|
// 방 시작 방송
|
|
struct BeginCountdownBroadcaster : public PartyManager::PartyFunctor
|
|
{
|
|
virtual bool operator()( AR_HANDLE handle )
|
|
{
|
|
StructPlayer::iterator itPlayer = StructPlayer::get( handle );
|
|
StructPlayer * pPlayer = (*itPlayer);
|
|
|
|
if( !pPlayer )
|
|
return false;
|
|
|
|
pPlayer->AddHuntaholicEnterableCount( -1 );
|
|
|
|
SendTimeSync( pPlayer );
|
|
|
|
PendMessage( pPlayer, &msg );
|
|
|
|
return true;
|
|
}
|
|
|
|
TS_SC_HUNTAHOLIC_BEGIN_COUNTDOWN msg;
|
|
} foBeginCountdownBroadcaster;
|
|
|
|
PartyManager::GetInstance().DoEachMember( pInstance->nPartyID, foBeginCountdownBroadcaster );
|
|
|
|
pInstance->tBeginTime = GetArTime() + GameRule::HUNTAHOLIC_DUNGEON_BEGIN_COUNTDOWN_TIME;
|
|
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_BEGIN_COUNTDOWN, 0, 0, pHuntaholicInfo->pHuntaholicBase->nID, pInstance->pInstanceBase->nID, pInstance->nInstanceNo, pInstance->nMaxMemberCount, !!pInstance->szPassword[ 0 ], PartyManager::GetInstance().GetMemberCount( pInstance->nPartyID ), 0, 0, 0,
|
|
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( pInstance->nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
const unsigned short HuntaholicManager::QuitHunting( const int nHuntaholicID, struct StructPlayer * pPlayer, const bool bNeedToReward, const HuntaholicManager::_HUNTING_RESULT eResult )
|
|
{
|
|
int nPartyID = pPlayer->GetPartyID();
|
|
if( !nPartyID )
|
|
return RESULT_ACCESS_DENIED;
|
|
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
HuntaholicInfo * pHuntaholicInfo = (*it);
|
|
|
|
if( pHuntaholicInfo->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( pHuntaholicInfo->csInstanceDungeon );
|
|
|
|
for( std::vector< InstanceDungeon * >::iterator itInstance = pHuntaholicInfo->vInstanceDungeon.begin() ; itInstance != pHuntaholicInfo->vInstanceDungeon.end() ; /* 루프에서 ++itInstance 처리 */ )
|
|
{
|
|
InstanceDungeon * pInstance = (*itInstance);
|
|
if( pInstance->nPartyID != nPartyID )
|
|
{
|
|
++itInstance;
|
|
continue;
|
|
}
|
|
|
|
std::string strPartyName( PartyManager::GetInstance().GetPartyName( pInstance->nPartyID ) );
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( pInstance->csInstance );
|
|
|
|
// eResult 가 지정되지 않았을 경우(eResult == HUNTING_RESULT_UNKNOWN) 목표 점수에 도달하지 못했으면 실질적으로 retire 처리
|
|
// bNeedToReward == false 여도 죽었으면 fail_by_death, retire 처리(실제로 bNeedToReward == false이면 eResult는 참조되지도 않음 -_ -)
|
|
_HUNTING_RESULT eFinalResult = eResult;
|
|
if( eFinalResult == HUNTING_RESULT_UNKNOWN )
|
|
{
|
|
if( pPlayer->IsDead() )
|
|
eFinalResult = HUNTING_RESULT_FAILED_BY_DEATH;
|
|
else if( pInstance->nScore < pHuntaholicInfo->pHuntaholicBase->nObjectivePoint )
|
|
{
|
|
if( pPlayer->IsGameTimeLimited() && pPlayer->GetContinuousPlayTime() >= GameRule::nMaxTiredGameTime )
|
|
eFinalResult = HUNTING_RESULT_FAILED_BY_GAMETIME_LIMIT;
|
|
else
|
|
eFinalResult = HUNTING_RESULT_RETIRED;
|
|
}
|
|
else if( !bNeedToReward )
|
|
eFinalResult = HUNTING_RESULT_RETIRED;
|
|
else
|
|
eFinalResult = HUNTING_RESULT_SUCCESS;
|
|
}
|
|
|
|
int nSuppliedRewardPoint = 0;
|
|
unsigned short nErrorCode = pInstance->quitHunting( pPlayer, bNeedToReward, eFinalResult, &nSuppliedRewardPoint );
|
|
|
|
if( nErrorCode != RESULT_SUCCESS )
|
|
return nErrorCode;
|
|
|
|
// 로비로 워프 처리
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
|
|
|
|
pPlayer->PendWarp( pHuntaholicInfo->pHuntaholicBase->posLobby.x, pHuntaholicInfo->pHuntaholicBase->posLobby.y, pInstance->pInstanceBase->nID );
|
|
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
|
|
}
|
|
|
|
// 성공해서 보상을 지급받았다면 지급된 보상을 랭킹에 적용
|
|
if( nSuppliedRewardPoint )
|
|
{
|
|
RankingManager::Instance().AddRankingScore( RankingManager::RANKING_TYPE_HUNTAHOLIC_THIS_MONTH, pPlayer->GetPlayerUID(), pPlayer->GetName(), nSuppliedRewardPoint, true );
|
|
RankingManager::Instance().AddRankingScore( RankingManager::RANKING_TYPE_HUNTAHOLIC_TOTAL, pPlayer->GetPlayerUID(), pPlayer->GetName(), nSuppliedRewardPoint, true );
|
|
}
|
|
|
|
// 아무도 남지 않은 경우 방 정보 삭제 대기 처리
|
|
if( !PartyManager::GetInstance().GetMemberCount( nPartyID ) )
|
|
{
|
|
pInstance->endHunting();
|
|
|
|
LOG::Log11N4S( LM_HUNTAHOLIC_DESTROY, pPlayer->GetAccountID(), pPlayer->GetSID(), nHuntaholicID, pInstance->pInstanceBase->nID, pInstance->nInstanceNo, pInstance->nMaxMemberCount, !!pInstance->szPassword[ 0 ], 0, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(),
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, strPartyName.c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
itInstance = pHuntaholicInfo->vInstanceDungeon.erase( itInstance );
|
|
|
|
pHuntaholicInfo->vPendedInstanceDungeonToBeDeleted.push_back( pInstance );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// 로그인 시 헌터홀릭 내에서 파티 중이었을 경우(섭다 등으로 인한 로그아웃 시 처리 안 된 상태) 파티 탈퇴시키고 로비로 이동만이라도 시켜야 함
|
|
if( pPlayer->IsInParty() )
|
|
{
|
|
if( PartyManager::GetInstance().IsLeader( nPartyID, pPlayer->GetPlayerUID() ) )
|
|
{
|
|
LOG::Log11N4S( LM_PARTY_DESTROY, pPlayer->GetAccountID(), pPlayer->GetSID(), nPartyID, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), 10, 0, 0, 0, 0,
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
BroadcastPartyDestroy( nPartyID );
|
|
|
|
PartyManager::GetInstance().DestroyParty( nPartyID );
|
|
}
|
|
else
|
|
{
|
|
BroadcastPartyLeave( pPlayer );
|
|
|
|
LOG::Log11N4S( LM_PARTY_LEAVE, pPlayer->GetAccountID(), pPlayer->GetSID(), nPartyID, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), 4, 0, 0, 0, 0,
|
|
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), LOG::STR_NTS, "", 0 );
|
|
|
|
PartyManager::GetInstance().LeaveParty( nPartyID, pPlayer->GetPlayerUID() );
|
|
}
|
|
|
|
// 파티 탈퇴가 제대로 되었어야 함
|
|
assert( !PartyManager::GetInstance().IsMember( nPartyID, pPlayer->GetPlayerUID() ) );
|
|
}
|
|
|
|
unsigned char nLayer = pHuntaholicInfo->pHuntaholicBase->GetProperLobbyLayer( pPlayer->GetLevel() );
|
|
|
|
{
|
|
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
|
|
|
|
// quitHunting 에서 로비로 빼는 처리가 안된 경우에도 파탈 처리 제대로 안된 경우처럼 워프
|
|
if( IsHuntaholicDungeon( pPlayer->GetPos() ) && !pPlayer->IsPendWarp() )
|
|
{
|
|
if( nLayer != GameRule::HUNTAHOLIC_UNUSABLE_LOBBY_LAYER )
|
|
pPlayer->PendWarp( pHuntaholicInfo->pHuntaholicBase->posLobby.GetX(), pHuntaholicInfo->pHuntaholicBase->posLobby.GetY(), nLayer );
|
|
else
|
|
{
|
|
ArPosition posReturn;
|
|
pPlayer->GetPositionOnEnterInstanceGame( &posReturn );
|
|
pPlayer->PendWarp( posReturn.GetX(), posReturn.GetY(), 0 );
|
|
}
|
|
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
|
|
}
|
|
}
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
assert( 0 );
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
void HuntaholicManager::PendRespawnMonster( const int nHuntaholicID, const AR_UNIT x, const AR_UNIT y, const unsigned char nLayer, const int nMonsterID, const int nRespawnCount, const AR_TIME nLifeTime, const AR_HANDLE hInitialEnemy )
|
|
{
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->pHuntaholicBase->nID != nHuntaholicID )
|
|
continue;
|
|
|
|
// 지역 락을 걸고 호출되는 함수이므로 csInstanceDungeon을 걸면 안됨
|
|
{
|
|
THREAD_SYNCHRONIZE( (*it)->csPendedRespawn );
|
|
|
|
(*it)->vPendedRespawn.push_back( new GameContent::PENDED_RESPAWN_INFO( x, y, nLayer, nMonsterID, nRespawnCount, true, nLifeTime, hInitialEnemy/* , 0*/ ) );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
assert( 0 );
|
|
}
|
|
|
|
void HuntaholicManager::onProcess( int nThreadIdx )
|
|
{
|
|
char buf[255];
|
|
s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx );
|
|
ENV().Set( buf, "HuntaholicManager" );
|
|
|
|
AR_TIME t = GetArTime();
|
|
|
|
s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "HuntaholicManager(0x%08X)", (UINT_PTR)this );
|
|
s_ThreadInfo.last_execute_time = t;
|
|
|
|
if( !IsInitialized() )
|
|
return;
|
|
|
|
for( std::vector< HuntaholicInfo * >::iterator it = m_vHuntaholicInfo.begin() ; it != m_vHuntaholicInfo.end() ; ++it )
|
|
{
|
|
HuntaholicInfo * pHuntaholic = (*it);
|
|
|
|
{
|
|
THREAD_SYNCHRONIZE( pHuntaholic->csInstanceDungeon );
|
|
|
|
// 삭제 대기 중인 인스턴스 던전 삭제 처리
|
|
// * 현재 생성되어 있는 방들을 처리하기 전에 이걸 먼저 처리함으로써
|
|
// 이번 onProcess에서 삭제 대기 상태가 된 인스턴스를 다음 onProcess에서 삭제하도록 여유 시간을 발생시킴.
|
|
for( std::vector< InstanceDungeon * >::const_iterator itInstace = pHuntaholic->vPendedInstanceDungeonToBeDeleted.begin() ; itInstace != pHuntaholic->vPendedInstanceDungeonToBeDeleted.end() ; ++itInstace )
|
|
{
|
|
// 삭제 전에 락을 한 번 걸었다 풀어서 다른 프로세스가 인스턴스 정보를 사용 중일 가능성을 낮춘 후에 삭제
|
|
// 그래봐야 여기서 거는 락 때문에 기다리고 있는 쓰레드가 생기면 말짱 꽝이지만,
|
|
// 인스턴스 정보가 vPendedInstanceDungeonToBeDelete 리스트에 추가되고 여기까지 도달하기 전에 어지간한 것들은 다 종료되기에 충분할 것으로 가정
|
|
{
|
|
THREAD_SYNCHRONIZE( (*itInstace)->csInstance );
|
|
}
|
|
pHuntaholic->stInstanceNo.erase( (*itInstace)->nInstanceNo );
|
|
delete (*itInstace);
|
|
}
|
|
pHuntaholic->vPendedInstanceDungeonToBeDeleted.clear();
|
|
|
|
// 현재 생성되어 있는 인스턴스 던전 처리
|
|
for( std::vector< InstanceDungeon * >::iterator itInstance = pHuntaholic->vInstanceDungeon.begin() ; itInstance != pHuntaholic->vInstanceDungeon.end() ; /* 루프에서 ++itInstance 처리 */ )
|
|
{
|
|
InstanceDungeon * pInstance = (*itInstance);
|
|
|
|
THREAD_SYNCHRONIZE( pInstance->csInstance );
|
|
|
|
// 사냥 시작 전 상태인 방
|
|
if( !pInstance->bBegin )
|
|
{
|
|
// 파티가 정상적으로 유지되어 있는지 확인(일종의 유효성 검사, 정상적이면 LeaveInstanceDungeon에서 방이 삭제처리되었어야 함)
|
|
if( !PartyManager::GetInstance().GetMemberCount( pInstance->nPartyID ) )
|
|
{
|
|
assert( 0 );
|
|
PartyManager::GetInstance().DestroyParty( pInstance->nPartyID );
|
|
|
|
itInstance = pHuntaholic->vInstanceDungeon.erase( itInstance );
|
|
|
|
pHuntaholic->vPendedInstanceDungeonToBeDeleted.push_back( pInstance );
|
|
|
|
continue;
|
|
}
|
|
|
|
// 시작 버튼이 눌린 상태이고 시작 시각을 넘어갔으면 시작(시작 버튼 누르고 10초 후)
|
|
if( pInstance->tBeginTime && t >= pInstance->tBeginTime )
|
|
{
|
|
pInstance->beginHunting();
|
|
}
|
|
}
|
|
// 사냥 중인 방
|
|
else
|
|
{
|
|
// 사냥이 다 끝났거나 사냥 시간이 만료됐거나 아무도 없는 방 제거
|
|
if( pInstance->bEnd || pInstance->tBeginTime + pHuntaholic->pHuntaholicBase->tHuntingPeriod * 100 < t ||
|
|
!PartyManager::GetInstance().GetMemberCount( pInstance->nPartyID ) )
|
|
{
|
|
pInstance->endHunting();
|
|
|
|
itInstance = pHuntaholic->vInstanceDungeon.erase( itInstance );
|
|
|
|
pHuntaholic->vPendedInstanceDungeonToBeDeleted.push_back( pInstance );
|
|
|
|
continue;
|
|
}
|
|
|
|
// 종료 시간 1분 전 미만이고 공지된 적 없는 던전에는 종료 예정 공지
|
|
if( !pInstance->bEndNoticed && pInstance->tBeginTime + pHuntaholic->pHuntaholicBase->tHuntingPeriod * 100 - GameRule::HUNTAHOLIC_DUNGEON_END_NOTICE_TIME < t )
|
|
{
|
|
pInstance->bEndNoticed = true;
|
|
|
|
struct DungeonEndNotifier : PartyManager::PartyFunctor
|
|
{
|
|
virtual bool operator()( AR_HANDLE handle )
|
|
{
|
|
StructPlayer::iterator itPlayer = StructPlayer::get( handle );
|
|
StructPlayer * pPlayer = (*itPlayer);
|
|
|
|
if( !pPlayer )
|
|
return false;
|
|
|
|
PrintfChatMessage( false, CHAT_HUNTAHOLIC_SYSTEM, "@HUNTAHOLIC", pPlayer, "@1111\v#@min@#\v%d", static_cast< int >( GameRule::HUNTAHOLIC_DUNGEON_END_NOTICE_TIME / 6000 ) );
|
|
return true;
|
|
}
|
|
} fo;
|
|
|
|
PartyManager::GetInstance().DoEachMember( pInstance->nPartyID, fo );
|
|
}
|
|
|
|
// 종료랑 관계 없는 방이면 리젠 처리
|
|
pInstance->procMonsterRespawn( t );
|
|
pInstance->procHealingPropRespawn( t );
|
|
}
|
|
|
|
++itInstance;
|
|
}
|
|
}
|
|
|
|
// 리젠 대기 상태인 몬스터를 리젠 처리
|
|
// 위의 처리가 끝나고 하는 이유는 종료될 방은 종료시키고 나서 해야 쓸데없는 몹 리젠/삭제가 반복되지 않기 때문
|
|
// 아예 pHuntaholic->csInstanceDungeon 걸기 전에
|
|
pHuntaholic->ProcPendedMonsterRespawn();
|
|
}
|
|
}
|
|
|
|
void HuntaholicManager::assembleInstanceInfo( const InstanceDungeon * pInstance, TS_HUNTAHOLIC_INSTANCE_INFO * pInfo )
|
|
{
|
|
pInfo->instance_no = pInstance->nInstanceNo;
|
|
s_strcpy( pInfo->name, _countof( pInfo->name ), PartyManager::GetInstance().GetPartyName( pInstance->nPartyID ).c_str() );
|
|
pInfo->current_member_count = static_cast< unsigned char >( PartyManager::GetInstance().GetMemberCount( pInstance->nPartyID ) );
|
|
pInfo->max_member_count = pInstance->nMaxMemberCount;
|
|
pInfo->require_password = *pInstance->szPassword;
|
|
}
|