#include "StdAfx.h" #include "DnSkill.h" #include "DnTableDB.h" #include "DnItemTask.h" #include "IDnSkillUsableChecker.h" #include "IDnSkillProcessor.h" #include "DnDivideSEArgumentByTargets.h" #include "DnPlayerActor.h" #include "DnBlow.h" #include "TaskManager.h" #include "DnPartyTask.h" #include "DNUserSession.h" #include "DnPlayAniProcess.h" #include "DnPartialPlayProcessor.h" #include "DnApplySEWhenActionSetBlowEnabledProcessor.h" #include "DnChangeActionStrProcessor.h" #include "DnChangeActionStrByBubbleProcessor.h" #include "DnStateEffectApplyOnOffByBubbleProcessor.h" #include "DnStateBlow.h" #include "DnBubbleSystem.h" #include "DnObserverEventMessage.h" #include "DnPingpongBlow.h" #include "DnBasicBlow.h" #include "DnHealingBlow.h" #include "DnHPIncBlow.h" #include "DnInvincibleAtBlow.h" #include "DnProbabilityChecker.h" #include "DnCreateBlow.h" #include "DnAllowedSkillsBlow.h" #include "DnMonsterActor.h" #include "DnProjectile.h" #include "DnAdditionalStateInfoBlow.h" #include "DnAddStateBySkillGroupBlow.h" #include "DnTransformBlow.h" #include "DnBloodSuckingBlow.h" #include "DnOrderMySummonedMonsterBlow.h" #if defined(PRE_FIX_NEXTSKILLINFO) #include "DNGameDataManager.h" #endif // PRE_FIX_NEXTSKILLINFO #if defined(PRE_FIX_46381) #include "DnContinueBaseMPIncBlow.h" #endif // PRE_FIX_46381 using namespace BubbleSystem; using namespace boost; DECL_MULTISMART_PTR_STATIC( CDnSkill, MAX_SESSION_COUNT, 100 ) CDnSkill::CDnSkill( DnActorHandle hActor ) : CMultiSmartPtrBase< CDnSkill, MAX_SESSION_COUNT >(hActor->GetRoom()), m_hActor( hActor ), m_LastTimeToggleMPDecreaseTime( 0 ), m_bToggle( false ), m_bAura( false ), m_bItemSkill( false ), m_fPassiveActionSkillLength( 0.0f ), m_eElement( CDnState::ElementEnum_Amount ), m_iNextLevelSkillPoint( -1 ), m_bChainingPassiveSkill( false ), m_bAppliedPassiveSelfBlows( false ), m_bTempSkill( false ) { SecureZeroMemory( m_iNeedItemID, sizeof(m_iNeedItemID) ); SecureZeroMemory( m_iNeedItemDecreaseCount, sizeof(m_iNeedItemDecreaseCount) ); SecureZeroMemory( m_fHPConsumeType, sizeof(m_fHPConsumeType) ); SecureZeroMemory( m_fMPConsumeType, sizeof(m_fMPConsumeType) ); SecureZeroMemory( m_iNeedHP, sizeof(m_iNeedHP) ); SecureZeroMemory( m_iNeedMP, sizeof(m_iNeedMP) ); SecureZeroMemory( m_iIncreaseRange, sizeof(m_iIncreaseRange) ); SecureZeroMemory( m_iDecreaseHP, sizeof(m_iDecreaseHP) ); SecureZeroMemory( m_iDecreaseMP, sizeof(m_iDecreaseMP) ); SecureZeroMemory( m_fOriginalDelayTime, sizeof(m_fOriginalDelayTime) ); SecureZeroMemory( m_fDelayTime, sizeof(m_fDelayTime) ); m_iSkillID = 0; m_iSkillLevelID = 0; m_iDissolvable = 0; m_iDuplicateCount = 0; m_iSkillDuplicateMethod = 0; m_iEffectDuplicateMethod = 0; m_iLevel = 0; m_iMaxLevel = 0; m_eSkillType = SkillTypeEnum::Active; m_eDurationType = DurationTypeEnum::Instantly; m_eTargetType = TargetTypeEnum::Self; m_iLevelLimit = 0; m_fLeftDelayTime = 0.f; m_fCoolTime = 0.f; m_iAdditionalThreat = 0; m_iCPScore = 0; m_pEffectAction = NULL; m_fOnceDelayTime = 0.0f; m_fOnceElapsedDelayTime = 0.0f; m_bEquipItemSkill = false; m_iEquipIndex = -1; m_fCoolTimeAdjustBlowValue = 1.0f; m_fStartSuperArmor = 0.0f; m_bStartCanHit = true; m_fResetCooltime = 0.0f; m_dwLastUseSkillTimeStamp = 0; m_iExclusiveID = 0; m_iSelectedSkillLevelDataApplyType = PVE; // 디폴트는 pve 이다. m_iBaseSkillID = 0; m_iAppliedEnchantPassiveSkillID = 0; m_nPriority = 0; m_nItemID = -1; m_fCoolTimeMultipier = 1.0f; m_isIgnoreImmuneBackup = false; m_nLevelUpValue = 0; #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) m_nPrefixSkillType = -1; #endif // PRE_ADD_PREFIX_SYSTE_RENEW m_iGlobalSkillGroupID = 0; SecureZeroMemory( m_afGlobalCoolTime, sizeof(m_afGlobalCoolTime) ); m_fAnotherGlobalSkillCoolTime = 0.0f; m_nAnotherGlobakSkillID = 0; #if defined( PRE_ADD_ACADEMIC ) m_iSummonerDecreaseSP = 0; m_iSummonerDecreaseSPSkillID = 0; #endif // #if defined( PRE_ADD_ACADEMIC ) m_SummonMonsterID = -1; m_bAddStateEffectQueue = false; m_bEnchantedFromBubble = false; m_SkillStartTime = 0; m_bFinished = false; m_bIsPrefixTriggerSkill = false; m_iNeedJobClassID = 0; m_iNowLevelSkillPoint = 0; memset (m_iOriginalNeedMP, 0x00, sizeof(m_iOriginalNeedMP)); memset (m_aeNeedEquipType, 0x00, sizeof(m_aeNeedEquipType)); #if defined(PRE_FIX_64312) m_isAppliedSummonMonsterEnchantSkill = false; m_bIsSummonMonsterSkill = false; #endif // PRE_FIX_64312 #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) m_fDeltaGlobalCoolTime = 0.0f; #endif // PRE_ADD_TOTAL_LEVEL_SKILL } CDnSkill::~CDnSkill(void) { for( int iSelectedLevelData = PVE; iSelectedLevelData < NUM_SKILLLEVEL_APPLY_TYPE; ++iSelectedLevelData ) { SAFE_DELETE_PVEC( m_vlpUsableCheckers[ iSelectedLevelData ] ); } for( int iSelectedLevelData = PVE; iSelectedLevelData < NUM_SKILLLEVEL_APPLY_TYPE; ++iSelectedLevelData ) { SAFE_DELETE_PVEC( m_vlpProcessors[ iSelectedLevelData ] ); } for( int iSelectedLevelData = PVE; iSelectedLevelData < NUM_SKILLLEVEL_APPLY_TYPE; ++iSelectedLevelData ) { SAFE_DELETE_PVEC( m_vlpProcessorBackup[ iSelectedLevelData ] ); } #if defined(PRE_FIX_66175) for( int iSelectedLevelData = PVE; iSelectedLevelData < NUM_SKILLLEVEL_APPLY_TYPE; ++iSelectedLevelData ) { SAFE_DELETE_PVEC( m_vlUsableCheckersBackup[ iSelectedLevelData ] ); } #endif // PRE_FIX_66175 } void CDnSkill::SetHasActor( DnActorHandle hActor ) { _ASSERT( hActor && "CDnSkill::SetHasActor() 액터 핸들이 NULL 임" ); m_hActor = hActor; for( int iSelectedLevelData = PVE; iSelectedLevelData < NUM_SKILLLEVEL_APPLY_TYPE; ++iSelectedLevelData ) { int iNumChecker = (int)m_vlpUsableCheckers[ iSelectedLevelData ].size(); for( int iChecker = 0; iChecker < iNumChecker; ++iChecker ) { IDnSkillUsableChecker* pChecker = m_vlpUsableCheckers[ iSelectedLevelData ].at( iChecker ); pChecker->SetHasActor( hActor ); } } for( int iSelectedLevelData = PVE; iSelectedLevelData < NUM_SKILLLEVEL_APPLY_TYPE; ++iSelectedLevelData ) { int iNumProcessor = (int)m_vlpProcessors[ iSelectedLevelData ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pProcessor = m_vlpProcessors[ iSelectedLevelData ].at( iProcessor ); pProcessor->SetHasActor( hActor ); pProcessor->SetParentSkill( GetMySmartPtr() ); } } } DnSkillHandle CDnSkill::CreateSkill( DnActorHandle hActor, int iSkillTableID, int iLevel ) { CDnSkill* pNewSkill = NULL; // 스킬 발동 조건, 발동 프로세서들을 달아준다. // 각각 5개씩 있고 파라메터는 전부 합쳐 10개임. 변경될 가능성도 있다. pNewSkill = new CDnSkill( hActor ); bool bResult = pNewSkill->Initialize( iSkillTableID, iLevel ); if( false == bResult ) { delete pNewSkill; return CDnSkill::Identity(); } if( hActor && hActor->IsPlayerActor() ) { CDnPlayerActor* pPlayerActor = static_cast(hActor.GetPointer()); pNewSkill->RegisterObserver( pPlayerActor->GetBubbleSystem() ); } return pNewSkill->GetMySmartPtr(); } #ifdef PRE_FIX_GAMESERVER_OPTIMIZE bool CDnSkill::_LoadMonsterSkillLevelData( int iSkillTableID, int iLevel, int iSkillLevelDataApplyType ) { DNTableFileFormat* pSkillTable = GetDNTable( CDnTableDB::TSKILL ); DNTableFileFormat* pSkillLevelTable = GetDNTable( CDnTableDB::TSKILLLEVEL ); vector vlSkillLevelList; if( pSkillLevelTable->GetItemIDListFromField( "_SkillIndex", iSkillTableID, vlSkillLevelList ) <= 0 ) return false; if( !pSkillTable->IsExistItem( iSkillTableID) ) return false; // pve, pvp 대상인지 확인하여 걸러냄. vector::iterator iterLevelList = vlSkillLevelList.begin(); for( iterLevelList; iterLevelList != vlSkillLevelList.end(); ) { int iSkillLevelTableID = *iterLevelList; int iApplyType = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_ApplyType" )->GetInteger(); if( iApplyType != iSkillLevelDataApplyType ) iterLevelList = vlSkillLevelList.erase( iterLevelList ); else ++iterLevelList; } // 해당 대상의 데이터가 없는 경우엔 그냥 아무것도 안하고 리턴하면 된다. if( vlSkillLevelList.empty() ) return true; int iSkillLevelTableID = -1; for( int i = 0; i < (int)vlSkillLevelList.size(); ++i ) { int iNowLevel = pSkillLevelTable->GetFieldFromLablePtr( vlSkillLevelList.at(i), "_SkillLevel" )->GetInteger(); if( iNowLevel == iLevel ) { iSkillLevelTableID = vlSkillLevelList.at( i ); break; } } if( -1 == iSkillLevelTableID ) return false; char caLabel[ 32 ]; int iCheckerParamOffset = 0; int iProcessorParamOffset = 0; for( int i = 0; i < MAX_PROCESSOR_COUNT; ++i ) { // 발동조건 객체 이름을 찾는다. 파라메터 필드가 비어있으면 생성 함수들에서 NULL 리턴됨 sprintf_s( caLabel, "_UsableChecker%d", i + 1 ); int iUsableChecker = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLabel )->GetInteger(); sprintf_s( caLabel, "_Processor%d", i + 1 ); int iProcessor = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLabel )->GetInteger(); int iOffsetCheck = iCheckerParamOffset; IDnSkillUsableChecker* pUsableChecker = IDnSkillUsableChecker::Create( m_hActor, iUsableChecker, iSkillLevelTableID, &iCheckerParamOffset ); if( NULL != pUsableChecker ) { if( (iCheckerParamOffset - iOffsetCheck) != pUsableChecker->GetNumArgument() ) { OutputDebug( "[SkillLevelTable Error!] %d 의 파라메터 개수가 잘못되었습니다.\n", iSkillLevelTableID ); _ASSERT( !"스킬 레벨 테이블 파라메터 잘못됨. OutputDebug 출력 확인!" ); } this->AddUsableCheckers( pUsableChecker, iSkillLevelDataApplyType ); } iOffsetCheck = iProcessorParamOffset; IDnSkillProcessor* pSkillProcessor = IDnSkillProcessor::Create( m_hActor, iProcessor, iSkillLevelTableID, &iProcessorParamOffset, this->GetUseActionNames() ); if( NULL != pSkillProcessor ) { if( (iProcessorParamOffset - iOffsetCheck) != pSkillProcessor->GetNumArgument() ) { OutputDebug( "[SkillLevelTable Error!] %d 의 파라메터 개수가 잘못되었습니다.\n", iSkillLevelTableID ); _ASSERT( !"스킬 레벨 테이블 파라메터 잘못됨. OutputDebug 출력 확인!" ); } this->AddProcessor( pSkillProcessor, iSkillLevelDataApplyType ); } } // skill table m_strStaticName = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_StaticName" )->GetString(); char caLable[ 64 ]; ZeroMemory( caLable, sizeof(caLable) ); for( int i = 0; i < 2; ++i ) { sprintf_s( caLable, "_NeedWeaponType%d", i+1 ); int iEquipType = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLable )->GetInteger(); if( -1 != iEquipType ) m_aeNeedEquipType[ i ] = (CDnWeapon::EquipTypeEnum)iEquipType; else m_aeNeedEquipType[ i ] = CDnWeapon::EquipTypeEnum_Amount; } // 스킬 테이블의 최대 레벨은 신뢰할 수 없다. -_- // 실제 갯수로 업데이트. if( 0 == m_iMaxLevel ) { m_iMaxLevel = (int)vlSkillLevelList.size(); } else { _ASSERT( m_iMaxLevel == (int)vlSkillLevelList.size() ); } m_eSkillType = (SkillTypeEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_SkillType" )->GetInteger(); m_eDurationType = (DurationTypeEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_DurationType" )->GetInteger(); m_eTargetType = (TargetTypeEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_TargetType" )->GetInteger(); m_iDissolvable = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_Dissolvable" )->GetInteger(); m_iDuplicateCount = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_EffectAmassCount" )->GetInteger(); m_iSkillDuplicateMethod = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_SkillDuplicate" )->GetInteger(); m_iEffectDuplicateMethod = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_EffectDuplicate" )->GetInteger(); m_eElement = (CDnState::ElementEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_Element" )->GetInteger(); m_iNeedJobClassID = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_NeedJob" )->GetInteger(); if( (CDnState::ElementEnum)-1 == m_eElement ) m_eElement = CDnState::ElementEnum_Amount; // skill level table m_iSkillLevelID = iSkillLevelTableID; m_iLevel = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_SkillLevel" )->GetInteger(); m_iNowLevelSkillPoint = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_NeedSkillPoint" )->GetInteger(); #if defined(PRE_FIX_NEXTSKILLINFO) SKILL_LEVEL_INFO* pTabelInfo = g_pDataManager->GetSkillLevelTableIDList(iSkillTableID, iSkillLevelDataApplyType); int nextSkillLevel = m_iLevel + 1; if( m_iLevel < m_iMaxLevel ) { int nextLevelTableID = -1; SKILL_LEVEL_TABLE_IDS::iterator findIter = pTabelInfo->_SkillLevelTableIDs.find(nextSkillLevel); if (findIter != pTabelInfo->_SkillLevelTableIDs.end()) nextLevelTableID = findIter->second; m_iNextLevelSkillPoint = pSkillLevelTable->GetFieldFromLablePtr( nextLevelTableID, "_NeedSkillPoint" )->GetInteger(); } else m_iNextLevelSkillPoint = 0; #else if( m_iLevel < m_iMaxLevel ) m_iNextLevelSkillPoint = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID+1, "_NeedSkillPoint" )->GetInteger(); else m_iNextLevelSkillPoint = 0; #endif // PRE_FIX_NEXTSKILLINFO m_iIncreaseRange[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_AddRange" )->GetInteger(); m_iAdditionalThreat = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_AddThreat" )->GetInteger(); m_iCPScore = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_CPScore" )->GetInteger(); m_fStartSuperArmor = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_StartSuperArmor" )->GetFloat(); m_bStartCanHit = (pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_StartCanHit" )->GetInteger() == 1) ? true : false; // 상태 효과 정보 로딩 StateEffectStruct StateEffect; for( int i = 0; i < MAX_STATE_EFFECT_COUNT; ++i ) { sprintf_s( caLable, "_EffectClass%d", i + 1 ); StateEffect.nID = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLable )->GetInteger(); if( StateEffect.nID < 1 ) continue; sprintf_s( caLable, "_EffectClass%dApplyType", i + 1 ); int iApplyType = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLable )->GetInteger(); // 모두 적용임. 타겟만 다르게 해서 똑같은 상태효과 2개를 추가해준다. // 하지만 강화 패시브로 사용되는 스킬은 2개로 하지 않고 그냥 생성. 다른 스킬을 강화시키는 데이터 역할만 수행하기 때문. bool bApplyAll = (StateEffectApplyType::ApplyAll == iApplyType) && (SkillTypeEnum::EnchantPassive != m_eSkillType); if( bApplyAll ) StateEffect.ApplyType = StateEffectApplyType::ApplySelf; else StateEffect.ApplyType = (StateEffectApplyType)iApplyType; sprintf_s( caLable, "_EffectClassValue%d", i + 1 ); StateEffect.szValue = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, caLable )->GetString(); sprintf_s( caLable, "_EffectClassValue%dDuration", i + 1 ); StateEffect.nDurationTime = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, caLable )->GetInteger(); m_vlStateEffectList[ iSkillLevelDataApplyType ].push_back( StateEffect ); // 모두 적용이면 타겟으로 바꿔서 똑같이 한 번 더 넣어줌. if( bApplyAll ) { StateEffect.ApplyType = StateEffectApplyType::ApplyTarget; m_vlStateEffectList[ iSkillLevelDataApplyType ].push_back( StateEffect ); } } SkillInfo& MySkillInfo = m_SkillInfo[ iSkillLevelDataApplyType ]; MySkillInfo.iSkillID = m_iSkillID; MySkillInfo.iSkillLevelID = m_iSkillLevelID; MySkillInfo.iLevel = m_iLevel; MySkillInfo.iSkillDuplicateMethod = m_iSkillDuplicateMethod; MySkillInfo.iDuplicateCount = m_iDuplicateCount; MySkillInfo.eSkillType = m_eSkillType; MySkillInfo.eDurationType = m_eDurationType; MySkillInfo.eTargetType = m_eTargetType; MySkillInfo.eApplyType = StateEffect.ApplyType; MySkillInfo.iDissolvable = m_iDissolvable; MySkillInfo.eSkillElement = m_eElement; MySkillInfo.hSkillUser = m_hActor; // Note: 액터가 항상 유효한 것은 아님 if( m_hActor ) MySkillInfo.iSkillUserTeam = m_hActor->GetTeam(); if( m_hActor && _tcslen(m_hActor->GetName()) > 0 ) MySkillInfo.strUserName = m_hActor->GetName(); MySkillInfo.szEffectOutputIDs = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_StateEffectTableID" )->GetString(); return true; } bool CDnSkill::InitializeMonsterSkill( int iSkillTableID, int iLevel ) { m_iSkillID = iSkillTableID; // 각 모드별로 데이터 로드. 먼저 PVP 를 읽는다. // PVE 는 디폴트 값이기 때문에 모드별로 나뉘어지는 값이 아니면 디폴트 값으로 채워지게 된다. // 몬스터 스킬 생성 함수이므로 PVE 만 호출. if( false == _LoadSkillLevelData( iSkillTableID, iLevel, PVP ) ) return false; if( false == _LoadSkillLevelData( iSkillTableID, iLevel, PVE ) ) return false; int iNumProcessor = (int)m_vlpProcessors[ PVE ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pProcessor = m_vlpProcessors[ PVE ].at( iProcessor ); // 사용하는 액션이 있으면 외부에서 조회용으로 데이터 채워놓음 if( pProcessor->GetType() == IDnSkillProcessor::PLAY_ANI ) m_setUseActionNames.insert( static_cast(pProcessor)->GetActionName() ); else if( pProcessor->GetType() == IDnSkillProcessor::PARTIAL_PLAY_ANI ) { CDnPartialPlayProcessor* pPartialPlayAni = static_cast(pProcessor); m_setUseActionNames.insert( pPartialPlayAni->GetStartActionName() ); m_setUseActionNames.insert( pPartialPlayAni->GetLoopActionName() ); m_setUseActionNames.insert( pPartialPlayAni->GetEndActionName() ); } } // 초기화 했을 때는 pve 모드가 디폴트임. m_iSelectedSkillLevelDataApplyType = PVE; return true; } DnSkillHandle CDnSkill::CreateMonsterSkill( DnActorHandle hActor, int iSkillTableID, int iLevel ) { if( hActor ) { CDnSkill* pNewSkill = pNewSkill = new CDnSkill( hActor ); if( pNewSkill ) { bool bResult = pNewSkill->InitializeMonsterSkill( iSkillTableID, iLevel ); if( false == bResult ) { SAFE_DELETE( pNewSkill ); return CDnSkill::Identity(); } if( hActor->IsPlayerActor() ) { CDnPlayerActor* pPlayerActor = static_cast(hActor.GetPointer()); pNewSkill->RegisterObserver( pPlayerActor->GetBubbleSystem() ); } return pNewSkill->GetMySmartPtr(); } else { SAFE_DELETE( pNewSkill ); return CDnSkill::Identity(); } } return CDnSkill::Identity(); } #endif // #ifdef PRE_FIX_GAMESERVER_OPTIMIZE void CDnSkill::CreateSkillInfo( int nSkillID, int nSkillLevel, CDnSkill::SkillInfo & sSkillInfo, std::vector & vecSkillEffect, bool bUseBattleGround/* = false*/ ) { if( nSkillID == 0 || nSkillLevel == 0 ) return; DNTableFileFormat* pSkillTable = NULL; DNTableFileFormat* pSkillLevelTable = NULL; if (bUseBattleGround) { pSkillTable = GetDNTable( CDnTableDB::TBATTLEGROUNDSKILL ); pSkillLevelTable = GetDNTable( CDnTableDB::TBATTLEGROUNDSKILLLEVEL ); } else { pSkillTable = GetDNTable( CDnTableDB::TSKILL ); pSkillLevelTable = GetDNTable( CDnTableDB::TSKILLLEVEL ); } if (pSkillLevelTable == NULL || pSkillTable == NULL) { _DANGER_POINT(); return; } std::vector vlSkillLevelList; if( pSkillLevelTable->GetItemIDListFromField( "_SkillIndex", nSkillID, vlSkillLevelList ) <= 0 ) return; int iSkillLevelTableID = -1; for( int i = 0; i < (int)vlSkillLevelList.size(); ++i ) { int iNowLevel = pSkillLevelTable->GetFieldFromLablePtr( vlSkillLevelList.at(i), "_SkillLevel" )->GetInteger(); if( iNowLevel == nSkillLevel ) { iSkillLevelTableID = vlSkillLevelList.at( i ); break; } } if( -1 == iSkillLevelTableID ) return; sSkillInfo.iSkillID = nSkillID; sSkillInfo.iSkillLevelID = iSkillLevelTableID; #ifdef PRE_FIX_SYNC_ENCHANT_SKILL sSkillInfo.iAppliedEnchantSkillID = 0; #endif sSkillInfo.iLevel = nSkillLevel; sSkillInfo.eDurationType = (CDnSkill::DurationTypeEnum)pSkillTable->GetFieldFromLablePtr( nSkillID, "_DurationType" )->GetInteger(); sSkillInfo.eTargetType = (CDnSkill::TargetTypeEnum)pSkillTable->GetFieldFromLablePtr( nSkillID, "_TargetType" )->GetInteger(); sSkillInfo.iSkillDuplicateMethod = pSkillTable->GetFieldFromLablePtr( nSkillID, "_SkillDuplicate" )->GetInteger(); sSkillInfo.iDuplicateCount = pSkillTable->GetFieldFromLablePtr( nSkillID, "_EffectAmassCount" )->GetInteger(); sSkillInfo.iDissolvable = pSkillTable->GetFieldFromLablePtr( nSkillID, "_Dissolvable" )->GetInteger(); sSkillInfo.szEffectOutputIDToClient = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_StateEffectTableID" )->GetString(); sSkillInfo.bFromBuffProp = true; // 상태 효과 정보 로딩 CDnSkill::StateEffectStruct StateEffect; char caLable[ 64 ]; ZeroMemory( caLable, sizeof(caLable) ); for( int i = 0; i < MAX_STATE_EFFECT_COUNT; ++i ) { sprintf_s( caLable, "_EffectClass%d", i + 1 ); StateEffect.nID = pSkillTable->GetFieldFromLablePtr( nSkillID, caLable )->GetInteger(); if( StateEffect.nID < 1 ) continue; sprintf_s( caLable, "_EffectClass%dApplyType", i + 1 ); StateEffect.ApplyType = (CDnSkill::StateEffectApplyType)pSkillTable->GetFieldFromLablePtr( nSkillID, caLable )->GetInteger(); sprintf_s( caLable, "_EffectClassValue%d", i + 1 ); StateEffect.szValue = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, caLable )->GetString(); sprintf_s( caLable, "_EffectClassValue%dDuration", i + 1 ); StateEffect.nDurationTime = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, caLable )->GetInteger(); vecSkillEffect.push_back( StateEffect ); } } void CDnSkill::CreateBattleGroundSkillInfo( int nSkillID, int nSkillLevel, CDnSkill::SkillInfo & sSkillInfo, std::vector & vecSkillEffect ) { CreateSkillInfo(nSkillID, nSkillLevel, sSkillInfo, vecSkillEffect, true); } bool CDnSkill::_LoadSkillLevelData( int iSkillTableID, int iLevel, int iSkillLevelDataApplyType ) { DNTableFileFormat* pSkillTable = GetDNTable( CDnTableDB::TSKILL ); DNTableFileFormat* pSkillLevelTable = GetDNTable( CDnTableDB::TSKILLLEVEL ); vector vlSkillLevelList; if( pSkillLevelTable->GetItemIDListFromField( "_SkillIndex", iSkillTableID, vlSkillLevelList ) <= 0 ) return false; if( !pSkillTable->IsExistItem( iSkillTableID) ) return false; // pve, pvp 대상인지 확인하여 걸러냄. vector::iterator iterLevelList = vlSkillLevelList.begin(); for( iterLevelList; iterLevelList != vlSkillLevelList.end(); ) { int iSkillLevelTableID = *iterLevelList; int iApplyType = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_ApplyType" )->GetInteger(); if( iApplyType != iSkillLevelDataApplyType ) iterLevelList = vlSkillLevelList.erase( iterLevelList ); else ++iterLevelList; } // 해당 대상의 데이터가 없는 경우엔 그냥 아무것도 안하고 리턴하면 된다. if( vlSkillLevelList.empty() ) return true; int iSkillLevelTableID = -1; for( int i = 0; i < (int)vlSkillLevelList.size(); ++i ) { int iNowLevel = pSkillLevelTable->GetFieldFromLablePtr( vlSkillLevelList.at(i), "_SkillLevel" )->GetInteger(); if( iNowLevel == iLevel ) { iSkillLevelTableID = vlSkillLevelList.at( i ); break; } } if( -1 == iSkillLevelTableID ) return false; #if defined(PRE_FIX_NEXTSKILLINFO) //NextLevel이 연속으로 있지 않을 수 있다.. 그래서 여기서 다음 레벨 데이타 테이블 ID를 찾아 놓는다. int iMinSkillLevelTableID = -1; int iNextSkillLevelTableID = -1; SKILL_LEVEL_INFO* pTableInfo = g_pDataManager->GetSkillLevelTableIDList(iSkillLevelTableID, iSkillLevelDataApplyType); SKILL_LEVEL_TABLE_IDS::iterator findIter = pTableInfo->_SkillLevelTableIDs.find(pTableInfo->_MinLevel); if (findIter != pTableInfo->_SkillLevelTableIDs.end()) iMinSkillLevelTableID = findIter->second; findIter = pTableInfo->_SkillLevelTableIDs.find(iLevel + 1); if (findIter != pTableInfo->_SkillLevelTableIDs.end()) iNextSkillLevelTableID = findIter->second; #endif // PRE_FIX_NEXTSKILLINFO char caLabel[ 32 ]; int iCheckerParamOffset = 0; int iProcessorParamOffset = 0; for( int i = 0; i < MAX_PROCESSOR_COUNT; ++i ) { // 발동조건 객체 이름을 찾는다. 파라메터 필드가 비어있으면 생성 함수들에서 NULL 리턴됨 sprintf_s( caLabel, "_UsableChecker%d", i + 1 ); int iUsableChecker = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLabel )->GetInteger(); sprintf_s( caLabel, "_Processor%d", i + 1 ); int iProcessor = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLabel )->GetInteger(); int iOffsetCheck = iCheckerParamOffset; IDnSkillUsableChecker* pUsableChecker = IDnSkillUsableChecker::Create( m_hActor, iUsableChecker, iSkillLevelTableID, &iCheckerParamOffset ); if( NULL != pUsableChecker ) { if( (iCheckerParamOffset - iOffsetCheck) != pUsableChecker->GetNumArgument() ) { OutputDebug( "[SkillLevelTable Error!] %d 의 파라메터 개수가 잘못되었습니다.\n", iSkillLevelTableID ); _ASSERT( !"스킬 레벨 테이블 파라메터 잘못됨. OutputDebug 출력 확인!" ); } this->AddUsableCheckers( pUsableChecker, iSkillLevelDataApplyType ); } iOffsetCheck = iProcessorParamOffset; IDnSkillProcessor* pSkillProcessor = IDnSkillProcessor::Create( m_hActor, iProcessor, iSkillLevelTableID, &iProcessorParamOffset, this->GetUseActionNames() ); if( NULL != pSkillProcessor ) { if( (iProcessorParamOffset - iOffsetCheck) != pSkillProcessor->GetNumArgument() ) { OutputDebug( "[SkillLevelTable Error!] %d 의 파라메터 개수가 잘못되었습니다.\n", iSkillLevelTableID ); _ASSERT( !"스킬 레벨 테이블 파라메터 잘못됨. OutputDebug 출력 확인!" ); } this->AddProcessor( pSkillProcessor, iSkillLevelDataApplyType ); } } // skill table m_strStaticName = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_StaticName" )->GetString(); char caLable[ 64 ]; ZeroMemory( caLable, sizeof(caLable) ); for( int i = 0; i < 2; ++i ) { sprintf_s( caLable, "_NeedWeaponType%d", i+1 ); int iEquipType = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLable )->GetInteger(); if( -1 != iEquipType ) m_aeNeedEquipType[ i ] = (CDnWeapon::EquipTypeEnum)iEquipType; else m_aeNeedEquipType[ i ] = CDnWeapon::EquipTypeEnum_Amount; } // 스킬 테이블의 최대 레벨은 신뢰할 수 없다. -_- // 실제 갯수로 업데이트. if( 0 == m_iMaxLevel ) { m_iMaxLevel = (int)vlSkillLevelList.size(); } else { _ASSERT( m_iMaxLevel == (int)vlSkillLevelList.size() ); } m_eSkillType = (SkillTypeEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_SkillType" )->GetInteger(); m_eDurationType = (DurationTypeEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_DurationType" )->GetInteger(); m_eTargetType = (TargetTypeEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_TargetType" )->GetInteger(); m_iDissolvable = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_Dissolvable" )->GetInteger(); m_iDuplicateCount = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_EffectAmassCount" )->GetInteger(); m_iSkillDuplicateMethod = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_SkillDuplicate" )->GetInteger(); m_iEffectDuplicateMethod = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_EffectDuplicate" )->GetInteger(); m_eElement = (CDnState::ElementEnum)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_Element" )->GetInteger(); m_iNeedJobClassID = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_NeedJob" )->GetInteger(); if( (CDnState::ElementEnum)-1 == m_eElement ) m_eElement = CDnState::ElementEnum_Amount; m_iExclusiveID = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_DuplicatedSkillType" )->GetInteger(); m_iBaseSkillID = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_BaseSkillID" )->GetInteger(); m_iGlobalSkillGroupID = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_GlobalSkillGroup" )->GetInteger(); float fGlobalCoolTime = 0.0f; if( PVE == iSkillLevelDataApplyType ) fGlobalCoolTime = (float)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_GlobalCoolTimePvE" )->GetInteger() / 1000.0f; else if( PVP == iSkillLevelDataApplyType ) fGlobalCoolTime = (float)pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_GlobalCoolTimePvP" )->GetInteger() / 1000.0f; m_afGlobalCoolTime[ iSkillLevelDataApplyType ] = fGlobalCoolTime; // skill level table m_iSkillLevelID = iSkillLevelTableID; m_iLevel = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_SkillLevel" )->GetInteger(); m_iNowLevelSkillPoint = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_NeedSkillPoint" )->GetInteger(); if( m_iLevel < m_iMaxLevel ) #if defined(PRE_FIX_NEXTSKILLINFO) m_iNextLevelSkillPoint = pSkillLevelTable->GetFieldFromLablePtr( iNextSkillLevelTableID, "_NeedSkillPoint" )->GetInteger(); #else m_iNextLevelSkillPoint = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID+1, "_NeedSkillPoint" )->GetInteger(); #endif // PRE_FIX_NEXTSKILLINFO else m_iNextLevelSkillPoint = 0; m_iNeedItemID[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_NeedItem" )->GetInteger(); m_iNeedItemDecreaseCount[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_NeedItemDecreaseCount" )->GetInteger(); m_iIncreaseRange[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_AddRange" )->GetInteger(); m_iDecreaseHP[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_DecreaseHP" )->GetInteger(); m_iDecreaseMP[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_DecreaseSP" )->GetInteger(); m_iLevelLimit = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_LevelLimit" )->GetInteger(); m_fDelayTime[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_DelayTime" )->GetInteger() / 1000.f; // 만약 글로벌 스킬 그룹이 설정되어있다면 스킬 쿨타임을 글로벌 쿨타임으로 대체 시켜준다. if( 0 < m_iGlobalSkillGroupID ) m_fDelayTime[ iSkillLevelDataApplyType ] = m_afGlobalCoolTime[ iSkillLevelDataApplyType ]; m_fOriginalDelayTime[ iSkillLevelDataApplyType ] = m_fDelayTime[ iSkillLevelDataApplyType ]; m_iAdditionalThreat = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_AddThreat" )->GetInteger(); m_fHPConsumeType[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_HPConsumeType" )->GetFloat(); m_fMPConsumeType[ iSkillLevelDataApplyType ] = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_SPConsumeType" )->GetFloat(); m_iCPScore = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_CPScore" )->GetInteger(); m_fStartSuperArmor = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_StartSuperArmor" )->GetFloat(); m_bStartCanHit = (pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_StartCanHit" )->GetInteger() == 1) ? true : false; // 상태 효과 정보 로딩 StateEffectStruct StateEffect; for( int i = 0; i < MAX_STATE_EFFECT_COUNT; ++i ) { sprintf_s( caLable, "_EffectClass%d", i + 1 ); StateEffect.nID = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLable )->GetInteger(); if( StateEffect.nID < 1 ) continue; sprintf_s( caLable, "_EffectClass%dApplyType", i + 1 ); int iApplyType = pSkillTable->GetFieldFromLablePtr( iSkillTableID, caLable )->GetInteger(); // 모두 적용임. 타겟만 다르게 해서 똑같은 상태효과 2개를 추가해준다. // 하지만 강화 패시브로 사용되는 스킬은 2개로 하지 않고 그냥 생성. 다른 스킬을 강화시키는 데이터 역할만 수행하기 때문. bool bApplyAll = (StateEffectApplyType::ApplyAll == iApplyType) && (SkillTypeEnum::EnchantPassive != m_eSkillType); if( bApplyAll ) { StateEffect.ApplyType = StateEffectApplyType::ApplySelf; StateEffect.bApplyAllPair = true; } else StateEffect.ApplyType = (StateEffectApplyType)iApplyType; sprintf_s( caLable, "_EffectClassValue%d", i + 1 ); StateEffect.szValue = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, caLable )->GetString(); sprintf_s( caLable, "_EffectClassValue%dDuration", i + 1 ); StateEffect.nDurationTime = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, caLable )->GetInteger(); m_vlStateEffectList[ iSkillLevelDataApplyType ].push_back( StateEffect ); // 모두 적용이면 타겟으로 바꿔서 똑같이 한 번 더 넣어줌. if( bApplyAll ) { StateEffect.ApplyType = StateEffectApplyType::ApplyTarget; m_vlStateEffectList[ iSkillLevelDataApplyType ].push_back( StateEffect ); } } SkillInfo& MySkillInfo = m_SkillInfo[ iSkillLevelDataApplyType ]; MySkillInfo.iSkillID = m_iSkillID; MySkillInfo.iSkillLevelID = m_iSkillLevelID; MySkillInfo.iLevel = m_iLevel; MySkillInfo.iSkillDuplicateMethod = m_iSkillDuplicateMethod; MySkillInfo.iDuplicateCount = m_iDuplicateCount; MySkillInfo.eSkillType = m_eSkillType; MySkillInfo.eDurationType = m_eDurationType; MySkillInfo.eTargetType = m_eTargetType; MySkillInfo.eApplyType = StateEffect.ApplyType; MySkillInfo.iDissolvable = m_iDissolvable; MySkillInfo.eSkillElement = m_eElement; MySkillInfo.hSkillUser = m_hActor; // Note: 액터가 항상 유효한 것은 아님 if( m_hActor ) MySkillInfo.iSkillUserTeam = m_hActor->GetTeam(); if( m_hActor && _tcslen(m_hActor->GetName()) > 0 ) MySkillInfo.strUserName = m_hActor->GetName(); MySkillInfo.szEffectOutputIDs = pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID, "_StateEffectTableID" )->GetString(); if( 0.0f == m_fHPConsumeType[ iSkillLevelDataApplyType ] ) { m_iNeedHP[ iSkillLevelDataApplyType ] = m_iDecreaseHP[ iSkillLevelDataApplyType ]; } else { m_iNeedHP[ iSkillLevelDataApplyType ] = int((float)m_hActor->GetMaxHP() * m_fHPConsumeType[ iSkillLevelDataApplyType ]); } #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) m_nPrefixSkillType = pSkillTable->GetFieldFromLablePtr( iSkillTableID, "_Group" )->GetInteger(); //스킬 정보에 접미사 스킬 그룹ID를 설정 해 놓는다..(클라이언트는 몰라도 됨?...) SetPrefixSkillType(m_nPrefixSkillType); #endif // PRE_ADD_PREFIX_SYSTE_RENEW return true; } void CDnSkill::_OnInitialize( void ) { // 129번 ChangeActionSet 상태효과와 DnApplySEWhenActionSetBlowEnabledProcessor 발현 타입이 있다면 ChangeActionSet 을 제외한 모든 상태효과를 // 발현타입 객체에 몰아주고 삭제. 추후에 바뀐 액션에서만 상태효과가 유효하도록 발현타입에서 컨트롤 하게 된다. // 우선 그냥 포인터를 물려볼까.. for( int k = PVE; k < NUM_SKILLLEVEL_APPLY_TYPE; ++k ) { CDnApplySEWhenActionSetBlowEnabledProcessor* pApplySEWhenActionSetBlowEnableProcessor = static_cast(GetProcessor( IDnSkillProcessor::APPLY_SE_WHEN_ACTIONSET_ENABLED, k )); if( pApplySEWhenActionSetBlowEnableProcessor ) { // 이 발현타입이 있는데 ChangeActionSet 상태효과와 ChangeActionStr 발현타입이 없으면 오류.. bool bValid = false; for( int i = 0; i < (int)m_vlStateEffectList[ k ].size(); ++i ) { StateEffectStruct& StateEffect = m_vlStateEffectList[ k ].at( i ); if( STATE_BLOW::BLOW_129 == StateEffect.nID ) { bValid = true; break; } } if( bValid ) { CDnChangeActionStrProcessor* pChangeActionStrProcessor = static_cast(GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR, k )); if( pChangeActionStrProcessor ) bValid = true; else bValid = false; } if( bValid ) { for( int i = 0; i < (int)m_vlStateEffectList[ k ].size(); ++i ) { StateEffectStruct& StateEffect = m_vlStateEffectList[ k ].at( i ); if( STATE_BLOW::BLOW_129 != StateEffect.nID ) { pApplySEWhenActionSetBlowEnableProcessor->AddStateEffect( &StateEffect ); StateEffect.bApplyInProcessor = true; } } } } } } bool CDnSkill::Initialize( int iSkillTableID, int iLevel ) { m_iSkillID = iSkillTableID; // 각 모드별로 데이터 로드. 먼저 PVP 를 읽는다. // PVE 는 디폴트 값이기 때문에 모드별로 나뉘어지는 값이 아니면 디폴트 값으로 채워지게 된다. // 몬스터인 경우엔 pvp 데이터가 없으므로 함수 안에서 아무것도 안되고 리턴된다. // 만약 플레이어인데 아무것도 없다면 잘못된 거임. if( false == _LoadSkillLevelData( iSkillTableID, iLevel, PVP ) ) return false; if( false == _LoadSkillLevelData( iSkillTableID, iLevel, PVE ) ) return false; // 초기화 이후에 따로 모아놓을 정보들. DNTableFileFormat* pSkillTable = GetDNTable( CDnTableDB::TSKILL ); RefreshDecreaseMP(); int iNumProcessor = (int)m_vlpProcessors[ PVE ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pProcessor = m_vlpProcessors[ PVE ].at( iProcessor ); // 사용하는 액션이 있으면 외부에서 조회용으로 데이터 채워놓음 if( pProcessor->GetType() == IDnSkillProcessor::PLAY_ANI ) m_setUseActionNames.insert( static_cast(pProcessor)->GetActionName() ); else if( pProcessor->GetType() == IDnSkillProcessor::PARTIAL_PLAY_ANI ) { CDnPartialPlayProcessor* pPartialPlayAni = static_cast(pProcessor); m_setUseActionNames.insert( pPartialPlayAni->GetStartActionName() ); m_setUseActionNames.insert( pPartialPlayAni->GetLoopActionName() ); m_setUseActionNames.insert( pPartialPlayAni->GetEndActionName() ); } } // 초기화 했을 때는 pve 모드가 디폴트임. m_iSelectedSkillLevelDataApplyType = PVE; _OnInitialize(); if (m_eDurationType == SummonOnOff) OnInitializeSummonMonsterInfo(); return true; } bool CDnSkill::AddUsableCheckers( IDnSkillUsableChecker* pUsableChecker, int iSelectedLevelData ) { bool bResult = false; if( pUsableChecker ) { m_vlpUsableCheckers[ iSelectedLevelData ].push_back( pUsableChecker ); bResult = true; } return bResult; } bool CDnSkill::AddProcessor( IDnSkillProcessor* pProcessor, int iSelectedLevelData ) { bool bResult = false; if( pProcessor ) { m_vlpProcessors[ iSelectedLevelData ].push_back( pProcessor ); bResult = true; } return bResult; } bool CDnSkill::IsSatisfyWeapon( void ) { bool bSatisfy = true; if( (CDnWeapon::EquipTypeEnum_Amount != m_aeNeedEquipType[ 0 ]) || (CDnWeapon::EquipTypeEnum_Amount != m_aeNeedEquipType[ 1 ]) ) { bSatisfy = false; if( CDnWeapon::EquipTypeEnum_Amount != m_aeNeedEquipType[ 0 ] && CDnWeapon::EquipTypeEnum_Amount != m_aeNeedEquipType[ 1 ] ) { if( CDnWeapon::IsSubWeapon( m_aeNeedEquipType[ 0 ] ) == CDnWeapon::IsSubWeapon( m_aeNeedEquipType[ 1 ] ) ) // 1. 둘다 주무기이거나 보조무기 인 경우 or { int nWeapon = CDnWeapon::IsSubWeapon( m_aeNeedEquipType[ 0 ] ) ? 1 : 0; #ifdef _GAMESERVER if( m_hActor->GetWeapon( nWeapon ) && m_aeNeedEquipType[ 0 ] == m_hActor->GetWeapon( nWeapon )->GetEquipType() || m_hActor->GetWeapon( nWeapon ) && m_aeNeedEquipType[ 1 ] == m_hActor->GetWeapon( nWeapon )->GetEquipType() ) #else // _GAMESERVER if( m_hActor->GetWeapon( nWeapon, false ) && m_aeNeedEquipType[ 0 ] == m_hActor->GetWeapon( nWeapon, false )->GetEquipType() || m_hActor->GetWeapon( nWeapon, false ) && m_aeNeedEquipType[ 1 ] == m_hActor->GetWeapon( nWeapon, false )->GetEquipType() ) #endif // _GAMESERVER bSatisfy = true; } else // 2. 주무기, 보조무기인 경우 and { if( CDnWeapon::IsSubWeapon( m_aeNeedEquipType[ 0 ] ) ) { #ifdef _GAMESERVER if( m_hActor->GetWeapon( 1 ) && m_aeNeedEquipType[ 0 ] == m_hActor->GetWeapon( 1 )->GetEquipType() && m_hActor->GetWeapon( 0 ) && m_aeNeedEquipType[ 1 ] == m_hActor->GetWeapon( 0 )->GetEquipType() ) #else // _GAMESERVER if( m_hActor->GetWeapon( 1, false ) && m_aeNeedEquipType[ 0 ] == m_hActor->GetWeapon( 1, false )->GetEquipType() && m_hActor->GetWeapon( 0, false ) && m_aeNeedEquipType[ 1 ] == m_hActor->GetWeapon( 0, false )->GetEquipType() ) #endif // _GAMESERVER bSatisfy = true; } else { #ifdef _GAMESERVER if( m_hActor->GetWeapon( 0 ) && m_aeNeedEquipType[ 0 ] == m_hActor->GetWeapon( 0 )->GetEquipType() && m_hActor->GetWeapon( 1 ) && m_aeNeedEquipType[ 1 ] == m_hActor->GetWeapon( 1 )->GetEquipType() ) #else // _GAMESERVER if( m_hActor->GetWeapon( 0, false ) && m_aeNeedEquipType[ 0 ] == m_hActor->GetWeapon( 0, false )->GetEquipType() && m_hActor->GetWeapon( 1, false ) && m_aeNeedEquipType[ 1 ] == m_hActor->GetWeapon( 1, false )->GetEquipType() ) #endif // _GAMESERVER bSatisfy = true; } } } else { for( int i=0; iGetWeapon( k ) && m_aeNeedEquipType[ i ] == m_hActor->GetWeapon( k )->GetEquipType() ) #else // _GAMESERVER if( m_hActor->GetWeapon( k, false ) && m_aeNeedEquipType[ i ] == m_hActor->GetWeapon( k, false )->GetEquipType() ) #endif // _GAMESERVER { bSatisfy = true; break; } } if( bSatisfy ) break; } } } } return bSatisfy; } CDnSkill::UsingResult CDnSkill::CanExecute( void ) { UsingResult eResult = UsingResult::Failed; if( !m_hActor ) return UsingResult::Failed; if( m_hActor->IsPlayerActor() ) { CDnPlayerActor* pPlayerActor = static_cast(m_hActor.GetPointer()); if( pPlayerActor && pPlayerActor->IsSwapSingleSkin() ) { if( pPlayerActor->IsTransformSkill(GetClassID()) == false ) return UsingResult::Failed; } } if (m_hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_176)) { DNVector(DnBlowHandle) vlBlows; CDnAllowedSkillsBlow* pAllowedSkillBlow = NULL; m_hActor->GatherAppliedStateBlowByBlowIndex(STATE_BLOW::BLOW_176, vlBlows); for (int i = 0; i < (int)vlBlows.size(); ++i) { pAllowedSkillBlow = static_cast(vlBlows[i].GetPointer()); if (!pAllowedSkillBlow) continue; if (!pAllowedSkillBlow->IsAllowSkill(m_iSkillID)) return UsingResult::Failed; } } // 잔여 SP 체크, 체력 체크, 레벨 체크 등등 // 오라나 토글은 현재 활성화 중이라면 MP 상관 없이 끌 수 있다. if( !IsToggleOn() && !IsAuraOn() ) if( m_hActor->GetSP() < m_iNeedMP[ m_iSelectedSkillLevelDataApplyType ] ) return UsingResult::Failed; // 워리어의 릴리브같은 스킬은 수면, 스턴 중일때도 사용가능해야 한다. // 테이블에 공격불가 무시 발현타입을 추가할까하다가 현재 릴리브에서만 의미있는 것이므로 // 다른 곳에서 어떤 식으로 쓰이게 될지 좀 더 지켜보고 규격화 시키도록 한다. // 우선 Dissolve 상태효과 있는 스킬은 행동 불가 체크를 건너뛴다. // 스킬에 해당하는 액션의 State 시그널에 반드시 IgnorectCantAction 이 켜져 있어야 스킬 액션이 나간다. bool bPassCheckCantAction = false; for( DWORD i = 0; i < GetStateEffectCount(); ++i ) { //#40480 결빙상태에서 단축슬롯 활성화를 위해서.. CDnSkill::StateEffectStruct* pSE = GetStateEffectFromIndex( i ); if( STATE_BLOW::BLOW_069 == pSE->nID ) { // 76643 ( Dissolvable 이 2면 그냥 비활성화 해달라고 합니다 ) for( int i=0 ; iGetNumAppliedStateBlow() ; ++i ) { DnBlowHandle hBlow = m_hActor->GetAppliedStateBlow( i ); if( hBlow ) { const CDnSkill::SkillInfo* pSkillInfo = hBlow->GetParentSkillInfo(); if( pSkillInfo && pSkillInfo->iDissolvable == 2 ) return UsingResult::Failed; } } bPassCheckCantAction = true; break; } if( STATE_BLOW::BLOW_155 == pSE->nID ) { bPassCheckCantAction = true; break; } } // 스킬 사용불가 상태효과가 있으면 mp 소모하는 스킬은 사용할 수 없다. if( 0 < m_hActor->GetCantUseSkillSEReferenceCount() ) if( 0 < m_iNeedMP[ m_iSelectedSkillLevelDataApplyType ] ) return UsingResult::Failed; // 67번 스킬 제한 상태효과가 있으면 액티브 스킬은 사용할 수 없음. if( m_eSkillType == CDnSkill::Active ) { int iAppliedStateBlow = m_hActor->GetNumAppliedStateBlow(); for( int iBlow = 0; iBlow < iAppliedStateBlow; ++iBlow ) { DnBlowHandle hBlow = m_hActor->GetAppliedStateBlow( iBlow ); if( hBlow && hBlow->GetBlowIndex() == STATE_BLOW::BLOW_067 ) return UsingResult::Failed; } } // HP if( m_hActor->GetHP() < m_iNeedHP[ m_iSelectedSkillLevelDataApplyType ] ) return UsingResult::Failed; bool isCharacterLevelCheck = true; //스킬 레벨업 아이템에 의해 스킬레벨업이 된 스킬은 캐릭터 레벨 체크 하지 않도록 한다. isCharacterLevelCheck = (GetLevelUpValue() == 0); if (isCharacterLevelCheck) { if( m_hActor->GetLevel() < m_iLevelLimit ) return UsingResult::Failed; } // 필요 무기가 있다면 장착했는지 확인. 최대 2개임. 둘 중 하나만 충족되도 스킬 사용 가능. if( (CDnWeapon::EquipTypeEnum_Amount != m_aeNeedEquipType[ 0 ]) || (CDnWeapon::EquipTypeEnum_Amount != m_aeNeedEquipType[ 1 ]) ) { bool bSatisfy = false; for( int i = 0; i < MAX_SKILL_NEED_EQUIP_COUNT; ++i ) { if( CDnWeapon::EquipTypeEnum_Amount != m_aeNeedEquipType[ i ] ) { for( int k = 0; k < 2; ++k ) { // #11120 관련. 발차기 상태에서는 장착하고 있는 무기를 얻지 못하기 때문에 명시적으로 원래 함수로 장착하고 있는 무기를 얻어오자. if( m_hActor->CDnActor::GetWeapon( k ) && m_aeNeedEquipType[ i ] == m_hActor->CDnActor::GetWeapon( k )->GetEquipType() ) { bSatisfy = true; break; } } if( bSatisfy ) break; } } if( false == bSatisfy ) return UsingResult::Failed; } // 화살 같은 소모성 아이템의 갯수 확인 bool bCheckNeedItem = true; if( GetRoom() && bIsExtremitySkill() && static_cast(GetRoom())->bIsLadderRoom() ) // 래더에서 궁극기 스킬 bCheckNeedItem = false; if( bCheckNeedItem && m_iNeedItemID[ m_iSelectedSkillLevelDataApplyType ] > 0 ) { int iNumNeedItem = CDnItemTask::GetInstance( m_hActor->GetRoom() ).ScanItemFromID( m_hActor, m_iNeedItemID[ m_iSelectedSkillLevelDataApplyType ], NULL ); if( iNumNeedItem < m_iNeedItemDecreaseCount[ m_iSelectedSkillLevelDataApplyType ] ) return UsingResult::Failed; } // 직업 체크 //if( m_hActor->GetClassID() <= CDnActor::Reserved6 ) if( m_hActor->IsPlayerActor() ) { //CDnPlayerActor* pActor = dynamic_cast(m_hActor.GetPointer()); CDnPlayerActor* pActor = static_cast(m_hActor.GetPointer()); if( NULL == pActor ) return UsingResult::Failed; if( 0 != m_iNeedJobClassID ) { if( pActor->IsPassJob( m_iNeedJobClassID ) == false ) return UsingResult::Failed; } // 배틀 모드가 아니면 스킬 발동 불가! if( !pActor->IsBattleMode() ) return UsingResult::Failed; } switch( m_eDurationType ) { case CDnSkill::Instantly: case CDnSkill::Buff: case CDnSkill::Debuff: case CDnSkill::SummonOnOff: case CDnSkill::StanceChange: { // 쿨타임이 끝나지 않았다면 스킬 발동 불가.. 우선 클라에서 체크해서 보내주므로 서버에선 약간의 유예 시간을 남겨둔다. // 몬스터이거나 오토 패시브 스킬인 경우엔 칼같이 체크. if( m_hActor->IsMonsterActor() || SkillTypeEnum::AutoPassive == m_eSkillType ) { if( m_fCoolTime > 0.0f ) return UsingResult::FailedByCooltime; } else { if( m_fLeftDelayTime > 0.5f ) return UsingResult::FailedByCooltime; } } break; case CDnSkill::TimeToggle: break; case CDnSkill::ActiveToggle: case CDnSkill::ActiveToggleForSummon: break; case CDnSkill::Aura: break; } if( m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].empty() ) eResult = UsingResult::Success; else { int iNumChecker = (int)m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iChecker = 0; iChecker < iNumChecker; ++iChecker ) { IDnSkillUsableChecker* pChecker = m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].at( iChecker ); if( false == pChecker->CanUse() ) { eResult = UsingResult::FailedByUsableChecker; break; } else eResult = UsingResult::Success; } } return eResult; } float CDnSkill::GetDelayTime( void ) { #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) float fDelayTime = 0.0f; if( 0.0f < m_fAnotherGlobalSkillCoolTime ) { fDelayTime = m_fAnotherGlobalSkillCoolTime; DnSkillHandle hAnotherSkill; if (m_hActor) hAnotherSkill = m_hActor->FindSkill(m_nAnotherGlobakSkillID); if (hAnotherSkill) { if (hAnotherSkill->GetAnotherGlobalSkillID() != GetClassID()) fDelayTime = hAnotherSkill->GetDelayTime(); } } else //위m_fAnotherGlobalSkillCoolTime이 적용 되어 있으면 적용 안 하도록.. { fDelayTime = m_fDelayTime[ m_iSelectedSkillLevelDataApplyType ]; float fGlobalCoolTimeRate = 0.0f; if (m_hActor && m_hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_269) && m_iGlobalSkillGroupID > 0) { DNVector(DnBlowHandle) vlBlows; m_hActor->GatherAppliedStateBlowByBlowIndex(STATE_BLOW::BLOW_269, vlBlows); { int nCount = (int)vlBlows.size(); for (int i = 0; i < nCount; ++i) { DnBlowHandle hBlow = vlBlows[i]; if (hBlow && hBlow->IsEnd() == false) { fGlobalCoolTimeRate += hBlow->GetFloatValue(); } } } } fDelayTime -= fDelayTime * fGlobalCoolTimeRate; } return fDelayTime; #else if( 0.0f < m_fAnotherGlobalSkillCoolTime ) return m_fAnotherGlobalSkillCoolTime; return m_fDelayTime[ m_iSelectedSkillLevelDataApplyType ]; #endif // PRE_ADD_TOTAL_LEVEL_SKILL } void CDnSkill::OnBeginCoolTime() { m_dwLastUseSkillTimeStamp = timeGetTime(); m_fCoolTime = (GetDelayTime() == 0.0f) ? 0.0f : 1.0f; m_fCoolTimeAdjustBlowValue = 1.0f; m_fLeftDelayTime = GetDelayTime()*m_fCoolTimeAdjustBlowValue; m_fLeftDelayTime *= m_fCoolTimeMultipier; // [2011/03/09 semozz] // 시작할때 LeftDelayTime이 0.0이 되면 m_fCoolTime을 0.0f로 변경해야한다. // 그렇지 않으면 쿨타임이 1.0으로 계속 유지됨.(Process함수에서 m_fLeftDelayTime이 0이면 CoolTime갱신안됨. if (m_fLeftDelayTime == 0.0f) { m_fCoolTime = 0.0f; #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) m_fDeltaGlobalCoolTime = 0.0f; #endif // PRE_ADD_TOTAL_LEVEL_SKILL } } void CDnSkill::_OnBeginProcessException( void ) { // 결빙상태에서 워리어 릴리브 스킬을 쓰는 경우 바로 프레임 고정을 풀어준다. #12438 // 결빙상태효과가 걸린 상태에서 디버프 상태효과 해제 상태효과가 self 로 적용되는 스킬을 // 사용하는 경우로 일반화 시킴. if( m_hActor && ( m_hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_041 ) || m_hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_146 ) || m_hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_218 ) //#53900 Escape스킬 추가 )) { int iNumStateBlow = (int)m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].size(); for( int i = 0; i < iNumStateBlow; ++i ) { const StateEffectStruct& SE = m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].at( i ); if( STATE_BLOW::BLOW_069 == SE.nID && StateEffectApplyType::ApplySelf == SE.ApplyType ) { // 프레임 정지 상태효과 참조 인덱스를 모두 풀어준다. 프레임이 돌아야 액션이 돌아가고 // 액션이 돌아가야 릴리브 상태효과가 들어간다. while( 0 < m_hActor->GetFrameStopRefCount() ) m_hActor->RemovedFrameStop(); break; } } } //낙인 상태효과 제거 ClearStigmaStateEffect(); } void CDnSkill::ClearStigmaStateEffect() { ////////////////////////////////////////////////////////////////////////// //낙인 상태효과가 있는 스킬인지 확인... bool hasStigmaState = false; int nListCount = (int)m_vlStateEffectList[m_iSelectedSkillLevelDataApplyType].size(); for (int i = 0; i < nListCount; ++i) { const StateEffectStruct& SE = m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].at( i ); if (SE.nID == STATE_BLOW::BLOW_246) { hasStigmaState = true; break; } } //주위 낙인 효과를 찾아서 제거 해준다... if (hasStigmaState == true) { DNVector(DnActorHandle) hVecList; m_hActor->ScanActor(m_hActor->GetRoom(), *m_hActor->GetPosition(), FLT_MAX, hVecList); int nActorCount = (int)hVecList.size(); for (int i = 0; i < nActorCount; ++i) { DnActorHandle hActor = hVecList[i]; if (hActor && hActor->IsDie() == false && hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_246)) { DNVector(DnBlowHandle) vlhBlows; hActor->GetStateBlow()->GetStateBlowFromBlowIndex( STATE_BLOW::BLOW_246, vlhBlows ); int iNumBlow = (int)vlhBlows.size(); for( int j = 0; j < iNumBlow; ++j ) { DnBlowHandle hBlow = vlhBlows[j]; if (hBlow && hBlow->IsEnd() == false) { CDnSkill::SkillInfo* pSkillInfo = const_cast(hBlow->GetParentSkillInfo()); if (pSkillInfo && pSkillInfo->hSkillUser == m_hActor) { int nBlowID = hBlow->GetBlowID(); hActor->SendRemoveStateEffectFromID(nBlowID); //제거 패킷 보내고 hActor->GetStateBlow()->RemoveImediatlyStateEffectFromID(nBlowID); //즉시 제거 한다. } } } } } } ////////////////////////////////////////////////////////////////////////// } // 스킬에 달려있는 Processor 들 중에 선처리 해야하는 것들 먼저 처리. void CDnSkill::CheckProcessorOnBegin( void ) { CheckChangeActionStrByBubbleProcessor(); CheckStateEffectApplyOnOffByBubbleProcessor(); } void CDnSkill::CheckChangeActionStrByBubbleProcessor( void ) { CDnChangeActionStrByBubbleProcessor* pProcessor = static_cast(GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR_BY_BUBBLE )); if( pProcessor ) { bool bChanged = false; const char* pChangedActionName = pProcessor->GetChangeActionNameAndRemoveNeedBubble( &bChanged ); if( bChanged ) { // 액션을 바꿔준다. CDnPlayAniProcess* pPlayAniProcessor = static_cast(GetProcessor( IDnSkillProcessor::PLAY_ANI )); if (pPlayAniProcessor) pPlayAniProcessor->ChangeActionNameOnce( pChangedActionName ); } } } void CDnSkill::CheckStateEffectApplyOnOffByBubbleProcessor( void ) { // 버블 갯수에 따라 상태효과 갯수를 변경시키는 Processor CDnStateEffectApplyOnOffByBubbleProcessor* pProcessor = static_cast(GetProcessor(IDnSkillProcessor::STATE_EFFECT_APPLY_ONOFF_BY_BUBBLE)); if( pProcessor ) { pProcessor->SelectAvailableSE( m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ] ); } } void CDnSkill::OnBegin( LOCAL_TIME LocalTime, float fDelta ) { ApplyAddtionalStateInfo(); // 등록된 옵저버에게 스킬 사용 메시지 보냄. boost::shared_ptr pNotifyEvent( new CDnUseSkillMessage ); boost::shared_ptr pSkillUseEvent = shared_polymorphic_downcast( pNotifyEvent ); pSkillUseEvent->SetSkillID( m_iSkillID ); CDnObservable::Notify( pNotifyEvent ); CheckProcessorOnBegin(); // 같은 글로벌 ID 를 사용하는 다른 글로벌 스킬을 사용하여 셋팅된 쿨타임 값을 실제로 이 스킬 사용될 때는 제거한다. m_fAnotherGlobalSkillCoolTime = 0.0f; m_nAnotherGlobakSkillID = 0; m_SkillStartTime = LocalTime; //m_LastAuraCheckTime = 0; #if defined( PRE_ADD_ACADEMIC ) // 소환형 몬스터 액티브 토글 스킬은 쿨타임이 돌필요가 없습니다. if(m_eDurationType != DurationTypeEnum::ActiveToggleForSummon) //OnBeginCoolTime(); { //-------------------------------------------------- //[debug_skill] server //세콘늴鑒阮? //祺꼍鸞=6001 槿近랐땄=6003 莖슉黛듐=6204 섐똑댔샌=6208 if ( 6001 == GetClassID() || 6003 == GetClassID() || 6204 == GetClassID() || 6208 == GetClassID() ) { //int nSkillID = GetClassID(); } else { OnBeginCoolTime(); } //-------------------------------------------------- } #else //-------------------------------------------------- //[debug_skill] server //세콘늴鑒阮? //祺꼍鸞=6001 槿近랐땄=6003 莖슉黛듐=6204 섐똑댔샌=6208 if ( 6001 == GetClassID() || 6003 == GetClassID() || 6204 == GetClassID() || 6208 == GetClassID() ) { int nSkillID = GetClassID(); } else { OnBeginCoolTime(); } //-------------------------------------------------- #endif m_LastTimeToggleMPDecreaseTime = LocalTime; m_bFinished = false; // 아이템 소모로 셋팅되어있다면 소모하도록 처리. bool bCheckNeedItem = true; if( GetRoom() && bIsExtremitySkill() && static_cast(GetRoom())->bIsLadderRoom() ) // 래더에서 궁극기 스킬 bCheckNeedItem = false; if( bCheckNeedItem && m_iNeedItemID[ m_iSelectedSkillLevelDataApplyType ] > 0 ) { if( m_hActor && m_hActor->IsPlayerActor() ) { CDNUserItem* pUserItem = static_cast(m_hActor.GetPointer())->GetUserSession()->GetItem(); //int iNeedItemSlotIndex = pUserItem->FindInventorySlot( m_iNeedItemID, m_iNeedItemDecreaseCount ); if (!pUserItem->DeleteInventoryByItemID( m_iNeedItemID[ m_iSelectedSkillLevelDataApplyType ], m_iNeedItemDecreaseCount[ m_iSelectedSkillLevelDataApplyType ], DBDNWorldDef::UseItem::Use )) return; } } if( ActiveToggle != m_eDurationType && TimeToggle != m_eDurationType && ActiveToggleForSummon != m_eDurationType ) { CheckAndAddSelfStateEffect(); } int iNumProcessor = (int)m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pProcessor = m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].at( iProcessor ); pProcessor->OnBegin( LocalTime, fDelta, GetMySmartPtr() ); } if( false == m_bItemSkill ) { m_hActor->SetSkillSuperAmmor( int(m_hActor->GetSuperAmmor() * m_fStartSuperArmor) ); m_hActor->SetHittable( m_bStartCanHit ); } // 스킬 사용 관련 예외 상황 처리. _OnBeginProcessException(); if( m_hActor->IsPlayerActor() && 0.0f != m_fResetCooltime ) { int nRand = _rand(m_hActor->GetRoom())%100; if( nRand < (int)(m_fResetCooltime * 100) ) { static_cast(m_hActor.GetPointer())->RequestCooltimeReset( m_iSkillID ); m_fCoolTime = 0.0f; m_fLeftDelayTime = 0.0f; #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) m_fDeltaGlobalCoolTime = 0.0f; #endif // PRE_ADD_TOTAL_LEVEL_SKILL } } #if defined(PRE_ADD_50903) //스킬 시작될때 리셋 시킴.. SetHitCountForVarianceDamage(0); #endif // PRE_ADD_50903 } // 실제로 스킬이 발동 되었을 시에 처리 void CDnSkill::ProcessExecute( LOCAL_TIME LocalTime, float fDelta ) { switch( m_eDurationType ) { case DurationTypeEnum::TimeToggle: break; case DurationTypeEnum::Aura: break; } // 딸려 있는 프로세서 처리 int iNumProcessor = (int)m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pProcessor = m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].at( iProcessor ); pProcessor->Process( LocalTime, fDelta ); } // -1 인 경우는 대시 처럼 방향키를 누르고 있는동안 계속 발동되는 스킬. if( -1.0f != m_fPassiveActionSkillLength ) { m_fPassiveActionSkillLength -= fDelta; if( m_fPassiveActionSkillLength < 0.0f ) { m_fPassiveActionSkillLength = 0.0f; } } } void CDnSkill::OnEnd( LOCAL_TIME LocalTime, float fDelta ) { CheckAndRemoveInstantApplySelfStateEffect(); //접두어에서 추가된 상태 효과 제거 RemovePrefixBlow(); int iNumProcessor = (int)m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pProcessor = m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].at( iProcessor ); pProcessor->OnEnd( LocalTime, fDelta ); } m_SkillInfo->bIgnoreImmune = m_isIgnoreImmuneBackup; CheckProcessorOnEnd(); // 버블로 강화된 상태라면 원래대로. if( m_bEnchantedFromBubble ) { ReleaseEnchantSkill(); m_bEnchantedFromBubble = false; } RemoveAddtionalStateInfo(); #if defined(PRE_FIX_64312) if (m_isAppliedSummonMonsterEnchantSkill == true) { ReleaseEnchantSkill(); m_isAppliedSummonMonsterEnchantSkill = false; } #endif // PRE_FIX_64312 } void CDnSkill::CheckProcessorOnEnd( void ) { // 버블 갯수에 따라 상태효과 갯수를 변경시키는 Processor CDnStateEffectApplyOnOffByBubbleProcessor* pProcessor = static_cast(GetProcessor(IDnSkillProcessor::STATE_EFFECT_APPLY_ONOFF_BY_BUBBLE)); if( pProcessor ) { pProcessor->RestoreSEList( m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ] ); } } // 쿨 타임 등등의 처리 void CDnSkill::Process( LOCAL_TIME LocalTime, float fDelta ) { if( TIME_ACCELERATION_SKILL_ID != m_iSkillID && SPRIT_BOOST_SKILL_ID != m_iSkillID ) fDelta *= m_hActor->GetCoolTimeDeltaAdjustValue(); float fTempAdjustValue = m_fCoolTimeAdjustBlowValue; fTempAdjustValue *= m_fCoolTimeMultipier; switch( m_eDurationType ) { case DurationTypeEnum::Instantly: case DurationTypeEnum::Buff: case DurationTypeEnum::Debuff: case DurationTypeEnum::SummonOnOff: case DurationTypeEnum::StanceChange: { if( 0.0f == m_fOnceElapsedDelayTime ) { if( m_fLeftDelayTime == 0.0f ) return; m_fLeftDelayTime -= fDelta; if( m_fLeftDelayTime < 0.f ) { m_fLeftDelayTime = 0.f; #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) m_fDeltaGlobalCoolTime = 0.0f; #endif // PRE_ADD_TOTAL_LEVEL_SKILL } // [2011/03/09 semozz] // 윈드워커의 스킬중 쇼타임에 의해 fTempAdjustValue가 0이되는 경우가 발생.. // 예외 처리 추가 float fTempDelayTime = (GetDelayTime()*fTempAdjustValue); if (fTempDelayTime == 0.0f) m_fCoolTime = 0.0f; else m_fCoolTime = ( 1.0f / fTempDelayTime ) * m_fLeftDelayTime; } else { if( m_fOnceElapsedDelayTime == 0.0f ) { m_fOnceDelayTime = 0.0f; return; } m_fOnceElapsedDelayTime -= fDelta; if( m_fOnceElapsedDelayTime < 0.0f ) m_fOnceElapsedDelayTime = 0.0f; m_fCoolTime = ( 1.0f / (m_fOnceDelayTime*m_fCoolTimeAdjustBlowValue) ) * m_fOnceElapsedDelayTime; } } break; case DurationTypeEnum::TimeToggle: break; case DurationTypeEnum::ActiveToggle: case DurationTypeEnum::ActiveToggleForSummon: break; case DurationTypeEnum::Aura: break; } // 쿨이 종료된 상태라면 셋팅된 글로벌 스킬 쿨타임을 다시 없애준다. if( 0.0f < m_fAnotherGlobalSkillCoolTime && 0.0f == m_fCoolTime ) { m_fAnotherGlobalSkillCoolTime = 0.0f; m_nAnotherGlobakSkillID = 0; } } bool CDnSkill::IsFinished( void ) { bool bResult = false; // 오라 스킬이라면 맵 이동 하기 전엔 끝내지 않음. if( Aura == m_eDurationType ) return IsFinishedAuraSkill(); if( m_bFinished ) return m_bFinished; if( m_bChainingPassiveSkill ) return false; // MP 를 지속적으로 소모하는 스킬일경우 엠피를 모두 소진하였을때 스킬을 끝내줍니다. if( 0.0f < m_fPassiveActionSkillLength || -1.0f == m_fPassiveActionSkillLength ) { float fDecreaseMPRatio = 0.f; DNVector( DnBlowHandle ) vlhBlows; m_hActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_014, vlhBlows ); for( DWORD i=0; iGetParentSkillInfo()->iSkillID == GetClassID() ) { fDecreaseMPRatio = vlhBlows[i]->GetFloatValue(); break; } } if( m_hActor->GetSP() < m_iNeedMP[ m_iSelectedSkillLevelDataApplyType ] && fDecreaseMPRatio < 0.f ) return true; return false; } #ifdef PRE_FIX_76603 if( m_hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_232 ) ) { DNVector(DnBlowHandle) vlBlows; m_hActor->GatherAppliedStateBlowByBlowIndex(STATE_BLOW::BLOW_232, vlBlows); for (DWORD i = 0; i < vlBlows.size(); i++) { if( vlBlows[i] ) { CDnTransformBlow *pTransformBlow = static_cast( vlBlows[i].GetPointer() ); if( pTransformBlow && pTransformBlow->GetParentSkillInfo()->iSkillID == GetClassID() ) { if( pTransformBlow->CheckSkillDefendency() == true ) return false; } } } } #endif if( m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].empty() ) bResult = true; else { int iNumProcessor = (int)m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pProcessor = m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].at( iProcessor ); if( true == pProcessor->IsFinished() ) bResult = true; else { bResult = false; break; } } } // lazy evaluation m_bFinished = bResult; return bResult; } IDnSkillProcessor* CDnSkill::GetProcessor( int iType ) { IDnSkillProcessor* pResult = NULL; int iNumProcessor = (int)m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pSkillProcessor = m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].at( iProcessor ); if( iType == pSkillProcessor->GetType() ) { pResult = pSkillProcessor; break; } } return pResult; } IDnSkillProcessor* CDnSkill::GetProcessor( int iType, int iLevelDataApplyType ) { IDnSkillProcessor* pResult = NULL; int iNumProcessor = (int)m_vlpProcessors[ iLevelDataApplyType ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { IDnSkillProcessor* pSkillProcessor = m_vlpProcessors[ iLevelDataApplyType ].at( iProcessor ); if( iType == pSkillProcessor->GetType() ) { pResult = pSkillProcessor; break; } } return pResult; } void CDnSkill::CheckTargetCount( int iTargetActorCount ) { // 일단은 힐링 에이리어에서만 쓰이게 될 것임.. IDnSkillProcessor* pProcessor = GetProcessor( IDnSkillProcessor::DIVIDE_STATE_EFFECT_ARG ); if( pProcessor ) { // 브레스 오브 가데스 스킬 테이블에서는 타겟으로만 설정하고 여기서 본인의 상태효과를 추가하게 합니다. _ASSERT( "아직 이 부분은 브레스 오브 가데스에서만 쓰입니다..." && m_iSkillID == 3004 ); CDnDivideSEArgumentByTargets* pDivideSEProcessor = static_cast(pProcessor); int iSEArgument = pDivideSEProcessor->GetStateEffectArgument(); // 자기 자신까지 포함해서 하나 더 추가 int iDividedValue = iSEArgument / (iTargetActorCount+1); int iNumStateEffect = (int)m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].size(); char acBuf[ 256 ]; for( int iStateEffect = 0; iStateEffect < iNumStateEffect; ++iStateEffect ) { ZeroMemory( acBuf, sizeof(acBuf) ); StateEffectStruct& StateEffect = m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].at( iStateEffect ); itoa( iDividedValue, acBuf, 10 ); StateEffect.szValue.assign( acBuf ); // 자기 자신에게도 추가 int iID = m_hActor->CmdAddStateEffect( /*m_hActor,*/ GetInfo(), (STATE_BLOW::emBLOW_INDEX)StateEffect.nID, StateEffect.nDurationTime, StateEffect.szValue.c_str() ); } } #if defined(PRE_ADD_50903) SetHitCountForVarianceDamage(iTargetActorCount); #endif // PRE_ADD_50903 } void CDnSkill::CheckAndAddSelfStateEffect( void ) { map mapDuplicateResult; // 자기 자신에게 거는 상태효과는 따로 확률 중첩 처리 하지 않는다. CanApply eResult = CDnSkill::CanApplySkillStateEffect( m_hActor, GetMySmartPtr(), mapDuplicateResult ); if( CanApply::Fail != eResult ) { int nID(-1); StateEffectStruct *pStruct(NULL); // Note 한기: 2009.12.21 // 자신에게 사용하는 효과 적용 시간 관련 (#1911) // 자기 자신에게 사용하는 상태효과 적용 시그널을 찾아서 갯수를 지정한다. // 현재 패시브 스킬의 경우엔 playaniprocessor 를 설정하지 않기 때문에 이 부분에서 걸러진다. // 자기 자신에게 사용하는 상태효과 타이밍 시그널을 사용할 수 없음. // 따라서 추후에 패시브로 사용하는 액션 스킬들도 상태효과 타이밍 시그널이 필요하다면 // 추가적인 작업이 필요하다. bool bUseApplySelfStateBlowSignal = false; CDnPlayAniProcess* pPlayAniProcessor = static_cast(GetProcessor( IDnSkillProcessor::PLAY_ANI )); if( pPlayAniProcessor ) { const char* pActionName = pPlayAniProcessor->GetActionName(); CEtActionBase::ActionElementStruct* pActionElement = m_hActor->GetElement( pActionName ); _ASSERT( pActionElement ); if( pActionElement ) { int iApplyStateEffectSignalCount = 0; int iNumSignal = (int)pActionElement->pVecSignalList.size(); for( int iSignal = 0; iSignal < iNumSignal; ++iSignal ) { CEtActionSignal* pSignal = pActionElement->pVecSignalList.at( iSignal ); if( STE_ApplyStateEffect == pSignal->GetSignalIndex() ) { bUseApplySelfStateBlowSignal = true; ++iApplyStateEffectSignalCount; } } } } for( DWORD i=0; ibApplyInProcessor ) continue; if( pStruct->ApplyType == ApplySelf ) { // 액션이 끝날 시점에 종료되는 자기 자신에게 사용하는 상태효과. if( 0 == pStruct->nDurationTime || -1 == pStruct->nDurationTime ) { // 테이블에선 0으로 하나 -1로 하나 프로그램쪽에선 -1로 기준시킴 pStruct->nDurationTime = -1; // 한기 2009.7.27 // 자신에게 사용하는 효과 적용 시간 관련 (#1911) // 스킬 강화 문장으로 추가된 상태효과들은 원래 상태효과에 있던 상태효과들이 아니므로 // 자기 자신에게 거는 상태효과 타이밍 시그널을 무시하고 곧바로 추가. if( bUseApplySelfStateBlowSignal && (0 == pStruct->nGlyphID) ) { StateEffectStruct InstantSelfStateEffect = *pStruct; InstantSelfStateEffect.nDurationTime = pStruct->nDurationTime; m_hActor->AddStateEffectQueue( *GetInfo(), InstantSelfStateEffect ); SetAddStateEffectQueue(true); } else { // [2010/12/13 semozz] // 기존 패킷 없이 추가 되는 상태 효과들 처리를 위해 직접 생성하고 m_vlApplyNoPacketStateEffectList에 담아 놓는데, // 패킷으로 생성하고, m_vlApplyNoPacketStateEffectList에 담아 놓는다. // #32160 바뀐 스킬 중첩 처리 루틴이 아이템의 스킬에서도 적용되도록 처리. // 아이템 뿐만 아니라 ApplyStateEffect 시그널 안 붙어있는 스킬은 여기서 바로 상태효과 추가 되므로 빼야될 상태효과들 뺀다. m_hActor->RemoveResetStateBlow(); nID = m_hActor->CmdAddStateEffect( GetInfo(), (STATE_BLOW::emBLOW_INDEX)pStruct->nID, pStruct->nDurationTime, pStruct->szValue.c_str() ); if( -1 == nID ) continue; // Note 한기: 물약 같은 거 빨고 나서 상태효과 1프레임 돌고 적용될 시에 데미지 받아서 죽으면 클라엔 // 먹은 거 적용되고 서버엔 죽으면서 상태효과 전부 비워서 적용안 될 수가 있다.. HP 동기 틀어질 수도 있음. if( m_bItemSkill ) { DnBlowHandle hBlow = m_hActor->GetStateBlowFromID( nID ); hBlow->OnBegin( 0, 0.0f ); #ifndef _GAMESERVER if( hBlow->IsUseTableDefinedGraphicEffect() ) hBlow->AttachGraphicEffectDefaultType(); #endif m_hActor->OnBeginStateBlow( hBlow ); hBlow->SetState( STATE_BLOW::STATE_DURATION ); } m_vlApplySelfNoDurationStateEffectList.push_back( nID ); } } else { // 지속 시간이 있는 self 상태효과 if( bUseApplySelfStateBlowSignal && (0 == pStruct->nGlyphID) ) { m_hActor->AddStateEffectQueue( *GetInfo(), *pStruct ); SetAddStateEffectQueue(true); } else { // #32160 바뀐 스킬 중첩 처리 루틴이 아이템의 스킬에서도 적용되도록 처리. // 아이템 뿐만 아니라 ApplyStateEffect 시그널 안 붙어있는 스킬은 여기서 바로 상태효과 추가 되므로 빼야될 상태효과들 뺀다. m_hActor->RemoveResetStateBlow(); nID = m_hActor->CmdAddStateEffect( /*m_hActor,*/ GetInfo(), (STATE_BLOW::emBLOW_INDEX)pStruct->nID, pStruct->nDurationTime, pStruct->szValue.c_str() ); if( -1 == nID ) continue; } } } } } } void CDnSkill::CheckAndRemoveInstantApplySelfStateEffect( void ) { for( DWORD i=0; iCmdRemoveStateEffectFromID( m_vlApplySelfNoDurationStateEffectList[i] ); } if( m_hActor ) { // #40768 아이템 스킬은 MASkillUser::m_hItemSkill 로 MASkillUser::m_hProcessSkill 과 따로 놀고 있으므로 // m_hItemSkill 이 끝나지 않더라도 m_hProcessSkill 이 시작될 수 있으므로 그 상태에서 // m_hItemSkill 이 종료되어 이 함수가 호출되면 m_hProcessSkill 에 등록된 BlowQueue 를 날려버리게 된다. // 따라서 아이템 스킬은 구분해서 처리해주도록 한다. //#40760 //스킬 사용 시점에 접미사 스킬 발동될때 기존 스킬에서 등록된 상태효과가 //접미사 스킬 끝나는 시점에서 아래 함수 호출로 인해서 큐가 비워저 버리는 경우가 발생. //AddStateEffectQueue호출 시 SetAddStateEffectQueue(true)로 플래그 설정 해 놓고 //플래그가 설정되어 있는 스킬만 상태효과 큐를 초기화 하도록 한다.. if ( IsAddStateEffectQueue() ) m_hActor->ClearSelfStateSignalBlowQueue( IsItemSkill() ); } SAFE_DELETE_VEC( m_vlApplySelfNoDurationStateEffectList ); } DWORD CDnSkill::GetStateEffectCount( void ) { return (DWORD)m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].size(); } CDnSkill::StateEffectStruct* CDnSkill::GetStateEffectFromIndex( DWORD dwIndex ) { if( dwIndex >= m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].size() ) return NULL; return &m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ][ dwIndex ]; } bool CDnSkill::IsUseCheckerType( int iCheckerType ) { bool bResult = false; int iNumChecker = (int)m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iChecker = 0; iChecker < iNumChecker; ++iChecker ) { if( m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].at( iChecker )->GetType() == iCheckerType ) { bResult = true; break; } } return bResult; } bool CDnSkill::IsUseProcessorType( int iProcessorType ) { bool bResult = false; int iNumProcessor = (int)m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iProcessor = 0; iProcessor < iNumProcessor; ++iProcessor ) { if( m_vlpProcessors[ m_iSelectedSkillLevelDataApplyType ].at( iProcessor )->GetType() == iProcessorType ) { bResult = true; break; } } return bResult; } CDnSkill::SkillTypeEnum CDnSkill::GetSkillType( int nSkillID ) { if (nSkillID <= 0) return SkillTypeEnum::Active; DNTableFileFormat* pSkillTable = GetDNTable( CDnTableDB::TSKILL ); if( pSkillTable->IsExistItem( nSkillID ) == false ) return SkillTypeEnum::Active; return (SkillTypeEnum)pSkillTable->GetFieldFromLablePtr( nSkillID, "_SkillType" )->GetInteger(); } bool CDnSkill::IsUseActionNames( const set& setUseActionNames ) { list listIntersect; set_intersection( setUseActionNames.begin(), setUseActionNames.end(), m_setUseActionNames.begin(), m_setUseActionNames.end(), back_inserter(listIntersect) ); return !listIntersect.empty(); } void CDnSkill::_RefreshDecreaseMP( int iSkillLevelDataApplyType ) { if( 0.0f == m_fMPConsumeType[ iSkillLevelDataApplyType ] ) { if( !m_hActor || m_hActor->IsMonsterActor() ) { m_iNeedMP[ iSkillLevelDataApplyType ] = m_iDecreaseMP[ iSkillLevelDataApplyType ]; return; } if( m_hActor->IsPlayerActor() ) { #ifndef _ADD_NEW_MPCONSUME float fRatio = 1.f; if( m_hActor->GetLevel() > 0 ) { int nJobClassID = ((CDnPlayerActor*)m_hActor.GetPointer())->GetJobClassID(); float fDecreaseRatio = CPlayerLevelTable::GetInstance().GetValueFloat( nJobClassID , m_hActor->GetLevel(), CPlayerLevelTable::SPDecreaseRatio ); fRatio = fDecreaseRatio * m_hActor->GetLevelWeightValue(); } m_iNeedMP[ iSkillLevelDataApplyType ] = (int)( m_iDecreaseMP[ iSkillLevelDataApplyType ] * fRatio ); #else m_iNeedMP[iSkillLevelDataApplyType] = (int)(m_iDecreaseMP[iSkillLevelDataApplyType]); #endif } } else { m_iNeedMP[ iSkillLevelDataApplyType ] = int((float)m_hActor->GetMaxSP() * m_fMPConsumeType[ iSkillLevelDataApplyType ]); } m_iOriginalNeedMP[ iSkillLevelDataApplyType ] = m_iNeedMP[ iSkillLevelDataApplyType ]; } void CDnSkill::RefreshDecreaseMP( int iSkillLevelDataApplyType /*= NUM_SKILLLEVEL_APPLY_TYPE*/ ) { if( iSkillLevelDataApplyType == NUM_SKILLLEVEL_APPLY_TYPE ) { for( int iApplyType = PVE; iApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iApplyType ) _RefreshDecreaseMP( iApplyType ); } else { _RefreshDecreaseMP( iSkillLevelDataApplyType ); } } void CDnSkill::SetOnceCoolTime( float fDelayTime, float fElapsedDelayTime ) { _ASSERT( fElapsedDelayTime <= fDelayTime ); m_fOnceDelayTime = fDelayTime; m_fOnceElapsedDelayTime = fElapsedDelayTime; } void CDnSkill::SetPassiveSkillActionName( const char* pActionName ) { if( pActionName ) { // 같은 것이 오면 자동으로 추가 되지 않음. m_setUseActionNames.insert( string(pActionName) ); } } // 이슈 #6190 관련. CDnSkill::CanApply CDnSkill::CanApplySkillStateEffect( const SkillInfo* pUsingSkillInfo, DnActorHandle hTargetActor, int iSkillID, int iLevel, int iSkillDuplicateMethod, int iDuplicateCount, map& mapDuplicateResult, bool isHitProcess ) { // 상태효과 검사할때 리셋리스트 초기화 [2010/12/09 semozz] if (hTargetActor) hTargetActor->InitStateBlowIDToRemove(); CDnSkill::CanApply eResult = CDnSkill::CanApply::Apply; bool bExistingSameSkill = false; int iExistingSameSkillCount = 0; // 중복되어 같은 저렙 스킬의 상태효과가 제거될 시에 담아뒀다가 제거함. // 다른 스킬의 같은 상태효과가 걸려있는 경우도 있으므로 반드시 상태효과 생성시 부여받은 id 로 삭제한다. vector vlStateBlowIDToRemove; DNVector( DnBlowHandle ) vlhSameSkillBlows; int iNumAppliedStateEffect = (int)hTargetActor->GetNumAppliedStateBlow(); for( int iAppliedStateEffect = 0; iAppliedStateEffect < iNumAppliedStateEffect; ++iAppliedStateEffect ) { DnBlowHandle hExistingBlow = hTargetActor->GetAppliedStateBlow( iAppliedStateEffect ); const CDnSkill::SkillInfo* pExistingParentSkillInfo = NULL; if( hExistingBlow ) pExistingParentSkillInfo = hExistingBlow->GetParentSkillInfo(); // 모체 스킬 정보가 없는 상태효과는 스킬로 걸린 것이 아니므로 여기서 다룰 대상이 아님 if( NULL == pExistingParentSkillInfo ) continue; // 스킬 중복 카운트 체크 // 같은 스킬 아이디에 동일한 상태효과가 또 나타난다면 중복된 스킬임. bool bIsSameSkillID = false; #if defined(PRE_FIX_58505) //접미사 스킬 타입이 설정 되어 있지 않으면 스킬 아이디로 비교하고, //접미사 스킬 타입이 설정 되어 있으면 접미사 스킬 타입이 같은지 비교. if ( pUsingSkillInfo->bItemPrefixSkill == true && pUsingSkillInfo->nPrefixSkillType != CDnPrefixSkill::Prefix_Non ) bIsSameSkillID = pUsingSkillInfo->nPrefixSkillType == pExistingParentSkillInfo->nPrefixSkillType; else bIsSameSkillID = iSkillID == pExistingParentSkillInfo->iSkillID; #else if( iSkillID == pExistingParentSkillInfo->iSkillID ) bIsSameSkillID = true; #endif // PRE_FIX_58505 if (bIsSameSkillID == true) { // 같은 스킬 아이디가 처음 나온다면 처음엔 적용중 스킬 카운트 하나 올려줌. // #27335 - 앵클샷 경우 [2011/01/11 semozz] // 상대방이 나에게 앵크샷을 쏘고 CantMove상태효과가 적용 되어 있을때 // 내가 같은 앵클샷을 쏠때, 내 자신에 걸린 CantMove상태효과가 스킬ID가 같아서 // 같은 스킬 중복 처리에 걸리게 됨.. // 현재 상태효과의 ApplyType을 확인해서 // 현재 상태효과를 건 Actor와 TargetActor를 비교해서 // 유효한 Actor가 사용한게 아니면 같은 스킬이 아님으로 설정 DnActorHandle hSkillUserActor = pUsingSkillInfo->hSkillUser; DnSkillHandle hSkill; if (hSkillUserActor) hSkill = hSkillUserActor->FindSkill( iSkillID ); //Prop에의한 상태효과는 스킬 정보가 없을 수 있다. if (hSkill) { // 기존에 적용 중인, 지금 사용하려는 스킬의 상태효과 중 하나의 적용 대상 타입. StateEffectApplyType eExistingSEApplyType = ApplySelf; bExistingSameSkill = true; for( int i = 0; i < (int)hSkill->GetStateEffectCount(); ++i ) { const StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( i ); if( pSE->nID == hExistingBlow->GetBlowIndex() && pSE->ApplyType == pExistingParentSkillInfo->eApplyType ) { eExistingSEApplyType = pSE->ApplyType; break; } } //현재 적용된 상태효과의 applyType에 따라 switch(eExistingSEApplyType) { case StateEffectApplyType::ApplySelf: if (isHitProcess == false) bExistingSameSkill = (hTargetActor == hSkillUserActor); else bExistingSameSkill = false; break; case StateEffectApplyType::ApplyEnemy: bExistingSameSkill = (hTargetActor->GetTeam() != hSkillUserActor->GetTeam()); break; case StateEffectApplyType::ApplyTarget: bExistingSameSkill = (hTargetActor != hSkillUserActor); break; case StateEffectApplyType::ApplyFriend: bExistingSameSkill = (hTargetActor->GetTeam() == hSkillUserActor->GetTeam()); break; case StateEffectApplyType::ApplyAll: bExistingSameSkill = true; break; } } else bExistingSameSkill = true; if( bExistingSameSkill ) { iExistingSameSkillCount += hExistingBlow->GetDuplicateCount(); vlhSameSkillBlows.push_back( hExistingBlow ); } } if (pUsingSkillInfo->eTargetType == All //지금 사용 하는 스킬의 TargetType이 All이고, && (0 != iSkillDuplicateMethod && (iSkillDuplicateMethod == pExistingParentSkillInfo->iSkillDuplicateMethod)) //중첩 처리ID가 설정되어 있고, 기존 스킬과 동일한 ID일때, && (hTargetActor == pUsingSkillInfo->hSkillUser) //자기 자신 ) continue; // 지속효과 구분 인덱스가 0이 아니라면 스킬 효과 중복 관련 처리가 필요함. // 지속효과 구분 인덱스가 0이면 그냥 중첩됨. // 혹은 같은 스킬이면서 최대 중첩 카운트가 1이하인경우엔 스킬 대체되도록 처리. #if defined(PRE_FIX_58505) if( 0 != iSkillDuplicateMethod || (bIsSameSkillID == true && iDuplicateCount <= 1) ) #else if( 0 != iSkillDuplicateMethod || (iSkillID == pExistingParentSkillInfo->iSkillID && iDuplicateCount <= 1) ) #endif // PRE_FIX_58505 { // 기존에 실행되고 있던 스킬과 같은 지속효과 인덱스인가. 그렇다면 중첩이 가능한지 체크들어간다. // 또한 스킬을 쓴 유저의 팀이 같을때만 중첩처리를 하도록 한다. // 다른 팀인데 같은 스킬을 쓸 경우 나에게 걸린 디버프 상태효과가 해제되어 버린다. (#19812) if( (iSkillDuplicateMethod == pExistingParentSkillInfo->iSkillDuplicateMethod) && (pUsingSkillInfo->iSkillUserTeam == pExistingParentSkillInfo->iSkillUserTeam) ) { if( iLevel >= pExistingParentSkillInfo->iLevel ) { // 242번 상태효과는 한번 추가가 되면 리셋 되면 안된다. // 확률 체크에서 통과한다면 리셋시킬 상태효과에 넣어둠. // 실제 확률체크하는 객체는 기존에 적용된 상태효과 객체를 사용하지만 확률만 체크하는 것이기 때문에 // 그대로 사용한다. if( hExistingBlow->CanBegin() ) { bool isSelfStateEffect = false; //#60966 자신에게 적용하는 상태효과와 다른이에게 적용 하는 상태효과가 같이 있은 경우 //스킬 시작에서는 상관 없지만, Hit처리시에는 self상태효과는 다시 적용 되지 않는다. //그래서 히트 처리되는 중에 여기서 self적용된 상태효과를 제거 리스트에 추가 해 놓으면 //다시 추가 할 방법이 없다. //히트 처리시에 들어 온경우 Self적용 상태효과는 스킵.. if (isHitProcess == true) { StateEffectApplyType eExistingSEApplyType = ApplySelf; bool bCheck = false; DnActorHandle hExistingSkillUserActor = pExistingParentSkillInfo->hSkillUser; DnSkillHandle hSkill; if (hExistingSkillUserActor) hSkill = hExistingSkillUserActor->FindSkill( pExistingParentSkillInfo->iSkillID ); if (hSkill) { for( int i = 0; i < (int)hSkill->GetStateEffectCount(); ++i ) { const StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( i ); if( pSE->nID == hExistingBlow->GetBlowIndex() && pSE->ApplyType == pExistingParentSkillInfo->eApplyType ) { bCheck = true; eExistingSEApplyType = pSE->ApplyType; break; } } //자신에게 거는 상태효과는 적용 가능 하도록? 한다.. if ( StateEffectApplyType::ApplySelf == eExistingSEApplyType && bCheck ) { isSelfStateEffect = true; } } } if (isSelfStateEffect == false && hExistingBlow->GetBlowIndex() != STATE_BLOW::BLOW_242 ) { vlStateBlowIDToRemove.push_back( hExistingBlow->GetBlowID() ); mapDuplicateResult[ hExistingBlow->GetBlowIndex() ] = true; // 리셋되어야할 상태들을 담아 놓는다. [2010/12/08 semozz] // 여기서 리스트에 담긴 상태들은 OnSignal의 STE_ApplyStateEffect 시점에 상태 적용할때 // 기존의 상태를 제거 한다. hTargetActor->AddStateBlowIDToRemove( hExistingBlow->GetBlowID() ); } } else { eResult = CDnSkill::CanApply::Fail; // 효과 갱신 시 확률 체크에서 실패했으므로 추가하지 않는다. mapDuplicateResult[ hExistingBlow->GetBlowIndex() ] = false; } } else if( iLevel < pExistingParentSkillInfo->iLevel ) { // 효과 적용 안됨. eResult = CDnSkill::CanApply::Fail; // Note 한기: 추후에 그래픽적으로 뭔가 표시해줘야 한다면 이 곳에서 처리하면 됨. // [2011/01/20 semozz] // 체인라이트닝 시전이 끝나기 전 헤븐스 저지먼트를 사용시 문제점. // A 스킬 레벨이 높고, 그 뒤에 사용되는 B스킬의 레벨이 낮을때, DuplicationMethod가 같으면 // 여기서 스킬 추가 안됨으로 Self상태효과 적용이 되지 않음. Target은 상관 없을듯.. // 현재 상태효과를 사용한 액터를 선택 DnActorHandle hExistingSkillUserActor = pExistingParentSkillInfo->hSkillUser; DnSkillHandle hSkill; if (hExistingSkillUserActor) hSkill = hExistingSkillUserActor->FindSkill( pExistingParentSkillInfo->iSkillID ); if (hSkill) { // 기존에 적용 중인, 지금 사용하려는 스킬의 상태효과 중 하나의 적용 대상 타입. StateEffectApplyType eExistingSEApplyType = ApplySelf; for( int i = 0; i < (int)hSkill->GetStateEffectCount(); ++i ) { const StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( i ); if( pSE->nID == hExistingBlow->GetBlowIndex() && pSE->ApplyType == pExistingParentSkillInfo->eApplyType ) { eExistingSEApplyType = pSE->ApplyType; break; } } //자신에게 거는 상태효과는 적용 가능 하도록? 한다.. if (StateEffectApplyType::ApplySelf == eExistingSEApplyType) { eResult = CDnSkill::CanApply::Apply; vlStateBlowIDToRemove.push_back( hExistingBlow->GetBlowID() ); mapDuplicateResult[ hExistingBlow->GetBlowIndex() ] = true; hTargetActor->AddStateBlowIDToRemove( hExistingBlow->GetBlowID() ); } } } } } } if( false == vlStateBlowIDToRemove.empty() ) { // 스킬 효과 대체. 중첩 카운트 세었던 것 처리할 필요 없다. bExistingSameSkill = false; eResult = CDnSkill::CanApply::ApplyDuplicateSameSkill; } // 상태 효과를 중첩해서 적용해야하는 경우, 최대 중첩 갯수가 넘으면 안된다. // 높은 레벨의 상태효과가 이미 적용중이라면 중첩처리가 되지 않는다. if( CDnSkill::CanApply::Fail != eResult ) { if( bExistingSameSkill ) { if( iDuplicateCount <= iExistingSameSkillCount ) { // 최대 중첩 갯수를 넘는 경우 기존 상태효과의 시간만 초기화 한다. int iNumSameSkillBlowsToResetDurationTime = (int)vlhSameSkillBlows.size(); for( int iBlow = 0; iBlow < iNumSameSkillBlowsToResetDurationTime; ++iBlow ) { DnBlowHandle hBlow = vlhSameSkillBlows.at( iBlow ); ////////////////////////////////////////////////////////////////////////// //#53448 //249번 상태효과 경우 중첩 갯수 이상은 추가 될 수 없다. //최대 중첩 갯수를 넘은 경우 아래 루프를 돌면서 기존 상태효과 제거 하고, 다시 추가 작업 필요 없음. if (hBlow && (hBlow->GetBlowIndex() == STATE_BLOW::BLOW_249 || hBlow->GetBlowIndex() == STATE_BLOW::BLOW_242) ) continue; ////////////////////////////////////////////////////////////////////////// // 아예 삭제했다가 새로 추가하는 것이 명확함. int nDurationTime = int(hBlow->GetDurationTime() * 1000.0f); hBlow->ResetDurationTime(); CDnSkill::SkillInfo SkillInfo = *(hBlow->GetParentSkillInfo()); STATE_BLOW::emBLOW_INDEX BlowIndex = hBlow->GetBlowIndex(); string strValue( hBlow->GetValue() ); // 이 시점에서 즉시 지워저야 한다. [2011/01/18 semozz] int nBlowID = hBlow->GetBlowID(); // 패킷 보낼꺼 보내고 hTargetActor->SendRemoveStateEffectFromID(nBlowID); // 바로 삭제한다.. if (hTargetActor->GetStateBlow()) hTargetActor->GetStateBlow()->RemoveImediatlyStateEffectFromID(nBlowID); // 여기에 결빙같은 확률 있는 상태효과를 100% 성공시켜야 하므로 // CanBegin 함수 호출하지 않도록 bCheckCanBegin 플래그를 꺼서 호출. hTargetActor->CmdAddStateEffect( &SkillInfo, BlowIndex, nDurationTime, strValue.c_str(), false, false ); } eResult = CDnSkill::CanApply::Fail; } } } return eResult; } CDnSkill::CanApply CDnSkill::CanApplySkillStateEffect( DnActorHandle hTargetActor, DnSkillHandle hUsingSkill, map& mapDuplicateResult, bool isHitProcess ) { const SkillInfo* pUsingSkillInfo = hUsingSkill->GetInfo(); int iSkillID = hUsingSkill->GetClassID(); int iLevel = hUsingSkill->GetLevel(); int iSkillDuplicateMethod = hUsingSkill->GetDuplicateMethod(); int iDuplicateCount = hUsingSkill->GetMaxDuplicateCount(); // 스킬에서 상태 효과들중에 지속시간이 하나라도 있으면 지속시간이 있는걸로 판단 // 해당 스킬이 지속시간이 없다면 그냥 추가 될 수 있도록 한다. [2010/11/12 semozz] bool hasDuration = false; for( DWORD k = 0; k < hUsingSkill->GetStateEffectCount(); k++ ) { CDnSkill::StateEffectStruct *pLocalStruct = hUsingSkill->GetStateEffectFromIndex(k); if( pLocalStruct && (IsNeedCheckApplyStateBlow((STATE_BLOW::emBLOW_INDEX)pLocalStruct->nID) || pLocalStruct->nDurationTime > 0) ) { hasDuration = true; break; } #if defined(PRE_FIX_52267) //#52267 //상태효과 ApplyType이 Self가 아니어도 지속시간이 0인 경우 CanApplySkillStateEffect함수 호출이 되지 않아서 //의도치 않게 중첩이 되는 경우가 발생한다. //ApplyType이 self가 아니고, durationTime이 0인경우도 체크 가능 하도록 hasDuration을 true로 설정 한다. else if (pLocalStruct->nDurationTime == 0 && pLocalStruct->ApplyType != StateEffectApplyType::ApplySelf) { hasDuration = true; break; } #endif // PRE_FIX_52267 } bool bResist = false; if( !hTargetActor->IsHittableSkill( iSkillID , bResist ) ) { if( bResist ) { hTargetActor->SendAddSEFail( CDnStateBlow::ADD_FAIL_BY_IMMUNE , STATE_BLOW::BLOW_154 ); } return CDnSkill::CanApply::Fail; } if ( false == hasDuration ) { return CDnSkill::CanApply::Apply; } return CanApplySkillStateEffect( pUsingSkillInfo, hTargetActor, iSkillID, iLevel, iSkillDuplicateMethod, iDuplicateCount, mapDuplicateResult, isHitProcess ); } // 액션 재생 프로세서가 반드시 있는 액티브 스킬의 동작 계승 요청! void CDnSkill::OnChainInput( const char* pActionName ) { if( GetSkillType() == CDnSkill::Active && ( GetDurationType() == CDnSkill::Instantly || GetDurationType() == CDnSkill::Buff || GetDurationType() == CDnSkill::Debuff || GetDurationType() == CDnSkill::StanceChange ) ) { IDnSkillProcessor* pProcessor = GetProcessor( IDnSkillProcessor::PLAY_ANI ); _ASSERT( pProcessor ); if( pProcessor ) { CDnPlayAniProcess* pPlayAniProcessor = static_cast( pProcessor ); pPlayAniProcessor->OnChainInput( pActionName ); } } else if( GetSkillType() == CDnSkill::Passive ) { CEtActionBase::ActionElementStruct* pStruct = m_hActor->GetElement( pActionName ); if( pStruct ) { m_fPassiveActionSkillLength += (float)pStruct->dwLength / s_fDefaultFps; m_bChainingPassiveSkill = true; } } } bool CDnSkill::IsChainInputAction( const char* pActionName ) { IDnSkillProcessor* pProcessor = GetProcessor( IDnSkillProcessor::PLAY_ANI ); if( pProcessor ) { CDnPlayAniProcess* pPlayAniProcessor = static_cast( pProcessor ); if( strcmp( pActionName , pPlayAniProcessor->GetChainActionName() ) == 0 ) { return true; } } return false; } bool CDnSkill::CheckChainingPassiveSkill( void ) { bool bResult = m_bChainingPassiveSkill; m_bChainingPassiveSkill = false; return bResult; } void CDnSkill::AddGlyphStateEffect( int nGlyphID ) { DNTableFileFormat* pGlyphTable = GetDNTable( CDnTableDB::TGLYPHSKILL ); if( !pGlyphTable ) return; #if defined(PRE_ADD_65808) int monsterID = -1; if (IsSummonMonsterRecall(monsterID) == true) { m_hActor->AddSummonMonsterGlyphInfo(monsterID, nGlyphID); return; } #endif // PRE_ADD_65808 char caLable[64]; // 스킬 강화 문장 같은 경우 pvp/pve 모두 셋팅해준다. for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { // 원래 상태효과 갖고 있던 갯수 int iNumOriginalStateEffect = (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); // 상태 효과 추가 StateEffectStruct StateEffect; StateEffect.nGlyphID = nGlyphID; for( int i = 0; i < MAX_GLYPH_STATE_EFFECT_COUNT; ++i ) { sprintf_s( caLable, "_EffectClass%d", i + 1 ); StateEffect.nID = pGlyphTable->GetFieldFromLablePtr( nGlyphID, caLable )->GetInteger(); if( StateEffect.nID < 1 ) continue; sprintf_s( caLable, "_EffectClass%dApplyType", i + 1 ); int iApplyType = pGlyphTable->GetFieldFromLablePtr( nGlyphID, caLable )->GetInteger(); bool bApplyAll = (StateEffectApplyType::ApplyAll == iApplyType); // 모두 적용임. 타겟만 다르게 해서 똑같은 상태효과 2개를 추가해준다. if( bApplyAll ) { StateEffect.ApplyType = StateEffectApplyType::ApplySelf; StateEffect.bApplyAllPair = true; } else StateEffect.ApplyType = (StateEffectApplyType)iApplyType; sprintf_s( caLable, "_EffectClassValue%d", i + 1 ); StateEffect.szValue = pGlyphTable->GetFieldFromLablePtr( nGlyphID, caLable )->GetString(); // 지속시간은 기존에 있던 상태효과에서 그대로 대체한다. bool bFound = false; for( int k = 0; k < iNumOriginalStateEffect; ++k ) { const StateEffectStruct& OriginalSE = m_vlStateEffectList[ iSkillLevelApplyType ].at( k ); if( OriginalSE.nID == StateEffect.nID ) { StateEffect.nDurationTime = OriginalSE.nDurationTime; bFound = true; } } // 만약 같은 상태효과가 없다면 지속시간은 그냥 0 으로 해준다. // 그럼 액션과 동일한 간격만큼 상태효과가 지속됨. if( false == bFound ) StateEffect.nDurationTime = 0; m_vlStateEffectList[ iSkillLevelApplyType ].push_back( StateEffect ); // 모두 적용이면 타겟으로 바꿔서 똑같이 한 번 더 넣어줌. if( bApplyAll ) { StateEffect.ApplyType = StateEffectApplyType::ApplyTarget; m_vlStateEffectList[ iSkillLevelApplyType ].push_back( StateEffect ); } } } // 스킬 속성 추가 GlyphEffectStruct GlyphEffect; GlyphEffect.eEffectIndex = pGlyphTable->GetFieldFromLablePtr( nGlyphID, "_GlyphSkillEffect" )->GetInteger(); GlyphEffect.fEffectValue = pGlyphTable->GetFieldFromLablePtr( nGlyphID, "_GlyphSkillEffectValue" )->GetFloat(); GlyphEffect.nGlyphID = nGlyphID; switch( GlyphEffect.eEffectIndex ) { case SKILLMP_PRO : { for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) m_iNeedMP[ iSkillLevelApplyType ] += (int)( (float)m_iOriginalNeedMP[ iSkillLevelApplyType ] * GlyphEffect.fEffectValue ); break; } case SKILLDELAY_PRO : { for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) m_fDelayTime[ iSkillLevelApplyType ] += m_fOriginalDelayTime[ iSkillLevelApplyType ] * GlyphEffect.fEffectValue; break; } case SKILLDELAY_RESET_PRO : { m_fResetCooltime += GlyphEffect.fEffectValue; break; } case SKILLDURATION_PRO : { // pair <상태효과, 기간> for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { bool bEmpty = m_vlStateDurationList[iSkillLevelApplyType].empty(); for( int itr = 0; itr < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++itr ) { if( bEmpty ) m_vlStateDurationList[iSkillLevelApplyType].push_back( make_pair(m_vlStateEffectList[ iSkillLevelApplyType ][itr].nID, m_vlStateEffectList[ iSkillLevelApplyType ][itr].nDurationTime) ); else m_vlStateEffectList[ iSkillLevelApplyType ][itr].nDurationTime = m_vlStateDurationList[iSkillLevelApplyType][itr].second; } } float fRatio = GlyphEffect.fEffectValue; for( int itr = 0; itr < (int)m_vGlyphEffectList.size(); ++itr ) { if( SKILLDURATION_PRO == m_vGlyphEffectList[itr].eEffectIndex ) fRatio += m_vGlyphEffectList[itr].fEffectValue; } for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { for( int itr = 0; itr < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++itr ) m_vlStateEffectList[ iSkillLevelApplyType ][itr].nDurationTime += (int)(m_vlStateEffectList[ iSkillLevelApplyType ][itr].nDurationTime * fRatio); } break; } case SKILLATTACKHEAL_ABSOLUTE: { float fValue = GlyphEffect.fEffectValue; for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { for( int itr = 0; itr < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++itr ) { if( m_vlStateEffectList[ iSkillLevelApplyType ][itr].nID == STATE_BLOW::BLOW_248 ) { m_vlStateEffectList[ iSkillLevelApplyType ][itr].szValue = FormatA( "%f", fValue + atof(m_vlStateEffectList[ iSkillLevelApplyType ][itr].szValue.c_str()) ).c_str(); } } } } break; } m_vGlyphEffectList.push_back( GlyphEffect ); } void CDnSkill::DelGlyphStateEffect( int nGlyphID ) { #if defined(PRE_ADD_65808) int monsterID = -1; if (IsSummonMonsterRecall(monsterID) == true) { m_hActor->RemoveSummonMonsterGlyphInfo(monsterID, nGlyphID); return; } #endif // PRE_ADD_65808 // 스킬 강화 문장 같은 경우 pvp/pve 모두 셋팅해준다. for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { for( DNVector(StateEffectStruct)::iterator Itor = m_vlStateEffectList[ iSkillLevelApplyType ].begin(); Itor < m_vlStateEffectList[ iSkillLevelApplyType ].end(); ) { if( nGlyphID == Itor->nGlyphID ) Itor = m_vlStateEffectList[ iSkillLevelApplyType ].erase(Itor); else ++Itor; } } for( std::vector::iterator Itor = m_vGlyphEffectList.begin(); Itor < m_vGlyphEffectList.end(); ) { if( nGlyphID == Itor->nGlyphID ) { switch( Itor->eEffectIndex ) { case SKILLMP_PRO : { for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) m_iNeedMP[ iSkillLevelApplyType ] -= (int)( (float)m_iOriginalNeedMP[ iSkillLevelApplyType ] * Itor->fEffectValue ); break; } case SKILLDELAY_PRO : { for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) m_fDelayTime[ iSkillLevelApplyType ] -= m_fOriginalDelayTime[ iSkillLevelApplyType ] * Itor->fEffectValue; break; } case SKILLDELAY_RESET_PRO : { m_fResetCooltime -= Itor->fEffectValue; break; } case SKILLDURATION_PRO : { // pair <상태효과, 기간> bool bExist = false, bOnce = true; float fRatio = 0.0f; for( int itr = 0; itr < (int)m_vGlyphEffectList.size(); ++itr ) { if( Itor->nGlyphID == m_vGlyphEffectList[itr].nGlyphID && bOnce) { bOnce = false; continue; } if( SKILLDURATION_PRO == m_vGlyphEffectList[itr].eEffectIndex ) { bExist = true; fRatio += m_vGlyphEffectList[itr].fEffectValue; } } if( !bExist ) { for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { for( int itr = 0; itr < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++itr ) m_vlStateEffectList[ iSkillLevelApplyType ][itr].nDurationTime = m_vlStateDurationList[iSkillLevelApplyType][itr].second; m_vlStateDurationList[iSkillLevelApplyType].clear(); } } else { for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { for( int itr = 0; itr < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++itr ) m_vlStateEffectList[ iSkillLevelApplyType ][itr].nDurationTime = m_vlStateDurationList[iSkillLevelApplyType][itr].second + (int)(m_vlStateDurationList[iSkillLevelApplyType][itr].second * fRatio); } } break; } case SKILLATTACKHEAL_ABSOLUTE: { float fValue = Itor->fEffectValue; for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { for( int itr = 0; itr < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++itr ) { if( m_vlStateEffectList[ iSkillLevelApplyType ][itr].nID == STATE_BLOW::BLOW_248 ) m_vlStateEffectList[ iSkillLevelApplyType ][itr].szValue = FormatA( "%f", atof(m_vlStateEffectList[ iSkillLevelApplyType ][itr].szValue.c_str()) - fValue).c_str(); } } } break; } Itor = m_vGlyphEffectList.erase( Itor ); } else ++Itor; } } void CDnSkill::UpdateSkillCoolTimeExactly( void ) { // 96, 171번 쿨타임 변경 상태효과가 걸려있는 경우 서버에서 체크하지 않도록 처리. // 겜서버에서 쿨타임 갱신이 10프레임으로 줄어들었으므로 조작감 문제때문에 이렇게 풀어놓음. 2011.07.25 DWORD dwNowTime = timeGetTime(); if( DWORD(GetDelayTime()*1000.0f) < dwNowTime - m_dwLastUseSkillTimeStamp ) { ResetCoolTime(); } } bool CDnSkill::SelectLevelDataType( int iSkillLevelDataType, bool bPlayerSummonedMonster/* = false*/ ) { _ASSERT( (m_hActor->IsPlayerActor() || bPlayerSummonedMonster) && (PVE <= iSkillLevelDataType) && (iSkillLevelDataType < NUM_SKILLLEVEL_APPLY_TYPE) ); if( (m_hActor->IsPlayerActor() || bPlayerSummonedMonster) && (PVE <= iSkillLevelDataType) && (iSkillLevelDataType < NUM_SKILLLEVEL_APPLY_TYPE) ) { m_iSelectedSkillLevelDataApplyType = iSkillLevelDataType; return true; } return false; }; bool CDnSkill::IsNeedCheckApplyStateBlow( STATE_BLOW::emBLOW_INDEX emBlowIndex ) { bool bResult = false; if( STATE_BLOW::BLOW_030 == emBlowIndex ) bResult = true; return bResult; } int CDnSkill::GetBaseSkillID( void ) { int iResult = 0; if( EnchantPassive == m_eSkillType ) { iResult = m_iBaseSkillID; } return iResult; } // 클라이언트도 동기화 맞추어주세요 CheckAndDivideStateEffectArgument 이 함수도 수정해주세요 bool CDnSkill::CheckAndUnifyStateEffectArgument( int iSkillLevelApplyType, StateEffectStruct* pEnchantSkillSE ) { bool bResult = false; if( CDnCreateBlow::IsBasicBlow( (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID ) ) { for( int i = 0; i < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++i ) { StateEffectStruct* pDestSkillSE = &(m_vlStateEffectList[ iSkillLevelApplyType ].at( i )); if( pDestSkillSE->nID == pEnchantSkillSE->nID ) { CDnBasicBlow::AddStateEffectValue( pDestSkillSE->szValue.c_str(), pEnchantSkillSE->szValue.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam = pEnchantSkillSE->szValue; bResult = true; } } } else if( STATE_BLOW::BLOW_016 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID ) { for( int i = 0; i < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++i ) { StateEffectStruct* pDestSkillSE = &(m_vlStateEffectList[ iSkillLevelApplyType ].at( i )); if( pDestSkillSE->nID == pEnchantSkillSE->nID ) { CDnHPIncBlow::AddStateEffectValue( pDestSkillSE->szValue.c_str(), pEnchantSkillSE->szValue.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam = pEnchantSkillSE->szValue; bResult = true; } } } else if( STATE_BLOW::BLOW_208 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID ) { for( int i = 0; i < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++i ) { StateEffectStruct* pDestSkillSE = &(m_vlStateEffectList[ iSkillLevelApplyType ].at( i )); if( pDestSkillSE->nID == pEnchantSkillSE->nID ) { CDnPingpongBlow::AddStateEffectValue( pDestSkillSE->szValue.c_str(), pEnchantSkillSE->szValue.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam = pEnchantSkillSE->szValue; bResult = true; } } } else if( STATE_BLOW::BLOW_140 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID ) { for( int i = 0; i < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++i ) { StateEffectStruct* pDestSkillSE = &(m_vlStateEffectList[ iSkillLevelApplyType ].at( i )); if( pDestSkillSE->nID == pEnchantSkillSE->nID ) { CDnHealingBlow::AddStateEffectValue( pDestSkillSE->szValue.c_str(), pEnchantSkillSE->szValue.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam = pEnchantSkillSE->szValue; bResult = true; } } } else if( STATE_BLOW::BLOW_227 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID ) { for( int i = 0; i < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++i ) { StateEffectStruct* pDestSkillSE = &(m_vlStateEffectList[ iSkillLevelApplyType ].at( i )); if( pDestSkillSE->nID == pEnchantSkillSE->nID ) { CDnBloodSuckingBlow::AddStateEffectValue( pDestSkillSE->szValue.c_str(), pEnchantSkillSE->szValue.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam = pEnchantSkillSE->szValue; bResult = true; } } } else if( STATE_BLOW::BLOW_215 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID ) { for( int i = 0; i < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++i ) { StateEffectStruct* pDestSkillSE = &(m_vlStateEffectList[ iSkillLevelApplyType ].at( i )); if( pDestSkillSE->nID == pEnchantSkillSE->nID ) { CDnOrderMySummonedMonsterBlow::AddStateEffectValue( pDestSkillSE->szValue.c_str(), pEnchantSkillSE->szValue.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam = pEnchantSkillSE->szValue; bResult = true; } } } else if( STATE_BLOW::BLOW_179 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID || STATE_BLOW::BLOW_121 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID || STATE_BLOW::BLOW_129 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID || STATE_BLOW::BLOW_062 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID || STATE_BLOW::BLOW_242 == (STATE_BLOW::emBLOW_INDEX)pEnchantSkillSE->nID ) { for( int i = 0; i < (int)m_vlStateEffectList[ iSkillLevelApplyType ].size(); ++i ) { StateEffectStruct* pDestSkillSE = &(m_vlStateEffectList[ iSkillLevelApplyType ].at( i )); if( pDestSkillSE->nID == pEnchantSkillSE->nID ) { pDestSkillSE->strEnchantSkillSEParam = pDestSkillSE->szValue; pDestSkillSE->szValue = pEnchantSkillSE->szValue; bResult = true; break; } } } return bResult; } bool CDnSkill::ApplyEnchantSkill( DnSkillHandle hEnchantPassiveSkill ) { bool bSuccess = false; if( 0 < m_iAppliedEnchantPassiveSkillID ) return false; _ASSERT( m_iSkillID == hEnchantPassiveSkill->GetBaseSkillID() ); _ASSERT( EnchantPassive != m_eSkillType ); if( EnchantPassive != m_eSkillType ) { int iNowLevelDataType = m_iSelectedSkillLevelDataApplyType; for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { // 기존에 존재하는 상태효과엔 인자값을 더해주고 새로운 상태효과는 리스트에 추가. // 쿨타임은 강화 패시브 스킬의 쿨타임으로 덮어씌워준다. SelectLevelDataType( iSkillLevelApplyType ); hEnchantPassiveSkill->SelectLevelDataType( iSkillLevelApplyType ); int iNumStateEffect = (int)hEnchantPassiveSkill->GetStateEffectCount(); for( int i = 0; i < iNumStateEffect; ++i ) { StateEffectStruct* pEnchantPassiveSE = hEnchantPassiveSkill->GetStateEffectFromIndex( i ); // #40643 핑퐁밤EX 처럼 특수하게 구현된 상태효과의 인자를 건드리는 경우.. // 어쩔 수 없이 합쳐줘야 한다. 실제로 m_vlStateEffectList 에 추가되는 것이 아님. if( false == CheckAndUnifyStateEffectArgument( iSkillLevelApplyType, pEnchantPassiveSE ) ) { // 공격력 강화 상태효과던 아니건 추가해주면 스킬 사용시 자동으로 능력치 리프레시 된다. m_vlStateEffectList[ iSkillLevelApplyType ].push_back( *pEnchantPassiveSE ); m_vlStateEffectList[ iSkillLevelApplyType ].back().nFromEnchantPassiveSkillID = hEnchantPassiveSkill->GetClassID(); } } // 액션 프로세서를 강화 스킬의 것으로 변경한다. IDnSkillProcessor* pMyPlayAniProcessor = GetProcessor( IDnSkillProcessor::PLAY_ANI ); if( pMyPlayAniProcessor ) { IDnSkillProcessor* pEnchantPassiveSkillPlayAniProcessor = hEnchantPassiveSkill->GetProcessor( IDnSkillProcessor::PLAY_ANI ); if( pEnchantPassiveSkillPlayAniProcessor ) { // 기존 것 백업. IDnSkillProcessor* pBackup = new CDnPlayAniProcess; pBackup->CopyFrom( pMyPlayAniProcessor ); m_vlpProcessorBackup[ iSkillLevelApplyType ].push_back( pBackup ); // 강화 패시브 스킬의 것으로 데이터 적용. pMyPlayAniProcessor->CopyFrom( pEnchantPassiveSkillPlayAniProcessor ); } } IDnSkillProcessor* pMyPartialPlayAniProcessor = GetProcessor( IDnSkillProcessor::PARTIAL_PLAY_ANI ); if( pMyPartialPlayAniProcessor ) { IDnSkillProcessor* pEnchantPassivePartialSkillPlayAniProcessor = hEnchantPassiveSkill->GetProcessor( IDnSkillProcessor::PARTIAL_PLAY_ANI ); if( pEnchantPassivePartialSkillPlayAniProcessor ) { // 기존 것 백업. IDnSkillProcessor* pBackup = new CDnPartialPlayProcessor; pBackup->CopyFrom( pMyPartialPlayAniProcessor ); m_vlpProcessorBackup[ iSkillLevelApplyType ].push_back( pBackup ); pMyPartialPlayAniProcessor->CopyFrom( pEnchantPassivePartialSkillPlayAniProcessor ); } } IDnSkillProcessor* pMyChangeActionStrProcessor = GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR ); if( pMyChangeActionStrProcessor ) { IDnSkillProcessor* pEnchantPassiveChangeActionStrProcessor = hEnchantPassiveSkill->GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR ); if( pEnchantPassiveChangeActionStrProcessor ) { // 기존 것 백업. IDnSkillProcessor* pBackup = new CDnChangeActionStrProcessor; pBackup->CopyFrom( pMyChangeActionStrProcessor ); m_vlpProcessorBackup[ iSkillLevelApplyType ].push_back( pBackup ); pMyChangeActionStrProcessor->CopyFrom( pEnchantPassiveChangeActionStrProcessor ); } } #ifdef PRE_ADD_ENCHANTSKILL_BUBBLE_ACTION IDnSkillProcessor* pMyChangeActionStrByBubbleProcessor = GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR_BY_BUBBLE ); if( pMyChangeActionStrByBubbleProcessor ) { IDnSkillProcessor* pEnchantPassiveChangeActionStrProcessor = hEnchantPassiveSkill->GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR_BY_BUBBLE ); if( pEnchantPassiveChangeActionStrProcessor ) { IDnSkillProcessor* pBackup = new CDnChangeActionStrByBubbleProcessor( ); pBackup->CopyFrom( pMyChangeActionStrByBubbleProcessor ); m_vlpProcessorBackup[ iSkillLevelApplyType ].push_back( pBackup ); pMyChangeActionStrByBubbleProcessor->CopyFrom( pEnchantPassiveChangeActionStrProcessor ); } } #endif m_eElement = hEnchantPassiveSkill->GetElement(); m_SkillInfo[iSkillLevelApplyType].eSkillElement = hEnchantPassiveSkill->GetElement(); #ifdef PRE_FIX_SYNC_ENCHANT_SKILL bool bSummonMonsterToggleSkill = ( m_isAppliedSummonMonsterEnchantSkill && hEnchantPassiveSkill->GetDurationType() == CDnSkill::ActiveToggleForSummon ); if( bSummonMonsterToggleSkill == false ) { m_eDurationType = hEnchantPassiveSkill->GetDurationType(); m_SkillInfo[iSkillLevelApplyType].eDurationType = hEnchantPassiveSkill->GetDurationType(); } #endif #if defined(PRE_FIX_66175) //#66175 //UsableChecker를 베이스 스킬과 EX스킬 교체한다... vector& enchangeSkillUsableChecker = hEnchantPassiveSkill->GetUsableChecker(iSkillLevelApplyType); //EX스킬에 UsableChecker가 설정 되어 있는 경우만 교체 하도록 한다... if (enchangeSkillUsableChecker.size() > 0) { vector& baseSkillUsableChecker = GetUsableChecker(iSkillLevelApplyType); { //자신의 Checker들을 백업 리스트에 담아 놓는다. vector::iterator iter = baseSkillUsableChecker.begin(); vector::iterator endIter = baseSkillUsableChecker.begin(); //백업 리스트에 저장 해놓고... m_vlUsableCheckersBackup[iSkillLevelApplyType].clear(); for (; iter != endIter; ++iter) { m_vlUsableCheckersBackup[iSkillLevelApplyType].push_back((*iter)); } } //EX스킬의 usableChecker를 베이스 스킬에 적용한다. baseSkillUsableChecker.clear(); vector::iterator iter = enchangeSkillUsableChecker.begin(); vector::iterator endIter = enchangeSkillUsableChecker.end(); for (; iter != endIter; ++iter) { IDnSkillUsableChecker* pChecker = (*iter); baseSkillUsableChecker.push_back(pChecker->Clone()); } } #endif // PRE_FIX_66175 //EffectIDs도 변경.. string enchantEffectIDs = hEnchantPassiveSkill->GetEffectOutputIDs(iSkillLevelApplyType); string enchantEffectIDToClient = hEnchantPassiveSkill->GetEffectOutputIDToClient(iSkillLevelApplyType); m_BackupEffectOutputIDs[iSkillLevelApplyType] = m_SkillInfo[iSkillLevelApplyType].szEffectOutputIDs; m_BackupEffectOutputIDToClient[iSkillLevelApplyType] = m_SkillInfo[iSkillLevelApplyType].szEffectOutputIDToClient; //SC_CMDADDSTATEEFFECT 패킷을 클라이언트로 보낼때, szEffectOutputIDToClient값이 없으면 클라이언트는 테이블 값을 읽어서 사용한다. //EX스킬을 사용 시 스킬 ID는 베이스 스킬 ID가 전달 되기 때문에, EX스킬에의해 변경된 값을 사용 하지 못하게 됨. //eX스킬 적용시 szEffectOutputIDToClient값을 EX스킬 값으로 변경 해놓는다. m_SkillInfo[iSkillLevelApplyType].szEffectOutputIDs = enchantEffectIDs; m_SkillInfo[iSkillLevelApplyType].szEffectOutputIDToClient = enchantEffectIDs; #ifdef PRE_FIX_SYNC_ENCHANT_SKILL m_SkillInfo[iSkillLevelApplyType].iAppliedEnchantSkillID = hEnchantPassiveSkill->GetClassID(); #endif } SelectLevelDataType( iNowLevelDataType ); m_iAppliedEnchantPassiveSkillID = hEnchantPassiveSkill->GetClassID(); bSuccess = true; } return bSuccess; } void CDnSkill::ApplyEnchantSkillOnceFromBubble( DnSkillHandle hEnchantPassiveSkill ) { m_bEnchantedFromBubble = true; ApplyEnchantSkill( hEnchantPassiveSkill ); } bool CDnSkill::CheckAndDivideStateEffectArgument( StateEffectStruct* pDestSkillSE ) { if( pDestSkillSE->strEnchantSkillSEParam.empty() == true ) return false; bool bResult = false; if( CDnCreateBlow::IsBasicBlow( (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID ) ) { CDnBasicBlow::RemoveStateEffectValue( pDestSkillSE->szValue.c_str(), pDestSkillSE->strEnchantSkillSEParam.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam.clear(); bResult = true; } else if( STATE_BLOW::BLOW_016 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID ) { CDnHPIncBlow::RemoveStateEffectValue( pDestSkillSE->szValue.c_str(), pDestSkillSE->strEnchantSkillSEParam.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam.clear(); bResult = true; } else if( STATE_BLOW::BLOW_208 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID ) { CDnPingpongBlow::RemoveStateEffectValue( pDestSkillSE->szValue.c_str(), pDestSkillSE->strEnchantSkillSEParam.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam.clear(); bResult = true; } else if( STATE_BLOW::BLOW_140 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID ) { CDnHealingBlow::RemoveStateEffectValue( pDestSkillSE->szValue.c_str(), pDestSkillSE->strEnchantSkillSEParam.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam.clear(); bResult = true; } else if( STATE_BLOW::BLOW_227 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID ) { CDnBloodSuckingBlow::RemoveStateEffectValue( pDestSkillSE->szValue.c_str(), pDestSkillSE->strEnchantSkillSEParam.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam.clear(); bResult = true; } else if( STATE_BLOW::BLOW_215 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID ) { CDnOrderMySummonedMonsterBlow::RemoveStateEffectValue( pDestSkillSE->szValue.c_str(), pDestSkillSE->strEnchantSkillSEParam.c_str(), pDestSkillSE->szValue ); pDestSkillSE->strEnchantSkillSEParam.clear(); bResult = true; } else if( STATE_BLOW::BLOW_179 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID || STATE_BLOW::BLOW_121 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID || STATE_BLOW::BLOW_129 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID || STATE_BLOW::BLOW_062 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID || STATE_BLOW::BLOW_242 == (STATE_BLOW::emBLOW_INDEX)pDestSkillSE->nID ) { pDestSkillSE->szValue = pDestSkillSE->strEnchantSkillSEParam; pDestSkillSE->strEnchantSkillSEParam.clear(); bResult = true; } return bResult; } void CDnSkill::ReleaseEnchantSkill( void ) { if( 0 == m_iAppliedEnchantPassiveSkillID ) return; _ASSERT( EnchantPassive != m_eSkillType ); if( EnchantPassive != m_eSkillType ) { int iNowLevelDataType = m_iSelectedSkillLevelDataApplyType; for( int iSkillLevelApplyType = PVE; iSkillLevelApplyType < NUM_SKILLLEVEL_APPLY_TYPE; ++iSkillLevelApplyType ) { // 기존에 존재하는 상태효과엔 인자값을 더해주고 새로운 상태효과는 리스트에 추가. // 쿨타임은 강화 패시브 스킬의 쿨타임으로 덮어씌워준다. SelectLevelDataType( iSkillLevelApplyType ); DNVector( StateEffectStruct )::iterator iter = m_vlStateEffectList[ iSkillLevelApplyType ].begin(); for( iter; iter != m_vlStateEffectList[ iSkillLevelApplyType ].end(); ) { if( false == CheckAndDivideStateEffectArgument( &(*iter) ) ) { if( 0 < iter->nFromEnchantPassiveSkillID ) { iter = m_vlStateEffectList[ iSkillLevelApplyType ].erase( iter ); continue; } } ++iter; } for( int i = 0; i < (int)m_vlpProcessorBackup[ iSkillLevelApplyType ].size(); ++i ) { IDnSkillProcessor* pProcessor = m_vlpProcessorBackup[ iSkillLevelApplyType ].at( i ); switch( pProcessor->GetType() ) { case IDnSkillProcessor::PLAY_ANI: { IDnSkillProcessor* pMyPlayAniProcessor = GetProcessor( IDnSkillProcessor::PLAY_ANI ); if( pMyPlayAniProcessor ) pMyPlayAniProcessor->CopyFrom( pProcessor ); } break; case IDnSkillProcessor::PARTIAL_PLAY_ANI: { IDnSkillProcessor* pMyPartialPlayAniProcessor = GetProcessor( IDnSkillProcessor::PARTIAL_PLAY_ANI ); if( pMyPartialPlayAniProcessor ) pMyPartialPlayAniProcessor->CopyFrom( pProcessor ); } break; case IDnSkillProcessor::CHANGE_ACTIONSTR: { IDnSkillProcessor* pMyChangeActionStrProcessor = GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR ); if( pMyChangeActionStrProcessor ) pMyChangeActionStrProcessor->CopyFrom( pProcessor ); } break; #ifdef PRE_ADD_ENCHANTSKILL_BUBBLE_ACTION case IDnSkillProcessor::CHANGE_ACTIONSTR_BY_BUBBLE: { IDnSkillProcessor* pMyChangeActionStrByBubbleProcessor = GetProcessor( IDnSkillProcessor::CHANGE_ACTIONSTR_BY_BUBBLE ); if( pMyChangeActionStrByBubbleProcessor ) pMyChangeActionStrByBubbleProcessor->CopyFrom( pProcessor ); } break; #endif } } SAFE_DELETE_PVEC( m_vlpProcessorBackup[ iSkillLevelApplyType ] ); #if defined(PRE_FIX_66175) if (m_vlUsableCheckersBackup[iSkillLevelApplyType].size() > 0) { std::swap(m_vlpUsableCheckers[iSkillLevelApplyType], m_vlUsableCheckersBackup[iSkillLevelApplyType]); } #endif // PRE_FIX_66175 //EffectIDs도 변경.. m_SkillInfo[iSkillLevelApplyType].szEffectOutputIDs = m_BackupEffectOutputIDs[iSkillLevelApplyType]; m_SkillInfo[iSkillLevelApplyType].szEffectOutputIDToClient = m_BackupEffectOutputIDToClient[iSkillLevelApplyType]; m_BackupEffectOutputIDs[iSkillLevelApplyType].clear(); m_BackupEffectOutputIDToClient[iSkillLevelApplyType].clear(); } SelectLevelDataType( iNowLevelDataType ); m_iAppliedEnchantPassiveSkillID = 0; } } IDnSkillUsableChecker* CDnSkill::GetChecker( int iType ) { IDnSkillUsableChecker* pResult = NULL; int iNumChecker = (int)m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iChecker = 0; iChecker < iNumChecker; ++iChecker ) { IDnSkillUsableChecker* pSkillChecker = m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].at( iChecker ); if( iType == pSkillChecker->GetType() ) { pResult = pSkillChecker; break; } } return pResult; } bool CDnSkill::IsExistUsableChecker( int iType ) // 정의된 UsableChecker을 가지고있느냐? { int iNumChecker = (int)m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iChecker = 0; iChecker < iNumChecker; ++iChecker ) { IDnSkillUsableChecker* pSkillChecker = m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].at( iChecker ); if( iType == pSkillChecker->GetType() ) return true; } return false; } bool CDnSkill::IsFinishedAuraSkill() { //오라 스킬일때 지속 절대/비율 마나 증가가 있을때 총 마나 변화량을 합산해서 지금 남은 마나와 비교해서 //스킬 지속 가능 여부를 판단한다. if (m_hActor) { float fIncManaDelta = 0.0f; //절대 증가 분 DNVector( DnBlowHandle ) vlManaAbIncBlows; m_hActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_013, vlManaAbIncBlows ); if (!vlManaAbIncBlows.empty()) { for( int i = 0; i < (int)vlManaAbIncBlows.size(); ++i ) { DnBlowHandle hBlow = vlManaAbIncBlows.at( i ); fIncManaDelta += hBlow->GetFloatValue(); } } //비율 증가 분 float fMaxMP = static_cast(m_hActor->GetMaxSP()); DNVector( DnBlowHandle ) vlManaRateIncBlows; m_hActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_014, vlManaRateIncBlows ); if (!vlManaRateIncBlows.empty()) { for( int i = 0; i < (int)vlManaRateIncBlows.size(); ++i ) { DnBlowHandle hBlow = vlManaRateIncBlows.at( i ); fIncManaDelta += fMaxMP * hBlow->GetFloatValue(); } } #if defined(PRE_FIX_46381) //231번 상태효과 적용. DNVector(DnBlowHandle) vlContinueBaseMPIncBlows; m_hActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_231, vlContinueBaseMPIncBlows); if (!vlContinueBaseMPIncBlows.empty()) { for (int i = 0; i < (int)vlContinueBaseMPIncBlows.size(); ++i) { DnBlowHandle hBlow = vlContinueBaseMPIncBlows.at(i); fIncManaDelta += static_cast(hBlow.GetPointer())->GetMPIncValue(); } } #endif // PRE_FIX_46381 //최종 -증가분이 현재 남은 마나보다 많으면 마나 모자름.. if (m_hActor->GetSP() < -(int)fIncManaDelta) return true; } return false; } void CDnSkill::AddPrefixBlow(DnActorHandle hActor, int nBlowID) { PREFIX_SKILL_BLOWLIST::iterator findIter = m_PrefixBlowList.find(hActor); if (findIter != m_PrefixBlowList.end()) { findIter->second.push_back(nBlowID); } else { BLOWLIST blowList; blowList.push_back(nBlowID); m_PrefixBlowList.insert(PREFIX_SKILL_BLOWLIST::value_type(hActor, blowList)); } } void CDnSkill::RemovePrefixBlow() { PREFIX_SKILL_BLOWLIST::iterator iter = m_PrefixBlowList.begin(); PREFIX_SKILL_BLOWLIST::iterator endIter = m_PrefixBlowList.end(); for (; iter != endIter; ++iter) { DnActorHandle hActor = iter->first; if (!hActor) continue; BLOWLIST& blowList = iter->second; for (int i = 0; i < (int)blowList.size(); ++i) { // [2010/12/13 semozz] // 이제는 모든 상태효과는 패킷으로 생성 되므로, 패킷으로 상태효과 제거한다. hActor->CmdRemoveStateEffectFromID( blowList[i] ); } blowList.clear(); } m_PrefixBlowList.clear(); } void CDnSkill::OnInitializeSummonMonsterInfo() { if (!m_hActor) return; set::iterator iter = m_setUseActionNames.begin(); set::iterator endIter = m_setUseActionNames.end(); string actionName; CEtActionBase::ActionElementStruct *pActionElement = NULL; bool bFindMonsterID = false; for (; iter != endIter; ++iter) { actionName = *iter; pActionElement = m_hActor->GetElement(actionName.c_str()); if (pActionElement) { CEtActionSignal *pSignal = NULL; for( DWORD i=0; ipVecSignalList.size(); i++ ) { pSignal = pActionElement->pVecSignalList[i]; if (pSignal->GetSignalIndex() == STE_SummonMonster) { SummonMonsterStruct* pStruct = (SummonMonsterStruct *)pSignal->GetData(); m_SummonMonsterID = pStruct ? pStruct->MonsterID : -1; if (m_SummonMonsterID != -1) { bFindMonsterID = true; break; } } } } if (bFindMonsterID) break; } } DnActorHandle CDnSkill::FindSummonMonster(int nMonsterID) { DnActorHandle hSummonMonster; if (m_hActor && m_hActor->IsPlayerActor()) { const list& listSummonMonster = m_hActor->GetSummonedMonsterList(); if( false == listSummonMonster.empty() ) { list::const_iterator iter = listSummonMonster.begin(); for( iter; iter != listSummonMonster.end(); ++iter ) { DnMonsterActorHandle hMonster = (*iter); if( hMonster && hMonster->GetMonsterClassID() == nMonsterID ) { hSummonMonster = hMonster; break; } } } } return hSummonMonster; } bool CDnSkill::SummonMonsterOff() { DnActorHandle hSummonMonster; if (m_SummonMonsterID != -1) hSummonMonster = FindSummonMonster(m_SummonMonsterID); if (hSummonMonster) { hSummonMonster->CmdSuicide(false, false); return true; } else return false; } bool CDnSkill::IsSkipStateBlow(const char* szSkipStateBlows, STATE_BLOW::emBLOW_INDEX blowIndex) { if (szSkipStateBlows == NULL) return false; std::string stringValue = szSkipStateBlows; std::vector tokens; std::string delimiters = ";"; TokenizeA(stringValue, tokens, delimiters); for (UINT i = 0; i < tokens.size(); ++i) { STATE_BLOW::emBLOW_INDEX skipStateBlowIndex = (STATE_BLOW::emBLOW_INDEX)(atoi(tokens[i].c_str())); if (skipStateBlowIndex == blowIndex) return true; } return false; } void CDnSkill::AddProjectile(CDnProjectile* pProjectile) { if (pProjectile) { LOCAL_TIME startTime = pProjectile->GetSkillStartTime(); SKILL_PROJECTILE_LIST::iterator findIter = m_ProjectileList.find(startTime); if (findIter == m_ProjectileList.end()) { PROJECTILE_LIST projectileList; projectileList.push_back(pProjectile); m_ProjectileList.insert(std::make_pair(startTime, projectileList)); } else { findIter->second.push_back(pProjectile); } } } void CDnSkill::RemoveProjectile(CDnProjectile* pProjectile) { if (pProjectile == NULL) return; LOCAL_TIME startTime = pProjectile->GetSkillStartTime(); SKILL_PROJECTILE_LIST::iterator findIter = m_ProjectileList.find(startTime); if (findIter != m_ProjectileList.end()) { PROJECTILE_LIST::iterator iter = findIter->second.begin(); for ( ; iter != findIter->second.end(); ++iter) { CDnProjectile* pTemp = *iter; if (pTemp == pProjectile) { iter = findIter->second.erase(iter); //리스트가 비었다면 메인 리스트에서 제거한다. if (findIter->second.empty() == true) m_ProjectileList.erase(findIter); break; } } } } int CDnSkill::GetProjectileCount(LOCAL_TIME startTime) { int nListCount = 0; SKILL_PROJECTILE_LIST::iterator findIter = m_ProjectileList.find(startTime); if (findIter != m_ProjectileList.end()) nListCount = (int)findIter->second.size(); return nListCount; } void CDnSkill::ApplyAddtionalStateInfo() { if( !m_hActor ) return; //#52905 [칼리]"중첩 추가 효과" //상태효과가 적용 되어 있을때 조건에 맞는 상태효과가 적용 되어 있으면 스킬의 상태효과에 추가 한다.. if( m_hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_253) ) { DNVector(DnBlowHandle) vlBlows; m_hActor->GatherAppliedStateBlowByBlowIndex(STATE_BLOW::BLOW_253, vlBlows); int nListCount = (int)vlBlows.size(); for (int iIndex = 0; iIndex < nListCount; ++iIndex) { DnBlowHandle hBlow = vlBlows[iIndex]; if (hBlow && hBlow->IsEnd() == false) { CDnAdditionalStateInfoBlow* pAddtionalStateInfoBlow = static_cast(hBlow.GetPointer()); if (pAddtionalStateInfoBlow) { STATE_BLOW::emBLOW_INDEX DestBlowIndex = pAddtionalStateInfoBlow->GetDestStateIndex(); CDnSkill::StateEffectStruct addStateInfo = pAddtionalStateInfoBlow->GetTargetStateInfo(); int iNumStateBlow = (int)m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].size(); for( int i = 0; i < iNumStateBlow; ++i ) { const StateEffectStruct& SE = m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].at( i ); if( DestBlowIndex == SE.nID) { addStateInfo.nDurationTime = SE.nDurationTime; //지속 시간은 조건 상태효과 시간으로 설정한다. //추가할 상태효과 리스트를 만든다. m_AddtionalStateInfoList.push_back(addStateInfo); OutputDebug("추가 상태효과 적용됨.... %d, %s\n", addStateInfo.nID, addStateInfo.szValue.c_str()); } } } } } //추가할 상태효과 리스트 순회 하면서 실제 스킬 상태효과 리스트에 추가 한다. int nAddStateCount = (int)m_AddtionalStateInfoList.size(); for (int i = 0; i < nAddStateCount; ++i) { CDnSkill::StateEffectStruct addStateEffect = m_AddtionalStateInfoList[i]; m_vlStateEffectList[m_iSelectedSkillLevelDataApplyType].push_back(addStateEffect); } } if( m_hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_276) ) { DNVector(DnBlowHandle) vlBlows; m_hActor->GatherAppliedStateBlowByBlowIndex(STATE_BLOW::BLOW_276, vlBlows); for( DWORD i=0; i(vlBlows[i].GetPointer()); if( pAddStateBySkillGroupBlow ) pAddStateBySkillGroupBlow->ApplyAddtionalStateBlowFromSkill( GetClassID() ); } } } } void CDnSkill::RemoveAddtionalStateInfo() { //스킬 사용시 추가 상태효과 추가된 것들 제거... int nListCount = (int)m_AddtionalStateInfoList.size(); for (int iIndex = 0; iIndex < nListCount; ++iIndex) { StateEffectStruct addStateInfo = m_AddtionalStateInfoList[iIndex]; std::vector::iterator iter = m_vlStateEffectList[ m_iSelectedSkillLevelDataApplyType ].begin(); for( ; iter != m_vlStateEffectList[m_iSelectedSkillLevelDataApplyType].end(); ++iter) { StateEffectStruct SE = (*iter); if (SE.nID == addStateInfo.nID && SE.ApplyType == addStateInfo.ApplyType && /*SE.nDurationTime == addStateInfo.nDurationTime &&*/ //지속시간은 가변... SE.bAddtionalStateInfo == addStateInfo.bAddtionalStateInfo) { iter = m_vlStateEffectList[m_iSelectedSkillLevelDataApplyType].erase(iter); break; } } } m_AddtionalStateInfoList.clear(); if( m_hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_276) ) { DNVector(DnBlowHandle) vlBlows; m_hActor->GatherAppliedStateBlowByBlowIndex(STATE_BLOW::BLOW_276, vlBlows); for( DWORD i=0; i(vlBlows[i].GetPointer()); if( pAddStateBySkillGroupBlow ) pAddStateBySkillGroupBlow->RemoveAdditionalStateBlow(); } } } } #if defined(PRE_FIX_64312) bool CDnSkill::IsSummonMonsterSkill() { m_bIsSummonMonsterSkill = false; //스킬 Checker중에 소환몬스터 체크가 있으면 소환몬스터에 사용 하는 스킬???(기획의견..) int iNumChecker = (int)m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].size(); for( int iChecker = 0; iChecker < iNumChecker; ++iChecker ) { IDnSkillUsableChecker* pChecker = m_vlpUsableCheckers[ m_iSelectedSkillLevelDataApplyType ].at( iChecker ); if( pChecker && pChecker->GetType() == IDnSkillUsableChecker::SUMMON_CHECKER ) { m_bIsSummonMonsterSkill = true; break; } } return m_bIsSummonMonsterSkill; } void CDnSkill::AddSummonMonsterEnchantSkill(DnSkillHandle hSkill) { m_SummonMonsterEnchantSkill = hSkill; } void CDnSkill::RemoveSummonMonsterEnchantSkill() { m_SummonMonsterEnchantSkill.Identity(); } bool CDnSkill::ApplySummonMonsterEnchantSkill(DnSkillHandle hSkill) { m_isAppliedSummonMonsterEnchantSkill = true; return ApplyEnchantSkill(hSkill); } #endif // PRE_FIX_64312 #if defined(PRE_ADD_TOTAL_LEVEL_SKILL) float CDnSkill::GetOrigDelayTime() { float fDelayTime = 0.0f; if( 0.0f < m_fAnotherGlobalSkillCoolTime ) fDelayTime = m_fAnotherGlobalSkillCoolTime; else fDelayTime = m_fDelayTime[ m_iSelectedSkillLevelDataApplyType ]; return fDelayTime; } void CDnSkill::UpdateGlobalCoolTime(float fRate) { //글로벌 쿨타임이 설정 되어 있고, 쿨타임이 이미 시작 되었을 경우 if (m_iGlobalSkillGroupID > 0) { if (m_fLeftDelayTime > 0) { m_fDeltaGlobalCoolTime += fRate; float fOrigDelayTime = GetOrigDelayTime(); m_fLeftDelayTime -= fOrigDelayTime * fRate; } } } void CDnSkill::ResetGlobalCoolTime(float fRate) { //글로벌 쿨타임이 설정 되어 있고, 쿨타임이 이미 시작 되었을 경우 if (m_iGlobalSkillGroupID > 0 ) { //글로벌 쿨타임 변경 수치가 설정 된 경우 if (m_fDeltaGlobalCoolTime > 0.0) { float fOrigDelayTime = GetOrigDelayTime(); //적용된 수치가 지금 리셋 하려는 수치보다 작은 값인 경우 if (m_fDeltaGlobalCoolTime < fRate) fRate = m_fDeltaGlobalCoolTime; //적용된 수치는 감소 시켜준다.. m_fDeltaGlobalCoolTime -= fRate; if (m_fDeltaGlobalCoolTime < 0.0f) m_fDeltaGlobalCoolTime = 0.0f; //현재 쿨타임이 남아 있는 경우 if (m_fLeftDelayTime > 0) m_fLeftDelayTime += fOrigDelayTime * fRate; } } } #endif // PRE_ADD_TOTAL_LEVEL_SKILL #if defined(PRE_ADD_65808) bool CDnSkill::IsSummonMonsterRecall(int& monsterID) { //PlayAniProcess를 이용하고, 해당 동작에 SummonMonster시그널이 존재 하면 true를 리턴.. CDnPlayAniProcess* pAniProcess = static_cast(GetProcessor( IDnSkillProcessor::PLAY_ANI )); if (pAniProcess && m_hActor) { const char* szActionName = pAniProcess->GetActionName(); if (szActionName == NULL) return false; CEtActionBase::ActionElementStruct *pStruct = m_hActor->GetElement(szActionName); if (pStruct) { int selectedMonsterID = -1; CEtActionSignal *pSignal = NULL; for (int i = 0; i < (int)pStruct->pVecSignalList.size(); ++i) { pSignal = pStruct->pVecSignalList[i]; if (pSignal == NULL) continue; int signalIndex = pSignal->GetSignalIndex(); switch(signalIndex) { case STE_SummonMonster: { SummonMonsterStruct* pSummonMonsterInfo = (SummonMonsterStruct *)pSignal->GetData(); if (pSummonMonsterInfo) selectedMonsterID = pSummonMonsterInfo->MonsterID; } break; case STE_SummonMonsterInfo: { SummonMonsterInfoStruct* pInfo = (SummonMonsterInfoStruct*)pSignal->GetData(); if (pInfo) selectedMonsterID = pInfo->MonsterID; } break; } if (selectedMonsterID != -1) break; } if (selectedMonsterID != -1) { monsterID = selectedMonsterID; return true; } } } return false; } #endif // PRE_ADD_65808 #if defined(_GAMESERVER) bool CoolTimeInfo::Process(LOCAL_TIME localTime, float fDelta) { switch( m_eDurationType ) { case CDnSkill::DurationTypeEnum::Instantly: case CDnSkill::DurationTypeEnum::Buff: case CDnSkill::DurationTypeEnum::Debuff: case CDnSkill::DurationTypeEnum::SummonOnOff: case CDnSkill::DurationTypeEnum::StanceChange: { if( m_fLeftDelayTime == 0.0f ) return false; m_fLeftDelayTime -= fDelta; if( m_fLeftDelayTime < 0.f ) m_fLeftDelayTime = 0.f; m_fCoolTime = ( 1.0f / (m_fDelayTime*m_fCoolTimeAdjustBlowValue) ) * m_fLeftDelayTime; } break; case CDnSkill::DurationTypeEnum::TimeToggle: break; case CDnSkill::DurationTypeEnum::ActiveToggle: case CDnSkill::DurationTypeEnum::ActiveToggleForSummon: break; case CDnSkill::DurationTypeEnum::Aura: break; } return (m_fCoolTime != 0.0f && m_fLeftDelayTime > 0.0f); } void CoolTimeInfo::OnBeginCoolTime() { m_fCoolTime = (m_fDelayTime == 0.f) ? 0.0f : 1.0f; m_fCoolTimeAdjustBlowValue = 1.0f; m_fLeftDelayTime = m_fDelayTime * m_fCoolTimeAdjustBlowValue; } void CoolTimeInfo::SetInfo(DnSkillHandle &hSkill) { m_nSkillID = hSkill->GetClassID(); m_hActor = hSkill->GetActor(); m_eDurationType = hSkill->GetDurationType(); m_fCoolTimeAdjustBlowValue = 1.0f; m_fDelayTime = hSkill->GetDelayTime(); } #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) void CoolTimeInfo::SetInfo(CDnPrefixSkill* pPrefixSkill) { m_nSkillID = pPrefixSkill->GetSkillType(); DnSkillHandle hSkill = pPrefixSkill->GetSkillHandle(); if (hSkill) { m_hActor = hSkill->GetActor(); m_eDurationType = hSkill->GetDurationType(); m_fCoolTimeAdjustBlowValue = 1.0f; m_fDelayTime = hSkill->GetDelayTime(); } } #endif // PRE_ADD_PREFIX_SYSTE_RENEW bool CoolTimeManager::IsCoolTime(int nSkillID) { COOLTIME_LIST::iterator findIter = m_CoolTimeList.find(nSkillID); //아직 등록되어 있다면 쿨타임이 끝나지 않았다?? return (findIter != m_CoolTimeList.end()); } void CoolTimeManager::AddCoolTime(DnSkillHandle hSkill) { if (!hSkill) return; int nSkillID = hSkill->GetClassID(); COOLTIME_LIST::iterator findIter = m_CoolTimeList.find(nSkillID); //같은 스킬 ID가 없을때만 등록한다. if (findIter == m_CoolTimeList.end()) { CoolTimeInfo *pCoolTimeInfo = new CoolTimeInfo(); pCoolTimeInfo->SetInfo(hSkill); pCoolTimeInfo->OnBeginCoolTime(); m_CoolTimeList.insert(COOLTIME_LIST::value_type(nSkillID, pCoolTimeInfo)); } } #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) void CoolTimeManager::AddCoolTime(CDnPrefixSkill* pPrefixSkill) { if (!pPrefixSkill) return; int nSkillType = pPrefixSkill->GetSkillType(); COOLTIME_LIST::iterator findIter = m_CoolTimeList.find(nSkillType); //같은 스킬 ID가 없을때만 등록한다. if (findIter == m_CoolTimeList.end()) { CoolTimeInfo *pCoolTimeInfo = new CoolTimeInfo(); pCoolTimeInfo->SetInfo(pPrefixSkill); pCoolTimeInfo->OnBeginCoolTime(); m_CoolTimeList.insert(COOLTIME_LIST::value_type(nSkillType, pCoolTimeInfo)); } } #endif // PRE_ADD_PREFIX_SYSTE_RENEW void CoolTimeManager::RemoveCoolTime(int nSkillID) { COOLTIME_LIST::iterator findIter = m_CoolTimeList.find(nSkillID); if (findIter != m_CoolTimeList.end()) { CoolTimeInfo* pCoolTimeInfo = findIter->second; if (pCoolTimeInfo) delete pCoolTimeInfo; m_CoolTimeList.erase(findIter); } } void CoolTimeManager::Process(LOCAL_TIME localTime, float fDelta) { COOLTIME_LIST::iterator iter = m_CoolTimeList.begin(); COOLTIME_LIST::iterator endIter = m_CoolTimeList.end(); std::list removeCoolTimeList; for (; iter != endIter; ++iter) { if (!iter->second->Process(localTime, fDelta)) removeCoolTimeList.push_back(iter->first); } if (!removeCoolTimeList.empty()) { std::list::iterator iter = removeCoolTimeList.begin(); std::list::iterator endIter = removeCoolTimeList.end(); for (; iter != endIter; ++iter) { RemoveCoolTime((*iter)); } removeCoolTimeList.clear(); } } void CoolTimeManager::InitList() { COOLTIME_LIST::iterator iter = m_CoolTimeList.begin(); COOLTIME_LIST::iterator endIter = m_CoolTimeList.end(); for (; iter != endIter; ++iter) { CoolTimeInfo* pCoolTimeInfo = iter->second; if (pCoolTimeInfo) delete pCoolTimeInfo; } m_CoolTimeList.clear(); } #endif // _GAMESERVER #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) CDnPrefixSkill::CDnPrefixSkill(int nPrefixType) { m_nPrefixType = nPrefixType; m_fProbability = 0.0f; } CDnPrefixSkill::~CDnPrefixSkill() { } void CDnPrefixSkill::SetSkillHandle(DnSkillHandle hSkill) { m_hSkill = hSkill; } void CDnPrefixSkill::UpdateCandidateSkill() { std::list::iterator iter = m_SkillList.begin(); std::list::iterator endIter = m_SkillList.end(); float fDelayTime = FLT_MAX; DnSkillHandle hSelectedSkill; for (; iter != endIter; ++iter) { DnSkillHandle hSkill = *iter; if (!hSkill) continue; float fSkillDelayTime = hSkill->GetDelayTime(); if (fSkillDelayTime < fDelayTime) { hSelectedSkill = hSkill; fDelayTime = fSkillDelayTime; } } if (hSelectedSkill) SetSkillHandle(hSelectedSkill); } void CDnPrefixSkill::AddSkill(DnSkillHandle hSkill) { if (!hSkill) return; m_SkillList.push_back(hSkill); //해당 스킬의 확률값을 가져온다. CDnProbabilityChecker* pProbabilityChecker = static_cast(hSkill->GetChecker(IDnSkillUsableChecker::PROB_CHECKER)); m_fProbability += pProbabilityChecker ? pProbabilityChecker->GetProbability() : 0.0f; UpdateCandidateSkill(); AddStateEffectInfo(hSkill); } void CDnPrefixSkill::RemoveSkill(DnSkillHandle hSkill) { std::list::iterator iter = m_SkillList.begin(); std::list::iterator endIter = m_SkillList.end(); for (; iter != endIter; ++iter) { if ((*iter) == hSkill) { //해당 스킬의 확률값을 가져온다. CDnProbabilityChecker* pProbabilityChecker = static_cast(hSkill->GetChecker(IDnSkillUsableChecker::PROB_CHECKER)); m_fProbability -= pProbabilityChecker ? pProbabilityChecker->GetProbability() : 0.0f; m_SkillList.erase(iter); break; } } UpdateCandidateSkill(); //스킬 제거 될때 스킬 리스트 돌면서 상태효과 다시 적용하도록.. UpdateStateEffects(); } void CDnPrefixSkill::UpdateStateEffects() { //1.기존 상태효과 설정 값을 제거.. m_vlStateEffectList.clear(); //스킬 리스트 돌면서 상태효과 값 적용. std::list::iterator iter = m_SkillList.begin(); std::list::iterator endIter = m_SkillList.end(); for (; iter != endIter; ++iter) { DnSkillHandle hSkill = (*iter); if (hSkill) { AddStateEffectInfo(hSkill); } } } void CDnPrefixSkill::AddStateEffectInfo(DnSkillHandle hSkill) { if (m_vlStateEffectList.empty()) { //hSkill의 상태효과 설정값을 그대로 리스트에 추가 한다. int nStateEffectCount = hSkill->GetStateEffectCount(); for (int i = 0; i < nStateEffectCount; ++i) { CDnSkill::StateEffectStruct* pAddStateEffect = hSkill->GetStateEffectFromIndex(i); if (pAddStateEffect) m_vlStateEffectList.push_back(*pAddStateEffect); } } else { //상태효과 갯수가 맞지 않으면 추가 못함.. if (m_vlStateEffectList.size() != hSkill->GetStateEffectCount()) return; switch(m_nPrefixType) { case Prefix_000: //파괴의 AddStateEffectInfo_Prefix_000(hSkill); break; case Prefix_001: //마법의 AddStateEffectInfo_Prefix_001(hSkill); break; case Prefix_002: //곰의 무기 AddStateEffectInfo_Prefix_002(hSkill); break; case Prefix_003: //바람의 무기 AddStateEffectInfo_Prefix_003(hSkill); break; case Prefix_004: //지혜의 무기 AddStateEffectInfo_Prefix_004(hSkill); break; case Prefix_005: //가혹한 AddStateEffectInfo_Prefix_005(hSkill); break; case Prefix_006: //어둠의 무기 AddStateEffectInfo_Prefix_006(hSkill); break; case Prefix_007: //생명의 무기 AddStateEffectInfo_Prefix_007(hSkill); break; case Prefix_008: //마나의 무기 AddStateEffectInfo_Prefix_008(hSkill); break; case Prefix_009: //활기의 AddStateEffectInfo_Prefix_009(hSkill); break; case Prefix_010: //치명적인 AddStateEffectInfo_Prefix_010(hSkill); break; case Prefix_011: //구속의 AddStateEffectInfo_Prefix_011(hSkill); break; case Prefix_012: //충격의 AddStateEffectInfo_Prefix_012(hSkill); break; case Prefix_013: //용자의 AddStateEffectInfo_Prefix_013(hSkill); break; case Prefix_014: //기사의 AddStateEffectInfo_Prefix_014(hSkill); break; case Prefix_015: //철벽의 무기 AddStateEffectInfo_Prefix_015(hSkill); break; case Prefix_016: //장막의 무기 AddStateEffectInfo_Prefix_016(hSkill); break; case Prefix_017: //불의 무기 AddStateEffectInfo_Prefix_017(hSkill); break; case Prefix_018: //물의 무기 AddStateEffectInfo_Prefix_018(hSkill); break; case Prefix_019: //빛의 무기 AddStateEffectInfo_Prefix_019(hSkill); break; case Prefix_020: //철벽의 방어구 AddStateEffectInfo_Prefix_020(hSkill); break; case Prefix_021: //장막의 방어구 AddStateEffectInfo_Prefix_021(hSkill); break; case Prefix_022: //곰의 방어구 AddStateEffectInfo_Prefix_022(hSkill); break; case Prefix_023: //바람의 방어구 AddStateEffectInfo_Prefix_023(hSkill); break; case Prefix_024: //지혜의 방어구 AddStateEffectInfo_Prefix_024(hSkill); break; case Prefix_025: //건강한 AddStateEffectInfo_Prefix_025(hSkill); break; case Prefix_026: //행운의 AddStateEffectInfo_Prefix_026(hSkill); break; case Prefix_027: //생명의 방어구 AddStateEffectInfo_Prefix_027(hSkill); break; case Prefix_028: //마나의 방어구 AddStateEffectInfo_Prefix_028(hSkill); break; // case Prefix_029: //견고한 // AddStateEffectInfo_Prefix_029(hSkill); // break; case Prefix_030: //유연한 AddStateEffectInfo_Prefix_030(hSkill); break; case Prefix_031: //의지의 AddStateEffectInfo_Prefix_031(hSkill); break; // case Prefix_032: //강인한 // AddStateEffectInfo_Prefix_032(hSkill); // break; // case Prefix_033: //불굴의 // AddStateEffectInfo_Prefix_033(hSkill); // break; case Prefix_034: //불의 방어구 AddStateEffectInfo_Prefix_034(hSkill); break; case Prefix_035: //물의 방어구 AddStateEffectInfo_Prefix_035(hSkill); break; case Prefix_036: //빛의 방어구 AddStateEffectInfo_Prefix_036(hSkill); break; case Prefix_037: //어둠의 방어구 AddStateEffectInfo_Prefix_037(hSkill); break; default: break; } } } void CDnPrefixSkill::AddStateEffectInfo( STATE_BLOW::emBLOW_INDEX blowList[], int nCount, DnSkillHandle hSkill ) { if (!hSkill) return; int nAddStateEffectCount = hSkill->GetStateEffectCount(); //상태효과 갯수가 4개 if (nAddStateEffectCount != nCount) { OutputDebug("접두사 스킬 Type[%d]의 상태효과 갯수가 다름...!!\n", m_nPrefixType); return; } for (int i = 0; i < nCount; ++i) { CDnSkill::StateEffectStruct* pAddStateEffect = hSkill->GetStateEffectFromIndex(i); CDnSkill::StateEffectStruct* pOrigStateEffect = &m_vlStateEffectList[i]; //상태효과 Index가 BLOW_213이 아니면 잘못된거다.. if ((STATE_BLOW::emBLOW_INDEX)pOrigStateEffect->nID != blowList[i]) { OutputDebug("상태효과 Index가 다름..%d이어야 하는데 %d이네...!!!\n", blowList[i], pOrigStateEffect->nID); continue; } //두개 상태효과 Index가 다르면.. if (pAddStateEffect->nID != pOrigStateEffect->nID) { OutputDebug("두 상태효과 Index가 다름..Orig[%d], Add[%d]..!!!\n", pOrigStateEffect->nID, pAddStateEffect->nID); continue; } std::string szNewValue = ""; CDnCreateBlow::AddStateEffectValue((STATE_BLOW::emBLOW_INDEX)pOrigStateEffect->nID, pOrigStateEffect->szValue.c_str(), pAddStateEffect->szValue.c_str(), szNewValue); OutputDebug("원래 상태효과 설정값(%s), 추가 되는 상태효과 설정값(%s), 최종 결과(%s)\n", pOrigStateEffect->szValue.c_str(), pAddStateEffect->szValue.c_str(), szNewValue.c_str()); pOrigStateEffect->szValue = szNewValue; //지속 시간은 큰 값으로 변경. pOrigStateEffect->nDurationTime = max(pOrigStateEffect->nDurationTime, pAddStateEffect->nDurationTime); } } void CDnPrefixSkill::AddStateEffectInfo_Prefix_000(DnSkillHandle hSkill) //파괴의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_213, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_001(DnSkillHandle hSkill) //마법의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_214, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_002(DnSkillHandle hSkill) //곰의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_221, STATE_BLOW::BLOW_124, STATE_BLOW::BLOW_128, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_003(DnSkillHandle hSkill) //바람의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_221, STATE_BLOW::BLOW_126, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_004(DnSkillHandle hSkill) //지혜의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_214, STATE_BLOW::BLOW_036, STATE_BLOW::BLOW_037, STATE_BLOW::BLOW_038, STATE_BLOW::BLOW_039, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_005(DnSkillHandle hSkill) //가혹한 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_157, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_006(DnSkillHandle hSkill) //어둠의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_035, STATE_BLOW::BLOW_182, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_007(DnSkillHandle hSkill) //생명의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_016, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_008(DnSkillHandle hSkill) //마나의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_018, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_009(DnSkillHandle hSkill) //활기의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_014, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_010(DnSkillHandle hSkill) //치명적인 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_158, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_011(DnSkillHandle hSkill) //구속의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_070, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_012(DnSkillHandle hSkill) //충격의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_221, STATE_BLOW::BLOW_175, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_013(DnSkillHandle hSkill) //용자의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_079, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_014(DnSkillHandle hSkill) //기사의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_076, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_015(DnSkillHandle hSkill) //철벽의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_004, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_016(DnSkillHandle hSkill) //장막의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_094, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_017(DnSkillHandle hSkill) //불의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_032, STATE_BLOW::BLOW_182, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_018(DnSkillHandle hSkill) //물의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_033, STATE_BLOW::BLOW_182, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_019(DnSkillHandle hSkill) //빛의 무기 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_034, STATE_BLOW::BLOW_182, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_020(DnSkillHandle hSkill) //철벽의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_134, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill);; } void CDnPrefixSkill::AddStateEffectInfo_Prefix_021(DnSkillHandle hSkill) //장막의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_135, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_022(DnSkillHandle hSkill) //곰의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_124, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_023(DnSkillHandle hSkill) //바람의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_126, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_024(DnSkillHandle hSkill) //지혜의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_036, STATE_BLOW::BLOW_037, STATE_BLOW::BLOW_038, STATE_BLOW::BLOW_039, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_025(DnSkillHandle hSkill) //건강한 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_124, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_026(DnSkillHandle hSkill) //행운의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_111, STATE_BLOW::BLOW_112, STATE_BLOW::BLOW_113, STATE_BLOW::BLOW_114, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_027(DnSkillHandle hSkill) //생명의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_016, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_028(DnSkillHandle hSkill) //마나의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_018, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_029(DnSkillHandle hSkill) //견고한 { // STATE_BLOW::emBLOW_INDEX blowList[] = // { // }; // // int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); // // AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_030(DnSkillHandle hSkill) //유연한 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_076, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_031(DnSkillHandle hSkill) //의지의 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_156, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_032(DnSkillHandle hSkill) //강인한 { // STATE_BLOW::emBLOW_INDEX blowList[] = // { // }; // // int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); // // AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_033(DnSkillHandle hSkill) //불굴의 { // STATE_BLOW::emBLOW_INDEX blowList[] = // { // }; // // int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); // // AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_034(DnSkillHandle hSkill) //불의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_036, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_035(DnSkillHandle hSkill) //물의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_037, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_036(DnSkillHandle hSkill) //빛의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_038, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } void CDnPrefixSkill::AddStateEffectInfo_Prefix_037(DnSkillHandle hSkill) //어둠의 방어구 { STATE_BLOW::emBLOW_INDEX blowList[] = { STATE_BLOW::BLOW_039, }; int nCount = sizeof(blowList) / sizeof(STATE_BLOW::emBLOW_INDEX); AddStateEffectInfo(blowList, nCount, hSkill); } #endif // PRE_ADD_PREFIX_SYSTE_RENEW void CDnSkill::OnAnotherGlobalSkillBeginCoolTime( DnSkillHandle hSkill ) { if( hSkill ) { m_fAnotherGlobalSkillCoolTime = hSkill->GetDelayTime(); m_nAnotherGlobakSkillID = hSkill->GetClassID(); } }