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

3385 lines
104 KiB
C++

#include <ctime>
#include <set>
#include <queue>
#include <mmo/ArcadiaServer.h>
#include <toolkit/XEnv.h>
#include <mmo/ArRegion.h>
#include <toolkit/XStringUtil.h>
#include <toolkit/XSTLUtil.h>
#include <toolkit/XConsole.h>
#include "LogClient/LogClient.h"
#include "DungeonManager.h"
#include "StructPlayer.h"
#include "StructSummon.h"
#include "SendMessage.h"
#include "GameMessage.h"
#include "ChannelManager.h"
#include "GameProc.h"
#include "NPCProc.h"
#include "GuildManager.h"
#include "PartyManager.h"
#include "RoamingManager.h"
#include "DB_Commands.h"
#include "FieldPropManager.h"
#include "TimeUtil.h"
static const int DUNGEON_SIEGE_NOTICE_START_TIME = 60 * 60 + 30; // 60분 30초 전부터 공지 시작 (오차 생겨도 60분부터 공지 나가도록)
static const int DUNGEON_SIEGE_NOTICE_TIME[] = { 60 * 60 + 30, 60 * 30 + 30, 60 * 10 + 30 }; // 던전 시즈 공지 시간 (60분, 30분, 10분에 공지)
static const int DUNGEON_SIEGE_GUILD_NOTICE_TIME = 60 * 5 + 30; // 5분 30초 전에 양쪽 길드에 5분후 입장 가능 공지
static const int DUNGEON_SIEGE_KICK_TIME = 60; // 1분 후에 쫓겨남
static const int DUNGEON_RAID_TIME_OUT = 3600 * 4; // 4시간 후에 종료
static const int DUNGEON_RAID_NOTICE_PERIOD = 60 * 5;
static const int WEEK_TIME_IN_SECOND = 3600 * 24 * 7;
static const int GUARDIAN_RANGE = 60 * GameRule::DEFAULT_UNIT_SIZE;
static const int DUNGEON_BOSS_COUNT = 2; // 던전당 보스는 두 마리~
struct _DUNGEON_RAID_INFO : StructMonster::MonsterDeleteHandler
{
_DUNGEON_RAID_INFO( int _guild_id, int _raid_record, time_t raid_end_time, struct _DUNGEON_INFO * _pDungeonInfo ) : raid_starting( false ), raid_complete( false ), guild_id ( _guild_id ), start_time( 0 ), end_time( raid_end_time ), last_notice_time( 0 ), raid_record( _raid_record ), layer( 0 ), pDungeonInfo( _pDungeonInfo )
{
for( int i = 0; i < DUNGEON_BOSS_COUNT; ++i )
{
boss_dead[i] = false;
}
}
int guild_id;
AR_TIME start_time;
time_t end_time;
time_t last_notice_time;
AR_TIME raid_record;
unsigned char layer;
bool boss_dead[DUNGEON_BOSS_COUNT];
bool raid_starting;
bool raid_complete;
std::vector< struct StructMonster * > vMonsters;
struct _DUNGEON_INFO * pDungeonInfo;
protected:
virtual void onMonsterDelete( struct StructMonster * pMonster );
};
struct _TACTICAL_POSITION_INFO
{
_TACTICAL_POSITION_INFO()
{
id = 0;
own_by_original_owner = true;
prop_id = 0;
}
int id;
ArPosition pos;
bool own_by_original_owner;
int prop_id;
};
typedef std::pair< int /* RoamingID */, GameContent::ROAMING_CREATURE_RESPAWN_INFO > ROAMING_MONSTER_RESPAWN_INFO;
struct _DUNGEON_INFO : StructMonster::MonsterDeleteHandler
{
_DUNGEON_INFO()
{
id = 0;
type = DungeonManager::DUNGEON_TYPE_UNKNOWN;
level = 0;
core_id = 0;
start_time = 0;
end_time = 0;
raid_start_time = 0;
raid_end_time = 0;
core_offset_z = 0.0f;
core_around_x = 0.0f;
core_around_y = 0.0f;
core_around_z = 0.0f;
core_scale_x = 0.0f;
core_scale_y = 0.0f;
core_scale_z = 0.0f;
core_is_lock_height = false;
core_lock_height = 0.0f;
max_guild_party = 0;
max_raid_party = 0;
connector_id = 0;
prev_owner_guild_id = 0;
owner_guild_id = 0;
raid_guild_id = 0;
best_raid_time = 0;
original_owner_guild_id = 0;
last_dungeon_siege_finish_time = 0;
last_dungeon_raid_wrap_up_time = 0;
last_global_notice_count = 0;
pDungeonCore = NULL;
pConnector = NULL;
bDungeonSiege = false;
bDungeonSiegeCreated = false;
bDungeonSiegeKicked = true;
bDungeonSiegeNeedToDestroy = false;
bNeedToChangePosition = false;
bDungeonOwnerChanged = false;
tax_rate = 0;
}
// m_CS를 제외한 복사 생성자(XCriticalSection의 복사 생성자를 정의하면 안 이래도 되긴 한다 -_ -)
_DUNGEON_INFO( const _DUNGEON_INFO & rhs )
: id( rhs.id )
, level( rhs.level )
, type( rhs.type )
, raid_start_pos( rhs.raid_start_pos )
, siege_start_pos( rhs.siege_start_pos )
, siege_defence_pos( rhs.siege_defence_pos )
, core_pos( rhs.core_pos )
, connector_pos( rhs.connector_pos )
, core_id( rhs.core_id )
, start_time( rhs.start_time )
, end_time( rhs.end_time )
, raid_start_time( rhs.raid_start_time )
, raid_end_time( rhs.raid_end_time )
, core_offset_z( rhs.core_offset_z )
, core_around_x( rhs.core_around_x )
, core_around_y( rhs.core_around_y )
, core_around_z( rhs.core_around_z )
, core_scale_x( rhs.core_scale_x )
, core_scale_y( rhs.core_scale_y )
, core_scale_z( rhs.core_scale_z )
, core_is_lock_height( rhs.core_is_lock_height )
, core_lock_height( rhs.core_lock_height )
, box( rhs.box )
, connector_id( rhs.connector_id )
, prev_owner_guild_id( rhs.prev_owner_guild_id )
, owner_guild_id( rhs.owner_guild_id )
, raid_guild_id( rhs.raid_guild_id )
, best_raid_time( rhs.best_raid_time )
, original_owner_guild_id( rhs.original_owner_guild_id )
, last_global_notice_count( rhs.last_global_notice_count )
, last_dungeon_siege_finish_time( rhs.last_dungeon_siege_finish_time )
, last_dungeon_raid_wrap_up_time( rhs.last_dungeon_raid_wrap_up_time )
, bDungeonSiege( rhs.bDungeonSiege )
, bDungeonSiegeCreated( rhs.bDungeonSiegeCreated )
, bDungeonSiegeKicked( rhs.bDungeonSiegeKicked )
, bDungeonSiegeNeedToDestroy( rhs.bDungeonSiegeNeedToDestroy )
, bNeedToChangePosition( rhs.bNeedToChangePosition )
, bDungeonOwnerChanged( rhs.bDungeonOwnerChanged )
, max_guild_party( rhs.max_guild_party )
, max_raid_party( rhs.max_raid_party )
, tax_rate( rhs.tax_rate )
, pDungeonCore( rhs.pDungeonCore )
#ifndef _DUNGEON_CORE_AS_MONSTER
, stCoreGuardianHandle( rhs.stCoreGuardianHandle )
#endif
, pConnector( rhs.pConnector )
, vGuardianRespawnInfo( rhs.vGuardianRespawnInfo )
, vEnvironmentalGuardianRespawnInfo( rhs.vEnvironmentalGuardianRespawnInfo )
, vRespawnedEnvironmentalGuardianInfo( rhs.vRespawnedEnvironmentalGuardianInfo )
, qPendedGuardianRespawn( rhs.qPendedGuardianRespawn )
, vRespawnInfo( rhs.vRespawnInfo )
, vRoamerID( rhs.vRoamerID )
, vRaidInfo( rhs.vRaidInfo )
, vMonsters( rhs.vMonsters )
, vRandomRespawnInfo( rhs.vRandomRespawnInfo )
, vTacticalPositionInfo( rhs.vTacticalPositionInfo )
, vNotices( rhs.vNotices )
{
s_memcpy( boss_id, sizeof( boss_id ), rhs.boss_id, sizeof( rhs.boss_id ) );
}
void procRespawnEnvironmentalGuardian();
int id;
int level;
int type;
ArPosition raid_start_pos;
ArPosition siege_start_pos;
ArPosition siege_defence_pos;
ArPosition core_pos;
ArPosition connector_pos;
int core_id;
int start_time;
int end_time;
int raid_start_time;
int raid_end_time;
float core_offset_z;
float core_around_x;
float core_around_y;
float core_around_z;
float core_scale_x;
float core_scale_y;
float core_scale_z;
bool core_is_lock_height;
float core_lock_height;
int boss_id[DUNGEON_BOSS_COUNT];
X2D::Box< AR_UNIT > box;
int connector_id;
int prev_owner_guild_id;
int owner_guild_id;
int raid_guild_id;
AR_TIME best_raid_time;
int original_owner_guild_id;
int last_global_notice_count;
time_t last_dungeon_siege_finish_time;
time_t last_dungeon_raid_wrap_up_time;
bool bDungeonSiege;
bool bDungeonSiegeCreated;
bool bDungeonSiegeKicked;
bool bDungeonSiegeNeedToDestroy;
bool bNeedToChangePosition;
bool bDungeonOwnerChanged;
int max_guild_party;
int max_raid_party;
int tax_rate;
#ifndef _DUNGEON_CORE_AS_MONSTER
StructFieldProp * pDungeonCore;
#else
StructMonster * pDungeonCore;
std::set< AR_HANDLE > stCoreGuardianHandle;
#endif
StructMonster * pConnector;
// 지역 락 -> _DUNGEON_INFO::m_CS -> GuildManager::m_IntfLock 순서여야 함.
mutable XCriticalSection m_CS;
struct RESPAWNED_ENVIRONMENTAL_GUARDIAN_INFO
{
RESPAWNED_ENVIRONMENTAL_GUARDIAN_INFO( StructMonster * _pMonster, GameContent::MONSTER_RESPAWN_INFO * _pRespawnInfo )
: pMonster( _pMonster )
, pRespawnInfo( _pRespawnInfo )
{
nNextRespawnTime = ( pMonster ) ? 0 : GetArTime();
}
StructMonster * pMonster;
AR_TIME nNextRespawnTime;
GameContent::MONSTER_RESPAWN_INFO * pRespawnInfo;
};
std::vector< GameContent::MONSTER_RESPAWN_INFO > vGuardianRespawnInfo;
std::vector< GameContent::MONSTER_RESPAWN_INFO > vEnvironmentalGuardianRespawnInfo;
std::vector< RESPAWNED_ENVIRONMENTAL_GUARDIAN_INFO > vRespawnedEnvironmentalGuardianInfo;
std::queue< int > qPendedGuardianRespawn;
std::vector< GameContent::MONSTER_RESPAWN_INFO > vRespawnInfo;
std::vector< int > vRoamerID;
std::vector< _DUNGEON_RAID_INFO * > vRaidInfo;
std::vector< struct StructMonster * > vMonsters; // guardian
std::vector< GameContent::RANDOM_MONSTER_RESPAWN_INFO > vRandomRespawnInfo;
std::vector< _TACTICAL_POSITION_INFO > vTacticalPositionInfo;
std::vector< std::string > vNotices;
protected:
virtual void onMonsterDelete( struct StructMonster * pMonster );
};
void _DUNGEON_RAID_INFO::onMonsterDelete( struct StructMonster * pMonster )
{
bool bChecked = false;
bool bEndDungeonRaid = true;
for( int i = 0; i < DUNGEON_BOSS_COUNT; ++i )
{
if( pDungeonInfo->boss_id[i] && !boss_dead[i] )
{
if( !bChecked && pDungeonInfo->boss_id[i] == pMonster->GetMonsterId() )
{
boss_dead[i] = true;
bChecked = true;
}
else
{
bEndDungeonRaid = false;
}
}
}
{
THREAD_SYNCRONIZE( pDungeonInfo->m_CS );
for( std::vector< struct StructMonster * >::iterator it = vMonsters.begin(); it != vMonsters.end(); ++it )
{
if( (*it) == pMonster )
{
vMonsters.erase( it );
break;
}
}
if( bEndDungeonRaid )
{
raid_complete = true;
}
}
}
void _DUNGEON_INFO::onMonsterDelete( struct StructMonster * pMonster )
{
THREAD_SYNCRONIZE( m_CS );
bool bEndDungeonSiege = false;
if( pConnector == pMonster )
{
bEndDungeonSiege = true;
pConnector = NULL;
}
else
{
#ifdef _DUNGEON_CORE_AS_MONSTER
if( pDungeonCore == pMonster )
{
pDungeonCore = NULL;
// 던전 코어의 주인 변경을 세팅하여 공수 교대 발생
DungeonManager::Instance().ChangeOwner( id, 0 );
}
// 코어 가디언 핸들 리스트에서 제거(코어 가디언이 아니었으면 삭제되는 것 없음)
else
{
stCoreGuardianHandle.erase( pMonster->GetHandle() );
}
#endif
for( std::vector< struct StructMonster * >::iterator it = vMonsters.begin(); it != vMonsters.end(); ++it )
{
if( (*it) == pMonster )
{
vMonsters.erase( it );
break;
}
}
for( std::vector< RESPAWNED_ENVIRONMENTAL_GUARDIAN_INFO >::iterator it = vRespawnedEnvironmentalGuardianInfo.begin() ; it != vRespawnedEnvironmentalGuardianInfo.end() ; ++it )
{
if( (*it).pMonster != pMonster )
continue;
(*it).nNextRespawnTime = GetArTime() + (*it).pRespawnInfo->interval;
(*it).pMonster = NULL;
break;
}
}
if( bEndDungeonSiege )
bDungeonSiegeNeedToDestroy = true;
}
void _DUNGEON_INFO::procRespawnEnvironmentalGuardian()
{
AR_TIME tCurrent = GetArTime();
for( std::vector< RESPAWNED_ENVIRONMENTAL_GUARDIAN_INFO >::iterator it = vRespawnedEnvironmentalGuardianInfo.begin() ; it != vRespawnedEnvironmentalGuardianInfo.end() ; /* 루프에서 ++it 처리 */ )
{
if( (*it).pMonster || (*it).nNextRespawnTime > tCurrent )
{
++it;
continue;
}
AR_UNIT x = (*it).pRespawnInfo->left;
AR_UNIT y = (*it).pRespawnInfo->top;
if( GameContent::IsBlocked( x, y ) )
{
_cprint( "Environmental guardian monster(%d) could not be respawned at (%f,%f) in a dungeon(%d).\n", (*it).pRespawnInfo->monster_id, x, y, id );
it = vRespawnedEnvironmentalGuardianInfo.erase( it );
continue;
}
StructMonster * pMob = respawnMonster( x, y, DungeonManager::DUNGEON_SIEGE_LAYER, (*it).pRespawnInfo->monster_id, (*it).pRespawnInfo->is_wandering, (*it).pRespawnInfo->way_point_id, this, false );
if( !pMob )
{
_cprint( "Environmental guardian monster(%d) could not be respawned in a dungeon(%d).\n", (*it).pRespawnInfo->monster_id, id );
it = vRespawnedEnvironmentalGuardianInfo.erase( it );
continue;
}
bool bOwnerGuardian = false;
int nTacticalPositionID = (*it).pRespawnInfo->id;
if( nTacticalPositionID == 0 )
{
if( original_owner_guild_id == owner_guild_id )
{
bOwnerGuardian = true;
}
}
else
{
for( std::vector< _TACTICAL_POSITION_INFO >::iterator itTac = vTacticalPositionInfo.begin(); itTac != vTacticalPositionInfo.end(); ++itTac )
{
if( (*itTac).id == nTacticalPositionID )
{
if( (*itTac).own_by_original_owner )
{
bOwnerGuardian = true;
}
break;
}
}
}
if( bOwnerGuardian )
{
pMob->SetDungeonOwnerGuardian();
}
else
{
pMob->SetDungeonSiegerGuardian();
}
vMonsters.push_back( pMob );
(*it).nNextRespawnTime = 0;
(*it).pMonster = pMob;
++it;
}
}
time_t DungeonManager::GetDungeonRaidStartTime( int start_time )
{
return GetWeekBeginTime() + start_time;
}
time_t DungeonManager::GetDungeonRaidEndTime( int end_time )
{
return GetWeekBeginTime() + end_time;
}
time_t DungeonManager::GetNextDungeonSiegeStartTime( int start_time, time_t last_dungeon_siege_finish_time )
{
time_t dungeon_siege_start_time = GetWeekBeginTime() + start_time;
while( dungeon_siege_start_time <= last_dungeon_siege_finish_time )
dungeon_siege_start_time += WEEK_TIME_IN_SECOND;
return dungeon_siege_start_time;
}
time_t DungeonManager::GetNextDungeonSiegeEndTime( int end_time, time_t dungeon_siege_start_time )
{
time_t dungeon_siege_end_time = GetWeekBeginTime() + end_time;
while( dungeon_siege_end_time <= dungeon_siege_start_time )
dungeon_siege_end_time += WEEK_TIME_IN_SECOND;
return dungeon_siege_end_time;
}
void DungeonManager::RegisterDungeonInfo( int id, int level, int type, AR_UNIT raid_start_x, AR_UNIT raid_start_y, AR_UNIT siege_start_x, AR_UNIT siege_start_y, AR_UNIT siege_defence_x, AR_UNIT siege_defence_y, int connector_id, AR_UNIT connector_x, AR_UNIT connector_y, int core_id, AR_UNIT core_x, AR_UNIT core_y, float core_offset_z, float core_around_x, float core_around_y, float core_around_z, float scale_x, float scale_y, float scale_z, bool core_is_lock_height, float core_lock_height, int boss01_id, int boss02_id, int raid_start_time, int raid_end_time, int start_time, int end_time, X2D::Box< AR_UNIT > box, int owner_guild_id, int raid_guild_id, int best_raid_time, int last_dungeon_siege_finish_time, int last_dungeon_raid_wrap_up_time, int tax_rate, int max_guild_party, int max_raid_party )
{
_DUNGEON_INFO info;
info.id = id;
info.level = level;
info.type = type;
info.raid_start_pos = ArPosition( raid_start_x, raid_start_y );
info.siege_start_pos = ArPosition( siege_start_x, siege_start_y );
info.siege_defence_pos = ArPosition( siege_defence_x, siege_defence_y );
info.connector_id = connector_id;
info.connector_pos = ArPosition( connector_x, connector_y );
info.core_id = core_id;
info.core_pos = ArPosition( core_x, core_y );
info.raid_start_time = raid_start_time;
info.raid_end_time = raid_end_time;
info.core_offset_z = core_offset_z;
info.core_around_x = core_around_x;
info.core_around_y = core_around_y;
info.core_around_z = core_around_z;
info.core_scale_x = scale_x;
info.core_scale_y = scale_y;
info.core_scale_z = scale_z;
info.core_is_lock_height = core_is_lock_height;
info.core_lock_height = core_lock_height;
info.boss_id[0] = boss01_id;
info.boss_id[1] = boss02_id;
info.start_time = start_time;
info.end_time = end_time;
info.box = box;
info.owner_guild_id = owner_guild_id;
info.raid_guild_id = raid_guild_id;
info.best_raid_time = best_raid_time;
info.original_owner_guild_id = owner_guild_id;
info.last_dungeon_siege_finish_time = last_dungeon_siege_finish_time;
info.last_dungeon_raid_wrap_up_time = last_dungeon_raid_wrap_up_time;
info.tax_rate = tax_rate;
info.max_guild_party = max_guild_party;
info.max_raid_party = max_raid_party;
m_vDungeonInfo.push_back( info );
}
void DungeonManager::LoadAllRaidInfo()
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
// 시즈 타입의 던전만 레이드 정보 로딩
if( (*it).type == DUNGEON_TYPE_SIEGE )
LoadRaidInfo( (*it).id, GetDungeonRaidStartTime( (*it).raid_start_time ) );
}
}
void DungeonManager::OnEnterDungeon( const int nDungeonID, struct StructPlayer *pPlayer, const unsigned char nLayer )
{
// AddState를 동반하므로 지역 락이 필요하다.
if( nLayer ) return;
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID ) return;
for( std::vector< _DUNGEON_INFO >::const_iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( GuildManager::GetInstance().GetAllianceID( nGuildID ) )
{
nGuildID = GuildManager::GetInstance().GetAllianceLeaderGuildID( GuildManager::GetInstance().GetAllianceID( nGuildID ) );
}
if( nGuildID == (*it).original_owner_guild_id )
pPlayer->AddDungeonState();
return;
}
}
}
void DungeonManager::OnExitDungeon( const int nDungeonID, struct StructPlayer *pPlayer, int nGuildID, const unsigned char nLayer )
{
// RemoveState를 동반하므로 지역 락이 필요하다.
if( nLayer ) return;
if( !nGuildID ) return;
for( std::vector< _DUNGEON_INFO >::const_iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( GuildManager::GetInstance().GetAllianceID( nGuildID ) )
{
nGuildID = GuildManager::GetInstance().GetAllianceLeaderGuildID( GuildManager::GetInstance().GetAllianceID( nGuildID ) );
}
// 퇴장 이전에 던전 소유 길드가 변경되었으나 그에 따른 처리는 비동기로 동작하므로 아직 수행되지 않았을 수도 있다.
// 따라서 소유권의 변동이 있었지만 그에 따른 처리가 발생하지 않았다면, 현재 던전 소유 길드는 아니지만 직전 던전 소유 길드인지를 확인하여 필요에 따라 퇴장 처리도 해주어야 한다.
if( nGuildID == (*it).original_owner_guild_id ||
( (*it).bDungeonOwnerChanged && nGuildID == (*it).prev_owner_guild_id ) )
pPlayer->RemoveDungeonState();
break;
}
}
}
void DungeonManager::RegisterTacticalPositionInfo( int dungeon_id, int id, AR_UNIT x, AR_UNIT y, int prop_id )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == dungeon_id )
{
for( std::vector< _TACTICAL_POSITION_INFO >::iterator itTac = (*it).vTacticalPositionInfo.begin(); itTac != (*it).vTacticalPositionInfo.end(); ++itTac )
{
if( (*itTac).id == id )
{
(*itTac).pos = ArPosition( x, y );
(*itTac).prop_id = prop_id;
return;
}
}
_TACTICAL_POSITION_INFO info;
info.id = id;
info.pos = ArPosition( x, y );
info.prop_id = prop_id;
(*it).vTacticalPositionInfo.push_back( info );
break;
}
}
}
void DungeonManager::RegisterDungeonRaidInfo( int dungeon_id, int guild_id, AR_TIME record, time_t raid_end_time )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == dungeon_id )
{
(*it).vRaidInfo.push_back( new _DUNGEON_RAID_INFO( guild_id, record, raid_end_time, &(*it) ) );
}
}
}
void DungeonManager::RegisterDungeonMonsterRespawnInfo( int nDungeonID, const struct GameContent::MONSTER_RESPAWN_INFO & info )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
(*it).vRespawnInfo.push_back( info );
return;
}
}
}
void DungeonManager::RegisterDungeonGuardianRespawnInfo( int nDungeonID, const GameContent::MONSTER_RESPAWN_INFO & info )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
(*it).vGuardianRespawnInfo.push_back( info );
return;
}
}
}
void DungeonManager::RegisterDungeonEnvironmentalGuardianRespawnInfo( int nDungeonID, const GameContent::MONSTER_RESPAWN_INFO & info )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
(*it).vEnvironmentalGuardianRespawnInfo.push_back( info );
return;
}
}
}
void DungeonManager::RegisterDungeonRoamerInfo( int nDungeonID, const int nRoamingID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
(*it).vRoamerID.push_back( nRoamingID );
return;
}
}
}
void DungeonManager::RegisterRandomDungeonMonsterRespawnInfo( int nDungeonID, const struct GameContent::RANDOM_MONSTER_RESPAWN_INFO & info )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
(*it).vRandomRespawnInfo.push_back( info );
return;
}
}
}
bool DungeonManager::Init()
{
ArcadiaServer::Instance().SetObjectPriority( this, ArObject::UPDATE_PRIORITY_HIGH );
return true;
}
bool DungeonManager::DeInit()
{
ArcadiaServer::Instance().SetObjectPriority( this, ArObject::UPDATE_PRIORITY_IDLE );
return true;
}
DungeonManager & DungeonManager::Instance()
{
static DungeonManager _inst;
return _inst;
}
ArPosition DungeonManager::GetRaidStartPosition( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).raid_start_pos;
}
}
assert( 0 );
return ArPosition();
}
ArPosition DungeonManager::GetSiegeStartPosition( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).siege_start_pos;
}
}
assert( 0 );
return ArPosition();
}
ArPosition DungeonManager::GetSiegeDefencePosition( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).siege_defence_pos;
}
}
assert( 0 );
return ArPosition();
}
void DungeonManager::wrapUpDungeonRaid( _DUNGEON_INFO * pDungeonInfo )
{
pDungeonInfo->raid_guild_id = 0;
pDungeonInfo->best_raid_time = AR_TIME( -1 );
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = pDungeonInfo->vRaidInfo.begin(); itRaidInfo != pDungeonInfo->vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->layer )
return;
}
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = pDungeonInfo->vRaidInfo.begin(); itRaidInfo != pDungeonInfo->vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->raid_record )
{
if( pDungeonInfo->best_raid_time > (*itRaidInfo)->raid_record )
{
pDungeonInfo->best_raid_time = (*itRaidInfo)->raid_record;
pDungeonInfo->raid_guild_id = (*itRaidInfo)->guild_id;
}
(*itRaidInfo)->raid_record = 0;
// 이번 주 기록 중에 성공 기록이 있다면 여긴 무조건 실패이므로 굳이 기록할 필요가 없음
// 성공 기록이 없다면 실패를 한 시점에 대한 정보를 남기는 겸 기록해 줌
AR_TIME tRecord = getDungeonRaidRecord( pDungeonInfo, (*itRaidInfo)->guild_id );
if( tRecord <= 0 )
DB().Push( new DB_UpdateDungeonRaidTime( pDungeonInfo->id, (*itRaidInfo)->guild_id, time( NULL ), (*itRaidInfo)->raid_record ) );
}
}
struct _myGuildListFunctor : public GuildManager::GuildListFunctor
{
_myGuildListFunctor( int _dungeon_id, int _own_guild_id, int _raid_guild_id ) : m_dungeon_id( _dungeon_id ), m_dungeon_own_guild_id( _own_guild_id ), m_dungeon_raid_guild_id( _raid_guild_id )
{
}
virtual bool operator()( int nGuildID, const char * szGuildName, const char * szGuildLeaderName, int nGuildLeaderLevel, int nGuildMemberCount, int nRaidDungeonID )
{
if( m_dungeon_id == nRaidDungeonID )
{
if( nGuildID != m_dungeon_own_guild_id && nGuildID != m_dungeon_raid_guild_id )
GuildManager::GetInstance().SetRaidDungeonID( nGuildID, 0 );
int nLeadPartyID = PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( nGuildID );
if( nLeadPartyID )
{
BroadcastPartyDestroy( nLeadPartyID );
LOG::Log11N4S( LM_PARTY_DESTROY, 0, 0, nLeadPartyID, 0, 0, 0, 7, 0, 0, 0, 0,
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nLeadPartyID ).c_str(), LOG::STR_NTS, "", 0 );
PartyManager::GetInstance().DestroyParty( nLeadPartyID );
}
}
return true;
}
int m_dungeon_id;
int m_dungeon_own_guild_id;
int m_dungeon_raid_guild_id;
} _fo( pDungeonInfo->id, pDungeonInfo->owner_guild_id, pDungeonInfo->raid_guild_id );
GuildManager::GetInstance().DoEachGuild( _fo );
pDungeonInfo->last_dungeon_raid_wrap_up_time = time( NULL );
DB().Push( new DB_UpdateDungeon( pDungeonInfo->id, pDungeonInfo->owner_guild_id, pDungeonInfo->raid_guild_id, pDungeonInfo->best_raid_time, pDungeonInfo->last_dungeon_siege_finish_time, pDungeonInfo->last_dungeon_raid_wrap_up_time, pDungeonInfo->tax_rate ) );
}
void DungeonManager::wrapUpDungeonSiege( _DUNGEON_INFO *pDungeonInfo )
{
// endDungeonSiege 함수에서 각 시즈공대 박살내주긴 하지만 end와 wrapUp 사이에 공대가 다시 만들어질 수도 있으므로 다시 박살내자
if( pDungeonInfo->owner_guild_id )
{
int nLeadPartyID = PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( pDungeonInfo->owner_guild_id );
if( nLeadPartyID )
{
BroadcastPartyDestroy( nLeadPartyID );
LOG::Log11N4S( LM_PARTY_DESTROY, 0, 0, nLeadPartyID, 0, 0, 0, 8, 0, 0, 0, 0,
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nLeadPartyID ).c_str(), LOG::STR_NTS, "", 0 );
PartyManager::GetInstance().DestroyParty( nLeadPartyID );
}
}
if( pDungeonInfo->raid_guild_id )
{
int nLeadPartyID = PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( pDungeonInfo->raid_guild_id );
if( nLeadPartyID )
{
BroadcastPartyDestroy( nLeadPartyID );
LOG::Log11N4S( LM_PARTY_DESTROY, 0, 0, nLeadPartyID, 0, 0, 0, 8, 0, 0, 0, 0,
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nLeadPartyID ).c_str(), LOG::STR_NTS, "", 0 );
PartyManager::GetInstance().DestroyParty( nLeadPartyID );
}
}
// 던전 시즈가 종료될 때 DB와 메모리상의 던전 레이드 기록을 모두 제거
// _DUNGEON_INFO 의 m_CS 락은 wrapUpDungeonRaid 함수 호출측(DungeonManager::onProcess)에서 걸었음
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = pDungeonInfo->vRaidInfo.begin(); itRaidInfo != pDungeonInfo->vRaidInfo.end(); ++itRaidInfo )
{
delete (*itRaidInfo);
}
pDungeonInfo->vRaidInfo.clear();
DB().Push( new DB_ClearDungeonRaidRecord( pDungeonInfo->id, 0 ) );
}
void DungeonManager::onChangeDungeonOwner( _DUNGEON_INFO * pDungeonInfo, const int nPrevOwnerID, const int nOwnerID )
{
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const int _nPrevOwnerID, const int _nOwnerID ) : nPrevOwnerID( _nPrevOwnerID ), nOwnerID( _nOwnerID ) {}
void operator()( ArObject *pObj ) const
{
StructPlayer *pPlayer = static_cast< StructPlayer * >( pObj );
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID ) return;
if( GuildManager::GetInstance().GetAllianceID( nGuildID ) )
{
nGuildID = GuildManager::GetInstance().GetAllianceLeaderGuildID( GuildManager::GetInstance().GetAllianceID( nGuildID ) );
}
if( nPrevOwnerID && nGuildID == nPrevOwnerID )
{
pPlayer->RemoveDungeonState();
}
else if( nOwnerID && nGuildID == nOwnerID )
{
pPlayer->AddDungeonState();
}
}
int nPrevOwnerID;
int nOwnerID;
} _sender( nPrevOwnerID, nOwnerID );
ForEachClientInDungeon( pDungeonInfo, DUNGEON_LAYER, _sender );
}
bool DungeonManager::beginDungeonRaid( _DUNGEON_RAID_INFO * pRaidInfo )
{
_DUNGEON_INFO * pDungeonInfo = pRaidInfo->pDungeonInfo;
if( !pRaidInfo->raid_starting )
return false;
struct _myPartyFuctor : PartyManager::PartyFunctor
{
_myPartyFuctor( const struct _DUNGEON_RAID_INFO * _pRaidInfo ) : pRaidInfo( _pRaidInfo ) {}
virtual bool operator()( AR_HANDLE handle )
{
StructPlayer::iterator it = StructPlayer::get( handle );
if ( !(*it)->IsPlayer() || !(*it)->IsLogin() || !(*it)->IsInWorld() || DungeonManager::Instance().IsRestrictedToEnter( pRaidInfo->pDungeonInfo->level, (*it)->GetLevel() ) )
return false;
(*it)->PendWarp( pRaidInfo->pDungeonInfo->raid_start_pos.x, pRaidInfo->pDungeonInfo->raid_start_pos.y, pRaidInfo->layer );
ArcadiaServer::Instance().SetObjectPriority( (*it), ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
return true;
};
const _DUNGEON_RAID_INFO * pRaidInfo;
} _fo( pRaidInfo );
PartyManager::GetInstance().DoEachAttackTeamMemberByGuildId( pRaidInfo->guild_id, _fo );
for( std::vector< GameContent::MONSTER_RESPAWN_INFO >::iterator itRespawn = pDungeonInfo->vRespawnInfo.begin(); itRespawn != pDungeonInfo->vRespawnInfo.end(); ++itRespawn )
{
for( unsigned int i = 0; i < (*itRespawn).max_num; ++i ) // always max~
{
AR_UNIT x, y;
unsigned try_cnt = 0;
bool bValidPos = true;
do
{
x = XRandom( (*itRespawn).left, (*itRespawn).right );
y = XRandom( (*itRespawn).top, (*itRespawn).bottom );
if( ++try_cnt > 300 )
{
bValidPos = false;
break;
}
} while( GameContent::IsBlocked( x, y ) );
if( bValidPos )
{
StructMonster * pMob = respawnMonster( x, y, pRaidInfo->layer, (*itRespawn).monster_id, (*itRespawn).is_wandering, (*itRespawn).way_point_id, pRaidInfo, false );
if( pMob )
{
pMob->SetDungeonRaidMonster();
pMob->CalculateStat();
pRaidInfo->vMonsters.push_back( pMob );
}
}
}
}
for( std::vector< GameContent::MONSTER_RESPAWN_INFO >::iterator itRespawn = pDungeonInfo->vGuardianRespawnInfo.begin(); itRespawn != pDungeonInfo->vGuardianRespawnInfo.end(); ++itRespawn )
{
for( unsigned int i = 0; i < (*itRespawn).max_num; ++i ) // always max~
{
AR_UNIT x = (*itRespawn).left;
AR_UNIT y = (*itRespawn).top;
if( GameContent::IsBlocked( x, y ) )
{
_cprint( "Guardian monster(%d) could not be respawned at (%f,%f) in a dungeon(%d).\n", (*itRespawn).monster_id, x, y, pDungeonInfo->id );
continue;
}
StructMonster * pMob = respawnMonster( x, y, pRaidInfo->layer, (*itRespawn).monster_id, (*itRespawn).is_wandering, (*itRespawn).way_point_id, pRaidInfo, false );
if( pMob )
{
pMob->SetDungeonRaidMonster();
pMob->CalculateStat();
pRaidInfo->vMonsters.push_back( pMob );
}
}
}
// 환경 가디언 몬스터는 레이드 진행 시에는 일반 몬스터 리젠 정보에 의해 리젠되며, 시즈 진행 시에만 별도로 리젠시킴
pRaidInfo->raid_starting = false;
for( std::vector< GameContent::RANDOM_MONSTER_RESPAWN_INFO >::iterator itRespawn = pDungeonInfo->vRandomRespawnInfo.begin(); itRespawn != pDungeonInfo->vRandomRespawnInfo.end(); ++itRespawn )
{
for( unsigned int i = 0; i < (*itRespawn).prespawn_count; ++i ) // prespawn count equals max, always max~
{
AR_UNIT x, y;
unsigned try_cnt = 0;
bool bValidPos = true;
do
{
GameContent::AREA_BOX randomBox = GameContent::GetRandomRespawnBox( (*itRespawn).random_area_id );
x = XRandom( randomBox.left, randomBox.right );
y = XRandom( randomBox.top, randomBox.bottom );
if( ++try_cnt > 300 )
{
bValidPos = false;
break;
}
} while( GameContent::IsBlocked( x, y ) );
if( bValidPos )
{
unsigned int monster_id = 0;
int nCum = 0;
int nKey = XRandom( 1, 100000000 );
for( std::vector< std::pair< int, int > >::const_iterator it = (*itRespawn).monster_list.begin(); it != (*itRespawn).monster_list.end(); it++ )
{
if( !(*it).first ) break;
nCum += (*it).second;
if( nKey > nCum ) continue;
monster_id = (*it).first;
break;
}
StructMonster * pMob = respawnMonster( x, y, pRaidInfo->layer, monster_id, (*itRespawn).is_wandering, (*itRespawn).way_point_id, pRaidInfo, false );
if( pMob )
{
pMob->SetDungeonRaidMonster();
pMob->CalculateStat();
pRaidInfo->vMonsters.push_back( pMob );
}
}
}
}
return true;
}
const AR_TIME DungeonManager::GetDungeonRaidRecord( int nDungeonID, int nGuildID ) const
{
for( std::vector< _DUNGEON_INFO >::const_iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
return getDungeonRaidRecord( &(*it), nGuildID );
}
}
return 0;
}
void DungeonManager::GetTopDungeonRaidRecord( const int nDungeonID, int & nGuildID, AR_TIME & nRecord ) const
{
for( std::vector< _DUNGEON_INFO >::const_iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
return getTopDungeonRaidRecord( &(*it), nGuildID, nRecord );
}
}
}
const AR_TIME DungeonManager::getDungeonRaidRecord( const _DUNGEON_INFO * pDungeonInfo, const int nGuildID ) const
{
time_t t = time( NULL );
if( GetDungeonRaidEndTime( pDungeonInfo->raid_end_time ) < t && pDungeonInfo->last_dungeon_raid_wrap_up_time > GetWeekBeginTime() )
{
if( nGuildID == pDungeonInfo->raid_guild_id )
return pDungeonInfo->best_raid_time;
else
return 0;
}
AR_TIME best_raid_record = 0;
for( std::vector< _DUNGEON_RAID_INFO * >::const_iterator itRaidInfo = pDungeonInfo->vRaidInfo.begin() ; itRaidInfo != pDungeonInfo->vRaidInfo.end() ; ++itRaidInfo )
{
if( (*itRaidInfo)->guild_id == nGuildID )
{
if( best_raid_record <= 0 || ( 0 < (*itRaidInfo)->raid_record && (*itRaidInfo)->raid_record < best_raid_record ) )
best_raid_record = (*itRaidInfo)->raid_record;
}
}
return best_raid_record;
}
void DungeonManager::getTopDungeonRaidRecord( const _DUNGEON_INFO * pDungeonInfo, int & nGuildID, AR_TIME & nRecord ) const
{
time_t t = time( NULL );
// 레이드 기간이 아니라면 공격자 길드의 레이드 정보를 사용(없으면 0)
if( GetDungeonRaidEndTime( pDungeonInfo->raid_end_time ) < t && pDungeonInfo->last_dungeon_raid_wrap_up_time > GetWeekBeginTime() )
{
if( nGuildID == pDungeonInfo->raid_guild_id )
{
nGuildID = pDungeonInfo->raid_guild_id;
nRecord = pDungeonInfo->best_raid_time;
}
else
{
nGuildID = 0;
nRecord = 0;
}
return;
}
// 레이드 기간이라면 기록 중 최고 기록을 찾음
if( pDungeonInfo->vRaidInfo.empty() )
{
nGuildID = 0;
nRecord = 0;
return;
}
int best_guild_id = 0;
AR_TIME best_raid_record = 0;
for( std::vector< _DUNGEON_RAID_INFO * >::const_iterator itRaidInfo = pDungeonInfo->vRaidInfo.begin() ; itRaidInfo != pDungeonInfo->vRaidInfo.end() ; ++itRaidInfo )
{
// 완료된 레이드 기록에 대해서만 지금까지 나온 최고 기록보다 좋은 기록인지 체크
if( best_raid_record <= 0 || ( 0 < (*itRaidInfo)->raid_record && (*itRaidInfo)->raid_record < best_raid_record ) )
{
best_guild_id = (*itRaidInfo)->guild_id;
best_raid_record = (*itRaidInfo)->raid_record;
}
}
if( best_guild_id )
{
nGuildID = best_guild_id;
nRecord = best_raid_record;
}
}
void DungeonManager::ClearDungeonRaidRecord( int nDungeonID, int nGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
clearDungeonRaidRecord( &(*it), nGuildID );
break;
}
}
DB().Push( new DB_ClearDungeonRaidRecord( nDungeonID, nGuildID ) );
}
void DungeonManager::clearDungeonRaidRecord( _DUNGEON_INFO *pDungeonInfo, int nGuildID )
{
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaid = pDungeonInfo->vRaidInfo.begin();
pDungeonInfo->vRaidInfo.empty() == false && itRaid != pDungeonInfo->vRaidInfo.end();
/* 루프에서 ++itRaid 처리 */ )
{
if( (*itRaid)->guild_id == nGuildID )
{
delete (*itRaid);
vector_fast_erase( &pDungeonInfo->vRaidInfo, itRaid );
// 마지막 요소와 현재 요소를 바꿔치기 했으니 현재 요소를 다시 검사해야 하므로 ++itRaid 안 함
// DB 갱신 따로 안 하니 DB_ClearDungeonRaidRecord를 쓰던 DB_DeleteGuild 등을 쓰던 알아서 처리해야 함
continue;
}
++itRaid;
}
}
time_t DungeonManager::GetLastRaidEndTime( int nDungeonID, int nGuildID )
{
time_t tLastRaidEndTime = 0;
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
// 레이드 기록 중에서 최근 종료 시간을 얻음
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin(); itRaidInfo != (*it).vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->guild_id == nGuildID && (*itRaidInfo)->end_time > GetWeekBeginTime() && (*itRaidInfo)->end_time > tLastRaidEndTime )
{
tLastRaidEndTime = (*itRaidInfo)->end_time;
}
}
return tLastRaidEndTime;
}
}
return 0;
}
AR_TIME DungeonManager::GetElaspedRaidTime( int nDungeonID, unsigned char layer )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin(); itRaidInfo != (*it).vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->layer == layer && !(*itRaidInfo)->end_time )
{
return GetArTime() - (*itRaidInfo)->start_time;
}
}
}
}
return -1;
}
unsigned char DungeonManager::GetRaidDungeonLayer( int nDungeonID, int nGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin(); itRaidInfo != (*it).vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->guild_id == nGuildID )
{
// 진행 중인 레이드 정보이면 해당 레이어 리턴
// 진행 중인 레이드 정보가 아니었다면 다음 레이드 정보 검사하도록 넘어감(1주간 레이드가 4회까지 가능하므로)
if( (*itRaidInfo)->layer && (*itRaidInfo)->start_time && !(*itRaidInfo)->raid_starting )
{
return (*itRaidInfo)->layer;
}
}
}
return 0;
}
}
return -1;
}
bool DungeonManager::IsRaidBegin( int nDungeonID, int nGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin(); itRaidInfo != (*it).vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->guild_id == nGuildID )
{
if( (*itRaidInfo)->layer && !(*itRaidInfo)->end_time )
return true;
//if( (*itRaidInfo)->raid_record )
// return true;
}
}
return false;
}
}
return false;
}
bool DungeonManager::BeginDungeonRaid( int nDungeonID, int nGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).bDungeonSiegeCreated )
return false;
if( (*it).owner_guild_id == nGuildID )
return false;
std::vector< unsigned char > vLayers;
_DUNGEON_RAID_INFO * pRaidInfo = NULL;
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin(); itRaidInfo != (*it).vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->guild_id == nGuildID )
{
if( (*itRaidInfo)->layer )
//return false;
continue;
if( (*itRaidInfo)->raid_record )
//return false;
continue;
pRaidInfo = (*itRaidInfo);
}
else if( (*itRaidInfo)->layer )
{
vLayers.push_back( (*itRaidInfo)->layer );
}
}
std::sort( vLayers.begin(), vLayers.end() );
unsigned char layer = DUNGEON_SIEGE_LAYER + 1;
for( std::vector< unsigned char >::iterator itLayers = vLayers.begin(); itLayers != vLayers.end(); ++itLayers, ++layer )
{
if( (*itLayers) != layer )
break;
}
if( layer >= CUSTOMIZE_DUNGEON_LAYER )
return false;
if( !pRaidInfo )
{
(*it).vRaidInfo.push_back( new _DUNGEON_RAID_INFO( nGuildID, 0, 0, &(*it) ) );
pRaidInfo = (*it).vRaidInfo.back();
}
pRaidInfo->layer = layer;
pRaidInfo->start_time = GetArTime();
pRaidInfo->last_notice_time = time( NULL );
pRaidInfo->raid_starting = true;
for( int i = 0; i < DUNGEON_BOSS_COUNT; ++i )
{
pRaidInfo->boss_dead[i] = false;
}
SendGuildChatMessage( CHAT_GUILD_SYSTEM, "@GUILD", nGuildID, "@681");
return true;
}
}
return false;
}
bool DungeonManager::endDungeonRaid( struct _DUNGEON_RAID_INFO * pRaidInfo, bool bCancel )
{
if( !pRaidInfo )
return false;
if( bCancel )
{
pRaidInfo->raid_record = AR_TIME(-1);
}
else
{
pRaidInfo->raid_record = GetArTime() - pRaidInfo->start_time;
}
pRaidInfo->start_time = 0;
pRaidInfo->end_time = time( NULL );
for( std::vector< StructMonster * >::iterator itMonster = pRaidInfo->vMonsters.begin(); itMonster != pRaidInfo->vMonsters.end(); )
{
if( (*itMonster)->IsEnable() )
{
(*itMonster)->SetDeleteHandler( NULL );
// 더이상의 스케쥴러 요청을 무시
(*itMonster)->Disable();
// 월드에서 제거한다.
if( (*itMonster)->IsInWorld() )
{
RemoveMonsterFromWorld( (*itMonster) );
}
// object delete 요청
ArcadiaServer::Instance().DeleteObject( (*itMonster) );
itMonster = pRaidInfo->vMonsters.erase( itMonster );
}
else
{
++itMonster;
}
}
// 레이드가 완료되었을 때, 기존에 성공한 기록이 없다면 무조건 기록
// 기존에 성공한 기록이 있을 경우는 이번이 성공이고, 이번 기록이 기존 기록보다 짧아야만 기록
AR_TIME tRecord = GetDungeonRaidRecord( pRaidInfo->pDungeonInfo->id, pRaidInfo->guild_id );
if( tRecord <= 0 || ( tRecord > 0 && pRaidInfo->raid_record > 0 && tRecord >= pRaidInfo->raid_record ) )
DB().Push( new DB_UpdateDungeonRaidTime( pRaidInfo->pDungeonInfo->id, pRaidInfo->guild_id, time( NULL ), pRaidInfo->raid_record ) );
int nBossKillCount = 0;
for( int i = 0; i < DUNGEON_BOSS_COUNT; ++i )
{
if( pRaidInfo->boss_dead[i] )
++nBossKillCount;
}
const char * szRes = bCancel ? "FAIL" : "SUCS";
broadcastRaidResult( pRaidInfo, !bCancel );
LOG::Log11N4S( LM_END_DUNGEON_RAID, 0, 0, 0, pRaidInfo->guild_id, 0, 0, 0, 0, ( pRaidInfo->raid_record == -1 ) ? -1 : ( pRaidInfo->raid_record / 100 ), nBossKillCount, pRaidInfo->pDungeonInfo->id, "", 0, "", 0, "", 0, szRes, LOG::STR_NTS );
int nLeadPartyID = PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( pRaidInfo->guild_id );
if( nLeadPartyID )
{
BroadcastPartyDestroy( nLeadPartyID );
LOG::Log11N4S( LM_PARTY_DESTROY, 0, 0, nLeadPartyID, 0, 0, 0, 9, 0, 0, 0, 0,
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nLeadPartyID ).c_str(), LOG::STR_NTS, "", 0 );
PartyManager::GetInstance().DestroyParty( nLeadPartyID );
}
return true;
}
int DungeonManager::GetDungeonID( AR_UNIT x, AR_UNIT y ) const
{
X2D::Point< AR_UNIT > pt( x, y );
for( std::vector< _DUNGEON_INFO >::const_iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).box.IsInclude( pt ) )
{
return (*it).id;
}
}
return 0;
}
int DungeonManager::GetDungeonLevel( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).level;
}
}
return 0;
}
int DungeonManager::GetMaxRaidParty( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).max_raid_party;
}
}
return 0;
}
int DungeonManager::GetMaxGuildParty( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).max_guild_party;
}
}
return 0;
}
int DungeonManager::GetOriginalOwnGuildID( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).original_owner_guild_id;
}
}
return 0;
}
int DungeonManager::GetOwnGuildID( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).owner_guild_id;
}
}
return 0;
}
bool DungeonManager::SetOwnGuildID( const int & nDungeonID, const int & nGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
// 이미 주인이면 패스
if( (*it).owner_guild_id == nGuildID )
{
_cprint( "DungeonManager::SetOwnGuildID: Already the owner of that dungeon.(DungeonID: %d, Guild: %d)\n", nDungeonID, nGuildID );
FILELOG( "DungeonManager::SetOwnGuildID: Already the owner of that dungeon.(DungeonID: %d, Guild: %d)", nDungeonID, nGuildID );
return false;
}
// 시즈 진행 중에는 주인 길드 변경 불가
if( (*it).bDungeonSiege )
{
_cprint( "DungeonManager::SetOwnGuildID: Tryed to change dungeon owner guild while a siege of that dungeon is going on.(DungeonID: %d, Guild: %d)\n", nDungeonID, nGuildID );
FILELOG( "DungeonManager::SetOwnGuildID: Tryed to change dungeon owner guild while a siege of that dungeon is going on.(DungeonID: %d, Guild: %d)", nDungeonID, nGuildID );
return false;
}
// 소유자 길드 변경 시 변경 대상 길드가 유효한 길드인지 체크
if( nGuildID && GuildManager::GetInstance().GetGuildName( nGuildID ).empty() )
{
_cprint( "DungeonManager::SetOwnGuildID: Invalid new owner guild id.(DungeonID: %d, Guild: %d)\n", nDungeonID, nGuildID );
FILELOG( "DungeonManager::SetOwnGuildID: Invalid new owner guild id.(DungeonID: %d, Guild: %d)", nDungeonID, nGuildID );
return false;
}
int nRaidDungeonID = GuildManager::GetInstance().GetRaidDungeonID( nGuildID );
// 현재 던전에 신청되지 않은 길드라면
if( nRaidDungeonID != nDungeonID )
{
// 타 던전에 신청되어 있는(혹은 소유 중인) 길드
if( nRaidDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator itRaid = m_vDungeonInfo.begin(); itRaid != m_vDungeonInfo.end(); ++itRaid )
{
if( (*itRaid).id == nRaidDungeonID )
{
THREAD_SYNCRONIZE1( (*itRaid).m_CS );
// 새로운 주인 길드가 타 던전 소유 중인 길드면 불가
if( (*itRaid).owner_guild_id == nGuildID )
{
_cprint( "DungeonManager::SetOwnGuildID: New owner guild is already owning a dungeon.(DungeonID: %d, Guild: %d)\n", nDungeonID, nGuildID );
FILELOG( "DungeonManager::SetOwnGuildID: New owner guild is already owning a dungeon.(DungeonID: %d, Guild: %d)", nDungeonID, nGuildID );
return false;
}
// 새로운 주인 길드가 타 던전 레이드/시즈 진행 중이면 불가
bool bIsInRaid = false;
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*itRaid).vRaidInfo.begin(); itRaidInfo != (*itRaid).vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->guild_id == nGuildID && (*itRaidInfo)->layer && !(*itRaidInfo)->end_time )
{
bIsInRaid = true;
break;
}
}
if( (*itRaid).bDungeonSiege || bIsInRaid )
{
_cprint( "DungeonManager::SetOwnGuildID: New owner guild is in a dungeon siege or raid.(DungeonID: %d, Guild: %d)\n", nDungeonID, nGuildID );
FILELOG( "DungeonManager::SetOwnGuildID: New owner guild is in a dungeon siege or raid.(DungeonID: %d, Guild: %d)", nDungeonID, nGuildID );
return false;
}
// 기존 던전 신청 정보 제거
clearDungeonRaidRecord( &(*itRaid), nGuildID );
DB().Push( new DB_ClearDungeonRaidRecord( (*itRaid).id, nGuildID ) );
if( (*itRaid).raid_guild_id == nGuildID )
{
(*itRaid).raid_guild_id = 0;
}
break;
}
}
}
GuildManager::GetInstance().SetRaidDungeonID( nGuildID, nDungeonID );
}
(*it).prev_owner_guild_id = (*it).owner_guild_id;
(*it).owner_guild_id = nGuildID;
(*it).original_owner_guild_id = nGuildID;
// 던전의 소유주가 바뀐다.
// 이에 따른 처리를 위해 던전 전체에 락이 필요한데 DropDungeonOwnership은 이미 지역락이 걸려있으므로 플래그만 세팅해둔다.
// 만약 이전에 발생한 소유주 변경에 따른 처리가 되지 않은 상태(bDungeonOwnerChanged == true)에서 다시 한 번 소유주가 바뀌게 되면 문제가 발생할 것이다.
assert( !(*it).bDungeonOwnerChanged );
(*it).bDungeonOwnerChanged = true;
// 주인이 바뀌는 경우에는 신규 던전 주인 길드의 레이드 기록도 함께 삭제 됨
DB().Push( new DB_UpdateDungeonOwnerGuild( nDungeonID, nGuildID ) );
LOG::Log11N4S( LM_DUNGEON_CHANGE_OWNER, 0, 0, 0, nGuildID, (*it).prev_owner_guild_id, 0, 0, 0, 0, 0, nDungeonID,
GuildManager::GetInstance().GetGuildName( nGuildID ).c_str(), LOG::STR_NTS,
GuildManager::GetInstance().GetGuildName( (*it).prev_owner_guild_id ).c_str(), LOG::STR_NTS,
"Script", LOG::STR_NTS, "", LOG::STR_NTS );
return true;
}
}
return false;
}
bool DungeonManager::ClearRaidGuildID( const int & nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).raid_guild_id )
{
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin();
(*it).vRaidInfo.empty() == false && itRaidInfo != (*it).vRaidInfo.end();
/* 루프에서 ++it 처리 */ )
{
if( (*itRaidInfo)->guild_id == (*it).raid_guild_id )
{
delete (*itRaidInfo);
vector_fast_erase( &(*it).vRaidInfo, itRaidInfo );
// 마지막 요소와 현재 요소를 바꿔치기 했으니 현재 요소를 다시 검사해야 하므로 ++itRaidInfo 안 함
continue;
}
++itRaidInfo;
}
(*it).raid_guild_id = 0;
}
}
}
return false;
}
int DungeonManager::GetRaidGuildID( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
return (*it).raid_guild_id;
}
}
return 0;
}
DungeonManager::DungeonManager()
{
}
DungeonManager::~DungeonManager()
{
}
bool DungeonManager::IsSiegeBegin( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
if( (*it).bDungeonSiege )
return true;
return false;
}
}
return false;
}
bool DungeonManager::IsEnterableSiegeDungeon( int nDungeonID, int nGuildID )
{
if( !nGuildID )
return false;
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
if( (*it).bDungeonSiege && (*it).owner_guild_id == nGuildID )
return true;
if( (*it).bDungeonSiege && (*it).raid_guild_id == nGuildID )
return true;
return false;
}
}
return false;
}
const char DungeonManager::IsRestrictedToEnter( const int nDungeonID, const struct StructCreature *pCreature, const bool bAllowLowLevel ) const
{
// 플레이어와 소환수를 제외하고는 입장 레벨 제한을 받지 않음
if( !pCreature->IsPlayer() && !pCreature->IsSummon() )
return 0;
for( std::vector< _DUNGEON_INFO >::const_iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
return IsRestrictedToEnter( (*it).level, pCreature->GetLevel() );
}
assert( 0 );
return -1;
}
const char DungeonManager::IsRestrictedToEnter( const int nDungeonLevel, const int nLevel, const bool bAllowLowLevel )
{
if( !bAllowLowLevel && nLevel < nDungeonLevel - GameRule::DUNGEON_ENTER_LEVEL_LIMIT_LOWER_RANGE )
return -1;
// 던전 상한 레벨 체크 안 함
// if( nLevel > nDungeonLevel + GameRule::DUNGEON_ENTER_LEVEL_LIMIT_UPPER_RANGE )
// return 1;
return 0;
}
void DungeonManager::onProcess( int nThreadNum )
{
extern volatile int g_nCurrentLocalFlag;
char buf[255];
s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadNum );
ENV().Set( buf, "DungeonManager" );
time_t t = time( NULL );
if( GameRule::bDisableDungeonRaidSiege )
return;
std::vector< std::string > vClosedDungeons;
XStringUtil::Split( ENV().GetString( "game.closed_dungeons", "" ).c_str(), vClosedDungeons, "|" );
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
for( std::vector< std::string >::iterator itClosed = vClosedDungeons.begin() ; itClosed != vClosedDungeons.end() ; ++itClosed )
{
if( atoi( (*itClosed).c_str() ) == (*it).id )
continue;
}
// 던전 중 시즈 타입이 아닐 경우 레이드와 시즈가 없으므로 밑의 처리를 해 줄 필요가 없다.
if( (*it).type != DUNGEON_TYPE_SIEGE )
continue;
time_t dungeon_siege_start_time = GetNextDungeonSiegeStartTime( (*it).start_time, (*it).last_dungeon_siege_finish_time );
time_t dungeon_siege_end_time = GetNextDungeonSiegeEndTime( (*it).end_time, dungeon_siege_start_time );
{
// notice 함수는 DUNGEON_SIEGE_LAYER 에 대해서만 방송을 하지만, wrapUpDungeonRaid는 생성된 레이드 던전 인스턴스 전체에 대해 동작하므로
// 이 위치에서는 레이어 범위를 한정지을 수 없음. 따라서 전체 레이어에 락을 걸고 처리함. (아놔 니미 ㅠㅜ)
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( GetRegionX( (*it).box.GetLeft() ), GetRegionY( (*it).box.GetTop() ),
GetRegionX( (*it).box.GetRight() ), GetRegionY( (*it).box.GetBottom() ) ) );
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).vNotices.size() )
{
notice( &(*it) );
}
if( t > GetDungeonRaidEndTime( (*it).raid_end_time ) && (*it).last_dungeon_raid_wrap_up_time < GetWeekBeginTime() )
{
wrapUpDungeonRaid( &(*it) );
}
}
if( (*it).bDungeonOwnerChanged )
{
// 던전 소유주가 바뀌었을 경우 해야하는 처리가 현재는 던전 소유 길드원의 지속효과 제거 뿐이므로 일반 던전 레이어(DUNGEON_LAYER)만 락을 건다.
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( GetRegionX( (*it).box.GetLeft() ), GetRegionY( (*it).box.GetTop() ),
GetRegionX( (*it).box.GetRight() ), GetRegionY( (*it).box.GetBottom() ), DUNGEON_LAYER ) );
THREAD_SYNCRONIZE( (*it).m_CS );
// 락이 걸린 상태로 정확한 값을 다시 한 번 검사
if( (*it).bDungeonOwnerChanged )
{
onChangeDungeonOwner( &(*it), (*it).prev_owner_guild_id, (*it).original_owner_guild_id );
(*it).bDungeonOwnerChanged = false;
}
}
if( t > dungeon_siege_start_time )
{
if( !(*it).bDungeonSiege && t < dungeon_siege_end_time )
BeginDungeonSiege( (*it).id );
// 여긴 전부 던전 시즈 관련 처리/방송이므로 시즈 레이어에 대해서만 락을 걸어도 됨(나중에라도 다른 레이어에 락이 필요하다는 게 확인되었다면 전체 레이어로 변경할 것)
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( GetRegionX( (*it).box.GetLeft() ), GetRegionY( (*it).box.GetTop() ),
GetRegionX( (*it).box.GetRight() ), GetRegionY( (*it).box.GetBottom() ), DUNGEON_SIEGE_LAYER ) );
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).bNeedToChangePosition )
{
changePosition( &(*it) );
(*it).bNeedToChangePosition = false;
broadcastSiegeStatus( &(*it) );
}
else if( (*it).pConnector || (*it).pDungeonCore )
{
broadcastSiegeStatus( &(*it) );
}
if( (*it).vNotices.size() )
{
notice( &(*it) );
}
if( t > dungeon_siege_end_time )
{
// 여기 이외에서도 던전 시즈가 끝나는 조건이 있을 수 있으므로 종료 처리를 직접적으로 하지 말고 종료 대기 플래그만 세팅
if( (*it).bDungeonSiege )
(*it).bDungeonSiegeNeedToDestroy = true;
}
else
{
#if _MSC_VER <= 1400 // For VC++ 2005(1400) or earlier
for( std::queue< int >::container_type::const_iterator itRespawn = (*it).qPendedGuardianRespawn.c.begin() ; itRespawn != (*it).qPendedGuardianRespawn.c.end() ; ++itRespawn )
#elif _MSC_VER <= 1600 // For VC++ 2008(1500), 2010(1600)
for( std::queue< int >::container_type::const_iterator itRespawn = (*it).qPendedGuardianRespawn._Get_container().begin() ; itRespawn != (*it).qPendedGuardianRespawn._Get_container().end() ; ++itRespawn )
#else
#error Please check reference for accessing the internal container class of 'std::queue' and write that code here.
#endif
{
RespawnGuardian( &(*it), (*itRespawn) );
}
(*it).qPendedGuardianRespawn = std::queue< int >();
(*it).procRespawnEnvironmentalGuardian();
}
// 위에서 플래그를 세팅하는 경우 외에도 시즈 종료 처리를 모두 여기서 함
if( (*it).bDungeonSiegeNeedToDestroy )
{
endDungeonSiege( &(*it) );
(*it).bDungeonSiegeNeedToDestroy = false;
}
}
else
{
if( t + DUNGEON_SIEGE_NOTICE_START_TIME > dungeon_siege_start_time ) // 던전 시즈 공지 시간대라면 공지 출력하자
{
if( (*it).last_global_notice_count < _countof( DUNGEON_SIEGE_NOTICE_TIME ) ) // 일반 공지
{
// "(*it).last_global_notice_count" 번째 시간대의 공지사항 출력
if( ( dungeon_siege_start_time - DUNGEON_SIEGE_NOTICE_TIME[ (*it).last_global_notice_count ] ) < t )
{
std::string strText;
char szBuf[255];
strText = "@686\v#@dungeon_name@#\v";
s_sprintf( szBuf, _countof( szBuf ), "@%d", (*it).id + 70000000 );
strText += szBuf;
strText += "\v#@num@#\v";
s_sprintf( szBuf, _countof( szBuf ), "%d", ( dungeon_siege_start_time - t ) / 60 );
strText += szBuf;
SendGlobalChatMessage( CHAT_NOTICE, "@NOTICE", strText.c_str(), static_cast<unsigned int>( strText.size() ) );
(*it).last_global_notice_count = (*it).last_global_notice_count + 1;
}
}
}
if( t + DUNGEON_SIEGE_GUILD_NOTICE_TIME > dungeon_siege_start_time ) // 5분 남음 양쪽 길드 5분후 입장 가능
{
if( !(*it).bDungeonSiegeCreated )
CreateDungeonSiege( (*it).id );
}
}
// RoamingManager::(InitRoamer/DeInitRoamer) 함수 호출을 (begin/end)DungeonRaid 함수 안에서 호출할 경우 지역락 -> StructRoamer::m_CS 순서에 문제가 생기므로
// 임시로 보관했다가 지역락을 해제한 이후에 RoamingManager::(InitRoamer/DeInitRoamer) 함수를 호출하도록 함.
std::set< unsigned char > stRoamerInitPendingLayer;
std::set< unsigned char > stRoamerDeinitPendingLayer;
{
// 여기서는 생성되어 있는 레이드 인스턴스 목록을 미리 확인할 수도 없고, (*it).m_CS 를 건 이후에 지역락을 걸 수도 없는 노릇이므로 전체 레이어에 락을 걸어 줌.
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( GetRegionX( (*it).box.GetLeft() ), GetRegionY( (*it).box.GetTop() ),
GetRegionX( (*it).box.GetRight() ), GetRegionY( (*it).box.GetBottom() ) ) );
THREAD_SYNCRONIZE( (*it).m_CS );
bool raid_time_over = false;
if( t > GetDungeonRaidEndTime( (*it).raid_end_time ) )
{
raid_time_over = true;
}
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin(); itRaidInfo != (*it).vRaidInfo.end(); ++itRaidInfo )
{
_DUNGEON_RAID_INFO * pRaidInfo = (*itRaidInfo);
if( pRaidInfo->raid_starting )
{
beginDungeonRaid( pRaidInfo );
stRoamerInitPendingLayer.insert( pRaidInfo->layer );
}
if( pRaidInfo->raid_complete )
{
if( pRaidInfo->layer && pRaidInfo->start_time )
{
endDungeonRaid( pRaidInfo, false );
stRoamerDeinitPendingLayer.insert( pRaidInfo->layer );
}
pRaidInfo->raid_complete = false;
}
if( pRaidInfo->layer && !pRaidInfo->start_time && pRaidInfo->end_time + DUNGEON_SIEGE_KICK_TIME < t )
{
broadcastRaidEnd( pRaidInfo );
kickPlayerInDungeon( &(*it), pRaidInfo->layer );
pRaidInfo->layer = 0;
}
if( raid_time_over && pRaidInfo->start_time )
{
endDungeonRaid( pRaidInfo, true );
stRoamerDeinitPendingLayer.insert( pRaidInfo->layer );
}
if( pRaidInfo->start_time && pRaidInfo->start_time + DUNGEON_RAID_TIME_OUT * 100 < GetArTime() )
{
endDungeonRaid( pRaidInfo, true );
stRoamerDeinitPendingLayer.insert( pRaidInfo->layer );
}
if( pRaidInfo->start_time && pRaidInfo->layer && pRaidInfo->last_notice_time + DUNGEON_RAID_NOTICE_PERIOD < t )
{
PrintfAttackTeamChatMessageByGuildID( false, CHAT_GUILD_SYSTEM, "@GUILD", pRaidInfo->guild_id, "@682\v#@num@#\v%d", (GetArTime() - pRaidInfo->start_time ) / 6000 );
pRaidInfo->last_notice_time = t;
}
}
if( !(*it).bDungeonSiegeCreated && !(*it).bDungeonSiegeKicked && (*it).last_dungeon_siege_finish_time + DUNGEON_SIEGE_KICK_TIME < t )
{
broadcastSiegeEnd( &(*it) );
kickPlayerInDungeon( &(*it), DUNGEON_SIEGE_LAYER );
wrapUpDungeonSiege( &(*it) );
(*it).bDungeonSiegeKicked = true;
}
}
// 락 순서 문제로 위에서 임시 보관한 RoamingManager::(InitRoamer/DeInitRoamer) 함수 호출을 처리
for( std::set< unsigned char >::const_iterator itRoamerInitLayer = stRoamerInitPendingLayer.begin() ; itRoamerInitLayer != stRoamerInitPendingLayer.end() ; ++itRoamerInitLayer )
{
for( std::vector< int >::const_iterator itRoamer = (*it).vRoamerID.begin() ; itRoamer != (*it).vRoamerID.end() ; ++itRoamer )
{
RoamingManager::Instance().InitRoamer( (*itRoamer), (*itRoamerInitLayer) );
}
}
for( std::set< unsigned char >::const_iterator itRoamerDeinitLayer = stRoamerDeinitPendingLayer.begin() ; itRoamerDeinitLayer != stRoamerDeinitPendingLayer.end() ; ++itRoamerDeinitLayer )
{
for( std::vector< int >::const_iterator itRoamer = (*it).vRoamerID.begin() ; itRoamer != (*it).vRoamerID.end() ; ++itRoamer )
{
RoamingManager::Instance().DeInitRoamer( (*itRoamer), (*itRoamerDeinitLayer) );
}
}
}
}
bool DungeonManager::IsDungeonRaidTime( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
// 시즈가 있는 던전이 아니면 체크할 필요가 없다.
if( (*it).type != DUNGEON_TYPE_SIEGE )
return false;
time_t cur_t = time( NULL );
if( cur_t > GetDungeonRaidEndTime( (*it).raid_end_time ) || cur_t < GetDungeonRaidStartTime( (*it).raid_start_time ) )
return false;
return true;
}
}
return false;
}
bool DungeonManager::IsDungeonSiegeAttacteamMakingPeriod( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
// 시즈가 있는 던전이 아니면 체크할 필요가 없다.
if( (*it).type != DUNGEON_TYPE_SIEGE )
return false;
time_t cur_t = time( NULL );
if( cur_t >= GetDungeonRaidEndTime( (*it).raid_end_time ) && cur_t <= GetNextDungeonSiegeEndTime( (*it).end_time, GetWeekBeginTime() ) )
return true;
return false;
}
}
return false;
}
bool DungeonManager::IsEndSiegeOfThisWeek( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
// 시즈가 있는 던전이 아니면 체크할 필요가 없다.
if( (*it).type != DUNGEON_TYPE_SIEGE )
return false;
if( (*it).last_dungeon_siege_finish_time < GetWeekBeginTime() )
return false;
return true;
}
}
return false;
}
void DungeonManager::CreateDungeonSiege( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
// 여긴 던전 시즈 관련 처리만 하므로 시즈 레이어만 락을 걸어 줌
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( GetRegionX( (*it).box.GetLeft() ), GetRegionY( (*it).box.GetTop() ),
GetRegionX( (*it).box.GetRight() ), GetRegionY( (*it).box.GetBottom() ), DUNGEON_SIEGE_LAYER ) );
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).bDungeonSiegeCreated )
return;
for( std::vector< _TACTICAL_POSITION_INFO >::iterator itTac = (*it).vTacticalPositionInfo.begin(); itTac != (*it).vTacticalPositionInfo.end(); ++itTac )
{
(*itTac).own_by_original_owner = true;
}
(*it).original_owner_guild_id = (*it).owner_guild_id;
{
if( GameContent::IsBlocked( (*it).core_pos.x, (*it).core_pos.y ) )
{
_cprint( "DungeonCore could not be respawned at (%f,%f) in a dungeon(%d).\n", (*it).core_pos.x, (*it).core_pos.y, nDungeonID );
FILELOG( "DungeonCore could not be respawned at (%f,%f) in a dungeon(%d).", (*it).core_pos.x, (*it).core_pos.y, nDungeonID );
}
#ifndef _DUNGEON_CORE_AS_MONSTER
GameContent::FIELD_PROP_RESPAWN_INFO core_info( (*it).core_id, (*it).core_pos.x, (*it).core_pos.y, DUNGEON_SIEGE_LAYER, (*it).core_offset_z, (*it).core_around_x, (*it).core_around_y, (*it).core_around_z, (*it).core_scale_x, (*it).core_scale_y, (*it).core_scale_z, (*it).core_is_lock_height, (*it).core_lock_height );
(*it).pDungeonCore = StructFieldProp::Create( NULL, &core_info, 0 );
#else
// 몬스터 형태 코어에서는 DungeonResource의 core_id가 몬스터 ID 를 나타냄
(*it).pDungeonCore = respawnMonster( (*it).core_pos.x, (*it).core_pos.y, DUNGEON_SIEGE_LAYER, (*it).core_id, false, 0, &(*it), false );
(*it).pDungeonCore->SetDungeonOwnerGuardian();
#endif
}
// 몬스터 형태 코어에서는 DungeonResource의 core_id가 몬스터 ID 를 나타냄
if( GameContent::IsBlocked( (*it).connector_pos.x, (*it).connector_pos.y ) )
{
_cprint( "Connector could not be respawned at (%f,%f) in a dungeon(%d).\n", (*it).connector_pos.x, (*it).connector_pos.y, nDungeonID );
FILELOG( "Connector could not be respawned at (%f,%f) in a dungeon(%d).", (*it).connector_pos.x, (*it).connector_pos.y, nDungeonID );
}
(*it).pConnector = respawnMonster( (*it).connector_pos.x, (*it).connector_pos.y, DUNGEON_SIEGE_LAYER, (*it).connector_id, false, 0, &(*it), false );
if( (*it).pConnector )
{
(*it).pConnector->SetDungeonSiegerGuardian();
}
for( std::vector< GameContent::MONSTER_RESPAWN_INFO >::iterator itRespawn = (*it).vGuardianRespawnInfo.begin(); itRespawn != (*it).vGuardianRespawnInfo.end(); ++itRespawn )
{
for( unsigned int i = 0; i < (*itRespawn).max_num; ++i ) // always max~
{
AR_UNIT x = (*itRespawn).left;
AR_UNIT y = (*itRespawn).top;
if( GameContent::IsBlocked( x, y ) )
{
_cprint( "Guardian monster(%d) could not be respawned at (%f,%f) in a dungeon(%d).\n", (*itRespawn).monster_id, x, y, (*it).id );
FILELOG( "Guardian monster(%d) could not be respawned at (%f,%f) in a dungeon(%d).", (*itRespawn).monster_id, x, y, (*it).id );
continue;
}
StructMonster * pMob = respawnMonster( x, y, DUNGEON_SIEGE_LAYER, (*itRespawn).monster_id, (*itRespawn).is_wandering, (*itRespawn).way_point_id, &(*it), false );
if( pMob )
{
pMob->SetDungeonOwnerGuardian();
(*it).vMonsters.push_back( pMob );
}
#ifdef _DUNGEON_CORE_AS_MONSTER
if( !(*itRespawn).id )
{
(*it).stCoreGuardianHandle.insert( pMob->GetHandle() );
}
#endif
}
}
for( std::vector< GameContent::MONSTER_RESPAWN_INFO >::iterator itRespawn = (*it).vEnvironmentalGuardianRespawnInfo.begin(); itRespawn != (*it).vEnvironmentalGuardianRespawnInfo.end(); ++itRespawn )
{
for( unsigned int i = 0; i < (*itRespawn).max_num; ++i ) // always max~
{
AR_UNIT x = (*itRespawn).left;
AR_UNIT y = (*itRespawn).top;
if( GameContent::IsBlocked( (*itRespawn).left, (*itRespawn).top ) )
{
_cprint( "Environmental guardian monster(%d) could not be respawned at (%f,%f) in a dungeon(%d).\n", (*itRespawn).monster_id, x, y, (*it).id );
FILELOG( "Environmental guardian monster(%d) could not be respawned at (%f,%f) in a dungeon(%d).", (*itRespawn).monster_id, x, y, (*it).id );
continue;
}
StructMonster * pMob = respawnMonster( x, y, DUNGEON_SIEGE_LAYER, (*itRespawn).monster_id, (*itRespawn).is_wandering, (*itRespawn).way_point_id, &(*it), false );
if( pMob )
{
pMob->SetDungeonOwnerGuardian();
(*it).vMonsters.push_back( pMob );
}
(*it).vRespawnedEnvironmentalGuardianInfo.push_back( _DUNGEON_INFO::RESPAWNED_ENVIRONMENTAL_GUARDIAN_INFO( pMob, &(*itRespawn) ) );
}
}
(*it).bDungeonSiegeCreated = true;
(*it).bDungeonSiegeKicked = false;
return;
}
}
}
void DungeonManager::BeginDungeonSiege( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
if( !(*it).bDungeonSiegeCreated )
CreateDungeonSiege( nDungeonID );
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).bDungeonSiege )
return;
// Dungeon siege start notice
PrintfGlobalChatMessage(CHAT_NOTICE, "@NOTICE", "@690\v#@dungeon_name@#\v@%d", (*it).id + 70000000);
// Defence guild enter notice
if( (*it).owner_guild_id )
PrintfGuildChatMessage( CHAT_GUILD, (*it).owner_guild_id, "@688\v#@guild_name@#\v%s", GuildManager::GetInstance().GetGuildName( (*it).owner_guild_id ).c_str() );
// Offense guild enter notice
if( (*it).raid_guild_id )
PrintfGuildChatMessage( CHAT_GUILD, (*it).raid_guild_id, "@689\v#@guild_name@#\v%s", GuildManager::GetInstance().GetGuildName( (*it).raid_guild_id ).c_str() );
(*it).bDungeonSiege = true;
// 서버 다운 없이 2회 이상 던전 시즈가 진행될 경우 지난 주의 던전 시즈 종료 시(endDungeonSiege)
// bNeedToChangePosition 이 true로 세팅된 상태로 종료되므로 시작하자마자 공수교대가 일어나는 불상사를 방지하기 위해 변수 초기화
(*it).bNeedToChangePosition = false;
return;
}
}
}
void DungeonManager::endDungeonSiege( _DUNGEON_INFO * pDungeonInfo )
{
if( !pDungeonInfo->bDungeonSiege )
return;
pDungeonInfo->bNeedToChangePosition = true;
if( pDungeonInfo->pDungeonCore )
{
#ifdef _DUNGEON_CORE_AS_MONSTER
pDungeonInfo->pDungeonCore->SetDeleteHandler( NULL );
pDungeonInfo->pDungeonCore->Disable();
if( pDungeonInfo->pDungeonCore->IsInWorld() )
{
RemoveMonsterFromWorld( pDungeonInfo->pDungeonCore );
}
ArcadiaServer::Instance().DeleteObject( pDungeonInfo->pDungeonCore );
#else
if( pDungeonInfo->pDungeonCore->IsInWorld() )
{
ArcadiaServer::Instance().RemoveObject( pDungeonInfo->pDungeonCore );
StructFieldProp::PendFreeFieldProp( pDungeonInfo->pDungeonCore );
}
#endif
pDungeonInfo->pDungeonCore = NULL;
}
bool bConnectorDestroyed = true;
if( pDungeonInfo->pConnector )
{
if( !pDungeonInfo->pConnector->IsDead() )
{
bConnectorDestroyed = false;
}
pDungeonInfo->pConnector->SetDeleteHandler( NULL );
// 더이상의 스케쥴러 요청을 무시
pDungeonInfo->pConnector->Disable();
// 월드에서 제거한다.
if( pDungeonInfo->pConnector->IsInWorld() )
{
RemoveMonsterFromWorld( pDungeonInfo->pConnector );
}
// object delete 요청
ArcadiaServer::Instance().DeleteObject( pDungeonInfo->pConnector );
pDungeonInfo->pConnector = NULL;
}
// 리젠 예약 정보 및 리젠 상태 정보 소거
pDungeonInfo->qPendedGuardianRespawn = std::queue< int >();
pDungeonInfo->vRespawnedEnvironmentalGuardianInfo.clear();
pDungeonInfo->stCoreGuardianHandle.clear();
for( std::vector< StructMonster * >::iterator itMonster = pDungeonInfo->vMonsters.begin(); itMonster != pDungeonInfo->vMonsters.end(); )
{
if( (*itMonster)->IsEnable() )
{
(*itMonster)->SetDeleteHandler( NULL );
// 더이상의 스케쥴러 요청을 무시
(*itMonster)->Disable();
// 월드에서 제거한다.
if( (*itMonster)->IsInWorld() )
{
RemoveMonsterFromWorld( (*itMonster) );
}
// object delete 요청
ArcadiaServer::Instance().DeleteObject( (*itMonster) );
itMonster = pDungeonInfo->vMonsters.erase( itMonster );
}
else
{
++itMonster;
}
}
broadcastSiegeResult( pDungeonInfo );
std::string strText;
if( bConnectorDestroyed )
{
strText = "@695\v#@dungeon_name@#\v";
}
else
{
strText = "@696\v#@dungeon_name@#\v";
}
char szBuf[256];
s_sprintf( szBuf, _countof( szBuf ), "@%d", pDungeonInfo->id + 70000000 );
strText += szBuf;
pDungeonInfo->vNotices.push_back( strText );
if( pDungeonInfo->owner_guild_id )
{
int nLeadPartyID = PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( pDungeonInfo->owner_guild_id );
if( nLeadPartyID )
{
BroadcastPartyDestroy( nLeadPartyID );
LOG::Log11N4S( LM_PARTY_DESTROY, 0, 0, nLeadPartyID, 0, 0, 0, 8, 0, 0, 0, 0,
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nLeadPartyID ).c_str(), LOG::STR_NTS, "", 0 );
PartyManager::GetInstance().DestroyParty( nLeadPartyID );
}
}
if( pDungeonInfo->raid_guild_id )
{
GuildManager::GetInstance().SetRaidDungeonID( pDungeonInfo->raid_guild_id, 0 );
int nLeadPartyID = PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( pDungeonInfo->raid_guild_id );
if( nLeadPartyID )
{
BroadcastPartyDestroy( nLeadPartyID );
LOG::Log11N4S( LM_PARTY_DESTROY, 0, 0, nLeadPartyID, 0, 0, 0, 8, 0, 0, 0, 0,
"", 0, "", 0, PartyManager::GetInstance().GetPartyName( nLeadPartyID ).c_str(), LOG::STR_NTS, "", 0 );
PartyManager::GetInstance().DestroyParty( nLeadPartyID );
}
}
pDungeonInfo->bDungeonSiege = false;
pDungeonInfo->bDungeonSiegeCreated = false;
pDungeonInfo->last_dungeon_siege_finish_time = time( NULL );
pDungeonInfo->last_global_notice_count = 0;
pDungeonInfo->raid_guild_id = 0;
pDungeonInfo->best_raid_time = 0;
DB().Push( new DB_UpdateDungeon( pDungeonInfo->id, pDungeonInfo->owner_guild_id, pDungeonInfo->raid_guild_id, pDungeonInfo->best_raid_time, pDungeonInfo->last_dungeon_siege_finish_time, pDungeonInfo->last_dungeon_raid_wrap_up_time, pDungeonInfo->tax_rate ) );
const char *szRes = "TIMEOUT";
if( bConnectorDestroyed )
{
szRes = "DEAD";
}
LOG::Log11N4S( LM_END_DUNGEON_SIEGE, 0, 0, 0, pDungeonInfo->owner_guild_id, pDungeonInfo->raid_guild_id, 0, 0, 0, 0, 0, pDungeonInfo->id,
GuildManager::GetInstance().GetGuildName( pDungeonInfo->owner_guild_id ).c_str(), LOG::STR_NTS,
GuildManager::GetInstance().GetGuildName( pDungeonInfo->raid_guild_id ).c_str(), LOG::STR_NTS,
"", 0,
szRes, LOG::STR_NTS );
return;
}
void DungeonManager::AddToDungeonRaidMonster( struct StructMonster * pMonster, int dungeon_id, unsigned char layer )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == dungeon_id )
{
THREAD_SYNCRONIZE( (*it).m_CS );
for( std::vector< _DUNGEON_RAID_INFO * >::iterator itRaidInfo = (*it).vRaidInfo.begin(); itRaidInfo != (*it).vRaidInfo.end(); ++itRaidInfo )
{
if( (*itRaidInfo)->layer == layer )
{
pMonster->SetDeleteHandler( (*itRaidInfo) );
(*itRaidInfo)->vMonsters.push_back( pMonster );
break;
}
}
break;
}
}
}
int DungeonManager::GetConnectorHP( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( nDungeonID != (*it).id )
continue;
THREAD_SYNCRONIZE( (*it).m_CS );
return (*it).pConnector ? (*it).pConnector->GetHPPercentage() : 0;
}
assert( 0 );
return 0;
}
int DungeonManager::GetDungeonCoreHP( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( nDungeonID != (*it).id )
continue;
THREAD_SYNCRONIZE( (*it).m_CS );
return (*it).pDungeonCore ? (*it).pDungeonCore->GetHPPercentage() : 0;
}
assert( 0 );
return 0;
}
void DungeonManager::PendRespawnGuardian( int nDungeonID, int nTacticalPositionID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id != nDungeonID )
continue;
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).bDungeonSiege )
(*it).qPendedGuardianRespawn.push( nTacticalPositionID );
break;
}
}
bool DungeonManager::IsOwner( int nDungeonID, int nTacticalPositionID, int nGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( nTacticalPositionID == 0 )
{
if( nGuildID == (*it).owner_guild_id )
return true;
return false;
}
for( std::vector< _TACTICAL_POSITION_INFO >::iterator itTac = (*it).vTacticalPositionInfo.begin(); itTac != (*it).vTacticalPositionInfo.end(); ++itTac )
{
if( (*itTac).id == nTacticalPositionID )
{
if( nGuildID == (*it).original_owner_guild_id )
{
if( (*itTac).own_by_original_owner )
return true;
return false;
}
else
{
if( (*itTac).own_by_original_owner )
return false;
return true;
}
break;
}
}
break;
}
}
return false;
}
bool DungeonManager::IsOwner( int nGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( nGuildID == (*it).owner_guild_id )
return true;
}
return false;
}
#ifdef _DUNGEON_CORE_AS_MONSTER
bool DungeonManager::IsCoreInvincible( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( nDungeonID != (*it).id )
continue;
THREAD_SYNCRONIZE( (*it).m_CS );
return ( (*it).bDungeonSiege && !(*it).stCoreGuardianHandle.empty() );
}
assert( 0 );
return true;
}
void DungeonManager::ClearDungeonSiegeCoreGuardian( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( nDungeonID != (*it).id )
continue;
// 시즈 레이어에 있는 코어 가디언들만 처리하므로 시즈 레이어만 락을 걸어 줌
ARCADIA_LOCK( ArcadiaServer::Instance().LockArea( GetRegionX( (*it).box.GetLeft() ), GetRegionY( (*it).box.GetTop() ),
GetRegionX( (*it).box.GetRight() ), GetRegionY( (*it).box.GetBottom() ), DUNGEON_SIEGE_LAYER ) );
THREAD_SYNCRONIZE( (*it).m_CS );
for( std::set< AR_HANDLE >::const_iterator itGuardian = (*it).stCoreGuardianHandle.begin() ; itGuardian != (*it).stCoreGuardianHandle.end() ; ++itGuardian )
{
StructCreature::iterator itCreature = StructCreature::get( (*itGuardian ) );
if( !(*itCreature) || !(*itCreature)->IsMonster() )
continue;
StructMonster *pGuardian = static_cast< StructMonster * >( (*itCreature) );
if( !pGuardian->IsInWorld() )
{
_cprint( "Clearing core guardians of dungeon siege(%d): A guardian not existing in world detected.\n", nDungeonID );
FILELOG( "Clearing core guardians of dungeon siege(%d): A guardian not existing in world detected.", nDungeonID );
if( pGuardian->IsEnable() )
pGuardian->Disable();
ArcadiaServer::Instance().DeleteObject( pGuardian );
continue;
}
if( !pGuardian->IsInWorld() )
continue;
if( pGuardian->IsDead() )
{
_cprint( "Clearing core guardians of dungeon siege(%d): A dead guardian existing in world detected.\n", nDungeonID );
FILELOG( "Clearing core guardians of dungeon siege(%d): A dead guardian existing in world detected.", nDungeonID );
if( pGuardian->IsEnable() )
pGuardian->Disable();
RemoveMonsterFromWorld( pGuardian );
ArcadiaServer::Instance().DeleteObject( pGuardian );
continue;
}
int nPrevHP = pGuardian->GetHP();
pGuardian->damage( pGuardian, nPrevHP );
BroadcastHPMPMsg( pGuardian, -nPrevHP, 0, true );
}
(*it).stCoreGuardianHandle.clear();
}
}
#endif
void DungeonManager::ChangeOwner( int nDungeonID, int nTacticalPositionID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( !(*it).bDungeonSiege )
break;
int prev_own_guild_id = 0;
int final_own_guild_id = 0;
if( nTacticalPositionID == 0 )
{
if( (*it).bNeedToChangePosition )
break;
int raid_guild_id = (*it).owner_guild_id;
(*it).owner_guild_id = (*it).raid_guild_id;
(*it).raid_guild_id = raid_guild_id;
if( !(*it).raid_guild_id )
(*it).bDungeonSiegeNeedToDestroy = true;
else
(*it).bNeedToChangePosition = true;
char szBuf[256];
std::string szMsg;
szMsg = "@694\v#@guild_name@#\v";
szMsg += GuildManager::GetInstance().GetGuildName( (*it).owner_guild_id );
szMsg += "\v#@prop_name@#\v@";
#ifndef _DUNGEON_CORE_AS_MONSTER
const FieldPropBase *pCoreFieldPropBase = (*it).pDungeonCore->GetFieldPropBase();
s_sprintf( szBuf, _countof( szBuf ), "%d", pCoreFieldPropBase->nPropTextId );
#else
const MonsterBase *pCoreMonsterBase = GameContent::GetMonsterInfo( (*it).core_id );
s_sprintf( szBuf, _countof( szBuf ), "%d", pCoreMonsterBase->name_id );
#endif
szMsg += szBuf;
(*it).vNotices.push_back( szMsg );
prev_own_guild_id = (*it).raid_guild_id;
final_own_guild_id = (*it).owner_guild_id;
}
else
{
for( std::vector< _TACTICAL_POSITION_INFO >::iterator itTac = (*it).vTacticalPositionInfo.begin(); itTac != (*it).vTacticalPositionInfo.end(); ++itTac )
{
if( (*itTac).id == nTacticalPositionID )
{
(*itTac).own_by_original_owner = !(*itTac).own_by_original_owner;
char szBuf[256];
int nGuildID = (*it).original_owner_guild_id;
if( !(*itTac).own_by_original_owner )
{
if( (*it).original_owner_guild_id == (*it).owner_guild_id )
{
nGuildID = (*it).raid_guild_id;
}
else
{
nGuildID = (*it).owner_guild_id;
}
}
std::string szMsg;
szMsg = "@692\v#@guild_name@#\v";
szMsg += GuildManager::GetInstance().GetGuildName( nGuildID );
szMsg += "\v#@prop_name@#\v@";
s_sprintf(szBuf, _countof( szBuf ), "%d", FieldPropManager::GetInstance().GetFieldPropBase( (*itTac).prop_id ).nPropTextId );
szMsg += szBuf;
(*it).vNotices.push_back( szMsg );
prev_own_guild_id = ( nGuildID == (*it).owner_guild_id ) ? (*it).raid_guild_id : (*it).owner_guild_id;
final_own_guild_id = nGuildID;
break;
}
}
}
LOG::Log11N4S( LM_DUNGEON_CHANGE_OWNER, 0, 0, 0, final_own_guild_id, prev_own_guild_id, 0, 0, 0, 0, nTacticalPositionID, nDungeonID,
GuildManager::GetInstance().GetGuildName( final_own_guild_id ).c_str(), LOG::STR_NTS,
GuildManager::GetInstance().GetGuildName( prev_own_guild_id ).c_str(), LOG::STR_NTS,
"", 0,
"", 0 );
break;
}
}
}
bool DungeonManager::DropDungeonOwnership( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( (*it).bDungeonSiege )
return false;
if( (*it).bDungeonSiegeCreated )
return false;
GuildManager::GetInstance().SetRaidDungeonID( (*it).owner_guild_id, 0 );
time_t tDungeonBlockTime = time( NULL ) + GameRule::GUILD_DUNGEON_RECHALLENGE_BLOCK_DURATION_SEC;
int nAllianceID = GuildManager::GetInstance().GetAllianceID( (*it).owner_guild_id );
if( nAllianceID )
{
struct _myGuildListFunctor : public GuildManager::GuildListFunctor
{
_myGuildListFunctor( time_t _tDungeonBlockTime )
: tDungeonBlockTime( _tDungeonBlockTime )
{}
virtual bool operator()( int nGuildID, const char * szGuildName, const char * szGuildLeaderName, int nGuildLeaderLevel, int nGuildMemberCount, int nRaidDungeonID )
{
return GuildManager::GetInstance().SetDungeonBlockTime( nGuildID, tDungeonBlockTime );
}
int nAllianceID;
time_t tDungeonBlockTime;
} _fo( tDungeonBlockTime );
GuildManager::GetInstance().DoEachAllianceGuild( nAllianceID, _fo );
}
else
{
GuildManager::GetInstance().SetDungeonBlockTime( (*it).owner_guild_id, tDungeonBlockTime );
}
(*it).prev_owner_guild_id = (*it).owner_guild_id;
(*it).owner_guild_id = 0;
(*it).original_owner_guild_id = 0;
// 던전 소유권을 포기하여 던전의 소유주가 바뀐다.
// 이에 따른 처리를 위해 던전 전체에 락이 필요한데 DropDungeonOwnership은 이미 지역락이 걸려있으므로 플래그만 세팅해둔다.
// 만약 이전에 발생한 소유주 변경에 따른 처리가 되지 않은 상태(bDungeonOwnerChanged == true)에서 다시 한 번 소유주가 바뀌게 되면 문제가 발생할 것이다.
assert( !(*it).bDungeonOwnerChanged );
(*it).bDungeonOwnerChanged = true;
DB().Push( new DB_UpdateDungeon( (*it).id, (*it).owner_guild_id, (*it).raid_guild_id, (*it).best_raid_time, (*it).last_dungeon_siege_finish_time, (*it).last_dungeon_raid_wrap_up_time, (*it).tax_rate ) );
LOG::Log11N4S( LM_DUNGEON_DROP_OWNERSHIP, 0, 0, 0, (*it).prev_owner_guild_id, 0, 0, 0, 0, 0, 0, nDungeonID, "", 0, "", 0, "", 0, "", 0 );
break;
}
}
return true;
}
void DungeonManager::EndDungeonSiege( int nDungeonID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( !(*it).bDungeonSiege )
return;
(*it).bDungeonSiegeNeedToDestroy = true;
break;
}
}
}
void DungeonManager::broadcastSiegeStatus( _DUNGEON_INFO * pDungeonInfo )
{
int ownerGuildID = GetOwnGuildID( pDungeonInfo->id );
int connectorHP = pDungeonInfo->pConnector ? pDungeonInfo->pConnector->GetHPPercentage() : 0;
int coreHP = pDungeonInfo->pDungeonCore ? pDungeonInfo->pDungeonCore->GetHPPercentage() : 0;
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const int _ownerGuildID, const int _connectorHP, const int _coreHP ) : ownerGuildID( _ownerGuildID ), connectorHP( _connectorHP ), coreHP( _coreHP ) {}
void operator()( ArObject *pObj ) const
{
char buf[256];
StructPlayer *pPlayer = static_cast< StructPlayer * >( pObj );
int nGuildID = pPlayer->GetGuildID();
if( GuildManager::GetInstance().GetAllianceID( nGuildID ) )
{
nGuildID = GuildManager::GetInstance().GetAllianceLeaderGuildID( GuildManager::GetInstance().GetAllianceID( nGuildID ) );
}
// 던전을 소유하여 방어자 길드일 경우 1, 그렇지 않아 공격자 길드일 경우 0
int nPosition = ( nGuildID == ownerGuildID );
s_sprintf( buf, _countof( buf ), "SIEGE_STATUS|%d|%d|%d|", connectorHP, coreHP, nPosition );
SendChatMessage( false, CHAT_RAID_SYSTEM, "@RAID", pPlayer, buf );
}
int ownerGuildID;
int connectorHP;
int coreHP;
} _sender( ownerGuildID, connectorHP, coreHP );
ForEachClientInDungeon( pDungeonInfo, DUNGEON_SIEGE_LAYER, _sender );
}
void DungeonManager::broadcastSiegeResult( _DUNGEON_INFO * pDungeonInfo )
{
char msgToOwner[256];
char msgToAttacker[256];
const bool isOwnerChanged = pDungeonInfo->owner_guild_id != pDungeonInfo->original_owner_guild_id;
std::string ownerGuildName = GuildManager::GetInstance().GetGuildName( pDungeonInfo->owner_guild_id );
if( isOwnerChanged )
{
// 공격 성공
PrintfGlobalChatMessage( CHAT_NOTICE, "@NOTICE", "@698\v#@dungeon_name@#\v@%d\v#@guild_name@#\v%s",
pDungeonInfo->id + 70000000, ownerGuildName.c_str() );
s_sprintf( msgToOwner, _countof( msgToOwner ), "SIEGE_RESULT|FAIL|DEF|%d|%s|", pDungeonInfo->id, ownerGuildName.c_str() );
s_sprintf( msgToAttacker, _countof( msgToAttacker ), "SIEGE_RESULT|SUCC|ATK|%d|%s|", pDungeonInfo->id, ownerGuildName.c_str() );
}
else if( pDungeonInfo->owner_guild_id )
{
// 방어 성공
PrintfGlobalChatMessage( CHAT_NOTICE, "@NOTICE", "@697\v#@dungeon_name@#\v@%d\v#@guild_name@#\v%s",
pDungeonInfo->id + 70000000, GuildManager::GetInstance().GetGuildName( pDungeonInfo->owner_guild_id ).c_str() );
s_sprintf( msgToOwner, _countof( msgToOwner ), "SIEGE_RESULT|SUCC|DEF|%d|%s|", pDungeonInfo->id, ownerGuildName.c_str() );
s_sprintf( msgToAttacker, _countof( msgToAttacker ), "SIEGE_RESULT|FAIL|ATK|%d|%s|", pDungeonInfo->id, ownerGuildName.c_str() );
}
else
{
// 원 소유주가 없었다면 점령이라는 개념 자체가 성립되지 않으므로 무시
return;
}
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const int _nDungeonID, const bool _bIsSuccess, const char * _msgToOwner, const char * _msgToAttacker, const int _originalOwnerGuildID )
: nDungeonID( _nDungeonID ), bIsSuccess( _bIsSuccess ), msgToOwner( _msgToOwner ), msgToAttacker( _msgToAttacker ), originalOwnerGuildID( _originalOwnerGuildID )
{}
void operator()( ArObject *pObj ) const
{
StructPlayer* player = static_cast< StructPlayer * >( pObj );
const bool bIsAttacker = player->GetGuildID() != originalOwnerGuildID;
SendChatMessage( false, CHAT_RAID_SYSTEM, "@RAID", player,
bIsAttacker ? msgToAttacker : msgToOwner );
// 공격자 입장에서 성공은 방어자 입장에서 실패이다.
player->UpdateTitleConditionByDungeonSiegeEnd( nDungeonID, bIsAttacker, bIsAttacker ? bIsSuccess : !bIsSuccess );
}
const int nDungeonID;
const bool bIsSuccess;
const char * msgToOwner;
const char * msgToAttacker;
const int originalOwnerGuildID;
} _sender( pDungeonInfo->id, isOwnerChanged, msgToOwner, msgToAttacker, pDungeonInfo->original_owner_guild_id );
ForEachClientInDungeon( pDungeonInfo, DUNGEON_SIEGE_LAYER, _sender );
pDungeonInfo->prev_owner_guild_id = pDungeonInfo->original_owner_guild_id;
pDungeonInfo->original_owner_guild_id = pDungeonInfo->owner_guild_id;
// 시즈의 결과로 던전의 소유주가 바뀐다.
// 이에 따른 처리를 위해 던전 전체에 락이 필요한데 broadcastSiegeResult는 시즈 레이어에만 락이 걸려있으므로 플래그만 세팅해둔다.
// 만약 이전에 발생한 소유주 변경에 따른 처리가 되지 않은 상태(bDungeonOwnerChanged == true)에서 다시 한 번 소유주가 바뀌게 되면 문제가 발생할 것이다.
assert( !pDungeonInfo->bDungeonOwnerChanged );
pDungeonInfo->bDungeonOwnerChanged = true;
}
void DungeonManager::broadcastSiegeEnd( _DUNGEON_INFO * pDungeonInfo )
{
struct _MessageSender : ArObjectFunctor
{
void operator()( ArObject *pObj ) const
{
SendChatMessage( false, CHAT_RAID_SYSTEM, "@RAID", static_cast< StructPlayer * >( pObj ), "SIEGE_END|" );
}
} _sender;
ForEachClientInDungeon( pDungeonInfo, DUNGEON_SIEGE_LAYER, _sender );
}
void DungeonManager::broadcastRaidEnd( _DUNGEON_RAID_INFO * pRaidInfo )
{
struct _MessageSender : ArObjectFunctor
{
void operator()( ArObject *pObj ) const
{
SendChatMessage( false, CHAT_RAID_SYSTEM, "@RAID", static_cast< StructPlayer * >( pObj ), "RAID_END|" );
}
} _sender;
ForEachClientInDungeon( pRaidInfo->pDungeonInfo, pRaidInfo->layer, _sender );
}
void DungeonManager::broadcastRaidResult( _DUNGEON_RAID_INFO * pRaidInfo, const bool success )
{
char buf[256];
if( success )
{
int hour, min, sec;
AR_TIME total_record = pRaidInfo->raid_record / 100;
hour = total_record / 3600;
total_record -= hour * 3600;
min = total_record / 60;
total_record -= min * 60;
sec = total_record;
int nAllianceID = GuildManager::GetInstance().GetAllianceID( pRaidInfo->guild_id );
std::string guildName = GuildManager::GetInstance().GetGuildName( pRaidInfo->guild_id );
if( nAllianceID )
{
PrintfAllianceChatMessage( CHAT_GUILD, nAllianceID, "@GUILD",
"@684\v#@guild_name@#\v%s\v#@dungeon_name@#\v@%d\v#@hour@#\v%d\v#@minute@#\v%d\v#@second@#\v%d",
guildName.c_str(), pRaidInfo->pDungeonInfo->id + 70000000, hour, min, sec);
}
else
{
PrintfGuildChatMessage( CHAT_GUILD, pRaidInfo->guild_id,
"@684\v#@guild_name@#\v%s\v#@dungeon_name@#\v@%d\v#@hour@#\v%d\v#@minute@#\v%d\v#@second@#\v%d",
guildName.c_str(), pRaidInfo->pDungeonInfo->id + 70000000, hour, min, sec);
}
s_sprintf( buf, _countof( buf ), "RAID_RESULT|SUCC|%d|%d|%d|", hour, min, sec );
}
else
{
SendGuildChatMessage( CHAT_GUILD, "@GUILD", pRaidInfo->guild_id, "@683" );
s_sprintf( buf, _countof( buf ), "RAID_RESULT|FAIL|" );
}
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const char * _msg ) : msg( _msg ) {}
void operator()( ArObject *pObj ) const
{
SendChatMessage( false, CHAT_RAID_SYSTEM, "@RAID", static_cast< StructPlayer * >( pObj ), msg );
}
const char * msg;
} _sender( buf );
ForEachClientInDungeon( pRaidInfo->pDungeonInfo, pRaidInfo->layer, _sender );
}
void DungeonManager::notice( _DUNGEON_INFO * pDungeonInfo )
{
struct _MessageSender : ArObjectFunctor
{
_MessageSender( std::vector< std::string > * _pvNotices ) { pvNotices = _pvNotices; }
void operator()( ArObject *pObj ) const
{
for( std::vector< std::string >::iterator it = pvNotices->begin(); it != pvNotices->end(); ++it )
SendChatMessage( false, CHAT_NOTICE, "@NOTICE", static_cast< StructPlayer * >( pObj ), (*it).c_str(), static_cast<unsigned int>( (*it).size() ) );
}
std::vector< std::string > * pvNotices;
} _sender( &( pDungeonInfo->vNotices ) );
ForEachClientInDungeon( pDungeonInfo, DUNGEON_SIEGE_LAYER, _sender );
pDungeonInfo->vNotices.clear();
}
void DungeonManager::ForEachClientInDungeon( _DUNGEON_INFO * pDungeonInfo, unsigned char layer, ArObjectFunctor& functor )
{
struct Sender : ArRegionFunctor
{
Sender( ArObjectFunctor& _functor ) : functor( _functor ) {}
void operator()( const struct ArRegion * pRegion )
{
pRegion->DoEachClient( functor );
}
ArObjectFunctor& functor;
} _fo( functor );
assert( ArcadiaServer::Instance().IsLocked( GetRegionX( pDungeonInfo->box.GetLeft() ), GetRegionY( pDungeonInfo->box.GetTop() ),
GetRegionX( pDungeonInfo->box.GetRight() ), GetRegionY( pDungeonInfo->box.GetBottom() ), layer ) );
ArcadiaServer::Instance().DoEachRegion( GetRegionX( pDungeonInfo->box.GetLeft() ), GetRegionY( pDungeonInfo->box.GetTop() ),
GetRegionX( pDungeonInfo->box.GetRight() ), GetRegionY( pDungeonInfo->box.GetBottom() ), layer, _fo );
}
void DungeonManager::changePosition( _DUNGEON_INFO * pDungeonInfo )
{
std::vector< AR_HANDLE > vResult;
ArcadiaServer::Instance().EnumMovableObjectInEveryRegion( GetRegionX( pDungeonInfo->box.GetLeft() ), GetRegionY( pDungeonInfo->box.GetTop() ),
GetRegionX( pDungeonInfo->box.GetRight() ), GetRegionY( pDungeonInfo->box.GetBottom() ), DUNGEON_SIEGE_LAYER, &vResult, true, false );
for( std::vector< AR_HANDLE >::iterator itObject = vResult.begin(); itObject != vResult.end(); ++itObject )
{
StructCreature::iterator itPlayer = StructCreature::get( (*itObject) );
if( (*itPlayer)->IsPlayer() )
{
StructPlayer * pPlayer = static_cast< StructPlayer * >( *itPlayer );
// 현재 던전의 시즈 참여자인지 검사(파티, 길드 ID가 존재하고,
// 공대 리드 파티 ID가 주인 또는 레이드 길드의 공대 리드 파티 ID 중 하나인지 검사
if( !pPlayer->IsInParty() || !pPlayer->IsInGuild() )
continue;
int nAttackTeamLeadPartyID = PartyManager::GetInstance().GetLinkedPartyLeadPartyID( pPlayer->GetPartyID() );
if( nAttackTeamLeadPartyID != PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( pDungeonInfo->owner_guild_id ) &&
nAttackTeamLeadPartyID != PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( pDungeonInfo->raid_guild_id ) )
{
continue;
}
if( PartyManager::GetInstance().GetAttackTeamGuildID( pPlayer->GetPartyID() ) == pDungeonInfo->owner_guild_id )
{
pPlayer->PendWarp( pDungeonInfo->siege_defence_pos.x, pDungeonInfo->siege_defence_pos.y, DUNGEON_SIEGE_LAYER );
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
}
else
{
pPlayer->PendWarp( pDungeonInfo->siege_start_pos.x, pDungeonInfo->siege_start_pos.y, DUNGEON_SIEGE_LAYER );
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
}
}
}
if( pDungeonInfo->pConnector )
{
pDungeonInfo->pConnector->SetHP( pDungeonInfo->pConnector->GetMaxHP() );
pDungeonInfo->pConnector->SetMP( pDungeonInfo->pConnector->GetMaxMP() );
if( pDungeonInfo->original_owner_guild_id == pDungeonInfo->owner_guild_id )
{
pDungeonInfo->pConnector->SetDungeonSiegerGuardian();
pDungeonInfo->pConnector->ResetDungeonOwnerGuardian();
}
else
{
pDungeonInfo->pConnector->ResetDungeonSiegerGuardian();
pDungeonInfo->pConnector->SetDungeonOwnerGuardian();
}
BroadcastStatusMessage( pDungeonInfo->pConnector );
}
#ifdef _DUNGEON_CORE_AS_MONSTER
if( pDungeonInfo->pDungeonCore )
{
pDungeonInfo->pDungeonCore->SetHP( pDungeonInfo->pDungeonCore->GetMaxHP() );
pDungeonInfo->pDungeonCore->SetMP( pDungeonInfo->pDungeonCore->GetMaxMP() );
}
else
{
if( GameContent::IsBlocked( pDungeonInfo->core_pos.x, pDungeonInfo->core_pos.y ) )
{
_cprint( "changePosition() DungeonCore could not be respawned at (%f,%f).\n", pDungeonInfo->core_pos.x, pDungeonInfo->core_pos.y );
FILELOG( "changePosition() DungeonCore could not be respawned at (%f,%f).", pDungeonInfo->core_pos.x, pDungeonInfo->core_pos.y );
}
pDungeonInfo->pDungeonCore = respawnMonster( pDungeonInfo->core_pos.x, pDungeonInfo->core_pos.y, DUNGEON_SIEGE_LAYER, pDungeonInfo->core_id, false, 0, pDungeonInfo, false );
}
if( pDungeonInfo->original_owner_guild_id == pDungeonInfo->owner_guild_id )
{
pDungeonInfo->pDungeonCore->SetDungeonOwnerGuardian();
pDungeonInfo->pDungeonCore->ResetDungeonSiegerGuardian();
}
else
{
pDungeonInfo->pDungeonCore->SetDungeonSiegerGuardian();
pDungeonInfo->pDungeonCore->ResetDungeonOwnerGuardian();
}
BroadcastStatusMessage( pDungeonInfo->pDungeonCore );
// 코어 주변의 가디언 리젠(프랍 코어일 때에는 코어 스크립트에서 호출해 줌)
DungeonManager::Instance().RespawnGuardian( pDungeonInfo, 0 );
#endif
}
void DungeonManager::RespawnGuardian( struct _DUNGEON_INFO * pDungeonInfo, int nTacticalPositionID )
{
bool bOwnerGuardian = false;
if( nTacticalPositionID == 0 )
{
if( pDungeonInfo->original_owner_guild_id == pDungeonInfo->owner_guild_id )
{
bOwnerGuardian = true;
}
}
else
{
for( std::vector< _TACTICAL_POSITION_INFO >::iterator itTac = pDungeonInfo->vTacticalPositionInfo.begin(); itTac != pDungeonInfo->vTacticalPositionInfo.end(); ++itTac )
{
if( (*itTac).id == nTacticalPositionID )
{
if( (*itTac).own_by_original_owner )
{
bOwnerGuardian = true;
}
break;
}
}
}
for( std::vector< GameContent::MONSTER_RESPAWN_INFO >::iterator itRespawn = pDungeonInfo->vGuardianRespawnInfo.begin(); itRespawn != pDungeonInfo->vGuardianRespawnInfo.end(); ++itRespawn )
{
for( unsigned int i = 0; i < (*itRespawn).max_num; ++i ) // always max~
{
ArPosition pos( (*itRespawn).left, (*itRespawn).top );
if( (*itRespawn).id != nTacticalPositionID )
break;
AR_UNIT x, y;
unsigned try_cnt = 0;
bool bValidPos = true;
do
{
x = XRandom( (*itRespawn).left, (*itRespawn).right );
y = XRandom( (*itRespawn).top, (*itRespawn).bottom );
if( ++try_cnt > 300 )
{
bValidPos = false;
break;
}
} while( GameContent::IsBlocked( x, y ) );
if( bValidPos )
{
StructMonster * pMob = respawnMonster( x, y, DUNGEON_SIEGE_LAYER, (*itRespawn).monster_id, (*itRespawn).is_wandering, (*itRespawn).way_point_id, pDungeonInfo, false );
if( pMob )
{
if( bOwnerGuardian )
{
pMob->SetDungeonOwnerGuardian();
}
else
{
pMob->SetDungeonSiegerGuardian();
}
pDungeonInfo->vMonsters.push_back( pMob );
BroadcastStatusMessage( pMob );
#ifdef _DUNGEON_CORE_AS_MONSTER
if( nTacticalPositionID == 0 )
{
pDungeonInfo->stCoreGuardianHandle.insert( pMob->GetHandle() );
}
#endif
}
}
}
}
for( std::vector< _DUNGEON_INFO::RESPAWNED_ENVIRONMENTAL_GUARDIAN_INFO >::iterator it = pDungeonInfo->vRespawnedEnvironmentalGuardianInfo.begin() ; it != pDungeonInfo->vRespawnedEnvironmentalGuardianInfo.end() ; ++it )
{
StructMonster * pEnvironmentalGuardian = (*it).pMonster;
if( (*it).pRespawnInfo->id != nTacticalPositionID || !pEnvironmentalGuardian )
continue;
if( bOwnerGuardian )
{
pEnvironmentalGuardian->ResetDungeonSiegerGuardian();
pEnvironmentalGuardian->SetDungeonOwnerGuardian();
}
else
{
pEnvironmentalGuardian->ResetDungeonOwnerGuardian();
pEnvironmentalGuardian->SetDungeonSiegerGuardian();
}
BroadcastStatusMessage( pEnvironmentalGuardian );
}
}
void DungeonManager::kickPlayerInDungeon( struct _DUNGEON_INFO * pDungeonInfo, unsigned char layer )
{
std::vector< AR_HANDLE > vResult;
ArcadiaServer::Instance().EnumMovableObjectInEveryRegion( GetRegionX( pDungeonInfo->box.GetLeft() ), GetRegionY( pDungeonInfo->box.GetTop() ),
GetRegionX( pDungeonInfo->box.GetRight() ), GetRegionY( pDungeonInfo->box.GetBottom() ), layer, &vResult, true, false );
for( std::vector< AR_HANDLE >::iterator itObject = vResult.begin(); itObject != vResult.end(); ++itObject )
{
StructCreature::iterator itPlayer = StructCreature::get( (*itObject) );
if( (*itPlayer)->IsPlayer() )
{
StructPlayer * pPlayer = static_cast< StructPlayer * >(*itPlayer);
ArPosition townPosition;
pPlayer->GetLastTownPosition( &townPosition );
layer = 0;
ArPosition pos = pPlayer->GetPos();
int current_channel = ChannelManager::GetChannelId( pos.x, pos.y );
int target_channel = ChannelManager::GetChannelId( townPosition.x, townPosition.y );
int dungeon_id = pDungeonInfo->id;
// 수련자의 섬 같이 유저수 제한 채널로 워프해야할 경우
if( target_channel && ChannelManager::GetChannelType( target_channel ) == ChannelManager::TYPE_USER_LIMIT )
{
if( current_channel == target_channel )
{
// 대상 동네가 출발 동네면 현재 레이어 사용
layer = pPlayer->GetLayer();
}
else
{
// 아니면 새로 받고...
layer = ChannelManager::GetProperLayer( townPosition.x, townPosition.y );
}
}
pPlayer->PendWarp( townPosition.x, townPosition.y, layer );
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
}
}
}
void DungeonManager::SetTaxRate( int nDungeonID, int nTaxRate )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
(*it).tax_rate = nTaxRate;
DB().Push( new DB_UpdateDungeon( (*it).id, (*it).owner_guild_id, (*it).raid_guild_id, (*it).best_raid_time, (*it).last_dungeon_siege_finish_time, (*it).last_dungeon_raid_wrap_up_time, (*it).tax_rate ) );
break;
}
}
}
void DungeonManager::GetTaxRate( int nDungeonID, int * nTaxRate, int * nOwnGuildID )
{
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
*nTaxRate = (*it).tax_rate;
*nOwnGuildID = (*it).owner_guild_id;
return;
}
}
}
ArPosition DungeonManager::GetNearestTacticalPosition( int nDungeonID, int nGuildID, ArPosition curPos )
{
ArPosition NearestPosition;
AR_UNIT distance = 0.0f;
for( std::vector< _DUNGEON_INFO >::iterator it = m_vDungeonInfo.begin(); it != m_vDungeonInfo.end(); ++it )
{
if( (*it).id == nDungeonID )
{
THREAD_SYNCRONIZE( (*it).m_CS );
if( !(*it).bDungeonSiege )
break;
if( (*it).owner_guild_id == nGuildID )
{
distance = (*it).siege_defence_pos.GetDistance( curPos );
NearestPosition = (*it).siege_defence_pos;
}
else if( (*it).raid_guild_id == nGuildID )
{
distance = (*it).siege_start_pos.GetDistance( curPos );
NearestPosition = (*it).siege_start_pos;
}
else
{
// 이새끼 뭐야?
break;
}
bool bOriginalOwner = nGuildID == (*it).original_owner_guild_id;
for( std::vector< _TACTICAL_POSITION_INFO >::iterator itTac = (*it).vTacticalPositionInfo.begin(); itTac != (*it).vTacticalPositionInfo.end(); ++itTac )
{
if( ( (*itTac).own_by_original_owner && bOriginalOwner ) || ( !(*itTac).own_by_original_owner && !bOriginalOwner ) )
{
if( (*itTac).pos.GetDistance( curPos ) < distance )
{
NearestPosition = (*itTac).pos;
distance = (*itTac).pos.GetDistance( curPos );
}
}
}
// 던전 시즈 중 사망시 가장 가까운 전략 거점보다 코어와 더 가까울 경우 방어지점을 선택
if( nGuildID == (*it).owner_guild_id && (*it).core_pos.GetDistance( curPos ) < distance )
{
NearestPosition = (*it).siege_defence_pos;
// 거리는 코어와의 거리라고 뻥을 침. 이후에 다른 처리가 추가되면 코어보다도 가까워야 선택되도록 하기 위함.
distance = NearestPosition.GetDistance( (*it).core_pos );
}
break;
}
}
return NearestPosition;
}