#include "StdAfx.h" #include "MASkillUser.h" #include "DnSkill.h" #include "IDnSkillUsableChecker.h" #include "IDnSkillProcessor.h" #include "InputReceiver.h" #include "DnProjectile.h" #include "DnPlayerActor.h" #include "DnChangeProjectileProcessor.h" #include "DnChangeActionStrProcessor.h" #include "DnBlow.h" #include "DnSkillTask.h" #include "DnStateBlow.h" #ifndef _GAMESERVER #include "DnCantUseActiveSkillBlow.h" #include "DnWorld.h" #include "TaskManager.h" #include "DnItemTask.h" #include "DnInterface.h" #else #include "DNUserSession.h" #include "DnApplySEWhenTargetNormalHitProcessor.h" #endif #if defined(_GAMESERVER) #include "DnProbabilityChecker.h" #endif #include "DnCreateBlow.h" #include "SmartPtrDef.h" #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) #include "TotalLevelSkillSystem.h" #endif // PRE_ADD_TOTAL_LEVEL_SKILL #ifdef _DEBUG #define new new(_NORMAL_BLOCK,__FILE__,__LINE__) #endif MASkillUser::MASkillUser() //: m_vSkillTargetPos( 0.f, 0.f, 0.f ) { m_LocalTime = 0; m_pActor = NULL; m_bIsValidActor = true; //m_fAdjustCoolTime = 1.0f; #ifdef _GAMESERVER m_bIgnoreCoolTime = false; #else m_bSkillExecutedThisFrame = false; #endif m_fCoolTimeDeltaAdjustValue = 1.0f; //ZeroMemory( m_aEternityItemApplyStat, sizeof(m_aEternityItemApplyStat) ); } MASkillUser::~MASkillUser() { #ifndef PRE_FIX_SKILLLIST for( DWORD i=0; isecond.hSkill) SAFE_RELEASE_SPTR(iter->second.hSkill); } SAFE_DELETE_MAP(m_prefixSystemDefenceSkills); //공격용 스킬 초기화 iter = m_prefixSystemOffenceSkills.begin(); endIter = m_prefixSystemOffenceSkills.end(); for (; iter != endIter; ++iter) { if (iter->second.hSkill) SAFE_RELEASE_SPTR(iter->second.hSkill); } SAFE_DELETE_MAP(m_prefixSystemOffenceSkills); #endif // _GAMESERVER #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) #if defined(_GAMESERVER) { //접두사 방어용 스킬 PREFIX_SKILL_INFO::iterator iter = m_PrefixDefenceSkills.begin(); PREFIX_SKILL_INFO::iterator endIter = m_PrefixDefenceSkills.end(); for (; iter != endIter; ++iter) { if(iter->second) SAFE_DELETE(iter->second); } m_PrefixDefenceSkills.clear(); //접두사 공격용 스킬 iter = m_PrefixOffenceSkills.begin(); endIter = m_PrefixOffenceSkills.end(); for (; iter != endIter; ++iter) { if(iter->second) SAFE_DELETE(iter->second); } m_PrefixOffenceSkills.clear(); //사용중인 접두사 공격용 스킬 리스트 m_ProcessPrefixOffenceSkills.clear(); } #endif // _GAMESERVER #endif // PRE_ADD_PREFIX_SYSTE_RENEW } bool MASkillUser::IsValidActor() { if( m_pActor ) return true; else { if( !m_bIsValidActor ) return false; m_pActor = dynamic_cast(this); if( !m_pActor ) m_bIsValidActor = false; } return true; } void MASkillUser::OnAddSkill( DnSkillHandle hSkill, bool isInitialize/* = false*/ ) { // 패시브 이면서 버프고 상태효과 Self 로 붙어있는 스킬은 곧바로 적용시켜 줌.. if( !IsValidActor() ) return; bool bPassiveBuff = false; bPassiveBuff = ApplyPassiveSkill( hSkill, isInitialize ); #ifdef _GAMESERVER if( CDnSkill::AutoPassive == hSkill->GetSkillType() ) m_listAutoPassiveSkills.push_back( hSkill ); #else const set& setUseAction = hSkill->GetUseActionSet(); set::const_iterator iter = setUseAction.begin(); for( iter; iter != setUseAction.end(); ++iter ) m_setUseActionNames.insert( *iter ); #endif if( bPassiveBuff ) m_vlhSelfPassiveBlowSkill.push_back( hSkill ); } bool MASkillUser::AddSkill( int nSkillTableID, int nLevel/* = 1*/, int iSkillLevelApplyType/* = CDnSkill::PVE*/ ) { if( IsExistSkill( nSkillTableID, nLevel ) ) return false; if( !IsValidActor() ) return false; #ifdef _GAMESERVER if( m_pActor == NULL || m_pActor->GetRoom() == NULL ) return false; #endif //레벨업 정보가 있으면 추가 한다. int nLevelUp = GetSkillLevelUpValue(nSkillTableID); nLevel += nLevelUp; DnSkillHandle hSkill = CDnSkill::CreateSkill( m_pActor->GetMySmartPtr(), nSkillTableID, nLevel ); //레벨업을 시도 했는데 스킬 생성이 안됐다면 if (nLevelUp != 0 && !hSkill) { OutputDebug("스킬 (%d) 레벨 (%d) 생성 실패.. 스킬 레벨 테이블 확인 요망!!!!!!\n", nSkillTableID, nLevel); _ASSERT( hSkill && "스킬 레벨업 시도 실패.. 레벨 테이블 확인" ); //다시 원래 레벨로 생성 시도.. nLevel -= nLevelUp; hSkill = CDnSkill::CreateSkill(m_pActor->GetMySmartPtr(), nSkillTableID, nLevel); nLevelUp = 0; } if( !hSkill ) return false; //추가된 레벨업 정보를 설정 한다. hSkill->SetLevelUpValue(nLevelUp); // 스킬레벨 테이블 적용타입 셋팅 if( m_pActor->IsPlayerActor() ) hSkill->SelectLevelDataType( iSkillLevelApplyType ); #ifndef PRE_FIX_SKILLLIST m_vlhSkillList.push_back( hSkill ); m_vbSelfAllocList.push_back( true ); #else m_vlSkillObjects.push_back( S_SKILL_OBJECT(hSkill, true) ); #endif // #ifndef PRE_FIX_SKILLLIST // 토글, 오라 스킬은 따로 리스트에 담아둠 CDnSkill::DurationTypeEnum eDurationType = hSkill->GetDurationType(); switch(eDurationType) { case CDnSkill::ActiveToggle: case CDnSkill::TimeToggle: case CDnSkill::ActiveToggleForSummon: m_vlhToggleSkills.push_back(hSkill); case CDnSkill::Aura: m_vlhAuraSkills.push_back(hSkill); } if( hSkill->GetActor() ) hSkill->SetHasActor( m_pActor->GetMySmartPtr() ); OnAddSkill( hSkill ); return true; } #if defined(PRE_FIX_62052) void MASkillUser::ApplyGlobalSkillCoolTime(DnSkillHandle hSkill) { int nGlobalSkillGroupID = hSkill ? hSkill->GetGlobalSkillGroupID() : 0; //GlobalSkillGroupID가 설정된 스킬이 추가 될때 기존에 같은 ID를 사용하느 스킬의 쿨타임을 추가 되는 스킬에도 설정 해준다. if (nGlobalSkillGroupID != 0) { for( DWORD i = 0; i < GetSkillCount(); ++i ) { // m_vlGroupedSkillIDs 의 값들은 테이블에서 그냥 끄집어 낸 것이기 때문에 // 실제 스킬을 습득했는지 여부가 확인되어야 한다. DnSkillHandle hExistSkill = GetSkillFromIndex( i ); float fElapsedDelayTime = hExistSkill->GetElapsedDelayTime(); if( hExistSkill && hExistSkill->GetGlobalSkillGroupID() == nGlobalSkillGroupID && fElapsedDelayTime > 0.0f) { DnSkillHandle hOrigSkill = FindSkill(hExistSkill->GetAnotherGlobalSkillID()); hSkill->OnAnotherGlobalSkillBeginCoolTime( hOrigSkill ); hSkill->SetElapsedDelayTime(fElapsedDelayTime); } } } } #endif // PRE_FIX_62052 bool MASkillUser::AddSkill( DnSkillHandle hSkill ) { #ifdef _GAMESERVER // 겜서버에서는 몬스터에서만 스킬 객체로 직접 addskill 합니다. // 플레이어 액터에게는 스킬 트리 관리가 중간에 들어가기 때문에 서버에서 가상함수로 // playeractor 가 오버라이드하여 사용하지 않는 이 함수 사용하면 안됩니다. // 추후에 사용하게 된다면 기억하기 위해서 assert 걸어놓습니다. //_ASSERT( IsValidActor() ); //_ASSERT( m_pActor->IsMonsterActor() ); #endif if( IsExistSkill( hSkill->GetClassID(), hSkill->GetLevel() ) ) return false; #if defined(PRE_FIX_62052) ApplyGlobalSkillCoolTime(hSkill); #endif // PRE_FIX_62052 #ifndef PRE_FIX_SKILLLIST m_vlhSkillList.push_back( hSkill ); m_vbSelfAllocList.push_back( false ); #else m_vlSkillObjects.push_back( S_SKILL_OBJECT(hSkill, false) ); #endif // #ifndef PRE_FIX_SKILLLIST // 토글, 오라 스킬은 따로 리스트에 담아둠 CDnSkill::DurationTypeEnum eDurationType = hSkill->GetDurationType(); switch(eDurationType) { case CDnSkill::ActiveToggle: case CDnSkill::TimeToggle: case CDnSkill::ActiveToggleForSummon: m_vlhToggleSkills.push_back(hSkill); case CDnSkill::Aura: m_vlhAuraSkills.push_back(hSkill); } OnAddSkill( hSkill ); return true; } bool MASkillUser::bIsPassiveSkill( DnBlowHandle hBlow ) { int iNumSkill = (int)m_vlhSelfPassiveBlowSkill.size(); for( int iSkill = 0; iSkill < iNumSkill; ++iSkill ) { DnSkillHandle hSkill = m_vlhSelfPassiveBlowSkill.at( iSkill ); _ASSERT( CDnSkill::Passive == hSkill->GetSkillType() ); const CDnSkill::SkillInfo* pSkillInfo = hBlow->GetParentSkillInfo(); if( pSkillInfo && (pSkillInfo->iSkillID == hSkill->GetClassID()) ) return true; //if( CDnSkill::Passive == hSkill->GetSkillType() ) //{ // int iNumStateEffect = hSkill->GetStateEffectCount(); // for( int i = 0; i < iNumStateEffect; ++i ) // { // CDnSkill::StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( i ); // if( CDnSkill::StateEffectApplyType::ApplySelf == pSE->ApplyType ) // { // if( CDnSkill::DurationTypeEnum::Buff == hSkill->GetDurationType() ) // { // if( hBlow->GetBlowIndex() == pSE->nID ) // return true; // } // } // } //} } return false; } bool MASkillUser::ApplyPassiveSkill( DnSkillHandle hSkill, bool isInitialize/* = false*/ ) { bool bPassiveBuf = false; // #24143 자기자신의 스킬들을 초기화할땐 CDnSkillTask 쪽에서 실제 획득한 스킬리스트를 기준으로 AddSkill() 함수를 // 호출해주기 때문에 문제 없지만 다른 플레이어의 경우엔 그냥 서버에서 밀어주는 스킬리스트(리셋된 레벨 0짜리 스킬 포함) // 그대로 AddSkill() 함수를 호출해주기 때문에 리셋된 패시브 스킬도 적용시켜줘버리는 문제가 있다. // 따라서 여기서 획득 상태가 아니면 패시브 버프 스킬을 발동시켜주지 않도록 한다. (hp/mp 최대치 증가 스킬등등..) // 게임서버쪽에서는 클라이언트와 다르게 각 CDnPlayerActor 객체들이 실제 보유한 스킬만 갖고 있다. // 클라이언트쪽에서는 LocalPlayerActor 인 경우에 리셋된 스킬의 정보도 필요하므로 PlayerActor 를 따로 구분 안하고 스킬리스트 모두 넣어준다.. #ifndef _GAMESERVER if( false == hSkill->IsAcquired() ) return false; #endif // #ifndef _GAMESERVER if( CDnSkill::Passive == hSkill->GetSkillType() ) { #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) bool isTotalLevelSkill = false; if (m_pActor && m_pActor->IsPlayerActor()) { CDnPlayerActor* pPlayerActor = static_cast(m_pActor); CDnTotalLevelSkillSystem* pTotalLevelSystem = pPlayerActor ? pPlayerActor->GetTotalLevelSkillSystem() : NULL; if (pTotalLevelSystem) isTotalLevelSkill = pTotalLevelSystem->IsTotalLevelSkill(hSkill->GetClassID()); } #endif // PRE_ADD_TOTAL_LEVEL_SKILL int iNumStateEffect = hSkill->GetStateEffectCount(); for( int i = 0; i < iNumStateEffect; ++i ) { CDnSkill::StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( i ); if( CDnSkill::StateEffectApplyType::ApplySelf == pSE->ApplyType ) { if( CDnSkill::DurationTypeEnum::Buff == hSkill->GetDurationType() ) { bPassiveBuf = true; if( hSkill->IsSatisfyWeapon() ) { int iBlowID = -1; DnBlowHandle hBlow; #ifdef _GAMESERVER if(m_pActor) { iBlowID = m_pActor->CDnActor::CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pSE->nID, -1, pSE->szValue.c_str(), true ); // Duration Time이 -1 이면 무한 적용임 OnApplyPassiveSkillBlow( iBlowID ); hBlow = m_pActor->GetStateBlowFromID(iBlowID); } #else if(m_pActor) hBlow = m_pActor->CDnActor::CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pSE->nID, -1, pSE->szValue.c_str(), true ); // Duration Time이 -1 이면 무한 적용임 #endif #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) if (isTotalLevelSkill == true && isInitialize == true && hBlow) hBlow->FromSourceItem(); #endif // PRE_ADD_TOTAL_LEVEL_SKILL hSkill->SetAppliedPassiveBlows( true ); #ifndef _FINAL_BUILD char szTemp[256] = { 0, }; WideCharToMultiByte( CP_ACP, 0, hSkill->GetName(), -1, szTemp, _countof(szTemp), NULL, NULL ); OutputDebug( "[패시브 버프 스킬 적용됨: %d] \"%s\" 상태효과Index: %d, Value:%s\n", hSkill->GetClassID(), szTemp, pSE->nID, pSE->szValue.c_str() ); #endif // #ifndef _FINAL_BUILD } #ifndef _FINAL_BUILD else { char szTemp[256] = { 0, }; WideCharToMultiByte( CP_ACP, 0, hSkill->GetName(), -1, szTemp, _countof(szTemp), NULL, NULL ); OutputDebug( "[패시브 버프 스킬 적용안됨: %d] \"%s\" 스킬에 지정된 필요 무기 타입이 맞지 않습니다.\n", hSkill->GetClassID(), szTemp ); } #endif // #ifndef _FINAL_BUILD } #ifndef _FINAL_BUILD else { char szTemp[256] = { 0, }; WideCharToMultiByte( CP_ACP, 0, hSkill->GetName(), -1, szTemp, _countof(szTemp), NULL, NULL ); OutputDebug( "[스킬 데이터 확인 요망: %d] \"%s\" 패시브 버프 스킬로 생각되나 Buff 로 설정되어있지 않아서 적용 안됨\n", hSkill->GetClassID(), szTemp ); } #endif // #ifndef _FINAL_BUILD } } } return bPassiveBuf; } void MASkillUser::ApplyPassiveSkills( void ) { if( !IsValidActor() ) return; int iNumSkill = (int)m_vlhSelfPassiveBlowSkill.size(); for( int iSkill = 0; iSkill < iNumSkill; ++iSkill ) { DnSkillHandle hSkill = m_vlhSelfPassiveBlowSkill.at( iSkill ); ApplyPassiveSkill(hSkill); } } void MASkillUser::OnRemoveSkill( DnSkillHandle hSkill ) { #ifndef _GAMESERVER const set& setUseAction = hSkill->GetUseActionSet(); set::const_iterator iter = setUseAction.begin(); for( iter; iter != setUseAction.end(); ++iter ) m_setUseActionNames.erase( *iter ); #endif } bool MASkillUser::RemoveSkillAll() { if( !IsValidActor() ) return false; #ifndef PRE_FIX_SKILLLIST int nSkillSize = (int)m_vlhSkillList.size(); for( int i=0 ; iGetClassID() ); _ASSERT( bRet ); } #else int nSkillSize = (int)m_vlSkillObjects.size(); for( int i=0 ; iGetClassID() ); _ASSERT( bRet ); } #endif // #ifndef PRE_FIX_SKILLLIST return true; } bool MASkillUser::EndStateEffectSkill(int nSkillTableID) { DNVector(DnBlowHandle) vlBlows; m_pActor->GetAllAppliedStateBlow(vlBlows); for (int i = 0; i < (int)vlBlows.size(); ++i) { DnBlowHandle hBlow = vlBlows.at(i); const CDnSkill::SkillInfo * skillInfo = hBlow->GetParentSkillInfo(); if (skillInfo->iSkillID == nSkillTableID) { hBlow->OnEnd(0, 0); m_pActor->OnEndStateBlow(hBlow); m_pActor->RemoveStateBlowFromID(hBlow->GetBlowID()); //? need test. } } /* DnSkillHandle hSkill = FindSkill(nSkillTableID); if (m_pActor && m_pActor->GetStateBlow()) { std::vector vecRemoveBlowID; DNVector(DnBlowHandle) vlBlows; m_pActor->GetAllAppliedStateBlowBySkillID(nSkillTableID, vlBlows); int iStateEffectCount = hSkill->GetStateEffectCount(); for (int iStateEffect = 0; iStateEffect < iStateEffectCount; ++iStateEffect) { const CDnSkill::StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex(iStateEffect); if (pSE && CDnSkill::StateEffectApplyType::ApplySelf == pSE->ApplyType) { for (DWORD BLOW_COUNT = 0; BLOW_COUNT < vlBlows.size(); BLOW_COUNT++) { if (vlBlows[BLOW_COUNT] && (vlBlows[BLOW_COUNT]->GetBlowIndex() == (STATE_BLOW::emBLOW_INDEX)pSE->nID)) { vecRemoveBlowID.push_back(vlBlows[BLOW_COUNT]->GetBlowID()); } } } } for (DWORD Index = 0; Index < vecRemoveBlowID.size(); Index++) { m_pActor->GetStateBlow()->RemoveImediatlyStateEffectFromID(vecRemoveBlowID[Index]); } } */ return true; } bool MASkillUser::RemoveSkill( int nSkillTableID ) { if( !IsValidActor() ) return false; int iNumPassiveBlowSkill = (int)m_vlhSelfPassiveBlowSkill.size(); for( int iPassiveBlowSkill = 0; iPassiveBlowSkill < iNumPassiveBlowSkill; ++iPassiveBlowSkill ) { DnSkillHandle hSkill = m_vlhSelfPassiveBlowSkill.at( iPassiveBlowSkill ); if( hSkill && hSkill->GetClassID() == nSkillTableID ) { if( m_pActor && m_pActor->GetStateBlow() ) { std::vector vecRemoveBlowID; DNVector(DnBlowHandle) vlBlows; m_pActor->GetAllAppliedStateBlowBySkillID( nSkillTableID , vlBlows ); int iStateEffectCount = hSkill->GetStateEffectCount(); for( int iStateEffect = 0; iStateEffect < iStateEffectCount; ++iStateEffect ) { const CDnSkill::StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( iStateEffect ); if( pSE && CDnSkill::StateEffectApplyType::ApplySelf == pSE->ApplyType ) { for( DWORD BLOW_COUNT=0; BLOW_COUNT < vlBlows.size(); BLOW_COUNT++ ) { if( vlBlows[BLOW_COUNT] && ( vlBlows[BLOW_COUNT]->GetBlowIndex() == (STATE_BLOW::emBLOW_INDEX)pSE->nID ) ) { vecRemoveBlowID.push_back( vlBlows[BLOW_COUNT]->GetBlowID() ); } } } } for( DWORD Index=0; IndexGetStateBlow()->RemoveImediatlyStateEffectFromID( vecRemoveBlowID[Index] ); } } // 2009.03.16 김밥 m_vlhSelfPassiveBlowSkill.erase( m_vlhSelfPassiveBlowSkill.begin()+iPassiveBlowSkill ); break; } } #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) //통합레벨 스킬인 경우 아랫쪽 루틴을 탈 필요가 없음... if (m_pActor && m_pActor->IsPlayerActor()) { CDnPlayerActor* pPlayerActor = static_cast(m_pActor); CDnTotalLevelSkillSystem *pTotalLevelSystem = pPlayerActor ? pPlayerActor->GetTotalLevelSkillSystem(): NULL; if (pTotalLevelSystem && pTotalLevelSystem->IsTotalLevelSkill(nSkillTableID)) return true; } #endif // PRE_ADD_TOTAL_LEVEL_SKILL #ifdef _GAMESERVER list::iterator iter = m_listAutoPassiveSkills.begin(); for( iter; m_listAutoPassiveSkills.end() != iter; ++iter ) { if( (*iter)->GetClassID() == nSkillTableID ) { m_listAutoPassiveSkills.erase( iter ); break; } } #endif #ifndef PRE_FIX_SKILLLIST for( DWORD i=0; iGetClassID() == nSkillTableID ) { // 토글이나 오라 스킬이라면 별도의 리스트에서 삭제 DnSkillHandle hSkill = m_vlhSkillList.at(i); #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { S_SKILL_OBJECT& SkillObject = m_vlSkillObjects.at( i ); if( SkillObject.hSkill && SkillObject.hSkill->GetClassID() == nSkillTableID ) { // 토글이나 오라 스킬이라면 별도의 리스트에서 삭제 DnSkillHandle hSkill = SkillObject.hSkill; #endif // #ifndef PRE_FIX_SKILLLIST if( CDnSkill::ActiveToggle == hSkill->GetDurationType() || CDnSkill::TimeToggle == hSkill->GetDurationType() || CDnSkill::ActiveToggleForSummon == hSkill->GetDurationType() ) { DNVector(DnSkillHandle)::iterator Localiter = find( m_vlhToggleSkills.begin(), m_vlhToggleSkills.end(), hSkill ); m_vlhToggleSkills.erase( Localiter ); } else if( CDnSkill::Aura == hSkill->GetDurationType() ) { if (m_hAuraSkill && m_hAuraSkill->GetClassID() == nSkillTableID && m_hAuraSkill->IsAuraOn()) { if (m_hAuraSkill == m_hProcessSkill) m_hProcessSkill.Identity(); m_hAuraSkill->EnableAura( false ); OnSkillAura(m_hAuraSkill, false); m_hAuraSkill.Identity(); } DNVector(DnSkillHandle)::iterator Localiter = find( m_vlhAuraSkills.begin(), m_vlhAuraSkills.end(), hSkill ); m_vlhAuraSkills.erase( Localiter ); } #ifndef PRE_FIX_SKILLLIST OnRemoveSkill( m_vlhSkillList[i] ); if( m_vbSelfAllocList[i] == true ) SAFE_RELEASE_SPTR( m_vlhSkillList[i] ); m_vlhSkillList.erase( m_vlhSkillList.begin() + i ); m_vbSelfAllocList.erase( m_vbSelfAllocList.begin() + i ); #else OnRemoveSkill( SkillObject.hSkill ); if( SkillObject.bSelfAlloc ) SAFE_RELEASE_SPTR( SkillObject.hSkill ); m_vlSkillObjects.erase( m_vlSkillObjects.begin()+i ); #endif // #ifndef PRE_FIX_SKILLLIST return true; } } return true; } #ifndef _GAMESERVER bool MASkillUser::ReplacementSkill( int nSkillTableID, int nLevel ) { if( !IsValidActor() ) return false; DnSkillHandle hSkill = CDnSkill::CreateSkill( m_pActor->GetMySmartPtr(), nSkillTableID, nLevel ); if( !hSkill ) return false; return ReplacementSkill( hSkill ); } bool MASkillUser::ReplacementSkill( DnSkillHandle hNewSkill ) { #ifndef PRE_FIX_SKILLLIST int iSkillID = hNewSkill->GetClassID(); for( DWORD i=0; iGetClassID() == iSkillID ) { // 패시브 블로우 스킬인가. 그렇다면 기존 상태효과 모두 없애고 새로 적용시켜 준다. DNVector(DnSkillHandle)::iterator iter = find( m_vlhSelfPassiveBlowSkill.begin(), m_vlhSelfPassiveBlowSkill.end(), m_vlhSkillList[ i ] ); if( m_vlhSelfPassiveBlowSkill.end() != iter ) { // 적용되어 있는 상태효과를 제거한다. // 부모스킬이 일치하는 상태효과를 찾아내서 삭제한다. if( !IsValidActor() ) continue; // HP, SP 올려주는 패시브 스킬은 삭제되면 원래 스킬 없는 상태의 순수 HP SP 로 현재 HP/SP가 // 클리핑 되게 때문에 미리 받아뒀다가 셋팅해준다.. INT64 iHP = m_pActor->GetHP(); int iSP = m_pActor->GetSP(); DNVector(DnBlowHandle) vlhAppliedSE; DNVector(DnBlowHandle) vlhSEToRemove; m_pActor->GetAllAppliedStateBlow( vlhAppliedSE ); int iNumAppliedSE = (int)vlhAppliedSE.size(); for( int iSE = 0; iSE < iNumAppliedSE; ++iSE ) { DnBlowHandle hAppliedBlow = vlhAppliedSE.at( iSE ); const CDnSkill::SkillInfo* pSkillInfo = hAppliedBlow->GetParentSkillInfo(); if( pSkillInfo->iSkillID == (*iter)->GetClassID() ) vlhSEToRemove.push_back( hAppliedBlow ); } int iNumSEToRemove = (int)vlhSEToRemove.size(); for( int iSE = 0; iSE < iNumSEToRemove; ++iSE ) { DnBlowHandle hBlow = vlhSEToRemove.at(iSE); int iID = hBlow->GetBlowID(); OutputDebug( "[패시브 버프 스킬 삭제됨: %d] %s 상태효과Index: %d, Value:%s\n", (*iter)->GetClassID(), (*iter)->GetName(), hBlow->GetBlowIndex(), hBlow->GetValue() ); m_pActor->RemoveStateBlowFromID( iID ); } m_vlhSelfPassiveBlowSkill.erase( iter ); ApplyPassiveSkill( hNewSkill ); m_vlhSelfPassiveBlowSkill.push_back( hNewSkill ); m_pActor->SetHP( iHP ); m_pActor->SetSP( iSP ); } if (m_hAuraSkill && m_hAuraSkill->GetClassID() == iSkillID && m_hAuraSkill->IsAuraOn()) { if (m_hAuraSkill == m_hProcessSkill) m_hProcessSkill.Identity(); m_hAuraSkill->EnableAura( false ); OnSkillAura(m_hAuraSkill, false); m_hAuraSkill.Identity(); } if( m_vbSelfAllocList[ i ] ) { // 자신이 생성한 스킬은 자신이 지워야 하므로. DnSkillHandle hSkillToRelease = m_vlhSkillList[ i ]; // 오라, 토글 스킬 리스트에서도 검색해서 대체 시킴 DNVector(DnSkillHandle)::iterator iterToggle = find( m_vlhToggleSkills.begin(), m_vlhToggleSkills.end(), hSkillToRelease ); if( m_vlhToggleSkills.end() != iterToggle ) *iterToggle = hNewSkill; DNVector(DnSkillHandle)::iterator iterAura = find( m_vlhAuraSkills.begin(), m_vlhAuraSkills.end(), hSkillToRelease ); if( m_vlhAuraSkills.end() != iterAura ) *iterAura = hNewSkill; OnRemoveSkill( hSkillToRelease ); SAFE_RELEASE_SPTR( hSkillToRelease ); } else { // 스킬 태스크에서 생성한 스킬 객체는 여기서 지우지 않는다. DnSkillHandle hPrevLevelSkill = m_vlhSkillList[ i ]; // 오라, 토글 스킬 리스트에서도 검색해서 대체 시킴 DNVector(DnSkillHandle)::iterator iterToggle = find( m_vlhToggleSkills.begin(), m_vlhToggleSkills.end(), hPrevLevelSkill ); if( m_vlhToggleSkills.end() != iterToggle ) *iterToggle = hNewSkill; DNVector(DnSkillHandle)::iterator iterAura = find( m_vlhAuraSkills.begin(), m_vlhAuraSkills.end(), hPrevLevelSkill ); if( m_vlhAuraSkills.end() != iterAura ) *iterAura = hNewSkill; } OnReplacementSkill( m_vlhSkillList[ i ], hNewSkill ); m_vlhSkillList[ i ] = hNewSkill; return true; } } return false; #else int iSkillID = hNewSkill->GetClassID(); for( DWORD i = 0; i < m_vlSkillObjects.size(); i++ ) { S_SKILL_OBJECT& SkillObject = m_vlSkillObjects.at( i ); DnSkillHandle hSkill = SkillObject.hSkill; if( hSkill->GetClassID() == iSkillID ) { // 패시브 블로우 스킬인가. 그렇다면 기존 상태효과 모두 없애고 새로 적용시켜 준다. DNVector(DnSkillHandle)::iterator iter = find( m_vlhSelfPassiveBlowSkill.begin(), m_vlhSelfPassiveBlowSkill.end(), hSkill ); if( m_vlhSelfPassiveBlowSkill.end() != iter ) { // 적용되어 있는 상태효과를 제거한다. // 부모스킬이 일치하는 상태효과를 찾아내서 삭제한다. if( !IsValidActor() ) continue; // HP, SP 올려주는 패시브 스킬은 삭제되면 원래 스킬 없는 상태의 순수 HP SP 로 현재 HP/SP가 // 클리핑 되게 때문에 미리 받아뒀다가 셋팅해준다.. INT64 iHP = m_pActor->GetHP(); int iSP = m_pActor->GetSP(); DNVector(DnBlowHandle) vlhAppliedSE; DNVector(DnBlowHandle) vlhSEToRemove; m_pActor->GetAllAppliedStateBlow( vlhAppliedSE ); int iNumAppliedSE = (int)vlhAppliedSE.size(); for( int iSE = 0; iSE < iNumAppliedSE; ++iSE ) { DnBlowHandle hAppliedBlow = vlhAppliedSE.at( iSE ); const CDnSkill::SkillInfo* pSkillInfo = hAppliedBlow->GetParentSkillInfo(); if( pSkillInfo->iSkillID == (*iter)->GetClassID() ) vlhSEToRemove.push_back( hAppliedBlow ); } int iNumSEToRemove = (int)vlhSEToRemove.size(); for( int iSE = 0; iSE < iNumSEToRemove; ++iSE ) { DnBlowHandle hBlow = vlhSEToRemove.at(iSE); int iID = hBlow->GetBlowID(); OutputDebug( "[패시브 버프 스킬 삭제됨: %d] %s 상태효과Index: %d, Value:%s\n", (*iter)->GetClassID(), (*iter)->GetName(), hBlow->GetBlowIndex(), hBlow->GetValue() ); m_pActor->RemoveStateBlowFromID( iID ); } m_vlhSelfPassiveBlowSkill.erase( iter ); ApplyPassiveSkill( hNewSkill ); m_vlhSelfPassiveBlowSkill.push_back( hNewSkill ); m_pActor->SetHP( iHP ); m_pActor->SetSP( iSP ); } if (m_hAuraSkill && m_hAuraSkill->GetClassID() == iSkillID && m_hAuraSkill->IsAuraOn()) { if (m_hAuraSkill == m_hProcessSkill) m_hProcessSkill.Identity(); m_hAuraSkill->EnableAura( false ); OnSkillAura(m_hAuraSkill, false); m_hAuraSkill.Identity(); } if( true == SkillObject.bSelfAlloc ) { // 자신이 생성한 스킬은 자신이 지워야 하므로. DnSkillHandle hSkillToRelease = hSkill; // 오라, 토글 스킬 리스트에서도 검색해서 대체 시킴 DNVector(DnSkillHandle)::iterator iterToggle = find( m_vlhToggleSkills.begin(), m_vlhToggleSkills.end(), hSkillToRelease ); if( m_vlhToggleSkills.end() != iterToggle ) *iterToggle = hNewSkill; DNVector(DnSkillHandle)::iterator iterAura = find( m_vlhAuraSkills.begin(), m_vlhAuraSkills.end(), hSkillToRelease ); if( m_vlhAuraSkills.end() != iterAura ) *iterAura = hNewSkill; OnRemoveSkill( hSkillToRelease ); SAFE_RELEASE_SPTR( hSkillToRelease ); } else { // 스킬 태스크에서 생성한 스킬 객체는 여기서 지우지 않는다. DnSkillHandle hPrevLevelSkill = hSkill; // 오라, 토글 스킬 리스트에서도 검색해서 대체 시킴 DNVector(DnSkillHandle)::iterator iterToggle = find( m_vlhToggleSkills.begin(), m_vlhToggleSkills.end(), hPrevLevelSkill ); if( m_vlhToggleSkills.end() != iterToggle ) *iterToggle = hNewSkill; DNVector(DnSkillHandle)::iterator iterAura = find( m_vlhAuraSkills.begin(), m_vlhAuraSkills.end(), hPrevLevelSkill ); if( m_vlhAuraSkills.end() != iterAura ) *iterAura = hNewSkill; } OnReplacementSkill( hSkill, hNewSkill ); SkillObject.hSkill = hNewSkill; return true; } } return false; #endif // #ifndef PRE_FIX_SKILLLIST } #endif bool MASkillUser::IsExistSkill( int nSkillTableID, int nLevel ) { #ifndef PRE_FIX_SKILLLIST for( DWORD i=0; iGetClassID() == nSkillTableID ) ) { if( nLevel == -1 ) return true; if( m_vlhSkillList[i]->GetLevel() == nLevel ) return true; } } return false; #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { S_SKILL_OBJECT& SkillObject = m_vlSkillObjects.at( i ); DnSkillHandle hSkill = SkillObject.hSkill; if( hSkill) { if( hSkill->GetClassID() == nSkillTableID ) { if( -1 == nLevel ) return true; if( hSkill->GetLevel() == nLevel ) return true; } } } return false; #endif // #ifndef PRE_FIX_SKILLLIST } bool MASkillUser::IsSelfAllocSkill( int nSkillTableID ) { #ifndef PRE_FIX_SKILLLIST for( DWORD i=0; iGetClassID() == nSkillTableID ) return m_vbSelfAllocList[i]; } return false; #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { if( m_vlSkillObjects.at( i ).hSkill->GetClassID() == nSkillTableID ) return m_vlSkillObjects.at( i ).bSelfAlloc; } return false; #endif // #ifndef PRE_FIX_SKILLLIST } void MASkillUser::SetSkillLevel( int nSkillTableID, int nValue ) { if( !IsSelfAllocSkill( nSkillTableID ) ) return; DnSkillHandle hSkill = FindSkill( nSkillTableID ); if( !hSkill ) return; if( hSkill->GetLevel() == nValue ) return; float fElapsedDelayTime = hSkill->GetElapsedDelayTime(); RemoveSkill( nSkillTableID ); AddSkill( nSkillTableID, nValue ); hSkill = FindSkill( nSkillTableID ); hSkill->SetElapsedDelayTime( fElapsedDelayTime ); } DnSkillHandle MASkillUser::FindSkill( int nSkillTableID ) { #ifndef PRE_FIX_SKILLLIST for( DWORD i=0; iGetClassID() == nSkillTableID ) return m_vlhSkillList[i]; } return CDnSkill::Identity(); #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { if( m_vlSkillObjects.at( i ).hSkill && m_vlSkillObjects.at( i ).hSkill->GetClassID() == nSkillTableID ) return m_vlSkillObjects.at( i ).hSkill; } return CDnSkill::Identity(); #endif // #ifndef PRE_FIX_SKILLLIST } DnSkillHandle MASkillUser::GetProcessSkill() { return m_hProcessSkill; } CDnSkill::UsingResult MASkillUser::CanExecuteSkill( DnSkillHandle hSkill ) { _ASSERT( hSkill && "MASkillUser::CanExecuteSkill() -> Invalid hSkill" ); #ifndef _GAMESERVER // 같은 프레임에 액티브 스킬 여러개 사용 불가. if( m_hProcessSkill && m_hProcessSkill->GetSkillType() == CDnSkill::Active && hSkill->GetSkillType() == CDnSkill::Active ) if( m_bSkillExecutedThisFrame ) return CDnSkill::UsingResult::Failed; #endif // [2011/02/22 semozz] // 애니메이션 전환 가능 체크는 클라이언트만 하면 될듯... #if !defined(_GAMESERVER) if (!hSkill->CheckAnimation()) return CDnSkill::UsingResult::Failed; #endif // _GAMESERVER #if !defined(_GAMESERVER) if( hSkill->GetGlyphActiveSkillDisable() ) return CDnSkill::UsingResult::Failed; #endif // _GAMESERVER return hSkill->CanExecute(); } #ifdef _GAMESERVER bool MASkillUser::ExecuteSkill( DnSkillHandle hSkill, LOCAL_TIME LocalTime, float fDelta ) #else bool MASkillUser::ExecuteSkill( DnSkillHandle hSkill, LOCAL_TIME LocalTime, float fDelta, bool bCheckValid /*= true*/, bool bAutoUsedFromServer /*= false*/, bool bSendPacketToServer /*= true */ ) #endif { _ASSERT( hSkill && "Invalid hSkill" ); if( !IsValidActor() ) return false; if( m_hProcessSkill ) { // #25154 오라 스킬은 오라를 껐을 때 onend 된다. if( false == m_hProcessSkill->IsAuraOn() ) m_hProcessSkill->OnEnd( m_LocalTime, 0 ); m_hProcessSkill.Identity(); } m_hProcessSkill = hSkill; switch( m_hProcessSkill->GetDurationType() ) { case CDnSkill::Instantly: case CDnSkill::Buff: case CDnSkill::Debuff: case CDnSkill::SummonOnOff: case CDnSkill::StanceChange: // 마을에서는 SP 안 깍이도록 #ifndef _GAMESERVER if( CDnWorld::MapTypeVillage != CDnWorld::GetInstance().GetMapType() ) #endif m_pActor->UseMP( -m_hProcessSkill->GetDecreaseMP() ); // Compile Error. Moved by kalliste //m_pActor->SetSP( m_pActor->GetSP() - m_hProcessSkill->GetDecreaseMP() ); // Excute 함수는 OnBegin 으로 이름이 바뀌었다. //m_hProcessSkill->Execute( LocalTime, fDelta ); m_hProcessSkill->OnBegin( LocalTime, fDelta ); // 글로벌 쿨타임이 설정되어있다면 같은 그룹의 스킬들도 쿨타임 시작.. if( m_hProcessSkill && 0 < m_hProcessSkill->GetGlobalSkillGroupID() ) { for( DWORD i = 0; i < GetSkillCount(); ++i ) { // m_vlGroupedSkillIDs 의 값들은 테이블에서 그냥 끄집어 낸 것이기 때문에 // 실제 스킬을 습득했는지 여부가 확인되어야 한다. DnSkillHandle hExistSkill = GetSkillFromIndex( i ); if( hExistSkill && hExistSkill != m_hProcessSkill && hExistSkill->GetGlobalSkillGroupID() == m_hProcessSkill->GetGlobalSkillGroupID() ) { // 글로벌 쿨타임으로 같이 도는 스킬들은 사용된 스킬의 쿨타임으로 바꿔준다. hExistSkill->ResetCoolTime(); hExistSkill->OnAnotherGlobalSkillBeginCoolTime( hSkill ); hExistSkill->OnBeginCoolTime(); } } } break; case CDnSkill::TimeToggle: case CDnSkill::ActiveToggle: case CDnSkill::ActiveToggleForSummon: { bool bToggleEnable = !hSkill->IsToggleOn(); hSkill->EnableToggle(bToggleEnable); OnSkillToggle(hSkill, bToggleEnable); #ifdef _GAMESERVER // NOTE: 토글이 켜질 당시의 공격력을 저장해둔다. 추후에 공격력 말고 다른 것들이 추가될 수도 있다. if( hSkill && hSkill->IsToggleOn() ) { m_ActorStateSnapshotForToggleProjectile.ResetState(); hSkill->CheckAndAddSelfStateEffect(); static_cast(this)->GetStateBlow()->Process( 0, 0.0f ); CDnActor* pActor = static_cast(this); m_ActorStateSnapshotForToggleProjectile = *(static_cast(pActor)); hSkill->CheckAndRemoveInstantApplySelfStateEffect(); } #endif } break;; case CDnSkill::Aura: { // 오라 스킬들끼리 토글링 해줌. 다른 오라 스킬이 켜지면 꺼지고, 현재 시전중인 오라 스킬이면 꺼준다. // 오라 스킬들을 먼저 종료시키고 새로 켜지는 오라를 실행해야 새로 실행될 오라에서 self blow 큐에 쌓인 것들이 // 클리어 되지 않고 제대로 적용된다. bool bProcessingAuraOff = false; // 같은 오라 스킬이 execute 되면 오라가 꺼지는 것임. int iNumAuraSkill = (int)m_vlhAuraSkills.size(); for( int iAuraSkill = 0; iAuraSkill < iNumAuraSkill; ++iAuraSkill ) { DnSkillHandle hAuraSkill = m_vlhAuraSkills.at( iAuraSkill ); _ASSERT( hAuraSkill->GetDurationType() == CDnSkill::Aura ); if( hAuraSkill->IsAuraOn() ) { if( m_hProcessSkill == hAuraSkill ) bProcessingAuraOff = true; hAuraSkill->EnableAura( false ); OnSkillAura( hAuraSkill, false ); break; } } // 다른 오라스킬을 사용해서 기존의 오라가 꺼지고 새로 발동됨. if( false == bProcessingAuraOff ) { for( int iAuraSkill = 0; iAuraSkill < iNumAuraSkill; ++iAuraSkill ) { DnSkillHandle hAuraSkill = m_vlhAuraSkills.at( iAuraSkill ); if( hAuraSkill == m_hProcessSkill ) { m_hProcessSkill->EnableAura( true ); OnSkillAura( m_hProcessSkill, true ); break; // Note 한기: m_hProcessSkill 스마트 포인터는 오라 스킬 사용하는 액션이 재생되는 동안은 유효해야 // 게임 서버에서 CDnPlayerActor::CmdStop() 쪽에서 걸러지기 때문에 겜 서버에서 해당 액션 시그널이 끝까지 // 처리됨. 따라서 CDnActor::OnChangeAction 쪽에서 ProcessSkill 을 Identity 시킴. } } } else { // 켜져 있던 오라가 꺼짐. m_hAuraSkill.Identity(); m_hProcessSkill.Identity(); } } break; } #ifndef _GAMESERVER m_bSkillExecutedThisFrame = true; #endif return true; } #ifdef _GAMESERVER void MASkillUser::UseAutoPassiveSkill( LOCAL_TIME LocalTime, float fDelta ) { list::iterator iter = m_listAutoPassiveSkills.begin(); for( iter; m_listAutoPassiveSkills.end() != iter; ++iter ) { DnSkillHandle hSkill = *iter; if( CDnSkill::UsingResult::Success == CanExecuteSkill( hSkill ) ) { BYTE pBuffer[128] = {0,}; CPacketCompressStream Stream( pBuffer, 128 ); int iSkillID = hSkill->GetClassID(); int iEnchantSkillID = hSkill->GetEnchantedSkillID(); char cLevel = hSkill->GetLevel(); EtVector2 vLook, vZVec; bool bUseApplySkillItem = false; char cUseSignalSkill = -1; bool bAutoUseFromServer = true; bool abSignalSkillCheck[ 3 ] = { false }; vLook = EtVec3toVec2( *m_pActor->GetLookDir() ); vZVec = EtVec3toVec2( *m_pActor->GetMoveVectorZ() ); Stream.Write( &iSkillID, sizeof(int) ); Stream.Write( &cLevel, sizeof(char) ); Stream.Write( &bUseApplySkillItem, sizeof(bool) ); Stream.Write( abSignalSkillCheck, sizeof(abSignalSkillCheck) ); Stream.Write( &vZVec, sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT ); Stream.Write( &vLook, sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT ); Stream.Write( &bAutoUseFromServer, sizeof(bool) ); Stream.Write( &iEnchantSkillID, sizeof(int) ); #ifdef PRE_ADD_POSITION_SYNC_BY_SKILL_USAGE Stream.Write( m_pActor->GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT ); #endif m_pActor->Send( eActor::SC_USESKILL, &Stream ); ExecuteSkill( hSkill, LocalTime, fDelta ); // 발동된 리스트에 넣어둔다. process 돌려야 하기 때문에, m_listProcessAutoPassiveSkills.push_back( hSkill ); // 사용된 오토 패시브 스킬은 맨 뒤에 붙인다. 실행 우선순위를 낮춤. m_listAutoPassiveSkills.erase( iter ); m_listAutoPassiveSkills.push_back( hSkill ); // ProcessSkill 은 아니므로 초기화 시킴. ProcessSkill 이 있으면 CmdStop 이 먹지 않는다. m_hProcessSkill.Identity(); break; } } } bool MASkillUser::IsProcessingAutoPassive( int iSkillID ) { list::iterator iter = m_listProcessAutoPassiveSkills.begin(); for( iter; iter != m_listProcessAutoPassiveSkills.end(); ++iter ) { DnSkillHandle hAutoPassive = *iter; if( hAutoPassive->GetClassID() == iSkillID ) return true; } return false; } void MASkillUser::EndAutoPassiveSkill( LOCAL_TIME LocalTime, float fDelta ) { list::iterator iter = m_listProcessAutoPassiveSkills.begin(); for( iter; iter != m_listProcessAutoPassiveSkills.end(); ++iter ) { DnSkillHandle hAutoPassive = *iter; hAutoPassive->OnEnd( LocalTime, fDelta ); } } #endif bool MASkillUser::HavePassiveSkill(int iSkillID) { int iNumSkill = (int)m_vlhSelfPassiveBlowSkill.size(); for (int iSkill = 0; iSkill < iNumSkill; ++iSkill) { DnSkillHandle hSkill = m_vlhSelfPassiveBlowSkill.at(iSkill); if (iSkillID == hSkill->GetClassID()) return true; } return false; } void MASkillUser::OnSkillProcess( LOCAL_TIME LocalTime, float fDelta ) { if( m_hAuraSkill ) { //게임 서버에서만 판단해서 서버쪽 종료 시키고/클라이언트로 패킷 전송... #if defined(_GAMESERVER) if( m_hAuraSkill->IsFinished() ) { if (m_pActor) m_pActor->CmdFinishAuraSkill(m_hAuraSkill->GetClassID()); // [2011/03/22 semozz] // 현재 스킬이 오라 스킬과 같은 스킬이면 현재 스킬도 초기화. if (m_hProcessSkill == m_hAuraSkill) m_hProcessSkill.Identity(); OnSkillAura( m_hAuraSkill, false ); } else #endif // _GAMESERVER m_hAuraSkill->ProcessExecute( LocalTime, fDelta ); } #ifdef _GAMESERVER if( m_pActor && m_pActor->IsPlayerActor() ) { list::iterator iter = m_listProcessAutoPassiveSkills.begin(); for( iter; m_listProcessAutoPassiveSkills.end() != iter; ) { DnSkillHandle hSkill = *iter; //스킬 핸들 유효성 체크 추가 if (!hSkill) { iter = m_listProcessAutoPassiveSkills.erase( iter ); } else { if( hSkill->IsFinished() ) { iter = m_listProcessAutoPassiveSkills.erase( iter ); } else { hSkill->ProcessExecute( LocalTime, fDelta ); ++iter; } } } m_PrefixSkillCoolTimeManager.Process(LocalTime, fDelta); iter = m_listProcessPreFixDefenceSkills.begin(); for( iter; m_listProcessPreFixDefenceSkills.end() != iter; ) { DnSkillHandle hSkill = *iter; //스킬 핸들 유효성 체크 추가 if (!hSkill) { iter = m_listProcessPreFixDefenceSkills.erase( iter ); } else { if( hSkill->IsFinished() ) { hSkill->OnEnd( LocalTime, fDelta ); iter = m_listProcessPreFixDefenceSkills.erase( iter ); } else { hSkill->ProcessExecute( LocalTime, fDelta ); ++iter; } } } iter = m_listProcessPreFixOffenceSkills.begin(); for( iter; m_listProcessPreFixOffenceSkills.end() != iter; ) { DnSkillHandle hSkill = *iter; if (!hSkill) { iter = m_listProcessPreFixOffenceSkills.erase( iter ); } else { if( hSkill->IsFinished() ) { hSkill->OnEnd( LocalTime, fDelta ); iter = m_listProcessPreFixOffenceSkills.erase( iter ); } else { hSkill->ProcessExecute( LocalTime, fDelta ); ++iter; } } } if (!m_prefixSystemDefenceSkills.empty()) { PREFIX_SYSTEM_SKILL_LIST::iterator iter = m_prefixSystemDefenceSkills.begin(); PREFIX_SYSTEM_SKILL_LIST::iterator enditer = m_prefixSystemDefenceSkills.end(); for (; iter != enditer; ++iter) { if (iter->second.hSkill) iter->second.hSkill->Process(LocalTime, fDelta); } } if (!m_prefixSystemOffenceSkills.empty()) { PREFIX_SYSTEM_SKILL_LIST::iterator iter = m_prefixSystemOffenceSkills.begin(); PREFIX_SYSTEM_SKILL_LIST::iterator enditer = m_prefixSystemOffenceSkills.end(); for (; iter != enditer; ++iter) { if (iter->second.hSkill) iter->second.hSkill->Process(LocalTime, fDelta); } } } #else m_bSkillExecutedThisFrame = false; #endif if( m_hProcessSkill ) { if( m_hProcessSkill->IsFinished() ) { m_hProcessSkill->OnEnd( LocalTime, fDelta ); m_hProcessSkill.Identity(); } else m_hProcessSkill->ProcessExecute( LocalTime, fDelta ); } // 쿨타임 감소 상태효과가 있다면 델타값에 영향을 줄 값을 계산한다. if( m_pActor && m_pActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_096 ) ) { DNVector(DnBlowHandle) vlhCoolTimeBlows; m_pActor->GetStateBlow()->GetStateBlowFromBlowIndex( STATE_BLOW::BLOW_096, vlhCoolTimeBlows ); // 쿨타임 감소 상태효과는 - 기준이기때문에 중첩되면 빼준다. float fCoolTimeDeltaAdjust = 1.0f; int iNumBlow = (int)vlhCoolTimeBlows.size(); for( int i = 0; i < iNumBlow; ++i ) { fCoolTimeDeltaAdjust -= (1.0f - vlhCoolTimeBlows.at( i )->GetFloatValue()); } // #40670 스피릿 부스트도 쿨타임 감소 상태효과를 사용하게 됨. // 중첩시 WeightTable 에서 제한을 받게 된다. float fCoolTimeAccelMax = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::CoolTimeAccellSE_Max ); if( fCoolTimeDeltaAdjust < 1.0f - fCoolTimeAccelMax ) fCoolTimeDeltaAdjust = 1.0f - fCoolTimeAccelMax; // 1.0f 을 정상수치 기준으로 상태효과 인자값을 전체 쿨타임에 그대로 적용한 결과값이 현재 최종 쿨타임 기준이므로 // 매 프레임 처리해주는 델타 값에 비율의 역수를 곱한다. // 결과로 나온 값은 각 스킬에서 루프를 돌 때 액터에서 얻어와서 직접 적용한다. 2415의 타임 엑셀러레이션 스킬만 제외하고. m_fCoolTimeDeltaAdjustValue = 1.0f / fCoolTimeDeltaAdjust; } else m_fCoolTimeDeltaAdjustValue = 1.0f; #ifdef _GAMESERVER #ifndef PRE_FIX_SKILLLIST #ifdef PRE_FIX_GAMESERVER_PERFOMANCE // 쿨타임 처리. 추후에 쿨 타임 뿐만 아니라 기타등등의 여러가지 지속적인 Processing 을 하게 될 것임. if( m_pActor && m_pActor->IsPlayerActor() /*&& m_pActor->IsAllowCallSkillProcess( fDelta )*/ ) { #endif // #ifdef PRE_FIX_GAMESERVER_PERFOMANCE for( DWORD i = 0; i < m_vlhSkillList.size(); i++ ) { if( m_vlhSkillList.at( i ) ) m_vlhSkillList[i]->Process( LocalTime, fDelta ); } #ifdef PRE_FIX_GAMESERVER_PERFOMANCE } #endif // #ifdef PRE_FIX_GAMESERVER_PERFOMANCE #else #ifdef PRE_FIX_GAMESERVER_PERFOMANCE if( m_pActor && m_pActor->IsPlayerActor() /*&& m_pActor->IsAllowCallSkillProcess( fDelta )*/ ) { #endif // #ifdef PRE_FIX_GAMESERVER_PERFOMANCE PROFILE_TIME_TEST_BLOCK_START( "OnSkillProcess() - SkillHandleLoop" ); // 쿨타임 처리. 추후에 쿨 타임 뿐만 아니라 기타등등의 여러가지 지속적인 Processing 을 하게 될 것임. for( DWORD i = 0; i < m_vlSkillObjects.size(); i++ ) { // 클라이언트인 경우엔 스킬 객체 관리를 하는 SkillTask 가 먼저 소멸되어 valid 체크를 해야 함. if( m_vlSkillObjects.at( i ).hSkill ) m_vlSkillObjects.at( i ).hSkill->Process( LocalTime, fDelta ); } PROFILE_TIME_TEST_BLOCK_END(); #ifdef PRE_FIX_GAMESERVER_PERFOMANCE } #endif // #ifdef PRE_FIX_GAMESERVER_PERFOMANCE #endif // #ifndef PRE_FIX_SKILLLIST #else #ifndef PRE_FIX_SKILLLIST for( DWORD i = 0; i < m_vlhSkillList.size(); i++ ) { if( m_vlhSkillList.at( i ) ) m_vlhSkillList[i]->Process( LocalTime, fDelta ); } #else // 쿨타임 처리. 추후에 쿨 타임 뿐만 아니라 기타등등의 여러가지 지속적인 Processing 을 하게 될 것임. for( DWORD i = 0; i < m_vlSkillObjects.size(); i++ ) { // 클라이언트인 경우엔 스킬 객체 관리를 하는 SkillTask 가 먼저 소멸되어 valid 체크를 해야 함. if( m_vlSkillObjects.at( i ).hSkill ) m_vlSkillObjects.at( i ).hSkill->Process( LocalTime, fDelta ); } #endif // #ifndef PRE_FIX_SKILLLIST #endif // #ifdef _GAMESERVER // 아이템에 붙은 스킬이 있다면 Process if( m_hItemSkill ) { if( m_hItemSkill->IsFinished() ) { m_hItemSkill->OnEnd( LocalTime, fDelta ); deque::iterator iter = find( m_dqhItemSkillList.begin(), m_dqhItemSkillList.end(), m_hItemSkill ); _ASSERT( m_dqhItemSkillList.end() != iter ); m_dqhItemSkillList.erase( iter ); SAFE_RELEASE_SPTR( m_hItemSkill ); } else m_hItemSkill->ProcessExecute( LocalTime, fDelta ); } for( DWORD i = 0; i < m_dqhItemSkillList.size(); ++i ) { m_dqhItemSkillList.at( i )->Process( LocalTime, fDelta ); } // 종료 예약된 스킬들 종료 시킴. int iNumReservedFinishSkill = (int)m_vlhReservedFinishSkill.size(); for( int iSkill = 0; iSkill < iNumReservedFinishSkill; ++iSkill ) { DnSkillHandle hSkill = m_vlhReservedFinishSkill.at( iSkill ); if( hSkill == m_hProcessSkill ) { m_hProcessSkill->OnEnd( LocalTime, fDelta ); m_hProcessSkill.Identity(); } else if( hSkill == m_hToggleSkill ) { _ASSERT( m_hToggleSkill->IsToggleOn() ); if( m_hToggleSkill->IsToggleOn() ) OnSkillToggle( m_hToggleSkill, false ); } else if( hSkill == m_hAuraSkill ) { _ASSERT( m_hAuraSkill->IsAuraOn() ); if( m_hAuraSkill->IsAuraOn() ) OnSkillAura( m_hAuraSkill, false ); } } if( false == m_vlhReservedFinishSkill.empty() ) m_vlhReservedFinishSkill.clear(); } // 이 함수는 몬스터든 플레이어든 무기든 OnSignal 에서 Projectile 이 걸리면 항상 호출된다!! void MASkillUser::OnSkillProjectile( CDnProjectile *pProjectile ) { if( !IsValidActor() ) return; // Projectile 의 겉모습을 실제로 바꿔준다. bool bClientSide = false; #if defined( _GAMESERVER ) if( m_pActor->GetClassID() <= CDnActor::Reserved6 && m_pActor->IsPlayerActor() && ((CDnPlayerActor*)m_pActor)->IsLocalActor() ) bClientSide = true; #endif // #if defined( _GAMESERVER ) if( m_hToggleSkill && m_hToggleSkill->IsToggleOn() ) { // 현재 토글 Enable 될 상태인 스킬을 처리하는 것임. _ASSERT( m_hToggleSkill->IsToggleOn() ); CDnChangeProjectileProcessor* pChangeProj = static_cast(m_hToggleSkill->GetProcessor( IDnSkillProcessor::CHANGE_PROJECTILE )); if( pChangeProj ) { int iCheckProjectileID = ( bClientSide ) ? pChangeProj->GetSourceWeaponID() : pChangeProj->GetChangeWeaponID(); if( pProjectile->GetClassID() == iCheckProjectileID ) { if( m_hToggleSkill->GetDurationType() == CDnSkill::ActiveToggle ) { if( !(m_hToggleSkill->GetDecreaseMP() > 0 && m_pActor->GetSP() < m_hToggleSkill->GetDecreaseMP()) ) { // 마을에서는 SP 안 깍이도록 #ifndef _GAMESERVER if( CDnWorld::MapTypeVillage != CDnWorld::GetInstance().GetMapType() ) #endif m_pActor->UseMP( -m_hToggleSkill->GetDecreaseMP() ); //m_pActor->SetSP( m_pActor->GetSP() - m_hToggleSkill->GetDecreaseMP() ); if( bClientSide ) { int iWeaponLength = pProjectile->GetWeaponLength() - pProjectile->GetOriginalWeaponLength(); *(CDnWeapon*)pProjectile = *pChangeProj->GetProjectile(); pProjectile->SetWeaponLength( iWeaponLength + pProjectile->GetOriginalWeaponLength() ); } OnSkillToggleProjectile( m_hToggleSkill, pProjectile ); if( m_pActor->GetSP() < m_hToggleSkill->GetDecreaseMP() ) { OnSkillToggle( m_hToggleSkill, false ); } } } } } else { // ChangeAction 이라고 지호씨가 추가한 거 있음 CDnChangeActionStrProcessor* pChangeAction = static_cast(m_hToggleSkill->GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR )); if( pChangeAction ) { if( m_hToggleSkill->GetDurationType() == CDnSkill::ActiveToggle ) { if( !(m_hToggleSkill->GetDecreaseMP() > 0 && m_pActor->GetSP() < m_hToggleSkill->GetDecreaseMP()) ) { // 마을에서는 SP 안 깍이도록 #ifndef _GAMESERVER if( CDnWorld::MapTypeVillage != CDnWorld::GetInstance().GetMapType() ) #endif m_pActor->UseMP( -m_hToggleSkill->GetDecreaseMP() ); //m_pActor->SetSP( m_pActor->GetSP() - m_hToggleSkill->GetDecreaseMP() ); OnSkillToggleProjectile( m_hToggleSkill, pProjectile ); if( m_pActor->GetSP() < m_hToggleSkill->GetDecreaseMP() ) { OnSkillToggle( m_hToggleSkill, false ); } } } } } } #ifdef _GAMESERVER if( pProjectile && !bClientSide ) { // Note 한기: 직접 데미지 계산에 필요한 ActorState 를 Projectile 에 실어서 날리는 방법으로 변경. // 토글 스킬이 아닌 프로젝타일을 쏘는 스킬인 경우엔 여기서 공격력 적용시켜줌 if( !m_hToggleSkill && m_hProcessSkill ) { static_cast(this)->GetStateBlow()->Process( 0, 0.0f ); CDnActor* pActor = static_cast(this); // 체인 스킬 상태효과를 쓰는 발사체는 // CDnState 복사가 매우 빈번하게 일어날 수 있으므로 shaared_ptr 사용. boost::shared_ptr pActorStateSnapshot = boost::shared_ptr(new CDnState); *pActorStateSnapshot = *(static_cast(pActor)); // 스킬인 경우에만 여기서 pProjectile->FromSkill( true ); pProjectile->SetShooterStateSnapshot( pActorStateSnapshot ); #if defined(PRE_FIX_65287) float fFinalDamageRate = 0.0f; if (pActor && pActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_050)) { DNVector(DnBlowHandle) vlhBlows; pActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_050, vlhBlows ); int iNumBlow = (int)vlhBlows.size(); for( int i = 0; i < iNumBlow; ++i ) { fFinalDamageRate += vlhBlows[i]->GetFloatValue(); } } pProjectile->SetShooterFinalDamageRate(fFinalDamageRate); #endif // PRE_FIX_65287 } else if( m_hToggleSkill && m_hToggleSkill->IsToggleOn() ) { // 현재 토글 스킬은 사용되지 않지만 추후 어찌될지 몰라 코드 남겨둠. 2011.03.15 // 토글을 켠 당시의 능력치로 프로젝타일에 셋팅해 줌. boost::shared_ptr pActorStatesnapshot = boost::shared_ptr( new CDnState ); *pActorStatesnapshot = m_ActorStateSnapshotForToggleProjectile; pProjectile->SetShooterStateSnapshot( pActorStatesnapshot ); pProjectile->FromSkill( true ); #if defined(PRE_FIX_65287) CDnActor* pActor = static_cast(this); float fFinalDamageRate = 0.0f; if (pActor && pActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_050)) { DNVector(DnBlowHandle) vlhBlows; pActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_050, vlhBlows ); int iNumBlow = (int)vlhBlows.size(); for( int i = 0; i < iNumBlow; ++i ) { fFinalDamageRate += vlhBlows[i]->GetFloatValue(); } } pProjectile->SetShooterFinalDamageRate(fFinalDamageRate); #endif // PRE_FIX_65287 } else if( m_pActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_121 ) || m_pActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_129 ) ) { // #30362 스탠드 액션 변경 상태효과에서 발사체를 쏘는 경우에도 스킬 발사체로 처리 함. pProjectile->FromSkill( true ); } } #endif } void MASkillUser::OnSkillToggleProjectile( DnSkillHandle hSkill, CDnProjectile *pProjectile ) { } void MASkillUser::OnSkillToggle( DnSkillHandle hSkill, bool bEnable ) { if( !IsValidActor() ) return; if( bEnable ) { switch( hSkill->GetDurationType() ) { case CDnSkill::ActiveToggle: case CDnSkill::ActiveToggleForSummon: // 마을에서는 SP 안 깍이도록 #ifndef _GAMESERVER if( CDnWorld::MapTypeVillage != CDnWorld::GetInstance().GetMapType() ) #endif // 토글링할땐 깍이지 않는다. 쓸 때 깍임 //m_pActor->SetSP( m_pActor->GetSP() - hSkill->GetDecreaseSP() ); hSkill->OnBegin( m_LocalTime, 0.0f ); break; case CDnSkill::TimeToggle: hSkill->OnBegin( m_LocalTime, 0.0f ); break; } SkillToggle(hSkill, true); } else { SkillToggle(hSkill, false); hSkill->OnEnd( m_LocalTime, 0.0f ); hSkill->EnableToggle( false ); } //if( bEnable ) { // switch( hSkill->GetDurationType() ) { // case CDnSkillBase::TimeToggle: // pActor->SetSP( pActor->GetSP() - hSkill->GetDecreaseSP() ); // hSkill->Execute( m_LocalTime, 0.f ); // break; // case CDnSkillBase::ActiveToggle: // hSkill->Execute( m_LocalTime, 0.f ); // break; // case CDnSkillBase::Aura: // pActor->SetSP( pActor->GetSP() - hSkill->GetDecreaseSP() ); // hSkill->Execute( m_LocalTime, 0.f ); // break; // } //} //else { // hSkill->Finish( m_LocalTime, 0.f ); //} } void MASkillUser::OnSkillAura( DnSkillHandle hSkill, bool bEnable ) { if( !IsValidActor() ) return; if( bEnable ) { switch( hSkill->GetDurationType() ) { case CDnSkill::Aura: // 마을에서는 SP 안 깍이도록 #ifndef _GAMESERVER if( CDnWorld::MapTypeVillage != CDnWorld::GetInstance().GetMapType() ) #endif m_pActor->UseMP( -hSkill->GetDecreaseMP() ); //m_pActor->SetSP( m_pActor->GetSP() - hSkill->GetDecreaseMP() ); hSkill->OnBegin( m_LocalTime, 0.0f ); break; } m_hAuraSkill = hSkill; } else { hSkill->OnEnd( m_LocalTime, 0.0f ); hSkill->EnableAura( false ); if( m_hAuraSkill == hSkill ) m_hAuraSkill.Identity(); } } void MASkillUser::ResetToggleSkill() { //if( m_hToggleSkill ) // m_hToggleSkill.Identity(); //int iNumToggle = (int)m_vlhToggleSkills.size(); //for( int i = 0; i < iNumToggle; ++i ) //{ // if( m_vlhToggleSkills.at(i)->IsToggleOn() ) // OnSkillToggle( m_vlhToggleSkills.at(i), false ); //} } void MASkillUser::ResetAuraSkill() { //if( m_hAuraSkill ) // m_hAuraSkill.Identity(); //int iNumAura = (int)m_vlhAuraSkills.size(); //for( int i = 0; i < iNumAura; ++i ) //{ // if( m_vlhAuraSkills.at(i)->IsAuraOn() ) // OnSkillAura( m_vlhAuraSkills.at(i), false ); //} } bool MASkillUser::IsProcessSkill() { if( !m_hProcessSkill ) return false; return !m_hProcessSkill->IsFinished(); return true; } DWORD MASkillUser::GetSkillCount() { #ifndef PRE_FIX_SKILLLIST return (DWORD)m_vlhSkillList.size(); #else return (DWORD)m_vlSkillObjects.size(); #endif // #ifndef PRE_FIX_SKILLLIST } DnSkillHandle MASkillUser::GetSkillFromIndex( DWORD dwIndex ) { #ifndef PRE_FIX_SKILLLIST if( dwIndex < 0 || dwIndex >= m_vlhSkillList.size() ) return CDnSkill::Identity(); return m_vlhSkillList[dwIndex]; #else if( dwIndex < 0 || dwIndex >= m_vlSkillObjects.size() ) return CDnSkill::Identity(); return m_vlSkillObjects.at( dwIndex ).hSkill; #endif // #ifndef PRE_FIX_SKILLLIST } CDnSkill::UsingResult MASkillUser::UseSkill( int nSkillTableID, bool bCheckValid, bool bAutoUseFromServer/* = false*/, int nLuaSkillIndex/*=-1*/ ) { CDnSkill::UsingResult eResult = CDnSkill::UsingResult::Failed; DnSkillHandle hSkill = FindSkill( nSkillTableID ); if( hSkill ) { if( m_pActor && m_pActor->IsPlayerActor() ) { if( hSkill->GetSkillType() == CDnSkill::SkillTypeEnum::EnchantPassive ) return CDnSkill::UsingResult::Hack; } #ifdef _GAMESERVER // 서버의 쿨타임 오차를 감안해서 0.5초의 여유를 두고 있지만, // 존이동할때나 기타 0.5초 이상 클라이언트와 벌어지는 다른 새로운 경우들이 생길 수 있으므로 // 최종 스킬 사용한 타임 스탬프를 찍어두어 데이터에 지정된 스킬의 쿨타임보타 간격이 크다면 // 서버의 스킬 객체에 저장되어있는 쿨타임을 초기화 시켜주도록 한다. (#19737) hSkill->UpdateSkillCoolTimeExactly(); #endif eResult = CanExecuteSkill( hSkill ); if( !bCheckValid || CDnSkill::UsingResult::Success == eResult ) { #if defined(_CLIENT) //스킬 사용이 성공이라도 소환 몬스터가 있다면 소환 몬스터를 소환 해제만 하고 실제 스킬 사용은 안됨.. if (hSkill->GetDurationType() == CDnSkill::DurationTypeEnum::SummonOnOff) { if (hSkill->SummonMonsterOff()) return eResult; } #endif // _CLIENT #ifdef _GAMESERVER ExecuteSkill( hSkill, m_LocalTime, 0.f ); #else ExecuteSkill( hSkill, m_LocalTime, 0.f, bCheckValid, bAutoUseFromServer ); #endif } #if defined(_CLIENT) else { // 스킬 사용이 실패 했더라도 소환 몬스터가 있다면 소환 몬스터 소환 해제 시킴. if (hSkill->GetDurationType() == CDnSkill::DurationTypeEnum::SummonOnOff) hSkill->SummonMonsterOff(); } #endif // _CLIENT } return eResult; } bool MASkillUser::UseItemSkill( int nSkillTableID, int nSkillLevelTableID, CDnItem::ItemSkillApplyType ItemSkillApplyType, int nItemID/* = -1*/ ) { if( !IsValidActor() ) return false; bool bResult = true; switch( ItemSkillApplyType ) { case CDnItem::ApplySkill: { // 여기서 생성된 아이템 스킬은 OnProcessSkill 함수에서 Finish 체크를 하여 리스트에서 알아서 삭제해준다. DnSkillHandle hSkill = CDnSkill::CreateSkill( m_pActor->GetMySmartPtr(), nSkillTableID, nSkillLevelTableID ); if( !hSkill ) { bResult = false; break; } // 아이템에 붙은 스킬로 셋팅해줌. hSkill->AsItemSkill(); #if defined(_GAMESERVER) hSkill->SetItemID(nItemID); #endif // _GAMESERVER m_dqhItemSkillList.push_back( hSkill ); ExecuteSkill( hSkill, m_LocalTime, 0.f ); m_hItemSkill = hSkill; } break; // 삭제 예정. case CDnItem::ApplyStateBlow: { //// 여기에 들어오는 Instant 아이템인 경우 CDnItemTask 에서 아이템 생성하고 곧바로 삭제함. //DnSkillHandle hSkill = CDnSkill::CreateSkill( m_pActor->GetMySmartPtr(), nSkillTableID, nSkillLevelTableID ); // //if( !hSkill ) // bResult = false; //hSkill->AsItemSkill(); //// 강제로 상태효과 동기를 맞춰주는 함수. //hSkill->OnBeginForceSync( m_LocalTime, 0.0f ); //hSkill->Release(); } break; } return bResult; } void MASkillUser::ResetSkillCoolTime( void ) { #ifndef PRE_FIX_SKILLLIST int iNumSkill = (int)m_vlhSkillList.size(); for( int iSkill = 0; iSkill < iNumSkill; ++iSkill ) { DnSkillHandle hSkill = m_vlhSkillList.at( iSkill ); if( hSkill ) hSkill->ResetCoolTime(); } #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { DnSkillHandle hSkill = m_vlSkillObjects.at( i ).hSkill; if (hSkill) hSkill->ResetCoolTime(); } #endif // #ifndef PRE_FIX_SKILLLIST #if defined(_GAMESERVER) m_listProcessPreFixDefenceSkills.clear(); m_listProcessPreFixOffenceSkills.clear(); if (!m_prefixSystemDefenceSkills.empty()) { PREFIX_SYSTEM_SKILL_LIST::iterator iter = m_prefixSystemDefenceSkills.begin(); PREFIX_SYSTEM_SKILL_LIST::iterator enditer = m_prefixSystemDefenceSkills.end(); for (; iter != enditer; ++iter) { if (iter->second.hSkill) iter->second.hSkill->ResetCoolTime(); } } if (!m_prefixSystemOffenceSkills.empty()) { PREFIX_SYSTEM_SKILL_LIST::iterator iter = m_prefixSystemOffenceSkills.begin(); PREFIX_SYSTEM_SKILL_LIST::iterator enditer = m_prefixSystemOffenceSkills.end(); for (; iter != enditer; ++iter) { if (iter->second.hSkill) iter->second.hSkill->ResetCoolTime(); } } m_PrefixSkillCoolTimeManager.InitList(); #endif // _GAMESERVER } void MASkillUser::ResetPvPSkillCoolTime() { #ifndef PRE_FIX_SKILLLIST int iNumSkill = (int)m_vlhSkillList.size(); for( int iSkill = 0; iSkill < iNumSkill; ++iSkill ) { DnSkillHandle hSkill = m_vlhSkillList.at( iSkill ); if( hSkill->GetSkillType() == CDnSkill::SkillTypeEnum::Active ) { hSkill->OnBeginCoolTime(); } } #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { DnSkillHandle hSkill = m_vlSkillObjects.at( i ).hSkill; if( hSkill->GetSkillType() == CDnSkill::SkillTypeEnum::Active ) { hSkill->OnBeginCoolTime(); } } #endif // #ifndef PRE_FIX_SKILLLIST } void MASkillUser::ResetLadderSkillCoolTime() { #ifndef PRE_FIX_SKILLLIST ResetSkillCoolTime(); int iNumSkill = (int)m_vlhSkillList.size(); for( int iSkill = 0; iSkill < iNumSkill; ++iSkill ) { DnSkillHandle hSkill = m_vlhSkillList.at( iSkill ); hSkill->SetElapsedDelayTime( hSkill->GetDelayTime()/2 ); } #else ResetSkillCoolTime(); for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { DnSkillHandle hSkill = m_vlSkillObjects.at( i ).hSkill; hSkill->SetElapsedDelayTime( hSkill->GetDelayTime()/2 ); } #endif // #ifndef PRE_FIX_SKILLLIST } #ifdef _GAMESERVER void MASkillUser::IgnoreSkillCoolTime( void ) { #ifndef PRE_FIX_SKILLLIST int iNumSkill = (int)m_vlhSkillList.size(); for( int iSkill = 0; iSkill < iNumSkill; ++iSkill ) { DnSkillHandle hSkill = m_vlhSkillList.at( iSkill ); hSkill->IgnoreCoolTime(); } m_bIgnoreCoolTime = true; #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { DnSkillHandle hSkill = m_vlSkillObjects.at( i ).hSkill; hSkill->IgnoreCoolTime(); } m_bIgnoreCoolTime = true; #endif // #ifndef PRE_FIX_SKILLLIST } #endif void MASkillUser::ReserveFinishSkill( DnSkillHandle hSkill ) { if( hSkill ) { m_vlhReservedFinishSkill.push_back( hSkill ); } } void MASkillUser::CancelUsingSkill( void ) { if( m_hProcessSkill ) { m_hProcessSkill->OnEnd( 0, 0.0f ); m_hProcessSkill.Identity(); } }; void MASkillUser::OnAttachWeapon( DnWeaponHandle hWeapon, int iChangedWeaponIndex ) { // 패시브 스킬들 중에 사용가능 무기를 정의한 것들이 있나 체크. // 다른 무기를 차고 있어서 패시브 상태효과들이 적용된 상태가 아니라면 활성화 시켜준다. int iNumSelfBlowPassiveSkills = (int)m_vlhSelfPassiveBlowSkill.size(); for( int i = 0; i < iNumSelfBlowPassiveSkills; ++i ) { DnSkillHandle hSkill = m_vlhSelfPassiveBlowSkill.at( i ); if( false == hSkill->IsAppliedPassiveBlows() ) { // 내부에서 사용가능 무기 체크 함. ApplyPassiveSkill( hSkill ); } } // rangechecker 있는 스킬들은 무기 찼을 때 업데이트 해준다. #ifndef _GAMESERVER #ifndef PRE_FIX_SKILLLIST int iNumSkill = (int)m_vlhSkillList.size(); for( int i = 0; i < iNumSkill; ++i ) { DnSkillHandle hSkill = m_vlhSkillList.at( i ); hSkill->UpdateRangeChecker( hSkill->GetSelectedLevelDataType() ); } #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { DnSkillHandle hSkill = m_vlSkillObjects.at( i ).hSkill; hSkill->UpdateRangeChecker( hSkill->GetSelectedLevelDataType() ); } #endif // #ifndef PRE_FIX_SKILLLIST #endif } void MASkillUser::OnDetachWeapon( DnWeaponHandle hWeapon, int iDetachedWeaponIndex ) { // 패시브 스킬들 중에 사용가능 무기를 정의한 것들이 있나 체크. // 요구하는 무기와 맞지 않는다면 패시브 스킬에 딸린 상태효과들을 제거한다. int iNumSelfBlowPassiveSkills = (int)m_vlhSelfPassiveBlowSkill.size(); for( int i = 0; i < iNumSelfBlowPassiveSkills; ++i ) { DnSkillHandle hSkill = m_vlhSelfPassiveBlowSkill.at( i ); if( hSkill->IsAppliedPassiveBlows() ) { if( false == hSkill->IsSatisfyWeapon() ) { int iNumStateBlow = hSkill->GetStateEffectCount(); for( int iBlow = 0; iBlow < iNumStateBlow; ++iBlow ) { const CDnSkill::StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( iBlow ); DNVector(DnBlowHandle) vlAppliedBlows; m_pActor->GatherAppliedStateBlowByBlowIndex( (STATE_BLOW::emBLOW_INDEX)pSE->nID, vlAppliedBlows ); int iNumAppliedBlow = (int)vlAppliedBlows.size(); for( int iAppliedBlow = 0; iAppliedBlow < iNumAppliedBlow; ++iAppliedBlow ) { DnBlowHandle hBlow = vlAppliedBlows.at( iAppliedBlow ); if( hBlow->GetParentSkillInfo()->iSkillID == hSkill->GetClassID() ) { // 현시점에서 곧바로 삭제 //m_pActor->GetStateBlow()->RemoveImediatlyStateEffectFromID( hBlow->GetBlowID() ); //RebirthBlow에서 OnBegin에서 타고 들어와서 삭제가 되는 경우가 발생. //즉시 지우지 않고 지움 리스트에 추가 하도록 수정 m_pActor->GetStateBlow()->RemoveStateBlowFromID(hBlow->GetBlowID()); } } } hSkill->SetAppliedPassiveBlows( false ); } } } // rangechecker 있는 스킬들은 무기 찼을 때 업데이트 해준다. #ifndef _GAMESERVER #ifndef PRE_FIX_SKILLLIST int iNumSkill = (int)m_vlhSkillList.size(); for( int i = 0; i < iNumSkill; ++i ) { DnSkillHandle hSkill = m_vlhSkillList.at( i ); hSkill->UpdateRangeChecker( hSkill->GetSelectedLevelDataType() ); } #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { DnSkillHandle hSkill = m_vlSkillObjects.at( i ).hSkill; hSkill->UpdateRangeChecker( hSkill->GetSelectedLevelDataType() ); } #endif // #ifndef PRE_FIX_SKILLLIST #endif } #ifdef _GAMESERVER void MASkillUser::SelectSkillLevelDataType( int iLevelDataType, bool bPlayerSummonedMonster/* = false*/ ) { #ifndef PRE_FIX_SKILLLIST for( int i = 0; i < (int)m_vlhSkillList.size(); ++i ) { DnSkillHandle hSkill = m_vlhSkillList.at( i ); hSkill->SelectLevelDataType( iLevelDataType, bPlayerSummonedMonster ); } #else for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { DnSkillHandle hSkill = m_vlSkillObjects.at( i ).hSkill; hSkill->SelectLevelDataType( iLevelDataType, bPlayerSummonedMonster ); } #endif // #ifndef PRE_FIX_SKILLLIST } int MASkillUser::GetSelectedSkillLevelDataType( void ) { #ifndef PRE_FIX_SKILLLIST if( m_vlhSkillList.empty() ) return CDnSkill::NUM_SKILLLEVEL_APPLY_TYPE; else return m_vlhSkillList.front()->GetSelectedLevelDataType(); #else if( m_vlSkillObjects.empty() ) return CDnSkill::NUM_SKILLLEVEL_APPLY_TYPE; else return m_vlSkillObjects.front().hSkill->GetSelectedLevelDataType(); #endif // #ifndef PRE_FIX_SKILLLIST } #endif #ifndef _GAMESERVER bool MASkillUser::IsUsingSkillAction( const char *szAction ) { if( m_setUseActionNames.find( szAction ) != m_setUseActionNames.end() ) return true; return false; } #endif void MASkillUser::AddStateBlowIDToRemove(int blowID) { m_vlStateBlowIDToRemove.insert(std::map::value_type(blowID, blowID)); } void MASkillUser::InitStateBlowIDToRemove() { m_vlStateBlowIDToRemove.clear(); } #ifdef _GAMESERVER void MASkillUser::RemoveApplySEWhenTargetNormalHitProcessor( IDnSkillProcessor* pProcessor ) { DNVector( IDnSkillProcessor* )::iterator iter = find( m_vlpApplySEWhenNormalHitProcessor.begin(), m_vlpApplySEWhenNormalHitProcessor.end(), pProcessor ); if( m_vlpApplySEWhenNormalHitProcessor.end() != iter ) m_vlpApplySEWhenNormalHitProcessor.erase( iter ); } #endif #if defined(_GAMESERVER) bool MASkillUser::AddPreFixSystemDefenceSkill(int slotIndex, DnSkillHandle hSkill) { if (!hSkill) { RemovePreFixSystemDefenceSkill(slotIndex); return false; } PREFIX_SYSTEM_SKILLINFO skillInfo; skillInfo.slotIndex = slotIndex; skillInfo.hSkill = hSkill; skillInfo.hSkill->SetItemPrefixSkill(); //해당 스킬의 확률값을 가져온다. CDnProbabilityChecker* pProbabilityChecker = static_cast(hSkill->GetChecker(IDnSkillUsableChecker::PROB_CHECKER)); skillInfo.fRatio = pProbabilityChecker ? pProbabilityChecker->GetProbability() : 0.0f; bool bResult = m_prefixSystemDefenceSkills.insert(PREFIX_SYSTEM_SKILL_LIST::value_type(slotIndex, skillInfo)).second; // if (bResult) // RefreshPrefixDefenceSkills(); #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) AddPrefixDefenceSkill(hSkill); #endif // PRE_ADD_PREFIX_SYSTE_RENEW return bResult; } void MASkillUser::RemovePreFixSystemDefenceSkill(int slotIndex) { PREFIX_SYSTEM_SKILL_LIST::iterator iter = m_prefixSystemDefenceSkills.find(slotIndex); if (iter != m_prefixSystemDefenceSkills.end()) { DnSkillHandle hSkill = iter->second.hSkill; #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) RemovePrefixDefenceSkill(hSkill); #endif // PRE_ADD_PREFIX_SYSTE_RENEW RemoveProcessPrefixDefenceSkill(hSkill); SAFE_RELEASE_SPTR(hSkill); m_prefixSystemDefenceSkills.erase(iter); //RefreshPrefixDefenceSkills(); } } void MASkillUser::RemoveProcessPrefixDefenceSkill(DnSkillHandle hSkill) { list::iterator iter = m_listProcessPreFixDefenceSkills.begin(); for( ; iter != m_listProcessPreFixDefenceSkills.end(); ) { DnSkillHandle hProcessPrefixDefenceSkill = *iter; if (hProcessPrefixDefenceSkill == hSkill) { iter = m_listProcessPreFixDefenceSkills.erase(iter); continue; } ++iter; } } void MASkillUser::RefreshPrefixDefenceSkills() { PREFIX_SYSTEM_SKILL_COLLECTION sameSkillList; //1. 슬롯별로 저장된 스킬들을 같은 스킬끼리 수집한다. GroupingPrefixDefenceSkillsBySameSkillID(m_prefixSystemDefenceSkills, sameSkillList); PREFIX_SYSTEM_SKILL_LIST tempCandiateDefenceSkills; //2. 같은 스킬들 확률을 더해서 확률을 결정하고, 같은 스킬중 레벨이 높은 녀석을 선택한다. CalculateProbabilitySkill(tempCandiateDefenceSkills, sameSkillList); //3. 스킬ID별로 레벨이 높은 녀석의 리스트에서 스킬의 우선순위별로 그룹핑한다. GroupingSkillByPriority(m_PrefixSystemCandiateDefenceSkills, tempCandiateDefenceSkills); } bool MASkillUser::AddPreFixSystemOffenceSkill(int slotIndex, DnSkillHandle hSkill) { if (!hSkill) { RemovePreFixSystemOffenceSkill(slotIndex); return false; } PREFIX_SYSTEM_SKILLINFO skillInfo; skillInfo.slotIndex = slotIndex; skillInfo.hSkill = hSkill; skillInfo.hSkill->SetItemPrefixSkill(); //해당 스킬의 확률값을 가져온다. CDnProbabilityChecker* pProbabilityChecker = static_cast(hSkill->GetChecker(IDnSkillUsableChecker::PROB_CHECKER)); skillInfo.fRatio = pProbabilityChecker ? pProbabilityChecker->GetProbability() : 0.0f; bool bResult = m_prefixSystemOffenceSkills.insert(PREFIX_SYSTEM_SKILL_LIST::value_type(slotIndex, skillInfo)).second; // if (bResult) // RefreshPrefixOffenceSkills(); #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) AddPrefixOffenceSkill(hSkill); #endif // PRE_ADD_PREFIX_SYSTE_RENEW return bResult; } void MASkillUser::RemovePreFixSystemOffenceSkill(int slotIndex) { PREFIX_SYSTEM_SKILL_LIST::iterator iter = m_prefixSystemOffenceSkills.begin(); for( ; iter != m_prefixSystemOffenceSkills.end(); ) { if( iter->first == slotIndex ) { DnSkillHandle hSkill = iter->second.hSkill; hSkill->OnEnd( 0, 0.f ); #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) RemovePrefixOffenceSkill(hSkill); #endif // PRE_ADD_PREFIX_SYSTE_RENEW RemoveProcessPrefixOffenceSkill(hSkill); SAFE_RELEASE_SPTR(hSkill); iter = m_prefixSystemOffenceSkills.erase(iter); continue; } ++iter; } } void MASkillUser::RemoveProcessPrefixOffenceSkill(DnSkillHandle hSkill) { list::iterator iter = m_listProcessPreFixOffenceSkills.begin(); for( ; iter != m_listProcessPreFixOffenceSkills.end(); ) { DnSkillHandle hProcessPrefixOffenceSkill = *iter; if (hProcessPrefixOffenceSkill == hSkill) { iter = m_listProcessPreFixOffenceSkills.erase(iter); continue; } ++iter; } } void MASkillUser::RefreshPrefixOffenceSkills(float fHitDamageProb) { PREFIX_SYSTEM_SKILL_COLLECTION sameSkillList; //1. 슬롯별로 저장된 스킬들을 같은 스킬끼리 수집한다. GroupingPrefixDefenceSkillsBySameSkillID(m_prefixSystemOffenceSkills, sameSkillList); PREFIX_SYSTEM_SKILL_LIST tempCandiateOffenceSkills; //2. 같은 스킬들 확률을 더해서 확률을 결정하고, 같은 스킬중 레벨이 높은 녀석을 선택한다. CalculateProbabilitySkill(tempCandiateOffenceSkills, sameSkillList); //3. 스킬ID별로 레벨이 높은 녀석의 리스트에서 스킬의 우선순위별로 그룹핑한다. GroupingSkillByPriority(m_PrefixSystemCandiateOffenceSkills, tempCandiateOffenceSkills, fHitDamageProb); } void MASkillUser::ProcessPrefixDefenceSkill(DnActorHandle hHitter) { //접두어 시스템 방어용 스킬 등록되 되어 있지 않다면 if (m_prefixSystemDefenceSkills.empty()) return; OutputDebug("접두어 방어용 스킬 시작-----------------------------!!\n"); //접두어 시스템 방어용 스킬중 선택.. RefreshPrefixDefenceSkills(); // 최종 리스트에 담긴 스킬중에서 우선순위가 가장 높은 리스트에 저장된 스킬만 사용한다. if (m_PrefixSystemCandiateDefenceSkills.empty()) { OutputDebug("접두어 방어용 스킬 선택된거 없음.-----------------------------!!\n"); return; } PREFIX_SYSTEM_SKILL_COLLECTION::iterator selectIter = m_PrefixSystemCandiateDefenceSkills.begin(); PRESIX_SYSTEM_SKILLS &skills = selectIter->second; PRESIX_SYSTEM_SKILLS::iterator iter = skills.begin(); PRESIX_SYSTEM_SKILLS::iterator endIter = skills.end(); for (; iter != endIter; ++iter) { DnSkillHandle hSkill = (*iter).hSkill; if (!hSkill /*|| CDnSkill::UsingResult::Success != hSkill->CanExecute()*/) continue; /* //쿨타임 남아 있으면 건너뜀. if (hSkill->GetCoolTime() != 0.0f && hSkill->GetElapsedDelayTime() > 0.0f) continue; */ OutputDebug("접두어 방어용 스킬(%d) 시작....\n", hSkill->GetClassID()); //Skill 쿨타임 시작? hSkill->OnBeginCoolTime(); m_PrefixSkillCoolTimeManager.AddCoolTime(hSkill); int nStateEffectCount = hSkill->GetStateEffectCount(); // 해당 스킬의 상태 효과별 적용 액터를 결정해서 중복 처리 확인하고, // 액터별로 상태 효과 적용을 해야 할듯 한데... for( DWORD k = 0; k < hSkill->GetStateEffectCount(); k++ ) { CDnSkill::StateEffectStruct *pLocalStruct = hSkill->GetStateEffectFromIndex(k); DnActorHandle hApplyActor; // 상태 효과 적용 타겟 타입에 따라 적용될 액터를 결정한다. switch( pLocalStruct->ApplyType ) { case CDnSkill::ApplySelf: // 자신한테 hApplyActor = m_pActor->GetActorHandle(); break; case CDnSkill::ApplyTarget: // 타겟한테 hApplyActor = hHitter; break; case CDnSkill::ApplyEnemy: // 적한테.. (같은 편이 아니면 나를 때린 놈이 적이다..) if( m_pActor->GetTeam() == hHitter->GetTeam() ) continue; else hApplyActor = hHitter; break; case CDnSkill::ApplyFriend: // 같은 편.. (같은 편이면 나를 때린 놈이 같은 편..?) if( m_pActor->GetTeam() != hHitter->GetTeam() ) continue; else hApplyActor = hHitter; break; } // 상태효과중 지속 시간이 0이고 자신에게 거는 상태 효과는 // 스킬의 m_vlApplyNoPacketStateEffectList에 담아 놓고 스킬 끝날때 // 지워 지도록 한다...[2011/01/13 semozz] bool isSelfPermentStateEffect = false; if (CDnSkill::ApplySelf == pLocalStruct->ApplyType && (0 == pLocalStruct->nDurationTime || -1 == pLocalStruct->nDurationTime)) { pLocalStruct->nDurationTime = -1; isSelfPermentStateEffect = true; } CDnStateBlow *pStateBlow = hApplyActor->GetStateBlow(); CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply; if (pStateBlow) eResult = pStateBlow->CanApplySkillStateEffect(hSkill->GetInfo(), *pLocalStruct); if (CDnSkill::CanApply::Fail != eResult) { hApplyActor->RemoveResetStateBlow(); int nBlowID = hApplyActor->CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pLocalStruct->nID, pLocalStruct->nDurationTime, pLocalStruct->szValue.c_str(), false, false ); if (isSelfPermentStateEffect) hSkill->AddPrefixBlow(hApplyActor, nBlowID); } hApplyActor->InitStateBlowIDToRemove(); } //사용된 방어 스킬은 리스트에 담아 놓는다.. m_listProcessPreFixDefenceSkills.push_back(hSkill); } OutputDebug("접두어 방어용 스킬 끝 -----------------------------!!\n"); } void MASkillUser::ProcessPrefixOffenceSkill(float fHitDamageProb) { //접두어 시스템 방어용 스킬 등록되 되어 있지 않다면 if (m_prefixSystemOffenceSkills.empty()) return; //접두어 시스템 공격용 스킬중 선택.. RefreshPrefixOffenceSkills(fHitDamageProb); // 최종 리스트에 담긴 스킬중에서 우선순위가 가장 높은 리스트에 저장된 스킬만 사용한다. if (m_PrefixSystemCandiateOffenceSkills.empty()) return; PREFIX_SYSTEM_SKILL_COLLECTION::iterator selectIter = m_PrefixSystemCandiateOffenceSkills.begin(); PRESIX_SYSTEM_SKILLS &skills = selectIter->second; PRESIX_SYSTEM_SKILLS::iterator iter = skills.begin(); PRESIX_SYSTEM_SKILLS::iterator endIter = skills.end(); for (; iter != endIter; ++iter) { DnSkillHandle hSkill = (*iter).hSkill; if (!hSkill/* || CDnSkill::UsingResult::Success != hSkill->CanExecute()*/) continue; //Skill 쿨타임 시작? hSkill->OnBeginCoolTime(); m_PrefixSkillCoolTimeManager.AddCoolTime(hSkill); int nStateEffectCount = hSkill->GetStateEffectCount(); // 해당 스킬의 상태 효과별 적용 액터를 결정해서 중복 처리 확인하고, // 액터별로 상태 효과 적용을 해야 할듯 한데... for( DWORD k = 0; k < hSkill->GetStateEffectCount(); k++ ) { CDnSkill::StateEffectStruct *pLocalStruct = hSkill->GetStateEffectFromIndex(k); DnActorHandle hApplyActor; // 상태 효과 적용 타겟 타입에 따라 적용될 액터를 결정한다. switch( pLocalStruct->ApplyType ) { case CDnSkill::ApplySelf: // 자신한테 hApplyActor = m_pActor->GetActorHandle(); break; case CDnSkill::ApplyTarget: // 타겟한테 continue; case CDnSkill::ApplyEnemy: // 적한테.. (같은 편이 아니면 나를 때린 놈이 적이다..) continue; break; case CDnSkill::ApplyFriend: // 같은 편.. (같은 편이면 나를 때린 놈이 같은 편..?) continue; break; } // 상태효과중 지속 시간이 0이고 자신에게 거는 상태 효과는 // 스킬의 m_vlApplyNoPacketStateEffectList에 담아 놓고 스킬 끝날때 // 지워 지도록 한다...[2011/01/13 semozz] bool isSelfPermentStateEffect = false; if (CDnSkill::ApplySelf == pLocalStruct->ApplyType && (0 == pLocalStruct->nDurationTime || -1 == pLocalStruct->nDurationTime)) { pLocalStruct->nDurationTime = -1; isSelfPermentStateEffect = true; } CDnStateBlow *pStateBlow = hApplyActor->GetStateBlow(); CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply; if (pStateBlow) eResult = pStateBlow->CanApplySkillStateEffect(hSkill->GetInfo(), *pLocalStruct); if (CDnSkill::CanApply::Fail != eResult) { hApplyActor->RemoveResetStateBlow(); int nBlowID = hApplyActor->CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pLocalStruct->nID, pLocalStruct->nDurationTime, pLocalStruct->szValue.c_str(), false, false ); if (isSelfPermentStateEffect) hSkill->AddPrefixBlow(hApplyActor, nBlowID); } hApplyActor->InitStateBlowIDToRemove(); } //사용된 방어 스킬은 리스트에 담아 놓는다.. m_listProcessPreFixOffenceSkills.push_back(hSkill); } } void MASkillUser::ApplyPrefixOffenceSkillToTarget(DnActorHandle hTarget) { // 최종 리스트에 담긴 스킬중에서 우선순위가 가장 높은 리스트에 저장된 스킬만 사용한다. if (m_listProcessPreFixOffenceSkills.empty()) return; list::iterator iter = m_listProcessPreFixOffenceSkills.begin(); list::iterator endIter = m_listProcessPreFixOffenceSkills.end(); for (; iter != endIter; ++iter) { DnSkillHandle hSkill = (*iter); if (!hSkill) continue; int nStateEffectCount = hSkill->GetStateEffectCount(); // 해당 스킬의 상태 효과별 적용 액터를 결정해서 중복 처리 확인하고, // 액터별로 상태 효과 적용을 해야 할듯 한데... for( DWORD k = 0; k < hSkill->GetStateEffectCount(); k++ ) { CDnSkill::StateEffectStruct *pLocalStruct = hSkill->GetStateEffectFromIndex(k); DnActorHandle hApplyActor; // 상태 효과 적용 타겟 타입에 따라 적용될 액터를 결정한다. switch( pLocalStruct->ApplyType ) { case CDnSkill::ApplySelf: // 자신한테 continue; case CDnSkill::ApplyTarget: // 타겟한테 hApplyActor = hTarget; break; case CDnSkill::ApplyEnemy: // 적한테.. (같은 편이 아니면 나를 때린 놈이 적이다..) if( m_pActor->GetTeam() == hTarget->GetTeam() ) continue; else hApplyActor = hTarget; break; case CDnSkill::ApplyFriend: // 같은 편.. (같은 편이면 나를 때린 놈이 같은 편..?) if( m_pActor->GetTeam() != hTarget->GetTeam() ) continue; else hApplyActor = hTarget; break; } // 상태효과중 지속 시간이 0이고 자신에게 거는 상태 효과는 // 스킬의 m_vlApplyNoPacketStateEffectList에 담아 놓고 스킬 끝날때 // 지워 지도록 한다...[2011/01/13 semozz] bool isSelfPermentStateEffect = false; if (CDnSkill::ApplySelf == pLocalStruct->ApplyType && (0 == pLocalStruct->nDurationTime || -1 == pLocalStruct->nDurationTime)) { pLocalStruct->nDurationTime = -1; isSelfPermentStateEffect = true; } CDnStateBlow *pStateBlow = hApplyActor->GetStateBlow(); CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply; if (pStateBlow) eResult = pStateBlow->CanApplySkillStateEffect(hSkill->GetInfo(), *pLocalStruct); if (CDnSkill::CanApply::Fail != eResult) { hApplyActor->RemoveResetStateBlow(); int nBlowID = hApplyActor->CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pLocalStruct->nID, pLocalStruct->nDurationTime, pLocalStruct->szValue.c_str(), false, false ); if (isSelfPermentStateEffect) hSkill->AddPrefixBlow(hApplyActor, nBlowID); } hApplyActor->InitStateBlowIDToRemove(); } } } void MASkillUser::CalculateProbabilitySkill(PREFIX_SYSTEM_SKILL_LIST &candidateSkills, PREFIX_SYSTEM_SKILL_COLLECTION &sameSkillList) { //후보 스킬 리스트 초기화 candidateSkills.clear(); PREFIX_SYSTEM_SKILL_COLLECTION::iterator iter = sameSkillList.begin(); PREFIX_SYSTEM_SKILL_COLLECTION::iterator endIter = sameSkillList.end(); for (; iter != endIter; ++iter) { //같은 스킬ID를 가진 스킬들 확률을 합산한다. PRESIX_SYSTEM_SKILLS::iterator skillIter = iter->second.begin(); PRESIX_SYSTEM_SKILLS::iterator skillEndIter = iter->second.end(); float fProbability = 0.0f; DnSkillHandle hSelectedSkill; int nSlotIndex = -1; for (; skillIter != skillEndIter; ++skillIter) { if (skillIter->hSkill) { fProbability += skillIter->fRatio; // 해당 스킬 if (!hSelectedSkill) { hSelectedSkill = skillIter->hSkill; nSlotIndex = skillIter->slotIndex; } else { if (hSelectedSkill->GetLevel() < skillIter->hSkill->GetLevel()) { hSelectedSkill = skillIter->hSkill; nSlotIndex = skillIter->slotIndex; } } } } // 선택된 스킬이 있으면 최종 스킬 후보 리스트에 추가한다. if (hSelectedSkill && -1 != nSlotIndex && 0.0f != fProbability) { int nSkillID = hSelectedSkill->GetClassID(); PREFIX_SYSTEM_SKILLINFO skillInfo; skillInfo.fRatio = fProbability; skillInfo.hSkill = hSelectedSkill; skillInfo.slotIndex = nSlotIndex; OutputDebug("같은 스킬(%d) 합산 최종 확률 (%f) 슬롯 Index(%d)로 결정됨!!!!!\n", skillInfo.hSkill->GetClassID(), skillInfo.fRatio, skillInfo.slotIndex); candidateSkills.insert(PREFIX_SYSTEM_SKILL_LIST::value_type(nSkillID, skillInfo)); } } } void MASkillUser::GroupingPrefixDefenceSkillsBySameSkillID(PREFIX_SYSTEM_SKILL_LIST &skillList, PREFIX_SYSTEM_SKILL_COLLECTION &sameSkillList) { PREFIX_SYSTEM_SKILL_LIST::iterator iter = skillList.begin(); PREFIX_SYSTEM_SKILL_LIST::iterator endIter = skillList.end(); for (; iter != endIter; ++iter) { int nSkillID = -1; int nSlotIndex = iter->first; //슬롯 인덱스 PREFIX_SYSTEM_SKILLINFO skillInfo = iter->second; if (!skillInfo.hSkill) continue; nSkillID = skillInfo.hSkill->GetClassID(); PREFIX_SYSTEM_SKILL_COLLECTION::iterator findIter = sameSkillList.find(nSkillID); if (findIter != sameSkillList.end()) { //이미 같은 스킬ID를 가진 스킬이 리스트에 있다면 skillInfo.slotIndex = nSlotIndex; findIter->second.push_back(skillInfo); } else { //아직 스킬 등록이 되어 있지 않다면 리스트를 만들어 등록.. PRESIX_SYSTEM_SKILLS skillList; skillInfo.slotIndex = nSlotIndex; skillList.push_back(skillInfo); sameSkillList.insert(PREFIX_SYSTEM_SKILL_COLLECTION::value_type(nSkillID, skillList)); } } } void MASkillUser::GroupingSkillByPriority(PREFIX_SYSTEM_SKILL_COLLECTION& prefixSystemSkills, PREFIX_SYSTEM_SKILL_LIST& tempCandiateSkills, float fHitDamageProb/* = 1.0f*/) { prefixSystemSkills.clear(); PREFIX_SYSTEM_SKILL_LIST::iterator iter = tempCandiateSkills.begin(); PREFIX_SYSTEM_SKILL_LIST::iterator endIter = tempCandiateSkills.end(); //대표 스킬 리스트를 순회 하면서 우선 순위별로 그룹핑을 한다. for (; iter != endIter; ++iter) { int nPriority = 0; PREFIX_SYSTEM_SKILLINFO skillInfo = iter->second; if (!skillInfo.hSkill) continue; nPriority = skillInfo.hSkill->GetPrefixSystemPriority(); //스킬 쿨타임 확인... //if (skillInfo.hSkill->GetCoolTime() > 0.0f && skillInfo.hSkill->GetElapsedDelayTime() > 0.0f) if (m_PrefixSkillCoolTimeManager.IsCoolTime(skillInfo.hSkill->GetClassID())) { OutputDebug("스킬(%d) 쿨타임 중.........!!!!\n", skillInfo.hSkill->GetClassID()); continue; } //확률계산 해서 통과된 스킬만 리스트에 추가한다... bool bExecuteable = rand() % 10000 <= (iter->second.fRatio * fHitDamageProb); if (!bExecuteable) { OutputDebug("스킬(%d) 확률에서 걸러짐...!!!!\n", skillInfo.hSkill->GetClassID()); continue; } OutputDebug("우선순위 (%d)에 스킬(%d) 등록됨!!!!\n", nPriority, skillInfo.hSkill->GetClassID()); PREFIX_SYSTEM_SKILL_COLLECTION::iterator findIter = prefixSystemSkills.find(nPriority); if (findIter != prefixSystemSkills.end()) { //이미 우선순위 정보가 등록 되어 있다면 //우선순위 리스트에 스킬 정보를 추가한다. findIter->second.push_back(skillInfo); } else { //아직 우선순위가 등록이 되어 있지 않다면.. //스킬 저장할 리스트 만들고 //스킬 정보 설정 해서 리스트에 추가한다. PRESIX_SYSTEM_SKILLS skillList; skillList.push_back(skillInfo); prefixSystemSkills.insert(PREFIX_SYSTEM_SKILL_COLLECTION::value_type(nPriority, skillList)); } } } void MASkillUser::EndPrefixSystemSkill( LOCAL_TIME LocalTime, float fDelta ) { list::iterator iter = m_listProcessPreFixDefenceSkills.begin(); for( iter; iter != m_listProcessPreFixDefenceSkills.end(); ++iter ) { DnSkillHandle hAutoPassive = *iter; hAutoPassive->OnEnd( LocalTime, fDelta ); } iter = m_listProcessPreFixOffenceSkills.begin(); for( iter; iter != m_listProcessPreFixOffenceSkills.end(); ++iter ) { DnSkillHandle hAutoPassive = *iter; hAutoPassive->OnEnd( LocalTime, fDelta ); } } void MASkillUser::InitPrefixOffenceSkills() { m_PrefixSystemCandiateOffenceSkills.clear(); } bool MASkillUser::IsPrefixTriggerSkill() { if (!m_hProcessSkill) return false; return m_hProcessSkill->IsPrefixTriggerSkill(); } #endif // _GAMESERVER void MASkillUser::UpdateSkillLevelUpInfo() { std::map::iterator iter = m_SkillLevelUpInfoList.begin(); std::map::iterator endIter = m_SkillLevelUpInfoList.end(); m_SkillLevelUpInfo.clear(); for (; iter != endIter; ++iter) { SkillLevelUpInfo& skillLevelUpInfo = iter->second; //스킬 ID / 레벨업 값이 정상이라면 if (skillLevelUpInfo.nSkillID != 0 && skillLevelUpInfo.nLevelUp != 0) { //스킬 레벨업 정보 리스트에서 같은 스킬 아이디를 찾는다. std::map::iterator findIter = m_SkillLevelUpInfo.find(skillLevelUpInfo.nSkillID); if (findIter != m_SkillLevelUpInfo.end()) { // 기존 레벨업 값이 더 크다면 건너뜀. if (findIter->second > skillLevelUpInfo.nLevelUp) continue; // 기존 레벨업 값이 크지 않으면 리스트에서 제거 한다.. m_SkillLevelUpInfo.erase(findIter); } // 새로운 스킬 레벨업 정보를 저장한다. m_SkillLevelUpInfo.insert(std::make_pair(skillLevelUpInfo.nSkillID, skillLevelUpInfo.nLevelUp)); } } } void MASkillUser::AddSkillLevelUpInfo(int nSlotIndex, int nSkillID, int nLevelUp) { DnSkillHandle hSkill = FindSkill(nSkillID); if( hSkill ) { if (hSkill->GetElapsedDelayTime() > 0.0f || hSkill->IsToggleOn()) return; } SkillLevelUpInfo skillLevelUpInfo; skillLevelUpInfo.nSkillID = nSkillID; skillLevelUpInfo.nLevelUp = nLevelUp; m_SkillLevelUpInfoList.insert(std::make_pair(nSlotIndex, skillLevelUpInfo)); //스킬 레벨업 정보 갱신.. UpdateSkillLevelUpInfo(); int nSkillLevelUp = GetSkillLevelUpValue(nSkillID); SkillLevelUp(nSkillID, nSkillLevelUp); } void MASkillUser::RemoveSkillLevelUpInfo(int nSlotIndex) { std::map::iterator findIter = m_SkillLevelUpInfoList.find(nSlotIndex); if (findIter != m_SkillLevelUpInfoList.end()) { //지워질 레벨업 아이템에 적용된 스킬 ID를 받아 놓는다. int nSkillID = findIter->second.nSkillID; DnSkillHandle hSkill = FindSkill(nSkillID); if( hSkill ) { if (hSkill->GetElapsedDelayTime() > 0.0f || hSkill->IsToggleOn()) return; } //레벨업 정보를 리스트에서 지우고 m_SkillLevelUpInfoList.erase(findIter); //레벨업 정보 갱신 UpdateSkillLevelUpInfo(); //레벨업 정보가 없어지므로 int nLevelUp = GetSkillLevelUpValue(nSkillID); SkillLevelUp(nSkillID, nLevelUp); } } int MASkillUser::GetSkillLevelUpValue(int nSkillID) { int nLevelUpValue = 0; int nLevelUpValueByNormalItem = 0; std::map::iterator findNormalIter = m_SkillLevelUpInfo.find(nSkillID); if (findNormalIter != m_SkillLevelUpInfo.end()) nLevelUpValueByNormalItem = findNormalIter->second; int nLevelUpValueByCashItem = 0; std::map::iterator findCashIter = m_CashSkillLevelUpInfo.find(nSkillID); if (findCashIter != m_CashSkillLevelUpInfo.end()) nLevelUpValueByCashItem = findCashIter->second; //캐시아이템의 레벨업 제한 값은 1 if (nLevelUpValueByCashItem > 1) nLevelUpValueByCashItem = 1; //레벨업 아이템은 중복 되지 않고, 수차가 높은 값을 적용 하도록 한다. nLevelUpValue = max(nLevelUpValueByNormalItem, nLevelUpValueByCashItem); return nLevelUpValue; } bool MASkillUser::ExistSkillLevelUpValue() { if( m_SkillLevelUpInfo.empty() && m_CashSkillLevelUpInfo.empty() ) return false; return true; } void MASkillUser::SkillLevelUp(int nSkillID, int nLevelUp) { //기존 스킬을 찾아서 스킬이 있고, 초기화 된 스킬이 아니면 레벨업 시킨다. DnSkillHandle hSkill = FindSkill(nSkillID); if (!hSkill) return; int nCurentSkillLevel = hSkill->GetLevel(); int nCurLevlUpValue = hSkill->GetLevelUpValue(); bool bChangeLevel = true; if (nCurentSkillLevel == 0) bChangeLevel = false; //새로 레벨업 할 수치와 현재 레벨업이 적용된 수치가 같으면 스킬을 변경할 필요가 없음. if (nCurLevlUpValue == nLevelUp) bChangeLevel = false; if (bChangeLevel) { //SetSkillLevel내부 AddSkill에서 원래 레벨에 지금의 증가치가 계산되어 스킬이 생성됨.. int nOrigLevel = nCurentSkillLevel - nCurLevlUpValue; ChangeSkillLevelUp(nSkillID, nOrigLevel); } #if defined(_GAMESERVER) #else else hSkill->DisableSkillByItemMove(false); #endif // _GAMESERVER } void MASkillUser::ChangeSkillLevelUp(int nSkillID, int nOrigLevel) { RemoveSkill(nSkillID); AddSkill(nSkillID, nOrigLevel); } // std::map m_CashSkillLevelUpInfoList; void MASkillUser::AddSkillLevelUpInfoByCashItem(int nSlotIndex, int nSkillID, int nLevelUp) { SkillLevelUpInfo skillLevelUpInfo; skillLevelUpInfo.nSkillID = nSkillID; skillLevelUpInfo.nLevelUp = nLevelUp; m_CashSkillLevelUpInfoList.insert(std::make_pair(nSlotIndex, skillLevelUpInfo)); //스킬 레벨업 정보 갱신.. UpdateSkillLevelUpInfoByCashItem(); int nSkillLevelUp = GetSkillLevelUpValue(nSkillID); SkillLevelUp(nSkillID, nSkillLevelUp); } void MASkillUser::UpdateSkillLevelUpInfoByCashItem() { std::map::iterator iter = m_CashSkillLevelUpInfoList.begin(); std::map::iterator endIter = m_CashSkillLevelUpInfoList.end(); m_CashSkillLevelUpInfo.clear(); for (; iter != endIter; ++iter) { SkillLevelUpInfo& skillLevelUpInfo = iter->second; //스킬 ID / 레벨업 값이 정상이라면 if (skillLevelUpInfo.nSkillID != 0 && skillLevelUpInfo.nLevelUp != 0) { //스킬 레벨업 정보 리스트에서 같은 스킬 아이디를 찾는다. std::map::iterator findIter = m_CashSkillLevelUpInfo.find(skillLevelUpInfo.nSkillID); if (findIter != m_CashSkillLevelUpInfo.end()) { // 기존 레벨업 값이 더 크다면 건너뜀. if (findIter->second > skillLevelUpInfo.nLevelUp) continue; // 기존 레벨업 값이 크지 않으면 리스트에서 제거 한다.. m_CashSkillLevelUpInfo.erase(findIter); } // 새로운 스킬 레벨업 정보를 저장한다. m_CashSkillLevelUpInfo.insert(std::make_pair(skillLevelUpInfo.nSkillID, skillLevelUpInfo.nLevelUp)); } } } void MASkillUser::RemoveSkillLevelUpInfoByCashItem(int nSlotIndex) { std::map::iterator findIter = m_CashSkillLevelUpInfoList.find(nSlotIndex); if (findIter != m_CashSkillLevelUpInfoList.end()) { //지워질 레벨업 아이템에 적용된 스킬 ID를 받아 놓는다. int nSkillID = findIter->second.nSkillID; //레벨업 정보를 리스트에서 지우고 m_CashSkillLevelUpInfoList.erase(findIter); //레벨업 정보 갱신 UpdateSkillLevelUpInfoByCashItem(); //레벨업 정보가 없어지므로 int nLevelUp = GetSkillLevelUpValue(nSkillID); SkillLevelUp(nSkillID, nLevelUp); } } #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) #if defined(_GAMESERVER) void MASkillUser::AddPrefixDefenceSkill(DnSkillHandle hSkill) { if (!hSkill) return; //이 스킬의 접두사 스킬 타입(행운/파괴....) int nPrefixSkillType = hSkill->GetPrefixSkillType(); PREFIX_SKILL_INFO::iterator findIter = m_PrefixDefenceSkills.find(nPrefixSkillType); //기존 카테고리가 있다면 if (findIter != m_PrefixDefenceSkills.end()) { //기존 접두사 스킬 관리 객체에 스킬을 추가 하고, 정보를 갱신한다. findIter->second->AddSkill(hSkill); } else { //아직 등록된 접두사 스킬이 없다면 새로 만들어서 등록 한다. CDnPrefixSkill* pPrefixSkill = new CDnPrefixSkill(nPrefixSkillType); pPrefixSkill->AddSkill(hSkill); m_PrefixDefenceSkills.insert(make_pair(nPrefixSkillType, pPrefixSkill)); } } void MASkillUser::RemovePrefixDefenceSkill(DnSkillHandle hSkill) { //이 스킬의 접두사 스킬 타입(행운/파괴....) int nPrefixSkillType = hSkill->GetPrefixSkillType(); PREFIX_SKILL_INFO::iterator findIter = m_PrefixDefenceSkills.find(nPrefixSkillType); //기존 카테고리가 있다면 if (findIter != m_PrefixDefenceSkills.end()) { //기존 접두사 스킬 관리 객체에서 스킬을 제거 한다. findIter->second->RemoveSkill(hSkill); //등록된 스킬이 없으면 리스트에서 제거 한다. if (findIter->second->GetSkillCount() == 0) { SAFE_DELETE(findIter->second); m_PrefixDefenceSkills.erase(findIter); } } } void MASkillUser::ProcessPrefixDefenceSkill_New(DnActorHandle hHitter) { //접두어 시스템 방어용 스킬 등록되 되어 있지 않다면 if (m_PrefixDefenceSkills.empty()) return; OutputDebug("접두어 방어용 스킬 시작-----------------------------!!\n"); //접두사 스킬 발동 확률 계산.. PREFIX_SKILL_INFO::iterator iter = m_PrefixDefenceSkills.begin(); PREFIX_SKILL_INFO::iterator endIter = m_PrefixDefenceSkills.end(); for (; iter != endIter; ++iter) { CDnPrefixSkill* pPrefixSkill = iter->second; if (!pPrefixSkill) continue; //접두사 스킬이 쿨타임 중인지 확인한다.. if (m_PrefixSkillCoolTimeManager.IsCoolTime(pPrefixSkill->GetSkillType())) { OutputDebug("접두사 방어 스킬(%d) 쿨타임 중!!!!!!!!!!\n", pPrefixSkill->GetSkillType()); continue; } //접두사 스킬 발동 확률 확인. bool bExecuteable = rand() % 10000 <= pPrefixSkill->GetProbability(); if (!bExecuteable) { OutputDebug("접두사 방어 스킬(%d) 확률에서 걸러짐...!!!!\n", pPrefixSkill->GetSkillType()); continue; } //대표 스킬 DnSkillHandle hSkill = pPrefixSkill->GetSkillHandle(); if (!hSkill) continue; hSkill->OnBeginCoolTime(); //Skill 쿨타임 시작? m_PrefixSkillCoolTimeManager.AddCoolTime(pPrefixSkill); int nStateEffectCount = pPrefixSkill->GetStateEffectCount(); // 해당 스킬의 상태 효과별 적용 액터를 결정해서 중복 처리 확인하고, // 액터별로 상태 효과 적용을 해야 할듯 한데... for( int k = 0; k < pPrefixSkill->GetStateEffectCount(); k++ ) { CDnSkill::StateEffectStruct *pLocalStruct = pPrefixSkill->GetStateEffectFromIndex(k); DnActorHandle hApplyActor; // 상태 효과 적용 타겟 타입에 따라 적용될 액터를 결정한다. switch( pLocalStruct->ApplyType ) { case CDnSkill::ApplySelf: // 자신한테 hApplyActor = m_pActor->GetActorHandle(); break; case CDnSkill::ApplyTarget: // 타겟한테 hApplyActor = hHitter; break; case CDnSkill::ApplyEnemy: // 적한테.. (같은 편이 아니면 나를 때린 놈이 적이다..) if( m_pActor->GetTeam() == hHitter->GetTeam() ) continue; else hApplyActor = hHitter; break; case CDnSkill::ApplyFriend: // 같은 편.. (같은 편이면 나를 때린 놈이 같은 편..?) if( m_pActor->GetTeam() != hHitter->GetTeam() ) continue; else hApplyActor = hHitter; break; } // 상태효과중 지속 시간이 0이고 자신에게 거는 상태 효과는 // 스킬의 m_vlApplyNoPacketStateEffectList에 담아 놓고 스킬 끝날때 // 지워 지도록 한다...[2011/01/13 semozz] bool isSelfPermentStateEffect = false; if (CDnSkill::ApplySelf == pLocalStruct->ApplyType && (0 == pLocalStruct->nDurationTime || -1 == pLocalStruct->nDurationTime)) { pLocalStruct->nDurationTime = -1; isSelfPermentStateEffect = true; } CDnStateBlow *pStateBlow = hApplyActor->GetStateBlow(); CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply; if (pStateBlow) eResult = pStateBlow->CanApplySkillStateEffect(hSkill->GetInfo(), *pLocalStruct); if (CDnSkill::CanApply::Fail != eResult) { hApplyActor->RemoveResetStateBlow(); int nBlowID = hApplyActor->CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pLocalStruct->nID, pLocalStruct->nDurationTime, pLocalStruct->szValue.c_str(), false, false ); OutputDebug("접두사 방어 스킬(%d) 상태효과 (%d)-(%s)\n", pPrefixSkill->GetSkillType(), pLocalStruct->nID, pLocalStruct->szValue.c_str()); if (isSelfPermentStateEffect) hSkill->AddPrefixBlow(hApplyActor, nBlowID); } hApplyActor->InitStateBlowIDToRemove(); } //사용된 방어 스킬은 리스트에 담아 놓는다.. m_listProcessPreFixDefenceSkills.push_back(hSkill); } OutputDebug("접두어 방어용 스킬 끝 -----------------------------!!\n"); } void MASkillUser::AddPrefixOffenceSkill(DnSkillHandle hSkill) { if (!hSkill) return; //이 스킬의 접두사 스킬 타입(행운/파괴....) int nPrefixSkillType = hSkill->GetPrefixSkillType(); PREFIX_SKILL_INFO::iterator findIter = m_PrefixOffenceSkills.find(nPrefixSkillType); //기존 카테고리가 있다면 if (findIter != m_PrefixOffenceSkills.end()) { //기존 접두사 스킬 관리 객체에 스킬을 추가 하고, 정보를 갱신한다. findIter->second->AddSkill(hSkill); } else { //아직 등록된 접두사 스킬이 없다면 새로 만들어서 등록 한다. CDnPrefixSkill* pPrefixSkill = new CDnPrefixSkill(nPrefixSkillType); pPrefixSkill->AddSkill(hSkill); m_PrefixOffenceSkills.insert(make_pair(nPrefixSkillType, pPrefixSkill)); } } void MASkillUser::RemovePrefixOffenceSkill(DnSkillHandle hSkill) { //이 스킬의 접두사 스킬 타입(행운/파괴....) int nPrefixSkillType = hSkill->GetPrefixSkillType(); PREFIX_SKILL_INFO::iterator findIter = m_PrefixOffenceSkills.find(nPrefixSkillType); //기존 카테고리가 있다면 if (findIter != m_PrefixOffenceSkills.end()) { //기존 접두사 스킬 관리 객체에서 스킬을 제거 한다. findIter->second->RemoveSkill(hSkill); //등록된 스킬이 없으면 리스트에서 제거 한다. if (findIter->second->GetSkillCount() == 0) { RemoveProcessPrefixOffenceSkill(findIter->second); SAFE_DELETE(findIter->second); m_PrefixOffenceSkills.erase(findIter); } } } //덤프 관련해서(한 프레임에서 동작해서 이럴일은 없을듯 한데..) 공격용 접미사 스킬이 제거 될때 //실행되고 있는 접미사 스킬 리스트에 같은 스킬이 있으면 제거 하도록한다. void MASkillUser::RemoveProcessPrefixOffenceSkill(CDnPrefixSkill* pRemovePrefixSkill) { if (NULL == pRemovePrefixSkill) return; list::iterator iter = m_ProcessPrefixOffenceSkills.begin(); for (; iter != m_ProcessPrefixOffenceSkills.end(); ) { CDnPrefixSkill* pPrefixSkill = (*iter); if (pPrefixSkill && pPrefixSkill == pRemovePrefixSkill) { iter = m_ProcessPrefixOffenceSkills.erase(iter); continue; } ++iter; } } void MASkillUser::ProcessPrefixOffenceSkill_New() { //접두어 시스템 방어용 스킬 등록되 되어 있지 않다면 if (m_PrefixOffenceSkills.empty()) return; //접두사 스킬 발동 전에 리스트를 비운다. m_ProcessPrefixOffenceSkills.clear(); //접두사 스킬 발동 확률 계산.. PREFIX_SKILL_INFO::iterator iter = m_PrefixOffenceSkills.begin(); PREFIX_SKILL_INFO::iterator endIter = m_PrefixOffenceSkills.end(); for (; iter != endIter; ++iter) { CDnPrefixSkill* pPrefixSkill = iter->second; if (!pPrefixSkill) continue; //접두사 스킬이 쿨타임 중인지 확인한다..(무기/방어구 구분이 된다??) if (m_PrefixSkillCoolTimeManager.IsCoolTime(pPrefixSkill->GetSkillType())) { OutputDebug("접두사 공격 스킬(%d) 쿨타임 중!!!!!!!!!!\n", pPrefixSkill->GetSkillType()); continue; } //접두사 스킬 발동 확률 확인. bool bExecuteable = rand() % 10000 <= pPrefixSkill->GetProbability(); if (!bExecuteable) { OutputDebug("접두사 공격 스킬(%d) 확률에서 걸러짐...!!!!\n", pPrefixSkill->GetSkillType()); continue; } //대표 스킬 DnSkillHandle hSkill = pPrefixSkill->GetSkillHandle(); if (!hSkill) continue; //Skill 쿨타임 시작? hSkill->OnBeginCoolTime(); m_PrefixSkillCoolTimeManager.AddCoolTime(pPrefixSkill); int nStateEffectCount = pPrefixSkill->GetStateEffectCount(); // 해당 스킬의 상태 효과별 적용 액터를 결정해서 중복 처리 확인하고, // 액터별로 상태 효과 적용을 해야 할듯 한데... for( int k = 0; k < pPrefixSkill->GetStateEffectCount(); k++ ) { CDnSkill::StateEffectStruct *pLocalStruct = pPrefixSkill->GetStateEffectFromIndex(k); DnActorHandle hApplyActor; // 상태 효과 적용 타겟 타입에 따라 적용될 액터를 결정한다. switch( pLocalStruct->ApplyType ) { case CDnSkill::ApplySelf: // 자신한테 hApplyActor = m_pActor->GetActorHandle(); break; case CDnSkill::ApplyTarget: // 타겟한테 continue; case CDnSkill::ApplyEnemy: // 적한테.. (같은 편이 아니면 나를 때린 놈이 적이다..) continue; break; case CDnSkill::ApplyFriend: // 같은 편.. (같은 편이면 나를 때린 놈이 같은 편..?) continue; break; } // 상태효과중 지속 시간이 0이고 자신에게 거는 상태 효과는 // 스킬의 m_vlApplyNoPacketStateEffectList에 담아 놓고 스킬 끝날때 // 지워 지도록 한다...[2011/01/13 semozz] bool isSelfPermentStateEffect = false; if (CDnSkill::ApplySelf == pLocalStruct->ApplyType && (0 == pLocalStruct->nDurationTime || -1 == pLocalStruct->nDurationTime)) { pLocalStruct->nDurationTime = -1; isSelfPermentStateEffect = true; } CDnStateBlow *pStateBlow = hApplyActor->GetStateBlow(); CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply; if (pStateBlow) eResult = pStateBlow->CanApplySkillStateEffect(hSkill->GetInfo(), *pLocalStruct); if (CDnSkill::CanApply::Fail != eResult) { hApplyActor->RemoveResetStateBlow(); int nBlowID = hApplyActor->CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pLocalStruct->nID, pLocalStruct->nDurationTime, pLocalStruct->szValue.c_str(), false, false ); // OutputDebug("접두사 공격 스킬(%d) 상태효과 자신한테 적용 (%d)-(%s)\n", pPrefixSkill->GetSkillType(), pLocalStruct->nID, pLocalStruct->szValue.c_str()); if (isSelfPermentStateEffect) hSkill->AddPrefixBlow(hApplyActor, nBlowID); } hApplyActor->InitStateBlowIDToRemove(); } //사용된 방어 스킬은 리스트에 담아 놓는다.. m_listProcessPreFixOffenceSkills.push_back(hSkill); //지금 발동된 공격용 접두사 스킬을 담아 놓는다. m_ProcessPrefixOffenceSkills.push_back(pPrefixSkill); } } void MASkillUser::ApplyPrefixOffenceSkillToTarget_New(DnActorHandle hTarget) { // 최종 리스트에 담긴 스킬중에서 우선순위가 가장 높은 리스트에 저장된 스킬만 사용한다. if (m_ProcessPrefixOffenceSkills.empty()) return; list::iterator iter = m_ProcessPrefixOffenceSkills.begin(); list::iterator endIter = m_ProcessPrefixOffenceSkills.end(); for (; iter != endIter; ++iter) { CDnPrefixSkill* pPrefixSkill = (*iter); if (!pPrefixSkill) continue; DnSkillHandle hSkill = pPrefixSkill->GetSkillHandle(); if (!hSkill) continue; int nStateEffectCount = pPrefixSkill->GetStateEffectCount(); // 해당 스킬의 상태 효과별 적용 액터를 결정해서 중복 처리 확인하고, // 액터별로 상태 효과 적용을 해야 할듯 한데... for( int k = 0; k < pPrefixSkill->GetStateEffectCount(); k++ ) { CDnSkill::StateEffectStruct *pLocalStruct = pPrefixSkill->GetStateEffectFromIndex(k); DnActorHandle hApplyActor; // 상태 효과 적용 타겟 타입에 따라 적용될 액터를 결정한다. switch( pLocalStruct->ApplyType ) { case CDnSkill::ApplySelf: // 자신한테 continue; case CDnSkill::ApplyTarget: // 타겟한테 hApplyActor = hTarget; break; case CDnSkill::ApplyEnemy: // 적한테.. (같은 편이 아니면 나를 때린 놈이 적이다..) if( m_pActor->GetTeam() == hTarget->GetTeam() ) continue; else hApplyActor = hTarget; break; case CDnSkill::ApplyFriend: // 같은 편.. (같은 편이면 나를 때린 놈이 같은 편..?) if( m_pActor->GetTeam() != hTarget->GetTeam() ) continue; else hApplyActor = hTarget; break; } // 상태효과중 지속 시간이 0이고 자신에게 거는 상태 효과는 // 스킬의 m_vlApplyNoPacketStateEffectList에 담아 놓고 스킬 끝날때 // 지워 지도록 한다...[2011/01/13 semozz] bool isSelfPermentStateEffect = false; if (CDnSkill::ApplySelf == pLocalStruct->ApplyType && (0 == pLocalStruct->nDurationTime || -1 == pLocalStruct->nDurationTime)) { pLocalStruct->nDurationTime = -1; isSelfPermentStateEffect = true; } CDnStateBlow *pStateBlow = hApplyActor->GetStateBlow(); CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply; if (pStateBlow) eResult = pStateBlow->CanApplySkillStateEffect(hSkill->GetInfo(), *pLocalStruct); if (CDnSkill::CanApply::Fail != eResult) { hApplyActor->RemoveResetStateBlow(); int nBlowID = hApplyActor->CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pLocalStruct->nID, pLocalStruct->nDurationTime, pLocalStruct->szValue.c_str(), false, false ); OutputDebug("접두사 공격 스킬(%d) 상태효과 상대에 적용 (%d)-(%s)\n", pPrefixSkill->GetSkillType(), pLocalStruct->nID, pLocalStruct->szValue.c_str()); if (isSelfPermentStateEffect) hSkill->AddPrefixBlow(hApplyActor, nBlowID); } hApplyActor->InitStateBlowIDToRemove(); } } //덤프 관련 의심 코드 정리 //적용하고 리스트를 비운다.. m_ProcessPrefixOffenceSkills.clear(); } #endif // _GAMESERVER #endif // PRE_ADD_PREFIX_SYSTE_RENEW void MASkillUser::SkillToggle(DnSkillHandle hSkill, bool isOn) { if (!hSkill) return; #if defined(_CLIENT) CDnSkill::DurationTypeEnum eDurationType = hSkill->GetDurationType(); if (eDurationType == CDnSkill::DurationTypeEnum::SummonOnOff) { hSkill->SummonMonsterOff(); } #endif // _CLIENT int skillID = hSkill->GetClassID(); TOGGLESKILLLIST::iterator findIter = m_ToggleOnSkillList.find(skillID); if (isOn) { //이미 스킬 등록되어 있음.. if (findIter != m_ToggleOnSkillList.end()) return; m_ToggleOnSkillList.insert(std::make_pair(skillID, hSkill)); } else { //등록된 스킬이 없다면 if (findIter == m_ToggleOnSkillList.end()) return; m_ToggleOnSkillList.erase(findIter); } } void MASkillUser::SummonOnOffSkillInit(DWORD dwSummonMonsterID) { DNVector(DnSkillHandle) summonOnOffSkills; #if defined(PRE_FIX_SKILLLIST) for( DWORD i=0; iGetDurationType() == CDnSkill::DurationTypeEnum::SummonOnOff) summonOnOffSkills.push_back(hSkill); } #endif // PRE_FIX_SKILLLIST for (DWORD i =0; i < summonOnOffSkills.size(); ++i) { DnSkillHandle hSkill = summonOnOffSkills[i]; if (hSkill && hSkill->GetSummonMonsterID() == dwSummonMonsterID) hSkill->EnableToggle(false); } } #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) void MASkillUser::UpdateGlobalCoolTime(float fRate) { for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { if(m_vlSkillObjects.at( i ).hSkill) m_vlSkillObjects.at( i ).hSkill->UpdateGlobalCoolTime(fRate); } } void MASkillUser::ResetGlobalCoolTime(float fRate) { for( DWORD i = 0; i < m_vlSkillObjects.size(); ++i ) { if(m_vlSkillObjects.at( i ).hSkill) m_vlSkillObjects.at( i ).hSkill->ResetGlobalCoolTime(fRate); } } #endif // PRE_ADD_TOTAL_LEVEL_SKILL