217 lines
9.5 KiB
C++
217 lines
9.5 KiB
C++
|
|
#include <toolkit/XStringUtil.h>
|
|
|
|
#include "LogClient/LogClient.h"
|
|
|
|
#include "BattleArenaCommon.h"
|
|
#include "StructSummon.h"
|
|
#include "StructPlayer.h"
|
|
#include "ChannelManager.h"
|
|
#include "LuaVM.h"
|
|
#include "ThreadPlayerHelper.h"
|
|
|
|
|
|
const char * FLAG_BATTLE_ARENA_EXERCISE_GAME_READY_STATE = "ExerciseReadyState";
|
|
const char * FLAG_BATTLE_ARENA_PROP_STATE = "PropState";
|
|
|
|
|
|
BattleArenaQuitFunctor::BattleArenaQuitFunctor( _ARENA_LEAVE_TYPE eLeaveType,
|
|
const BattleArenaBaseServer* pArenaBase,
|
|
unsigned char nInstanceNo )
|
|
: m_eLeaveType( eLeaveType )
|
|
, m_pArenaBase( pArenaBase )
|
|
, m_nInstanceNo( nInstanceNo )
|
|
{
|
|
}
|
|
|
|
void BattleArenaQuitFunctor::operator()( ArObject* pObj ) const
|
|
{
|
|
StructPlayer* pPlayer = static_cast< StructPlayer* >( pObj );
|
|
|
|
// _removePlayer나 _wrapUpInstance에 의해 경기에서 이탈된 상태의 유저만 QuitFuctor에 의해 외부로 워프되어야 함
|
|
assert( !pPlayer->GetBattleArenaID() && !pPlayer->GetBattleArenaInstanceNo() );
|
|
|
|
// 아레나 내부에 있지 않다면 아무것도 안 함
|
|
// * 외부에 있는데 내부로 PendWarp 된 상태이거나 BattleArenaManager::onProcess에서 vPendedEnterPlayerList에 포함되어 있지만 락은 풀린 시점이어서
|
|
// 곧 PendWarp 될 상태일 수도 있는데, vPendedEnterPlayerList 포함된 상태라면 이미 경기에서 이탈된 상태기 때문에
|
|
// 다시 락을 걸고 PendWarp 하기 직전에 검사하는 과정에서 탈락될 걸로 예상.
|
|
// 그리고 PendWarp까지 됐지만 아직 ProcessWarp가 호출되지 않은 경우에는 ProcessWarp 안에서 경기에 참여 상태인지 워프 처리 직전에 검사.
|
|
if( !pPlayer->IsInBattleArena() )
|
|
return;
|
|
|
|
// 일단 사망 상태면 부활시켜 줌(안 해주면 밖에서 부활하며 온갖 페널티를 먹거나 대모요정의 병을 날림)
|
|
// 아레나 내부에서 부활 시에는 모든 지속효과를 복구하지만 이 시점에서는 복구를 해주더라도 퇴장 처리에서 결국 모두 삭제되기 때문에 복구해주지 않는다.
|
|
pPlayer->Resurrect( CRT_BATTLE_ARENA, pPlayer->GetMaxHP(), pPlayer->GetMaxMP(), 0, true); //false
|
|
|
|
// 이탈 스크립트 실행(추가로 접속 종료에 의한 것이 아니라는 파라미터를 세팅해 줌)
|
|
std::vector< std::string > vTagReplacement;
|
|
vTagReplacement.push_back( "#@disconnect@#" );
|
|
vTagReplacement.push_back( "0" );
|
|
|
|
ThreadPlayerHelper TPHelper( pPlayer );
|
|
BattleArenaRunEventScript( m_pArenaBase, BASET_LEAVE_INSTANCE, m_nInstanceNo, pPlayer, &vTagReplacement );
|
|
|
|
// 지속효과 제거
|
|
pPlayer->RemoveAllStateByQuittingBattleArena();
|
|
|
|
if( pPlayer->GetMainSummon() )
|
|
pPlayer->GetMainSummon()->RemoveAllStateByQuittingBattleArena();
|
|
if( pPlayer->GetSubSummon() )
|
|
pPlayer->GetSubSummon()->RemoveAllStateByQuittingBattleArena();
|
|
|
|
ArPosition posExit;
|
|
pPlayer->GetPositionOnEnterInstanceGame( &posExit );
|
|
|
|
pPlayer->RestoreStatesOnLeaveInstanceGame( false );
|
|
|
|
unsigned char nLayer = 0;
|
|
|
|
// 수련자의 섬 같이 인원 제한 채널에 레이어 설정된 구역으로 워프해야 하는 경우 적당한 레이어 얻기
|
|
int nTargetChannel = ChannelManager::GetChannelId( posExit.GetX(), posExit.GetY() );
|
|
if( nTargetChannel && ChannelManager::GetChannelType( nTargetChannel ) == ChannelManager::TYPE_USER_LIMIT )
|
|
{
|
|
nLayer = ChannelManager::GetProperLayer( posExit.GetX(), posExit.GetY() );
|
|
}
|
|
|
|
// PendWarp는 지역락 걸고 해야되는데, 여긴 이미 csBattle이 걸려있어서 지역락을 유저마다 따로 걸어줄 수는 없음
|
|
// 근데 애초에 여기 들어올 때 LockWholeInstance나 LockWholeArena를 하고 들어올 거고,
|
|
// 모든 유저는 경기장 안에 있을 거라서 따로 지역락은 걸지 않아도 문제는 되지 않을 걸로 예상됨
|
|
pPlayer->PendWarp( posExit.GetX(), posExit.GetY(), nLayer );
|
|
pPlayer->SetMove( pPlayer->GetCurrentPosition( GetArTime() ), 0 );
|
|
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
|
|
}
|
|
|
|
|
|
bool IsValidBattleArenaInstanceNo( unsigned char nInstanceNo )
|
|
{
|
|
return nInstanceNo > 0 && nInstanceNo <= MAX_BATTLE_ARENA_INSTANCE_NO_PER_ARENA;
|
|
}
|
|
|
|
// 레벨에 따른 배틀 아레나 구분 등급 번호 얻기(유효하지 않은 레벨에 대해서는 -1을 반환, 그 외에는 0 ~ 2 사이의 값을 반환)
|
|
_BATTLE_GRADE GetBattleArenaGrade( int nLevel )
|
|
{
|
|
// 최소 레벨 미만
|
|
if( nLevel < GameRule::GetItemLevelLimitByRank( 3 ) )
|
|
return BG_INVALID;
|
|
// 최저 등급
|
|
if( nLevel < GameRule::GetItemLevelLimitByRank( 5 ) )
|
|
return BG_ROOKIE;
|
|
// 중간 등급
|
|
if( nLevel < GameRule::GetItemLevelLimitByRank( 7 ) )
|
|
return BG_GROW;
|
|
// 최고 등급
|
|
return BG_MAJOR;
|
|
}
|
|
|
|
bool IsValidBattleArenaGrade( _BATTLE_GRADE eGrade )
|
|
{
|
|
if( eGrade == BG_ROOKIE || eGrade == BG_GROW || eGrade == BG_MAJOR )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// 직접 호출해서 사용하지 말 것. 이 함수 대신 BattleArena::LockWholeArena, BattleArenaInstance::LockWholeInstance 함수를 사용할 것.
|
|
ArcadiaLock _LockWholeBattleArena( const BattleArenaBaseServer* pArenaBase, unsigned short nLayer )
|
|
{
|
|
return ArcadiaServer::Instance().LockArea( pArenaBase->nArenaRegionLeft, pArenaBase->nArenaRegionTop,
|
|
pArenaBase->nArenaRegionRight, pArenaBase->nArenaRegionBottom, nLayer );
|
|
}
|
|
|
|
|
|
// 아레나 이벤트별 스크립트에 대한 태그 처리 결과 반환
|
|
std::string GetBattleArenaEventScript( const BattleArenaBaseServer* pArenaBase,
|
|
_BATTLE_ARENA_SCRIPT_EXEC_TYPE eExecType,
|
|
unsigned char nInstanceNo,
|
|
const std::vector< std::string >* pvTagReplacement )
|
|
{
|
|
const char* pszScript = NULL;
|
|
switch( eExecType )
|
|
{
|
|
case BASET_CREATE_INSTANCE: pszScript = pArenaBase->strScriptOnCreate.c_str(); break;
|
|
case BASET_ENTER_INSTANCE: pszScript = pArenaBase->strScriptOnEnter.c_str(); break;
|
|
case BASET_START_INSTANCE: pszScript = pArenaBase->strScriptOnStart.c_str(); break;
|
|
case BASET_LEAVE_INSTANCE: pszScript = pArenaBase->strScriptOnLeave.c_str(); break;
|
|
case BASET_END_INSTANCE: pszScript = pArenaBase->strScriptOnEnd.c_str(); break;
|
|
case BASET_DESTROY_INSTANCE: pszScript = pArenaBase->strScriptOnDestroy.c_str(); break;
|
|
default:
|
|
assert( 0 );
|
|
return "";
|
|
}
|
|
|
|
// 스크립트가 없거나(이런 경우는 없지만) 길이가 0이면(첫 번째 문자가 NULL) 그냥 리턴
|
|
if( !pszScript || !( *pszScript ) )
|
|
return "";
|
|
|
|
std::string strScriptBuffer( pszScript );
|
|
|
|
// 레이어 번호 태그 처리
|
|
XStringUtil::ReplaceFormat( strScriptBuffer, "#@instance_no@#", "%d", (int)nInstanceNo );
|
|
|
|
// 그 외에 추가 태그 처리
|
|
if( pvTagReplacement )
|
|
{
|
|
size_t nTagCount = pvTagReplacement->size() / 2;
|
|
for( int nTagIndex = 0 ; nTagIndex < nTagCount ; ++nTagIndex )
|
|
{
|
|
const std::string& strTag = (*pvTagReplacement)[ nTagIndex * 2 ];
|
|
const std::string& strReplacement = (*pvTagReplacement)[ nTagIndex * 2 + 1 ];
|
|
|
|
XStringUtil::Replace( strScriptBuffer, strTag.c_str(), strReplacement.c_str() );
|
|
}
|
|
}
|
|
|
|
return strScriptBuffer;
|
|
}
|
|
|
|
void BattleArenaRunScriptWithLog( const BattleArenaBaseServer* pArenaBase,
|
|
_BATTLE_ARENA_SCRIPT_EXEC_TYPE eExecType,
|
|
unsigned char nInstanceNo,
|
|
const std::string& strScript,
|
|
const StructPlayer* pExecutor )
|
|
{
|
|
// 경기장 영역 내 혹은 스크립트 호출자 주변으로 지역락이 반드시 걸려 있어야 함
|
|
// * 유저간 상호작용은 csBattle 때문에 별 문제 없지만 스크립트가 뭘 할지는 모르는 거니까.
|
|
assert( ( pExecutor && ArcadiaServer::Instance().IsLocked( pExecutor ) ) ||
|
|
( !pExecutor && ArcadiaServer::Instance().IsLocked(
|
|
pArenaBase->nArenaRegionLeft,
|
|
pArenaBase->nArenaRegionTop,
|
|
pArenaBase->nArenaRegionRight,
|
|
pArenaBase->nArenaRegionBottom,
|
|
nInstanceNo ) ) );
|
|
|
|
LUA()->RunString( strScript.c_str() );
|
|
|
|
LOG::Log11N4S( LM_BATTLE_ARENA_PROCESS,
|
|
( pExecutor ) ? pExecutor->GetAccountID() : 0, ( pExecutor ) ? pExecutor->GetPlayerUID() : 0,
|
|
pArenaBase->nID, nInstanceNo,
|
|
0, 0, 0, 0, 0, 0,
|
|
eExecType,
|
|
( pExecutor ) ? pExecutor->GetAccountName() : "", LOG::STR_NTS,
|
|
( pExecutor ) ? pExecutor->GetAlias() : "", LOG::STR_NTS,
|
|
strScript.c_str(), LOG::STR_NTS,
|
|
( pExecutor ) ? pExecutor->GetAlias() : "", LOG::STR_NTS );
|
|
}
|
|
|
|
// 각 상황에 맞는 지역락 -> csArena -> csBattle을 걸고 호출해야 함
|
|
// * 스크립트 안에서 호출하는 함수가 BattleArenaManager의 멤버 함수를 호출하는 경우 csArena/csBattle을 중첩으로 걸 수도 있으나,
|
|
// CRITICAL_SECTION의 특성에 따라 중첩해서 거는 것이 허용됨.
|
|
// 단, csArena/csBattle을 걸고 BattleArenaRunEventScript를 호출하는 경우에는 반드시 현재 걸려있는 csArena/csBattle만 걸어야 함.
|
|
// csArena/csBattle은 실제로 1개가 아니고 각 아레나별/인스턴스별로 여러 개가 생성되어 있기 때문에
|
|
// BattleArenaRunEventScript 함수를 호출하는 시점에 걸려있던 csArena/csBattle과 이름은 같지만 다른 다른 인스턴스를
|
|
// 스크립트 안에서 걸게 되면 데드락을 유발하게 됨(이름은 같지만 서로 다른 락을 규칙성 없이 교차해서 걸게 되므로)
|
|
void BattleArenaRunEventScript( const BattleArenaBaseServer* pArenaBase,
|
|
_BATTLE_ARENA_SCRIPT_EXEC_TYPE eExecType,
|
|
unsigned char nInstanceNo,
|
|
const StructPlayer * pExecutor,
|
|
const std::vector< std::string >* pvTagReplacement )
|
|
{
|
|
std::string& strEventScript = GetBattleArenaEventScript( pArenaBase, eExecType, nInstanceNo, pvTagReplacement );
|
|
|
|
BattleArenaRunScriptWithLog( pArenaBase, eExecType, nInstanceNo, strEventScript, pExecutor );
|
|
}
|
|
|