DragonNest/GameCommon/DnStateBlow.cpp
2024-12-20 16:56:44 +08:00

1837 lines
104 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "StdAfx.h"
#include "DnStateBlow.h"
#include "DnCreateBlow.h"
#include "DnBlow.h"
#include "DnImmuneBlow.h"
#include "DnAllImmuneBlow.h"
#include "DnUsingSkillWhenDieBlow.h"
#include "DnTableDB.h"
#include "DnPlayerActor.h"
#include "DnProbInvincibleAtBlow.h"
#include "DnIgnoreEffectBlow.h"
#include "DnComboDamageLimitBlow.h"
#include "DnPileAddEffectBlow.h"
#ifdef PRE_ADD_DECREASE_EFFECT
#include "DnPuppetBlow.h"
#endif // PRE_ADD_DECREASE_EFFECT
#ifdef PRE_FIX_MEMOPT_EXT
#ifndef _GAMESERVER
#include "DnCommonUtil.h"
#endif
#endif
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
#ifndef _GAMESERVER
std::map<int, EffectOutputInfo*> CDnStateBlow::s_nMapEffectOutputInfo;
bool CDnStateBlow::s_bStopProcess = false;
#else
STATIC_DECL_INIT( CDnStateBlow, bool, s_bStopProcess ) = { false, };
#endif
#ifndef _GAMESERVER
int CDnStateBlow::s_iStateBlowIDCount = 0;
#else
STATIC_DECL_INIT( CDnStateBlow, int, s_iStateBlowIDCount ) = { 0, };
#endif
CDnStateBlow::CDnStateBlow( DnActorHandle hActor )
{
m_hActor = hActor;
ZeroMemory( m_aiApplied, sizeof(m_aiApplied) );
#ifndef _GAMESERVER
m_bAllowDiffuseVariation = true;
_LoadStateEffectOutputInfo();
#endif
m_bLockStateBlowList = false;
m_bLockRemoveReservedList = false;
}
CDnStateBlow::~CDnStateBlow(void)
{
DelAllStateBlow();
}
bool CDnStateBlow::InitializeClass()
{
#ifndef _GAMESERVER
_LoadStateEffectOutputInfo();
#endif
return true;
}
void CDnStateBlow::ReleaseClass()
{
#ifndef _GAMESERVER
_ReleaseStateEffectOutputInfo();
#endif
}
#ifndef _GAMESERVER
void CDnStateBlow::_LoadStateEffectOutputInfo( void )
{
if( !s_nMapEffectOutputInfo.empty() )
return;
DNTableFileFormat* pStateEffectTable = CDnTableDB::GetInstancePtr()->GetTable( CDnTableDB::TSTATEEFFECT );
#ifdef PRE_FIX_MEMOPT_EXT
DNTableFileFormat* pFileNameSox = CDnTableDB::GetInstancePtr()->GetTable(CDnTableDB::TFILE);
if (pFileNameSox == NULL)
return;
#endif
int iNumItem = pStateEffectTable->GetItemCount();
for( int i=0; i<iNumItem; i++ )
{
int iItem = pStateEffectTable->GetItemID(i);
EffectOutputInfo* pNewInfo = new EffectOutputInfo;
int iStateEffectIndex = pStateEffectTable->GetFieldFromLablePtr( iItem, "_StateEffectIndex" )->GetInteger();
_ASSERT( pNewInfo->iStateEffectIndex < STATE_BLOW::BLOW_MAX );
pNewInfo->iStateEffectIndex = iStateEffectIndex;
pNewInfo->iShowTimingType = pStateEffectTable->GetFieldFromLablePtr( iItem, "_ShowTimingType" )->GetInteger();
pNewInfo->iOutputType = pStateEffectTable->GetFieldFromLablePtr( iItem, "_EffectOutputType" )->GetInteger();
pNewInfo->iPlayType = pStateEffectTable->GetFieldFromLablePtr( iItem, "_EffectPlayType" )->GetInteger();
#ifdef PRE_FIX_MEMOPT_EXT
CommonUtil::GetFileNameFromFileEXT(pNewInfo->strSkinFileName, pStateEffectTable, iItem, "_EffectSkinFileName", pFileNameSox);
CommonUtil::GetFileNameFromFileEXT(pNewInfo->strAniFileName, pStateEffectTable, iItem, "_EffectAniFileName", pFileNameSox);
CommonUtil::GetFileNameFromFileEXT(pNewInfo->strActFileName, pStateEffectTable, iItem, "_EffectActFileName", pFileNameSox);
#else
pNewInfo->strSkinFileName = pStateEffectTable->GetFieldFromLablePtr( iItem, "_EffectSkinFileName" )->GetString();
pNewInfo->strAniFileName = pStateEffectTable->GetFieldFromLablePtr( iItem, "_EffectAniFileName" )->GetString();
pNewInfo->strActFileName = pStateEffectTable->GetFieldFromLablePtr( iItem, "_EffectActFileName" )->GetString();
#endif
pNewInfo->strActorActionName = pStateEffectTable->GetFieldFromLablePtr( iItem, "_ActorActionName" )->GetString();
pNewInfo->iUseDiffuseVariationMethod = pStateEffectTable->GetFieldFromLablePtr( iItem, "_UseDiffuseVariation" )->GetInteger();
pNewInfo->afDiffuseRed[ 0 ] = (float)pStateEffectTable->GetFieldFromLablePtr( iItem, "_DiffuseR1" )->GetFloat();
pNewInfo->afDiffuseBlue[ 0 ] = (float)pStateEffectTable->GetFieldFromLablePtr( iItem, "_DiffuseG1" )->GetFloat();
pNewInfo->afDiffuseGreen[ 0 ] = (float)pStateEffectTable->GetFieldFromLablePtr( iItem, "_DiffuseB1" )->GetFloat();
pNewInfo->afDiffuseRed[ 1 ] = (float)pStateEffectTable->GetFieldFromLablePtr( iItem, "_DiffuseR2" )->GetFloat();
pNewInfo->afDiffuseBlue[ 1 ] = (float)pStateEffectTable->GetFieldFromLablePtr( iItem, "_DiffuseG2" )->GetFloat();
pNewInfo->afDiffuseGreen[ 1 ] = (float)pStateEffectTable->GetFieldFromLablePtr( iItem, "_DiffuseB2" )->GetFloat();
pNewInfo->fDiffuseChangeSpeed = (float)pStateEffectTable->GetFieldFromLablePtr( iItem, "_DiffuseChangeSpeed" )->GetFloat();
// ; 로 구분된 인덱스 값들을 받아옴
const char* pIndices = pStateEffectTable->GetFieldFromLablePtr( iItem, "_LinkBoneIndex" )->GetString();
string strIndices( pIndices );
if( (int)strIndices.length() > 0 )
{
int nFoundPos = -1;
if( strIndices.at(strIndices.length()-1) != ';' )
strIndices.push_back( ';' );
while( true )
{
int nStartPos = nFoundPos + 1;
nFoundPos = (int)strIndices.find_first_of( ';', nStartPos );
if( nFoundPos != (int)string::npos )
{
string strIndex( strIndices.substr(nStartPos, nFoundPos-nStartPos) );
pNewInfo->vlDummyBoneIndices.push_back( atoi(strIndex.c_str()) );
}
else
break;
}
}
s_nMapEffectOutputInfo.insert( make_pair( iItem, pNewInfo ) );
}
}
void CDnStateBlow::_ReleaseStateEffectOutputInfo( void )
{
SAFE_DELETE_PMAP( TMapEffectOutputInfo, s_nMapEffectOutputInfo );
}
void CDnStateBlow::_ProcessDuplicateEffectBoneRotateShow( LOCAL_TIME LocalTime, float fDelta )
{
// 프로세스를 하기 전에 유효하지 않은 이펙트는 제거.
for( int i = 0; i < CDnActor::Max_FX_Dummy_Bone; ++i )
{
list<S_BONE_EFFECT>& listBoneEffect = m_aListBoneEffectStatus[ i ];
list<S_BONE_EFFECT>::iterator iter = listBoneEffect.begin();
for( iter; iter != listBoneEffect.end(); )
{
if( !iter->hEffect )
{
bool bNextShow = iter->bShow;
iter = listBoneEffect.erase( iter );
if( listBoneEffect.end() != iter )
if( bNextShow )
iter->Show( bNextShow );
continue;
}
++iter;
}
}
for( int i = 0; i < CDnActor::Max_FX_Dummy_Bone; ++i )
{
list<S_BONE_EFFECT>& listBoneEffect = m_aListBoneEffectStatus[ i ];
// 복수개가 겹쳐있는 본은 교차출력 처리를 해준다.
if( 1 < (int)listBoneEffect.size() )
{
list<S_BONE_EFFECT>::iterator iter = listBoneEffect.begin();
for( iter; iter != listBoneEffect.end(); ++iter )
{
if( iter->bShow )
{
iter->fOutputElapsedTime += fDelta;
if( 1.0f < iter->fOutputElapsedTime )
{
list<S_BONE_EFFECT>::iterator iterNext = iter;
iterNext++;
if( listBoneEffect.end() == iterNext )
iterNext = listBoneEffect.begin();
// 기존에 출력되던 이펙트는 숨기고.
iter->Show( false );
iter->fOutputElapsedTime = 0.0f;
// 새로 보여줄 이펙트를 보여준다.
iterNext->Show( true );
iterNext->fOutputElapsedTime = 0.f;
break;
}
}
}
}
}
}
#endif
void CDnStateBlow::Process( LOCAL_TIME LocalTime, float fDelta, bool bForceInitialize )
{
#ifdef _GAMESERVER
if( m_hActor && true == s_bStopProcess[ m_hActor->GetRoom()->GetRoomID() ] )
return;
#else
if( s_bStopProcess )
return;
#endif
if( !m_listBlowHandle.empty() )
{
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
#ifndef _GAMESERVER
if( m_bAllowDiffuseVariation )
{
// diffuse variation 숫자가 가장 높은 한 놈만 색깔 바뀌는 거 출력
BLOW_HANDLE_LIST_ITER iterAllowDV = m_listBlowHandle.end();
int iBiggest = 0;
for( ; iter != m_listBlowHandle.end(); ++iter )
{
if( (*iter) && (*iter)->IsUseTableDefinedGraphicEffect() )
{
(*iter)->AllowDiffuseVariation( false );
const EffectOutputInfo* pEffectInfo = (*iter)->GetEffectOutputInfo();
if( pEffectInfo && pEffectInfo->iUseDiffuseVariationMethod > 0 )
{
if( iBiggest < pEffectInfo->iUseDiffuseVariationMethod )
{
iBiggest = pEffectInfo->iUseDiffuseVariationMethod;
iterAllowDV = iter;
}
}
}
}
if( m_listBlowHandle.end() != iterAllowDV )
{
(*iterAllowDV)->AllowDiffuseVariation( true );
}
}
#endif
#ifndef _FINAL_BUILD
_CrashIfProcessListLocked();
#endif // #ifdef _FINAL_BUILD
// 상태효과 객체들 루프돌면서 process 중에 리스트가 제거될 경우. 체크.
// 파이널 릴리즈가 아닌경우 깔금하게 죽어라.
m_bLockStateBlowList = true;
Process_StateBlow_New(LocalTime, fDelta, bForceInitialize);
}
m_bLockStateBlowList = false;
#ifndef _GAMESERVER
#if !defined(SW_MODIFY_SE_EFFECTOUTPUT_20091119_jhk8211)
_ProcessDuplicateEffectBoneRotateShow( LocalTime, fDelta );
#endif
#endif
#if defined( _GAMESERVER )
// 여기서 StateBlow::Process() OnRebith() 가 불리게 되는 경우 내부적으로 자유롭게 StateBlow::Process() 를 돌릴 수가 없어서
// 구조적으로 다 끝나고 후처리로 돌리게 바꿔놓는다.
if( m_hActor )
m_hActor->OnStateBlowProcessAfter();
#endif // #if defined( _GAMESERVER )
RemoveStateBlowReservedList();
}
int CDnStateBlow::CanAddThisBlow( const CDnSkill::SkillInfo* pParentSkill, STATE_BLOW::emBLOW_INDEX BlowIndex )
{
int iResult = ADD_SUCCESS;
// #35335 무적 상태효과일 땐 화염, 독 상태효과가 적용되지 않음.
if( IsApplied( STATE_BLOW::BLOW_099 ) )
{
if( (STATE_BLOW::BLOW_042 == BlowIndex) ||
(STATE_BLOW::BLOW_044 == BlowIndex) )
return ADD_FAIL_BY_INVINCIBLE;
}
#ifdef _GAMESERVER
// 226번 특정 스킬 확률 무적 상태효과가 해당 스킬에 대해 발동중인가.
if( pParentSkill && IsApplied( STATE_BLOW::BLOW_226 ) )
{
DNVector( DnBlowHandle ) vlhBlows;
GetStateBlowFromBlowIndex( STATE_BLOW::BLOW_226, vlhBlows );
for( int i = 0; i < (int)vlhBlows.size(); ++i )
{
bool bCanAdd = static_cast<CDnProbInvincibleAtBlow*>(vlhBlows.at(i).GetPointer())->CanAddThisSkillsStateBlow( pParentSkill->iSkillID );
if( false == bCanAdd )
return ADD_FAIL_BY_PROB_SKILL_INVINCIBLE;
}
}
#endif // #ifdef _GAMESERVER
if (pParentSkill && pParentSkill->bIgnoreImmune)
return iResult;
#if defined (_GAMESERVER)
//#34452번 이슈와 관련있음
bool bCheckGuildPriority = false;
if (pParentSkill && pParentSkill->bIsGuildSkill == false)
bCheckGuildPriority = true;
#endif
// 현재 면역된 상태효과인지 체크
// 부활(무적) 상태효과인 경우 데미지 먹는 상태효과에 걸리지 않는다.
list<DnBlowHandle>::iterator iter = m_listBlowHandle.begin();
for( iter; iter != m_listBlowHandle.end(); )
{
DnBlowHandle hNowBlow = (*iter);
if( hNowBlow )
{
if( (hNowBlow->GetBlowIndex() == STATE_BLOW::BLOW_077) )
{
bool bImmuned = static_cast<CDnImmuneBlow*>(hNowBlow.GetPointer())->IsImmuned( pParentSkill, BlowIndex );
if( bImmuned )
iResult = ADD_FAIL_BY_IMMUNE;
}
else if( (hNowBlow->GetBlowIndex() == STATE_BLOW::BLOW_150) )
{
bool bImmuned = static_cast<CDnAllImmuneBlow*>(hNowBlow.GetPointer())->IsImmuned( pParentSkill, BlowIndex );
if( bImmuned )
iResult = ADD_FAIL_BY_IMMUNE;
}
else if( IsApplied(STATE_BLOW::BLOW_057) )
{
// 부활 중일때는 자기 자신이 사용한 스킬에서 자신에게 부여하는 상태효과.. 혹은, 버프/오라 스킬의 상태효과만 적용시킨다.
if( pParentSkill )
{
CDnSkill::SkillInfo* pSkillInfo = const_cast<CDnSkill::SkillInfo*>(pParentSkill);
if( pSkillInfo->hSkillUser )
{
if( !(pSkillInfo->hSkillUser == m_hActor) &&
(CDnSkill::Buff != pSkillInfo->eDurationType) &&
(CDnSkill::Aura != pSkillInfo->eDurationType) &&
(CDnSkill::StanceChange != pSkillInfo->eDurationType) )
{
iResult = ADD_FAIL_BY_REVIVAL;
}
}
}
}
else if (hNowBlow->GetBlowIndex() == STATE_BLOW::BLOW_252)
{
//#53722 특정 스킬을 면역하는 스킬효과 개발(hit는 되지만 상태효과 추가는 되지 않도록..)
CDnIngnoreEffectBlow* pIgnoreEffectBlow = static_cast<CDnIngnoreEffectBlow*>(hNowBlow.GetPointer());
if (pIgnoreEffectBlow && pParentSkill && pIgnoreEffectBlow->IsInvincibleAt(pParentSkill->iSkillID))
iResult = ADD_FAIL_BY_IMMUNE;
}
#if defined(_GAMESERVER)
else if ( hNowBlow->GetBlowIndex() == STATE_BLOW::BLOW_242 && BlowIndex == STATE_BLOW::BLOW_242)
{
//242번 상태효과는 같은 스킬 ID이고, 같은 유저 인 경우는 더이상 추가 되지 않도록 한다.
if (pParentSkill)
{
CDnSkill::SkillInfo* pParentSkillInfo = const_cast<CDnSkill::SkillInfo*>(pParentSkill);
CDnSkill::SkillInfo* pExistSkillInfo = const_cast<CDnSkill::SkillInfo*>(hNowBlow->GetParentSkillInfo());
// #56880 스킬 시작 시간이 같은지도 확인 해야 한다..
LOCAL_TIME parentSkillStartTime = 0;
LOCAL_TIME nowBlowSkillStartTime = 0;
CDnComboDamageLimitBlow* pNowBlow = static_cast<CDnComboDamageLimitBlow*>(hNowBlow.GetPointer());
if (pNowBlow)
nowBlowSkillStartTime = pNowBlow->GetSkillStartTime();
DnSkillHandle hParentSkill;
if (pParentSkillInfo->hSkillUser)
hParentSkill = pParentSkillInfo->hSkillUser->FindSkill(pParentSkillInfo->iSkillID);
if (hParentSkill)
parentSkillStartTime = pParentSkillInfo->projectileSkillStartTime != 0 ? pParentSkillInfo->projectileSkillStartTime : hParentSkill->GetSkillStartTime();
if (pExistSkillInfo)
{
if (pParentSkillInfo->hSkillUser &&
pParentSkillInfo->hSkillUser == pExistSkillInfo->hSkillUser && //스킬 사용자가 같고
pParentSkillInfo->iSkillID == pExistSkillInfo->iSkillID && //스킬이 같은경우
nowBlowSkillStartTime == parentSkillStartTime //스킬 시작 시간이 같은 경우
)
{
iResult = ADD_FAIL_BY_COMBOLIMITBLOW;
}
}
}
}
else if (hNowBlow->GetBlowIndex() == STATE_BLOW::BLOW_249)
{
//249번 상태효과 중첩 되고 활성화?(중첩카운트 설정 수치에 도달 했을 경우) 더이상 추가 되지 않도록한다..
CDnPileAddEffectBlow* pPileAddEffectBlow = static_cast<CDnPileAddEffectBlow*>(hNowBlow.GetPointer());
if (pPileAddEffectBlow && pPileAddEffectBlow->IsActivatedBlow())
iResult = ADD_FAIL_BY_COMBOLIMITBLOW; //따로 열거형 추가 안하고 그냥 이 녀석으로 사용함..
}
#endif // _GAMESERVER
#if defined (_GAMESERVER)
if (bCheckGuildPriority && hNowBlow->GetBlowIndex() == BlowIndex)
{
const CDnSkill::SkillInfo * pNowSkillInfo = static_cast<CDnBlow*>(hNowBlow.GetPointer())->GetParentSkillInfo();
if (pNowSkillInfo->eDurationType == pParentSkill->eDurationType && pNowSkillInfo->bIsGuildSkill)
{
//같은 블로우 효과가 있는데 길드스킬로 사용된것이라면 사용불가!
iResult = ADD_FAIL_BY_GUILDBLOW_PRIORITY;
}
}
#endif
}
++iter;
}
return iResult;
}
void CDnStateBlow::_CheckImmediatelyBegin( DnBlowHandle hBlow )
{
if( STATE_BLOW::BLOW_025 == hBlow->GetBlowIndex() )
{
// 팀 반전 같은 상태효과도 있어서 팀이 반전되면 유혹 상태효과 이펙트가
// 몹과 같은 팀이 되어 붙질 않으므로 먼저 이펙트를 처리한다. #14128
#ifndef _GAMESERVER
if( hBlow->IsUseTableDefinedGraphicEffect() )
hBlow->AttachGraphicEffectDefaultType();
#else
hBlow->CheckAndStartActorActionInEffectInfo();
#endif
hBlow->OnBegin( 0, 0.0f );
m_hActor->OnBeginStateBlow( hBlow );
// 곧바로 end 로 셋팅하는 blow 도 있기 때문에 체크.
if( STATE_BLOW::STATE_END != hBlow->GetBlowState() )
hBlow->SetState( STATE_BLOW::STATE_DURATION );
}
}
int CDnStateBlow::AddStateBlow( DnBlowHandle hBlow )
{
ASSERT(hBlow&&"CDnStateBlow::AddStateBlow");
if( !hBlow ) return -1;
if( false == hBlow->IsDuplicated() )
hBlow->SetBlowID( GenerateStateBlowID() );
#ifndef _GAMESERVER
if( m_bAllowDiffuseVariation )
{
const CDnSkill::SkillInfo* pParentSkillInfo = hBlow->GetParentSkillInfo();
std::string effectOutputIDs = pParentSkillInfo ? pParentSkillInfo->effectOutputIDs : "";
#ifdef PRE_ADD_SKILL_ADDTIONAL_BUFF // 해당스킬이 적에게 영향을 미치는경우이며 , Debuff가 설정이 된경우에는 설정된 디버프 정보로 셋팅해준다.
if(pParentSkillInfo && pParentSkillInfo->iSkillUserTeam != hBlow->GetActorHandle()->GetTeam())
{
//Debuff 이펙트 설정이 있는지 확인 해서...
std::vector<std::string> infoTokens;
std::string delimiters = ";";
bool isDebuggEffect = false;
TokenizeA(pParentSkillInfo->debuffEffectOutputIDs, infoTokens, delimiters);
int nTokenSize = (int)infoTokens.size();
for (int i = 0; i < nTokenSize; ++i)
{
int nEffectOutputID = atoi(infoTokens[i].c_str());
if (nEffectOutputID != 0)
{
isDebuggEffect = true;
break;
}
}
//정상적인 값이 있는 경우.. debuff이펙트 설정으로 변경한다.
if (isDebuggEffect)
effectOutputIDs = pParentSkillInfo->debuffEffectOutputIDs;
}
#endif
EffectOutputInfo *pEffectInfo = NULL;
std::vector<std::string> infoTokens;
std::string delimiters = ";";
TokenizeA(effectOutputIDs, infoTokens, delimiters);
int nTokenSize = (int)infoTokens.size();
for (int i = 0; i < nTokenSize; ++i)
{
int nEffectOutputID = atoi(infoTokens[i].c_str());
pEffectInfo = GetEffectOutputInfo(nEffectOutputID);
if (pEffectInfo && hBlow->IsMatchStateEffectIndex(pEffectInfo->iStateEffectIndex))
{
hBlow->SetEffectOutputInfo(pEffectInfo);
}
}
}
#endif
++m_aiApplied[ hBlow->GetBlowIndex() ];
m_listBlowHandle.push_back( hBlow );
_CheckImmediatelyBegin( hBlow );
return hBlow->GetBlowID();
}
int CDnStateBlow::RemoveStateBlowByBlowDefineIndex( STATE_BLOW::emBLOW_INDEX emBlowIndex )
{
// 해당 BlowID를 넘겨 받아서 처리 하는 부분이 없음. 그래서 -1로 리턴하도록하는데.. 어떨지...[2010/12/14 semozz]
AddRemoveStateBlowInfo(RemoveStateBlowInfo(RemoveStateBlowInfo::RSBI_BLOW_INDEX, emBlowIndex));
return -1;
}
void CDnStateBlow::GetStateBlowFromBlowIndex( STATE_BLOW::emBLOW_INDEX emBlowIndex, /*IN OUT*/ DNVector(DnBlowHandle)& vlhResult )
{
int nIndex = -1;
DnBlowHandle hFindedBlow;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); )
{
DnBlowHandle hBlow = (*iter);
if( hBlow && (hBlow->GetBlowIndex() == emBlowIndex) )
{
vlhResult.push_back( hBlow );
}
++iter;
}
}
bool CDnStateBlow::IsExistStateBlowFromBlowIndex( STATE_BLOW::emBLOW_INDEX emBlowIndex )
{
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter)
{
if( (*iter) && ((*iter)->GetBlowIndex() == emBlowIndex) )
return true;
}
return false;
}
bool CDnStateBlow::IsExistStateBlowFromBlowID( int nBlowID )
{
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
if( (*iter) && ((*iter)->GetBlowID() == nBlowID) )
return true;
}
return false;
}
DnBlowHandle CDnStateBlow::GetStateBlowFromID( int nStateBlowID )
{
DnBlowHandle hFindedBlow;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
DnBlowHandle hBlow = (*iter);
if( hBlow && (hBlow->GetBlowID() == nStateBlowID) )
{
hFindedBlow = hBlow;
break;
}
}
return hFindedBlow;
}
#ifndef _GAMESERVER
DnBlowHandle CDnStateBlow::GetStateBlowFromServerID( int nServerID )
{
DnBlowHandle hFindedBlow;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
DnBlowHandle hBlow = (*iter);
if( hBlow && (hBlow->GetServerBlowID() == nServerID) )
{
hFindedBlow = hBlow;
break;
}
}
return hFindedBlow;
}
#endif
void CDnStateBlow::RemoveStateBlowFromID( int nStateBlowID )
{
AddRemoveStateBlowInfo(RemoveStateBlowInfo(RemoveStateBlowInfo::RSBI_BLOW_ID, nStateBlowID));
}
DnBlowHandle CDnStateBlow::CreateStateBlow( DnActorHandle hActor, const CDnSkill::SkillInfo* pParentSkill,
STATE_BLOW::emBLOW_INDEX emBlowIndex, int nDurationTime, const char *szParam )
{
DnBlowHandle hBlow;
//242번 상태효과는 지속 시간을 무한대로? 자동 설정 하도록 한다..
//상태효과 자체에서 Process함수에서 조건에 맞으면 자동 종료 하도록 한다(서버쪽에서만 동작)
if (emBlowIndex == STATE_BLOW::BLOW_242)
nDurationTime = -1;
// 이슈 6190 관련.
//중첩
// 효과 중첩 개수가 1 이상인 스킬의 경우 레벨의 같은 스킬이 최대 중첩개수까지 중복되어 적용된다.
// 효과 중첩 개수가 1 이상이나 스킬의 레벨이 틀릴 경우에는 별도로 적용한다.
// 최대 중첩개수까지 적용되었을때 중첩이 되는 조건의 효과가 추가로 적용되면 지속시간만 다시 초기화 된다.
// 접두어 스킬의 상태효과인 경우 중첩 처리를 하지 않는다.
bool bItemPrefixSkill = false;
if( pParentSkill && pParentSkill->bItemPrefixSkill )
bItemPrefixSkill = true;
bool bDuplicated = false;
float fReduceTimeValue = 1.0f;
if (pParentSkill && pParentSkill->eDurationType != CDnSkill::DurationTypeEnum::Instantly)
{
fReduceTimeValue = GetImmuneReduceTimeValue(emBlowIndex);
nDurationTime = (int)((float)nDurationTime * fReduceTimeValue);
#ifdef PRE_ADD_DECREASE_EFFECT
#ifdef _GAMESERVER
if( hActor && emBlowIndex != STATE_BLOW::BLOW_247 && fReduceTimeValue != 1.0f )
{
hActor->SendAddSEFail( CDnStateBlow::ADD_DECREASE_EFFECT_BY_IMMUNE, emBlowIndex );
}
#endif // _GAMESERVER
#endif // PRE_ADD_DECREASE_EFFECT
}
//#56880 242번 상태효과는 중첩 처리 안되어야 한다...
//#52905 253번 상태효과는 중첩 처리 안되어야 한다...
if( emBlowIndex != STATE_BLOW::BLOW_242 &&
emBlowIndex != STATE_BLOW::BLOW_253 &&
false == bItemPrefixSkill &&
0 < m_aiApplied[ emBlowIndex ] )
{
DNVector(DnBlowHandle) vlhResult;
GetStateBlowFromBlowIndex( emBlowIndex, vlhResult );
_ASSERT( !vlhResult.empty() );
int iNumBlow = (int)vlhResult.size();
for( int iBlow = 0; iBlow < iNumBlow; ++iBlow )
{
DnBlowHandle hExistBlow = vlhResult.at( iBlow );
if( STATE_BLOW::STATE_END != hExistBlow->GetBlowState() )
{
const CDnSkill::SkillInfo* pExistParentSkillInfo = hExistBlow->GetParentSkillInfo();
if( pExistParentSkillInfo && pParentSkill )
{
// 스킬과 레벨이 같은경우는 효과를 중첩시킵니다.
bool bLevelCheck = pExistParentSkillInfo->iSkillLevelID == pParentSkill->iSkillLevelID;
bool bIndexCheck = pExistParentSkillInfo->iSkillID == pParentSkill->iSkillID;
// #65533 PileAddEffect 같은경우는 서로 다른 레벨에 의해 생성되어도 중첩시키도록 설정합니다.
if( emBlowIndex == STATE_BLOW::BLOW_249 )
bLevelCheck = true;
if( bLevelCheck && bIndexCheck )
{
STATE_BLOW BlowInfo;
BlowInfo.emBlowIndex = emBlowIndex;
if( nDurationTime != -1 )
BlowInfo.fDurationTime = (float)nDurationTime * 0.001f;
else
BlowInfo.fDurationTime = 0.0f;
BlowInfo.szValue = szParam;
//#53448 중첩시 최종 타격자로 SkillUser를 변경 해줘야 한다.
if (emBlowIndex == STATE_BLOW::BLOW_249)
hExistBlow->SetParentSkillInfo(pParentSkill);
hExistBlow->Duplicate( BlowInfo );
hBlow = hExistBlow;
bDuplicated = true;
// Remove List 에 있는 거 빼줍시다.
REMOVE_STATEBLOW_LIST::iterator removeInfoiter = m_RemoveStateBlowList.begin();
REMOVE_STATEBLOW_LIST::iterator removeInfoEndIter = m_RemoveStateBlowList.end();
for( REMOVE_STATEBLOW_LIST::iterator it = m_RemoveStateBlowList.begin(); it != m_RemoveStateBlowList.end(); it++ ) {
if( it->m_Type == RemoveStateBlowInfo::RSBI_BLOW_ID && hExistBlow->GetBlowID() == it->m_Value ) {
m_RemoveStateBlowList.erase( it );
break;
}
}
}
}
}
}
}
if( false == bDuplicated )
{
static CDnCreateBlow createBlow;
#ifdef PRE_MOD_MAGICALBREEZE_CHANGE_BLOW
STATE_BLOW::emBLOW_INDEX emOriginalBlowIndex = emBlowIndex;
emBlowIndex = CheckMagicalBreezeChangeBlow( pParentSkill, emBlowIndex, hActor );
hBlow = createBlow.CreateBlow( emBlowIndex, hActor, szParam );
if( !hBlow ) return hBlow;
hBlow->SetOriginalBlowIndex( emOriginalBlowIndex );
#else // PRE_MOD_MAGICALBREEZE_CHANGE_BLOW
hBlow = createBlow.CreateBlow( emBlowIndex, hActor, szParam );
if( !hBlow ) return hBlow;
#endif // PRE_MOD_MAGICALBREEZE_CHANGE_BLOW
if( nDurationTime == -1 )
hBlow->SetPermanent( true );
else
hBlow->SetDurationTime( nDurationTime * 0.001f );
#ifdef PRE_ADD_DECREASE_EFFECT
#ifdef _GAMESERVER
if( emBlowIndex == STATE_BLOW::BLOW_247 && fReduceTimeValue != 1.0f )
{
CDnPuppetBlow* pDnPuppetBlow = dynamic_cast<CDnPuppetBlow*>( hBlow.GetPointer() );
if( pDnPuppetBlow )
pDnPuppetBlow->SetShowReduce( true );
}
#endif // _GAMESERVER
#endif // PRE_ADD_DECREASE_EFFECT
}
return hBlow;
}
#ifdef PRE_MOD_MAGICALBREEZE_CHANGE_BLOW
STATE_BLOW::emBLOW_INDEX CDnStateBlow::CheckMagicalBreezeChangeBlow( const CDnSkill::SkillInfo* pParentSkill, STATE_BLOW::emBLOW_INDEX emBlowIndex, DnActorHandle hActor )
{
STATE_BLOW::emBLOW_INDEX emRtnBlowIndex = emBlowIndex;
#ifdef _GAMESERVER
if( hActor == NULL ) return emRtnBlowIndex;
if( pParentSkill == NULL ) return emRtnBlowIndex;
if( pParentSkill->eApplyType != CDnSkill::StateEffectApplyType::ApplySelf || pParentSkill->eSkillType != CDnSkill::SkillTypeEnum::Active ) return emRtnBlowIndex;
if( pParentSkill->bIsItemSkill ) return emRtnBlowIndex;
if( hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_180 ) )
{
switch( emBlowIndex )
{
case STATE_BLOW::BLOW_001: // 물리 공격력 절대값 변경 -> 마법 공격력 절대값 변경
emRtnBlowIndex = STATE_BLOW::BLOW_028;
break;
case STATE_BLOW::BLOW_002: // 물리 공격력 절대값 변경 -> 마법 공격력 절대값 변경
emRtnBlowIndex = STATE_BLOW::BLOW_029;
break;
case STATE_BLOW::BLOW_200: // 물리 공격력 절대값 변경 상태효과. 스킬 상태효과 계산 되기 전에 먼저 계산됨.
emRtnBlowIndex = STATE_BLOW::BLOW_202; // ->마법 공격력 절대값 변경 상태효과. 스킬 상태효과 계산 되기 전에 먼저 계산됨.
break;
case STATE_BLOW::BLOW_201: // 물리 공격력 비율 변경 상태효과. 스킬 상태효과 계산 되기 전에 먼저 계산됨.
emRtnBlowIndex = STATE_BLOW::BLOW_203; // ->마법 공격력 비율 변경 상태효과. 스킬 상태효과 계산 되기 전에 먼저 계산됨.
break;
}
}
#endif // _GAMESERVER
return emRtnBlowIndex;
}
#endif // PRE_MOD_MAGICALBREEZE_CHANGE_BLOW
int CDnStateBlow::GenerateStateBlowID()
{
#ifdef _GAMESERVER
s_iStateBlowIDCount[ m_hActor->GetRoom()->GetRoomID() ]++;
if( s_iStateBlowIDCount[ m_hActor->GetRoom()->GetRoomID() ] >= INT_MAX )
s_iStateBlowIDCount[ m_hActor->GetRoom()->GetRoomID() ] = 0;
return s_iStateBlowIDCount[ m_hActor->GetRoom()->GetRoomID() ];
#else
s_iStateBlowIDCount++;
if( s_iStateBlowIDCount >= INT_MAX )
s_iStateBlowIDCount = 0;
return s_iStateBlowIDCount;
#endif
}
void CDnStateBlow::OnChangedWeapon()
{
DnBlowHandle hBlow;
m_bLockStateBlowList = true;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetCallBackType()&SB_ONCHANGEDWEAPON) )
{
hBlow->OnChangedWeapon();
}
}
m_bLockStateBlowList = false;
}
bool CDnStateBlow::OnDefenseAttack( DnActorHandle hHitter, CDnState* pAttackerState, CDnDamageBase::SHitParam &HitParam, bool bHitSuccess )
{
DnBlowHandle hBlow;
bool bRetDefense(bHitSuccess);
m_bLockStateBlowList = true;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetCallBackType()&SB_ONDEFENSEATTACK) )
{
if( hBlow->OnDefenseAttack( hHitter, pAttackerState, HitParam, bHitSuccess ) )
{
bRetDefense = false;
}
}
}
if( bRetDefense )
{
iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetCallBackType()&SB_AFTERONDEFENSEATTACK) )
{
if( hBlow->OnDefenseAttack( hHitter, pAttackerState, HitParam, bHitSuccess ) )
{
bRetDefense = false;
}
}
}
}
m_bLockStateBlowList = false;
return bRetDefense;
}
int CDnStateBlow::OnUseMP( int iMPDelta )
{
int iResult = iMPDelta;
DnBlowHandle hBlow;
m_bLockStateBlowList = true;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
int iDelta = 0;
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetCallBackType()&SB_ONUSEMP) )
iDelta += hBlow->OnUseMP( iMPDelta );
}
m_bLockStateBlowList = false;
iResult += iDelta;
return iResult;
}
void CDnStateBlow::DelAllStateBlow()
{
DnBlowHandle hBlow;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow )
{
if( hBlow->IsDuration() )
{
if( !m_hActor )
g_Log.Log(LogType::_ROOMCRASH, L"[DelAllStateBlow] Invalid ActorHandle\n" );
}
SAFE_RELEASE_SPTR( hBlow );
}
}
m_listBlowHandle.clear();
}
DnBlowHandle CDnStateBlow::GetStateBlow( int iIndex )
{
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( int i = 0; i < iIndex; ++i )
++iter;
return *iter;
}
float CDnStateBlow::OnCalcDamage( float fOriginalDamage, CDnDamageBase::SHitParam& HitParam )
{
float fResult = 0.0f;
DnBlowHandle hBlow;
DnBlowHandle hHighLanderBlow;
DnBlowHandle hProbInvincibleAtBlow;
float fStateBlowResult = 0.0f;
float fHighLanderResult = 0.0f;
float fProbInvincibleAtBlow = 0.0f;
m_bLockStateBlowList = true;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetCallBackType()&SB_ONCALCDAMAGE) )
{
fStateBlowResult = hBlow->OnCalcDamage( fOriginalDamage, HitParam );
if( STATE_BLOW::BLOW_143 == hBlow->GetBlowIndex() )
{
hHighLanderBlow = hBlow;
fHighLanderResult = fStateBlowResult;
}
else
if( STATE_BLOW::BLOW_226 == hBlow->GetBlowIndex() )
{
hProbInvincibleAtBlow = hBlow;
fProbInvincibleAtBlow = fStateBlowResult;
}
fResult += fStateBlowResult;
}
}
m_bLockStateBlowList = false;
// 하이랜더, 특정 스킬 확률 무시 상태효과가 있다면 해당 결과값이 우선. (데미지 없음)
if( hHighLanderBlow )
fResult = fHighLanderResult;
else
if( hProbInvincibleAtBlow && fProbInvincibleAtBlow != 0.0f) //데미지 보정값이 있을 경우만.
fResult = fProbInvincibleAtBlow;
return fResult;
}
bool CDnStateBlow::OnDie( DnActorHandle hHitter )
{
bool bProcessed = false;
// 현재는 죽을 때 스킬 쓰는 상태효과 밖에 없으므로 루프 돌지 않고 직접 지목해서 처리.
if( IsApplied( STATE_BLOW::BLOW_137 ) )
{
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
DnBlowHandle hBlow = (*iter);
if( hBlow && hBlow->GetBlowIndex() == STATE_BLOW::BLOW_137 )
{
static_cast<CDnUsingSkillWhenDieBlow*>(hBlow.GetPointer())->OnDie();
#if defined(PRE_FIX_44884)
//UsingSkillWhenDieBlow가 설정된 액터는 OnDie호출시 HP를 1로 설정되어 OnDie호출 시점을 상태효과 처리로 미룬다.
//실제 Hitter를 알 수 없는 상황이라 여기에서 저장 해놓는다.
static_cast<CDnUsingSkillWhenDieBlow*>(hBlow.GetPointer())->SetFinalHitterActor(hHitter);
#endif // PRE_FIX_44884
bProcessed = true;
}
}
}
return bProcessed;
}
void CDnStateBlow::OnTargetHit( DnActorHandle hTargetActor )
{
DnBlowHandle hBlow;
m_bLockStateBlowList = true;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetCallBackType()&SB_ONTARGETHIT) )
{
hBlow->OnTargetHit( hTargetActor );
}
}
m_bLockStateBlowList = false;
}
void CDnStateBlow::ResetStateBlowBySkillType( int nSkillDurationType )
{
AddRemoveStateBlowInfo(RemoveStateBlowInfo(RemoveStateBlowInfo::RSBI_SKILL_DURATION_TYPE, nSkillDurationType));
}
#ifndef _GAMESERVER
void CDnStateBlow::RestoreAllBlowGraphicEffect()
{
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter) {
DnBlowHandle hBlow = *iter;
if( !hBlow ) continue;
if( hBlow->IsUseTableDefinedGraphicEffect() ) {
hBlow->AttachGraphicEffectDefaultType();
}
}
}
EffectOutputInfo *CDnStateBlow::GetEffectOutputInfo( int nItemID )
{
std::map<int, EffectOutputInfo*>::iterator it = s_nMapEffectOutputInfo.find( nItemID );
if( it == s_nMapEffectOutputInfo.end() ) return NULL;
return it->second;
}
#ifndef _GAMESERVER
void CDnStateBlow::RemoveStateBlowFromServerID( int nServerBlowID )
{
AddRemoveStateBlowInfo(RemoveStateBlowInfo(RemoveStateBlowInfo::RSBI_BLOW_SERVER_ID, nServerBlowID));
}
#endif
#if !defined(SW_MODIFY_SE_EFFECTOUTPUT_20091119_jhk8211)
void CDnStateBlow::AddEffectAttachedBone( int iBone, DnEtcHandle hEffectHandle )
{
// 현재 0 번 본을 제외하고 중첩 교차 출력 하지 않음. 그냥 다 보여줌. #16291
if( CDnActor::FXDummyBoneEnum::FX_01 != iBone )
return;
// 루프 돌면서 겹치는 본은 일정 시간마다 변경해서 뿌려주자.
_ASSERT( 0 <= iBone && iBone < CDnActor::Max_FX_Dummy_Bone );
if( 0 <= iBone && iBone < CDnActor::Max_FX_Dummy_Bone )
{
S_BONE_EFFECT BoneEffect;
BoneEffect.hEffect = hEffectHandle;
// 처음 추가되는 것이라면 show 처리.
// show( false ) 가 렌더링 되고 난 후 부터는 안 먹는다.
if( m_aListBoneEffectStatus[ iBone ].empty() )
BoneEffect.Show( true );
else
BoneEffect.Show( false );
m_aListBoneEffectStatus[ iBone ].push_back( BoneEffect );
}
}
void CDnStateBlow::DelEffectAttachedBone( int iBone, DnEtcHandle hEffectHandle )
{
// 현재 0 번 본을 제외하고 중첩 교차 출력 하지 않음. 그냥 다 보여줌. #16291
if( CDnActor::FXDummyBoneEnum::FX_01 != iBone )
return;
_ASSERT( 0 <= iBone && iBone < CDnActor::Max_FX_Dummy_Bone );
if( 0 <= iBone && iBone < CDnActor::Max_FX_Dummy_Bone )
{
list<S_BONE_EFFECT>& listBoneEffect = m_aListBoneEffectStatus[ iBone ];
list<S_BONE_EFFECT>::iterator iter = find( listBoneEffect.begin(),
listBoneEffect.end(), hEffectHandle );
if( listBoneEffect.end() != iter )
{
// 현재 중첩된 이펙트 본 자리이며 지금 삭제할 녀석이 현재 발동중이라면..
// 다음 본에게 곧장 차례를 넘긴다.
// 이미 어디선가 이펙트 객체가 삭제되었다면 bone effect status list 에서 삭제만 한다.
if( 1 < (int)listBoneEffect.size() )
{
if( iter->bShow )
{
list<S_BONE_EFFECT>::iterator iterNext = iter;
iterNext++;
if( listBoneEffect.end() == iterNext )
iterNext = listBoneEffect.begin();
iterNext->Show( true );
}
}
m_aListBoneEffectStatus[ iBone ].erase( iter );
}
}
}
#endif
#endif
void CDnStateBlow::OnCmdActionFromPacket( const char* pActionName )
{
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
BLOW_HANDLE_LIST_ITER iterEnd = m_listBlowHandle.end();
for( iter; iter != iterEnd; ++iter )
{
if( *iter )
(*iter)->OnCmdActionFromPacket( pActionName );
}
}
#ifdef _GAMESERVER
CDnSkill::CanApply CDnStateBlow::CanApplySkillStateEffect(const CDnSkill::SkillInfo *pUsingSkillInfo, const CDnSkill::StateEffectStruct &newStateEffect)
{
// 상태효과 검사할때 리셋리스트 초기화 [2010/12/09 semozz]
if (m_hActor)
m_hActor->InitStateBlowIDToRemove();
CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply;
// 1. 새로운 상태가 지속 시간이 있다면 추가 시킨다.
if ((CDnSkill::IsNeedCheckApplyStateBlow( (STATE_BLOW::emBLOW_INDEX)newStateEffect.nID)) ||
(newStateEffect.nDurationTime > 0) )
{
// [2010/11/12 semozz]
// 2. 지속 시간이 있는 StateBlow일 경우
// 일단 기존 스킬 사용 여부 체크와 동일한 코드 사용..
// 이 부분은 검토 필요
STATE_BLOW::emBLOW_INDEX newBlowIndex = (STATE_BLOW::emBLOW_INDEX)newStateEffect.nID;
if( IsApplied( newBlowIndex ) )
{
DNVector(DnBlowHandle) vlhResult;
GetStateBlowFromBlowIndex( newBlowIndex, vlhResult );
_ASSERT( !vlhResult.empty() );
vector<int> vlStateBlowIDToRemove;
DNVector( DnBlowHandle ) vlhSameSkillBlows;
bool bExistingSameSkill = false;
int iExistingSameSkillCount = 0;
int iNumBlow = (int)vlhResult.size();
for( int iBlow = 0; iBlow < iNumBlow; ++iBlow )
{
DnBlowHandle hExistingBlow = vlhResult.at( iBlow );
const CDnSkill::SkillInfo* pExistingParentSkillInfo = hExistingBlow ? hExistingBlow->GetParentSkillInfo() : NULL;
// 모체 스킬 정보가 없는 상태효과는 스킬로 걸린 것이 아니므로 여기서 다룰 대상이 아님
if ( NULL == pExistingParentSkillInfo )
continue;
// 스킬 중복 카운트 체크
// 같은 스킬 아이디에 동일한 상태효과가 또 나타난다면 중복된 스킬임.
bool bIsSameSkillID = false;
#if defined(PRE_FIX_58505)
//접미사 스킬 타입이 설정 되어 있지 않으면 스킬 아이디로 비교하고,
//접미사 스킬 타입이 설정 되어 있으면 접미사 스킬 타입이 같은지 비교.
if ( pUsingSkillInfo->bItemPrefixSkill == true && pUsingSkillInfo->nPrefixSkillType != CDnPrefixSkill::Prefix_Non )
bIsSameSkillID = pUsingSkillInfo->nPrefixSkillType == pExistingParentSkillInfo->nPrefixSkillType;
else
bIsSameSkillID = pUsingSkillInfo->iSkillID == pExistingParentSkillInfo->iSkillID;
#else
if( pUsingSkillInfo->iSkillID == pExistingParentSkillInfo->iSkillID )
bIsSameSkillID = true;
#endif // PRE_FIX_58505
if (bIsSameSkillID == true)
{
// 같은 스킬 아이디가 처음 나온다면 처음엔 적용중 스킬 카운트 하나 올려줌.
iExistingSameSkillCount += hExistingBlow->GetDuplicateCount();
bExistingSameSkill = true;
vlhSameSkillBlows.push_back( hExistingBlow );
}
if (pUsingSkillInfo->eTargetType == CDnSkill::TargetTypeEnum::All //지금 사용 하는 스킬의 TargetType이 All이고,
&& (0 != pUsingSkillInfo->iSkillDuplicateMethod && (pUsingSkillInfo->iSkillDuplicateMethod == pExistingParentSkillInfo->iSkillDuplicateMethod)) //중첩 처리ID가 설정되어 있고, 기존 스킬과 동일한 ID일때,
&& (m_hActor == pUsingSkillInfo->hSkillUser) //자기 자신
)
continue;
// 지속효과 구분 인덱스가 0이 아니라면 스킬 효과 중복 관련 처리가 필요함.
// 지속효과 구분 인덱스가 0이면 그냥 중첩됨.
// 혹은 같은 스킬이면서 최대 중첩 카운트가 1이하인경우엔 스킬 대체되도록 처리.
#if defined(PRE_FIX_58505)
if( 0 != pUsingSkillInfo->iSkillDuplicateMethod ||
(bIsSameSkillID == true && pUsingSkillInfo->iDuplicateCount <= 1) )
#else
if( 0 != pUsingSkillInfo->iSkillDuplicateMethod ||
(pUsingSkillInfo->iSkillID == pExistingParentSkillInfo->iSkillID && pUsingSkillInfo->iDuplicateCount <= 1) )
#endif // PRE_FIX_58505
{
// 기존에 실행되고 있던 스킬과 같은 지속효과 인덱스인가. 그렇다면 중첩이 가능한지 체크들어간다.
// 또한 스킬을 쓴 유저의 팀이 같을때만 중첩처리를 하도록 한다.
// 다른 팀인데 같은 스킬을 쓸 경우 나에게 걸린 디버프 상태효과가 해제되어 버린다. (#19812)
if( (pUsingSkillInfo->iSkillDuplicateMethod == pExistingParentSkillInfo->iSkillDuplicateMethod) &&
(pUsingSkillInfo->iSkillUserTeam == pExistingParentSkillInfo->iSkillUserTeam) )
{
if( pUsingSkillInfo->iLevel >= pExistingParentSkillInfo->iLevel )
{
// 242번 상태효과는 한번 추가가 되면 리셋 되면 안된다.
// 확률 체크에서 통과한다면 리셋시킬 상태효과에 넣어둠.
// 실제 확률체크하는 객체는 기존에 적용된 상태효과 객체를 사용하지만 확률만 체크하는 것이기 때문에
// 그대로 사용한다.
if( hExistingBlow->GetBlowIndex() != STATE_BLOW::BLOW_242 &&
hExistingBlow->CanBegin() )
{
vlStateBlowIDToRemove.push_back( hExistingBlow->GetBlowID() );
//mapDuplicateResult[ hExistingBlow->GetBlowIndex() ] = true;
// 리셋되어야할 상태들을 담아 놓는다. [2010/12/08 semozz]
// 여기서 리스트에 담긴 상태들은 OnSignal의 STE_ApplyStateEffect 시점에 상태 적용할때
// 기존의 상태를 제거 한다.
m_hActor->AddStateBlowIDToRemove(hExistingBlow->GetBlowID());
}
else
{
eResult = CDnSkill::CanApply::Fail; // 효과 갱신 시 확률 체크에서 실패했으므로 추가하지 않는다.
}
}
else if( pUsingSkillInfo->iLevel < pExistingParentSkillInfo->iLevel )
{
// 효과 적용 안됨.
eResult = CDnSkill::CanApply::Fail;
// Note 한기: 추후에 그래픽적으로 뭔가 표시해줘야 한다면 이 곳에서 처리하면 됨.
}
}
}
}
if( false == vlStateBlowIDToRemove.empty() )
{
// 스킬 효과 대체. 중첩 카운트 세었던 것 처리할 필요 없다.
bExistingSameSkill = false;
eResult = CDnSkill::CanApply::ApplyDuplicateSameSkill;
}
// 상태 효과를 중첩해서 적용해야하는 경우, 최대 중첩 갯수가 넘으면 안된다.
// 높은 레벨의 상태효과가 이미 적용중이라면 중첩처리가 되지 않는다.
if( CDnSkill::CanApply::Fail != eResult )
{
if( bExistingSameSkill )
{
if( pUsingSkillInfo->iDuplicateCount <= iExistingSameSkillCount )
{
// 최대 중첩 갯수를 넘는 경우 기존 상태효과의 시간만 초기화 한다.
int iNumSameSkillBlowsToResetDurationTime = (int)vlhSameSkillBlows.size();
for( int iBlow = 0; iBlow < iNumSameSkillBlowsToResetDurationTime; ++iBlow )
{
DnBlowHandle hBlow = vlhSameSkillBlows.at( iBlow );
//#53448 249번 상태효과 경우 중첩 갯수 이상은 추가 될 수 없다.
//최대 중첩 갯수를 넘은 경우 아래 루프를 돌면서 기존 상태효과 제거 하고, 다시 추가 작업 필요 없음.
if (hBlow && hBlow->GetBlowIndex() == STATE_BLOW::BLOW_249)
continue;
// 아예 삭제했다가 새로 추가하는 것이 명확함.
int nDurationTime = int(hBlow->GetDurationTime() * 1000.0f);
hBlow->ResetDurationTime();
CDnSkill::SkillInfo SkillInfo = *(hBlow->GetParentSkillInfo());
STATE_BLOW::emBLOW_INDEX BlowIndex = hBlow->GetBlowIndex();
string strValue( hBlow->GetValue() );
// 이 시점에서 즉시 지워저야 한다. [2011/01/18 semozz]
int nBlowID = hBlow->GetBlowID();
// 패킷 보낼꺼 보내고
m_hActor->SendRemoveStateEffectFromID(nBlowID);
// 바로 삭제한다..
if (m_hActor->GetStateBlow())
m_hActor->GetStateBlow()->RemoveImediatlyStateEffectFromID(nBlowID);
// 여기에 결빙같은 확률 있는 상태효과를 100% 성공시켜야 하므로
// CanBegin 함수 호출하지 않도록 bCheckCanBegin 플래그를 꺼서 호출.
m_hActor->CmdAddStateEffect( &SkillInfo, BlowIndex, nDurationTime, strValue.c_str(), false, false );
}
eResult = CDnSkill::CanApply::Fail;
}
}
}
}
}
return eResult;
}
#endif
bool RemoveStateBlowInfo::Compare(DnBlowHandle hBlow)
{
bool result = false;
if (!hBlow)
return result;
switch(m_Type)
{
case RSBI_BLOW_INDEX:
{
if( hBlow->IsEternity() == false && hBlow->IsFromSourceItem() == false )
result = (hBlow->GetBlowIndex() == m_Value);
}
break;
case RSBI_BLOW_ID:
{
result = (hBlow->GetBlowID() == m_Value);
}
break;
case RSBI_SKILL_DURATION_TYPE:
{
const CDnSkill::SkillInfo* pSkillInfo = hBlow->GetParentSkillInfo();
result = ( pSkillInfo && (CDnSkill::DurationTypeEnum)m_Value == pSkillInfo->eDurationType );
}
break;
#if !defined(_GAMESERVER)
case RSBI_BLOW_SERVER_ID:
{
result = (hBlow->GetServerBlowID() == m_Value);
}
break;
#endif
}
return result;
}
void CDnStateBlow::RemoveStateBlowReservedList()
{
REMOVE_STATEBLOW_LIST::iterator removeInfoiter = m_RemoveStateBlowList.begin();
#ifndef _FINAL_BUILD
_CrashIfRemoveListLocked();
#endif // #ifdef _FINAL_BUILD
m_bLockRemoveReservedList = true;
for (; removeInfoiter != m_RemoveStateBlowList.end(); ++removeInfoiter)
{
RemoveStateBlowInfo &removeInfo = (*removeInfoiter);
DnBlowHandle hBlow;
#ifndef _FINAL_BUILD
_CrashIfProcessListLocked();
#endif // #ifdef _FINAL_BUILD
m_bLockStateBlowList = true;
BLOW_HANDLE_LIST_ITER blowListiter = m_listBlowHandle.begin();
for( ; blowListiter != m_listBlowHandle.end(); )
{
hBlow = (*blowListiter);
if (removeInfo.Compare(hBlow))
{
//ResetStateBlowBySkillType 함수에서 패킷 클라이언트로 보내는 코드 있음.
if (RemoveStateBlowInfo::RSBI_SKILL_DURATION_TYPE == removeInfo.m_Type)
{
#if defined(_GAMESERVER)
STATE_BLOW::emBLOW_INDEX emBlowIndex = hBlow->GetStateBlow().emBlowIndex;
// 클라에게도 삭제하라고 패킷만 날려줌
m_hActor->SendRemoveStateEffect(emBlowIndex);
#endif // _GAMESERVER
}
_ASSERT( m_aiApplied[ hBlow->GetBlowIndex() ] > 0 );
--m_aiApplied[ hBlow->GetBlowIndex() ];
hBlow->OnEnd( 0, 0 );
m_hActor->OnEndStateBlow( hBlow );
SAFE_RELEASE_SPTR( hBlow );
blowListiter = m_listBlowHandle.erase(blowListiter);
//BlowIndex, DuratioType은 전부 찾아서 지운다.
if (RemoveStateBlowInfo::RSBI_BLOW_INDEX == removeInfo.m_Type ||
RemoveStateBlowInfo::RSBI_SKILL_DURATION_TYPE == removeInfo.m_Type)
continue;
else
break;
}
++blowListiter;
}
m_bLockStateBlowList = false;
}
m_RemoveStateBlowList.clear();
m_bLockRemoveReservedList = false;
}
void CDnStateBlow::AddRemoveStateBlowInfo(RemoveStateBlowInfo info)
{
//상태효과 제거를 위해 등록은 루프 돌면서도 가능..
m_RemoveStateBlowList.push_back(info);
}
void CDnStateBlow::RemoveImediatlyStateEffectFromID(int nStateBlowID)
{
#ifndef _FINAL_BUILD
_CrashIfProcessListLocked();
#endif // #ifdef _FINAL_BUILD
DnBlowHandle hBlow;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetBlowID() == nStateBlowID) )
{
_ASSERT( m_aiApplied[ hBlow->GetBlowIndex() ] > 0 );
--m_aiApplied[ hBlow->GetBlowIndex() ];
hBlow->OnEnd( 0, 0 );
m_hActor->OnEndStateBlow( hBlow );
OutputDebug("%s EndBlowID: %d Index: %d", __FUNCTION__, hBlow->GetBlowID(), hBlow->GetBlowIndex());
SAFE_RELEASE_SPTR( hBlow );
m_listBlowHandle.erase(iter);
break;
}
}
}
void CDnStateBlow::RemoveImediatlyStateEffectByBlowIndex(STATE_BLOW::emBLOW_INDEX emBlowIndex)
{
#ifndef _FINAL_BUILD
_CrashIfProcessListLocked();
#endif // #ifdef _FINAL_BUILD
int nIndex = -1;
DnBlowHandle hBlow;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); )
{
hBlow = (*iter);
if( hBlow && ( ( hBlow->IsEternity() == false && hBlow->IsFromSourceItem() == false ) && hBlow->GetBlowIndex() == emBlowIndex ) )
{
_ASSERT( m_aiApplied[ hBlow->GetBlowIndex() ] > 0 );
--m_aiApplied[ hBlow->GetBlowIndex() ];
hBlow->OnEnd( 0, 0 );
m_hActor->OnEndStateBlow( hBlow );
nIndex = hBlow->GetBlowID();
SAFE_RELEASE_SPTR( hBlow );
iter = m_listBlowHandle.erase(iter);
continue;
}
++iter;
}
}
#if !defined(_GAMESERVER)
void CDnStateBlow::RemoveImediatlyStateEffectByServerID(int nServerBlowID)
{
#ifndef _FINAL_BUILD
_CrashIfProcessListLocked();
#endif // #ifdef _FINAL_BUILD
DnBlowHandle hBlow;
BLOW_HANDLE_LIST_ITER iter = m_listBlowHandle.begin();
for( ; iter != m_listBlowHandle.end(); ++iter )
{
hBlow = (*iter);
if( hBlow && (hBlow->GetServerBlowID() == nServerBlowID) )
{
_ASSERT( m_aiApplied[ hBlow->GetBlowIndex() ] > 0 );
--m_aiApplied[ hBlow->GetBlowIndex() ];
hBlow->OnEnd( 0, 0 );
m_hActor->OnEndStateBlow( hBlow );
SAFE_RELEASE_SPTR( hBlow );
m_listBlowHandle.erase(iter);
break;
}
}
}
#endif // _GAMESERVER
bool CDnStateBlow::IsImmuned(STATE_BLOW::emBLOW_INDEX blowIndex)
{
// 무적 상태효과 있을 때 버프 상태효과를 제외하곤 전부 제외.
if( IsApplied( STATE_BLOW::BLOW_099 ) )
{
return false;
}
// 현재 면역된 상태효과인지 체크
// 부활(무적) 상태효과인 경우 데미지 먹는 상태효과에 걸리지 않는다.
list<DnBlowHandle>::iterator iter = m_listBlowHandle.begin();
for( iter; iter != m_listBlowHandle.end(); )
{
DnBlowHandle hNowBlow = (*iter);
if( hNowBlow )
{
if( (hNowBlow->GetBlowIndex() == STATE_BLOW::BLOW_077) )
{
bool bImmuned = static_cast<CDnImmuneBlow*>(hNowBlow.GetPointer())->IsImmuned( blowIndex );
if (bImmuned)
return true;
}
else
if( (hNowBlow->GetBlowIndex() == STATE_BLOW::BLOW_150) )
{
bool bImmuned = static_cast<CDnAllImmuneBlow*>(hNowBlow.GetPointer())->IsImmuned( blowIndex );
if (bImmuned)
return true;
}
}
++iter;
}
return false;
}
#ifndef _FINAL_BUILD
void CDnStateBlow::_CrashIfProcessListLocked( void )
{
if( m_bLockStateBlowList )
{
_ASSERT( 0 );
int* pNull = NULL;
*pNull = 0xffffeeee;
}
}
void CDnStateBlow::_CrashIfRemoveListLocked( void )
{
if( m_bLockRemoveReservedList )
{
_ASSERT( 0 );
int* pNull = NULL;
*pNull = 0xeeeeffff;
}
}
#endif // #ifdef _FINAL_BUILD
void CDnStateBlow::Process_StateBlow_Old(LOCAL_TIME LocalTime, float fDelta, bool bForceInitialize)
{
BLOW_HANDLE_LIST::iterator iter;
DnBlowHandle hBlow;
for( iter = m_listBlowHandle.begin(); iter != m_listBlowHandle.end(); )
{
hBlow = (*iter);
if( hBlow )
{
// 게임서버 덤프 수정 확인용.
if( !(hBlow->GetActorHandle()) )
hBlow->SetActorHandle( m_hActor );
//////////////////////////////////////////////////////////////////////////
if( hBlow->IsBegin() )
{
// 팀 반전 같은 상태효과도 있어서 팀이 반전되면 유혹 상태효과 이펙트가
// 몹과 같은 팀이 되어 붙질 않으므로 먼저 이펙트를 처리한다. #14128
#ifndef _GAMESERVER
if( hBlow->IsUseTableDefinedGraphicEffect() )
hBlow->AttachGraphicEffectDefaultType();
#else
hBlow->CheckAndStartActorActionInEffectInfo();
#endif
hBlow->OnBegin( LocalTime, fDelta );
m_hActor->OnBeginStateBlow( hBlow );
// 곧바로 end 로 셋팅하는 blow 도 있기 때문에 체크.
if( STATE_BLOW::STATE_END != hBlow->GetBlowState() )
hBlow->SetState( STATE_BLOW::STATE_DURATION );
}
else if( hBlow->IsDuration() )
{
hBlow->Process( LocalTime, fDelta );
}
else if( hBlow->IsEnd() )
{
RemoveStateBlowFromID(hBlow->GetBlowID());
}
}
// 각각의 blow 후에 process 후에 독, 화상 걸려서 interval 데미지에 죽거나 하는 경우엔 패시브 스킬이 바로 다시 붙게 되는데
// 죽었을 땐 부활 상태효과만 처리한다.
#ifdef _GAMESERVER
// 게임 서버일땐 액터가 gm trace 일 때도 아래 부활 처리 루틴으로 들어가기 때문에 조건을 하나 더 걸어준다.
if( !m_hActor->IsGMTrace() && !bForceInitialize && m_hActor->IsPlayerActor() && m_hActor->IsDie() )
#else
if( !bForceInitialize && m_hActor->IsPlayerActor() && m_hActor->IsDie() )
#endif
{
// 부활 상태효과가 없다면 루프 종료
bool bExistRebirth = false;
for( iter = m_listBlowHandle.begin(); m_listBlowHandle.end() != iter; ++iter )
{
if( !(*iter) )
continue;
if( (*iter)->GetBlowIndex() == STATE_BLOW::BLOW_057 || (*iter)->GetBlowIndex() == STATE_BLOW::BLOW_230 )
{
bExistRebirth = true;
break;
}
}
if( false == bExistRebirth )
break;
}
else
++iter;
}
}
void CDnStateBlow::Process_StateBlow_New(LOCAL_TIME LocalTime, float fDelta, bool bForceInitialize)
{
//죽은상태, Rebirth상태효과 존재 유무
bool bIsDie = false;
bool bExistRebirth = false;
BLOW_HANDLE_LIST::iterator rebirthStateIter = m_listBlowHandle.end();
BLOW_HANDLE_LIST::iterator spectatorStateIter = m_listBlowHandle.end();
#ifdef _GAMESERVER
// 게임 서버일땐 액터가 gm trace 일 때도 아래 부활 처리 루틴으로 들어가기 때문에 조건을 하나 더 걸어준다.
if( !m_hActor->IsGMTrace() && !bForceInitialize && m_hActor->IsPlayerActor() && m_hActor->IsDie() )
#else
if( !bForceInitialize && m_hActor->IsPlayerActor() && m_hActor->IsDie() )
#endif
{
bIsDie = true;
for( BLOW_HANDLE_LIST::iterator iter = m_listBlowHandle.begin(); m_listBlowHandle.end() != iter; ++iter )
{
if( !(*iter) )
continue;
if( (*iter)->GetBlowIndex() == STATE_BLOW::BLOW_057 )
{
bExistRebirth = true;
rebirthStateIter = iter;
}
if( (*iter)->GetBlowIndex() == STATE_BLOW::BLOW_230 )
{
bExistRebirth = true;
spectatorStateIter = iter;
}
}
}
//죽은 상태이고,
if (bIsDie)
{
// 죽은 상태효과에서 Rebirth상태효과가 존재 한다면 Rebirth상태효과만 처리
if (rebirthStateIter != m_listBlowHandle.end())
{
DnBlowHandle hRebirthBlow = (*rebirthStateIter);
Process_BlowHandle(hRebirthBlow, LocalTime, fDelta);
}
if (spectatorStateIter != m_listBlowHandle.end())
{
DnBlowHandle hSpectatorBlow = (*spectatorStateIter);
Process_BlowHandle(hSpectatorBlow, LocalTime, fDelta);
}
//죽은 상태일경우 다른 상태효과는 처리 안하도록 여기서 리턴...
return;
}
DnBlowHandle hBlow;
for( BLOW_HANDLE_LIST::iterator iter = m_listBlowHandle.begin(); iter != m_listBlowHandle.end(); )
{
hBlow = (*iter);
//유효하지 않은 상태효과는 리스트에서 바로 제거 해 버린다..
if (!hBlow)
{
iter = m_listBlowHandle.erase(iter);
continue;
}
Process_BlowHandle(hBlow, LocalTime, fDelta);
++iter;
}
}
void CDnStateBlow::Process_BlowHandle(DnBlowHandle hBlow, LOCAL_TIME LocalTime, float fDelta)
{
if (!hBlow)
return;
if( hBlow->IsBegin() )
{
// 팀 반전 같은 상태효과도 있어서 팀이 반전되면 유혹 상태효과 이펙트가
// 몹과 같은 팀이 되어 붙질 않으므로 먼저 이펙트를 처리한다. #14128
#ifndef _GAMESERVER
if( hBlow->IsUseTableDefinedGraphicEffect() )
hBlow->AttachGraphicEffectDefaultType();
#else
hBlow->CheckAndStartActorActionInEffectInfo();
#endif
hBlow->OnBegin( LocalTime, fDelta );
m_hActor->OnBeginStateBlow( hBlow );
// 곧바로 end 로 셋팅하는 blow 도 있기 때문에 체크.
if( STATE_BLOW::STATE_END != hBlow->GetBlowState() )
hBlow->SetState( STATE_BLOW::STATE_DURATION );
}
else if( hBlow->IsDuration() )
{
hBlow->Process( LocalTime, fDelta );
}
else if( hBlow->IsEnd() )
{
RemoveStateBlowFromID(hBlow->GetBlowID());
}
}
float CDnStateBlow::GetImmuneReduceTimeValue(STATE_BLOW::emBLOW_INDEX blowIndex)
{
float fReduceTimeValue = 1.0f;
//면역 상태효과를 돌면서 blowIndex에 면역인 상태효과에서 지속시간 비율 변경 값을 얻어 온다.
DNVector(DnBlowHandle) vlhResult;
GetStateBlowFromBlowIndex( STATE_BLOW::BLOW_077, vlhResult );
int nListCount = (int)vlhResult.size();
for (int i = 0; i < nListCount; ++i)
{
DnBlowHandle hExistBlow = vlhResult.at( i );
int nBlowIndex = (int)hExistBlow->GetFloatValue();
if ((STATE_BLOW::emBLOW_INDEX)(nBlowIndex) == blowIndex)
{
//blowIndex 지속시간 비율 변경 값중에 제일 작은 값을 얻는다..
float fValue = static_cast<CDnImmuneBlow*>(hExistBlow.GetPointer())->GetReduceTimeValue();
if (fReduceTimeValue > fValue)
fReduceTimeValue = fValue;
}
}
return fReduceTimeValue;
}
#ifdef _GAMESERVER
#ifdef PRE_ADD_PROJECTILE_SE_INFO
void CDnStateBlow::MakeCloneStateBlowList( BLOW_HANDLE_LIST BlowList )
{
if( BlowList.empty() )
return;
static CDnCreateBlow createBlow;
BLOW_HANDLE_LIST_ITER iter = BlowList.begin();
for( iter; iter != BlowList.end() ; ++iter )
{
DnBlowHandle hBlow = (*iter);
if( hBlow )
{
if( hBlow->GetBlowIndex() > STATE_BLOW::BLOW_NONE && hBlow->GetBlowIndex() < STATE_BLOW::BLOW_MAX )
{
DnBlowHandle hBlow = createBlow.CreateBlow( (*iter)->GetBlowIndex() , (*iter)->GetActorHandle(), (*iter)->GetValue() );
m_listBlowHandle.push_back( hBlow );
++m_aiApplied[ hBlow->GetBlowIndex() ];
}
}
}
}
#endif
#endif