#include "StdAfx.h" #include "DnFreezingBlow.h" #include "DnCantMoveBlow.h" #include "DnCantActionBlow.h" #include "DnTableDB.h" #include "DnFrameStopBlow.h" #include "DnMonsterActor.h" #include "EtActionSignal.h" #ifdef _GAMESERVER #include "DnGameRoom.h" #include "DnGameDataManager.h" #include "DnUserSession.h" #include "DnStateBlow.h" #else #include "DnEtcObject.h" #include "DnLocalPlayerActor.h" #include "DnInterface.h" #include "navigationmesh.h" #include "InputWrapper.h" #endif #if !defined( USE_BOOST_MEMPOOL ) #ifdef _DEBUG #define new new(_NORMAL_BLOCK,__FILE__,__LINE__) #endif #endif // #if !defined( USE_BOOST_MEMPOOL ) const int ICE_DAMAGE_ADD_PROBABILITY = 30; const LOCAL_TIME SHAKE_TIME = 3000; CDnFreezingBlow::CDnFreezingBlow( DnActorHandle hActor, const char* szValue ) : CDnBlow( hActor ), m_pCantMoveBlow( new CDnCantMoveBlow(hActor, NULL ) ), m_pCantActionBlow( new CDnCantActionBlow(hActor, NULL) ), m_pFrameStopBlow( new CDnFrameStopBlow(hActor, NULL) ), m_iDurabilityCount( 1 ), m_dwOriSpeedFrame( 0 ), m_bNestMap( false ) ,m_bPlayerCharacter( false ) #ifndef _GAMESERVER ,m_ComboCalc( CDnComboCalculator::CIRCULAR_CHECK ) ,m_bShake( false ) ,m_ShakeStartTime( 0 ) #endif { m_StateBlow.emBlowIndex = STATE_BLOW::BLOW_041; SetValue( szValue ); string strValue( szValue ); int iSemiColonIndex = (int)strValue.find_first_of( ';', 0 ); bool bValidArgument = (string::npos != iSemiColonIndex); _ASSERT( bValidArgument && "결빙 상태효과 효과 수치가 잘못 되었습니다. 세미콜론을 찾을 수가 없음" ); if( bValidArgument ) { string strProb = strValue.substr( 0, iSemiColonIndex ); string strDurCount = strValue.substr( iSemiColonIndex+1, strValue.length() ); // 확률 m_fValue = (float)atof( strProb.c_str() ); m_iDurabilityCount = (int)atoi( strDurCount.c_str() ); #ifndef _GAMESERVER std::vector vlKeysToCheck; vlKeysToCheck.push_back( g_WrappingKeyData[IW_MOVELEFT] ); vlKeysToCheck.push_back( g_WrappingKeyData[IW_MOVERIGHT] ); vlKeysToCheck.push_back( g_WrappingKeyData[IW_MOVELEFT] ); vlKeysToCheck.push_back( g_WrappingKeyData[IW_MOVERIGHT] ); vlKeysToCheck.push_back( g_WrappingKeyData[IW_MOVELEFT] ); vlKeysToCheck.push_back( g_WrappingKeyData[IW_MOVERIGHT] ); m_ComboCalc.SetKeysToCheck( vlKeysToCheck ); vlKeysToCheck.clear(); vlKeysToCheck.push_back( IW_MOVELEFT ); vlKeysToCheck.push_back( IW_MOVELEFT ); vlKeysToCheck.push_back( IW_MOVELEFT ); vlKeysToCheck.push_back( IW_MOVERIGHT ); vlKeysToCheck.push_back( IW_MOVERIGHT ); vlKeysToCheck.push_back( IW_MOVERIGHT ); vlKeysToCheck.push_back( IW_MOVEBACK ); vlKeysToCheck.push_back( IW_MOVEBACK ); vlKeysToCheck.push_back( IW_MOVEBACK ); vlKeysToCheck.push_back( IW_MOVEFRONT ); vlKeysToCheck.push_back( IW_MOVEFRONT ); vlKeysToCheck.push_back( IW_MOVEFRONT ); m_ComboCalc.SetPadsToCheck( vlKeysToCheck, 3.0f ); UseTableDefinedGraphicEffect( false ); if (m_pCantMoveBlow) m_pCantMoveBlow->UseTableDefinedGraphicEffect( false ); m_bPlayerCharacter = dynamic_cast(m_hActor.GetPointer()) ? true : false; #endif AddCallBackType( SB_ONCALCDAMAGE ); if (m_pCantActionBlow) m_pCantActionBlow->SetActionWhenCancelAttack( "Freezing" ); } #ifdef _GAMESERVER CDNGameRoom* pGameRoom = static_cast(m_hActor->GetRoom()); if( false == pGameRoom->bIsPvPRoom() ) { UINT uiSessionID = 0; pGameRoom->GetLeaderSessionID( uiSessionID ); CDNUserSession *pUserSession = pGameRoom ? pGameRoom->GetUserSession(uiSessionID) : NULL; if( pUserSession ) { const TMapInfo* pMapData = g_pDataManager->GetMapInfo( pUserSession->GetMapIndex() ); if( pMapData ) #if defined(PRE_ADD_DRAGON_FELLOWSHIP) m_bNestMap = CDnBlow::CheckEffectIgnoreMapType(pMapData->MapType, pMapData->MapSubType); #else // #if defined(PRE_ADD_DRAGON_FELLOWSHIP) m_bNestMap = (GlobalEnum::MAP_DUNGEON == pMapData->MapType) && ((GlobalEnum::MAPSUB_NEST == pMapData->MapSubType) || (GlobalEnum::MAPSUB_NESTNORMAL == pMapData->MapSubType)); #endif // #if defined(PRE_ADD_DRAGON_FELLOWSHIP) } } #else #if defined(PRE_ADD_DRAGON_FELLOWSHIP) m_bNestMap = CDnBlow::CheckEffectIgnoreMapType(); #else // #if defined(PRE_ADD_DRAGON_FELLOWSHIP) CDnWorld* pWorld = CDnWorld::GetInstancePtr(); m_bNestMap = (CDnWorld::MapTypeEnum::MapTypeDungeon == pWorld->GetMapType()) && ((CDnWorld::MapSubTypeEnum::MapSubTypeNest == pWorld->GetMapSubType() || CDnWorld::MapSubTypeEnum::MapSubTypeNestNormal == pWorld->GetMapSubType())); #endif // #if defined(PRE_ADD_DRAGON_FELLOWSHIP) #endif CheckBossMonster(); // [2010/12/13 semozz] // 상태효과가 상태를 가지고 있는 경우 계속 유지를 위해서 if (m_pCantMoveBlow) m_pCantMoveBlow->SetPermanent(true); if (m_pCantActionBlow) m_pCantActionBlow->SetPermanent(true); if (m_pFrameStopBlow) m_pFrameStopBlow->SetPermanent(true); } CDnFreezingBlow::~CDnFreezingBlow(void) { SAFE_DELETE( m_pCantMoveBlow ); SAFE_DELETE( m_pCantActionBlow ); SAFE_DELETE( m_pFrameStopBlow ); } #ifdef _GAMESERVER bool CDnFreezingBlow::CanBegin( void ) { bool bResult = true; int iProb = int(m_fValue * 10000.0f); if( _rand(GetRoom())%10000 > iProb ) { // 호출한 쪽에서 CanBegin 호출하고 실패시 즉시 상태효과 삭제토록 변경. //SetState( STATE_BLOW::STATE_END ); OutputDebug( "CDnFreezingBlow::CanBegin - Freezing Fail\n" ); bResult = false; } return bResult; } #endif // 처음에 결빙이 걸릴 확률이 Value 로 셋팅되므로 확률에 맞지 않는다면 그대로 끝내버림 void CDnFreezingBlow::OnBegin( LOCAL_TIME LocalTime, float fDelta ) { // 결빙지속시간 = <효과지속시간> * (1 – 물속성내성*상태보정weight값) float fGlobalStateEffectWeight = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::StateRevision ); float fIceResistance = m_hActor->GetElementDefense(CDnState::Ice); #ifdef _GAMESERVER // 지속시간을 내성에 따라 줄여준다. m_StateBlow.fDurationTime = m_StateBlow.fDurationTime * (1.0f - fIceResistance*fGlobalStateEffectWeight); #endif // CmdModifyStateEffect 를 보내기때문에 클라이언트에서는 따로 처리할필요없다 두번 처리되어서 지속시간의 동기가 틀어지는 경우가 생기므로 게임서버로만 뺍니다. // 행동불가, 이동불가 if( m_pCantMoveBlow ) m_pCantMoveBlow->OnBegin( LocalTime, fDelta ); if( m_pCantActionBlow ) m_pCantActionBlow->OnBegin( LocalTime, fDelta ); // TODO: 이펙트 출력.. 클라에서만 해당됨 #ifndef _GAMESERVER m_vlBreakActions.clear(); _AttachGraphicEffect(); m_vlBreakActions.push_back( "Break" ); //char szTemp[16]; //for( int i=0; ; i++ ) //{ // sprintf_s( szTemp, "Break_%c", 'a' + i ); // if( m_hEffect->IsExistAction( szTemp ) == false ) // break; // m_vlBreakActions.push_back( szTemp ); //} // 플레이어가 걸렸을 경우에 버튼 연타 UI 보여줌 if( m_bPlayerCharacter ) { GetInterface().ShowStickAniDialog( true ); } #else // 몬스터 액터라면 이펙트 테이블에 정의되어있는 액션이 클라로부터 패킷이 오지 않기 때문에 (CmdAction 은 클라에서 패킷 안보냄) // 여기서 따로 처리해준다. if( m_hActor->IsMonsterActor() ) { if( false == m_bIgnoreEffectAction ) { DNTableFileFormat* pStateEffectTable = CDnTableDB::GetInstancePtr()->GetTable( CDnTableDB::TSTATEEFFECT ); std::vector infoTokens; std::string delimiters = ";"; TokenizeA(m_ParentSkillInfo.szEffectOutputIDs, infoTokens, delimiters); int nTokenSize = (int)infoTokens.size(); for (int i = 0; i < nTokenSize; ++i) { int nEffectOutputID = atoi(infoTokens[i].c_str()); if (IsMatchStateEffectIndex(nEffectOutputID)) { string strActionName = pStateEffectTable->GetFieldFromLablePtr( nEffectOutputID, "_ActorActionName" )->GetString(); m_hActor->SetActionQueue( strActionName.c_str() ); } } } } // 클라이언트에게도 이 상태효과의 지속시간을 방금 업데이트 된 것으로 바꿔준다. // PVP 같은 보정테이블 값을 참조받는 경우 , 서버와 클라이언트의 동기가 틀어지는경우가 생긴다. m_hActor->CmdModifyStateEffect( GetBlowID(), m_StateBlow ); #endif //// 타격되면서 프레임 스피드가 변경될 때, 이 시점에서 아직 끝나지 않을 수 있다. //// 그렇게 되면 SetPlaySpeed 함수 호출이 효과가 없어지므로 리셋한번 해주고 한다. (#15065) //m_hActor->ResetPlaySpeed(); //m_hActor->SetPlaySpeed( DWORD(m_StateBlow.fDurationTime*1000.0f), 0.0f ); if( m_pFrameStopBlow ) m_pFrameStopBlow->OnBegin( LocalTime, fDelta ); _ForceLoopEnd( LocalTime, fDelta ); OutputDebug( "CDnFreezingBlow::OnBegin - BlowID:%d, Ice Resistance: %2.2f, Result DurationTime: %2.2f\n", GetBlowID(), fIceResistance, GetDurationTime() ); } #ifndef _GAMESERVER void CDnFreezingBlow::_ProcessShake( LOCAL_TIME LocalTime, float fDelta ) { LOCAL_TIME TimeGap = LocalTime - m_ShakeStartTime; if( TimeGap > SHAKE_TIME ) { m_bShake = false; return; } float fShakeDelta = 0.0f; float fStartRatio = 0.2f; float fEndRatio = 0.2f; float fShakeValue = 3.0f; if( LocalTime < m_ShakeStartTime + ( TimeGap * fStartRatio ) ) { fShakeDelta = 1.f / ( SHAKE_TIME * fStartRatio ) * ( TimeGap ); fShakeDelta *= fShakeValue; } else if( LocalTime < m_ShakeStartTime + ( SHAKE_TIME * fEndRatio ) ) { fShakeDelta = fShakeValue; } else { fShakeDelta = 1.f / ( SHAKE_TIME * fEndRatio ) * ( ( m_ShakeStartTime + SHAKE_TIME ) - LocalTime ); fShakeDelta *= fShakeValue; } if( fShakeDelta > 0.1f ) { //MatrixEx *pCross = m_hActor->GetMatEx(); MatrixEx CrossMove = *m_hActor->GetMatEx(); EtVector3 vPrevPos = CrossMove.m_vPosition; CrossMove.m_vPosition.x += -fShakeDelta + ( ( _rand()%(int)(fShakeDelta*20) ) / 10.f ); CrossMove.m_vPosition.y += -fShakeDelta + ( ( _rand()%(int)(fShakeDelta*20) ) / 10.f ); CrossMove.m_vPosition.z += -fShakeDelta + ( ( _rand()%(int)(fShakeDelta*20) ) / 10.f ); NavigationMesh *pNavMesh = INSTANCE(CDnWorld).GetNavMesh( CrossMove.m_vPosition ); if( !pNavMesh ) { if( CDnWorld::GetInstance().GetAttribute( CrossMove.m_vPosition ) != 0 ) { CrossMove.m_vPosition = vPrevPos; } CrossMove.m_vPosition.y = CDnWorld::GetInstance().GetHeight( CrossMove.m_vPosition ); } else { NavigationCell* pCurCell = NULL; if( pCurCell == NULL ) { pCurCell = pNavMesh->FindClosestCell( CrossMove.m_vPosition ); if( ( pCurCell == NULL ) || ( !pCurCell->IsPointInCellCollumn( CrossMove.m_vPosition ) ) ) { pCurCell = NULL; //EtVector3 vPrevPos = Cross.m_vPosition; if( CDnWorld::GetInstance().GetAttribute( CrossMove.m_vPosition ) != 0 ) { CrossMove.m_vPosition = vPrevPos; } CrossMove.m_vPosition.y = CDnWorld::GetInstance().GetHeight( CrossMove.m_vPosition ); } } if( pCurCell ) { //EtVector3 vPrevPos = Cross.m_vPosition; //Cross.MoveLocalZAxis( fValue ); int nSide = -1; NavigationCell *pLastCell = NULL; pCurCell->FindLastCollision( vPrevPos, CrossMove.m_vPosition, &pLastCell, nSide ); if( nSide != -1 ) { if( pLastCell->Link( nSide ) == NULL ) { EtVector2 vMoveDir2D( CrossMove.m_vPosition.x - vPrevPos.x, CrossMove.m_vPosition.z - vPrevPos.z ); float fMoveLength = EtVec2Length( &vMoveDir2D ); vMoveDir2D /= fMoveLength; EtVector2 vWallDir2D = pLastCell->Side( nSide )->EndPointB() - pLastCell->Side( nSide )->EndPointA(); EtVec2Normalize( &vWallDir2D, &vWallDir2D ); fMoveLength *= EtVec2Dot( &vWallDir2D, &vMoveDir2D ); CrossMove.m_vPosition.x = vPrevPos.x + fMoveLength * vWallDir2D.x; CrossMove.m_vPosition.z = vPrevPos.z + fMoveLength * vWallDir2D.y; int nNewSide = -1; pCurCell->FindLastCollision( vPrevPos, CrossMove.m_vPosition, &pLastCell, nNewSide ); if( nNewSide != -1 ) { if( pLastCell->Link( nNewSide ) ) { NavigationCell *pNewCell; pNewCell = pLastCell->Link( nNewSide ); if( pNewCell->IsPointInCellCollumn( CrossMove.m_vPosition ) ) { pCurCell = pNewCell; } else { CrossMove.m_vPosition.x = vPrevPos.x; CrossMove.m_vPosition.z = vPrevPos.z; } } else if( !pCurCell->IsPointInCellCollumn( CrossMove.m_vPosition ) ) { CrossMove.m_vPosition.x = vPrevPos.x; CrossMove.m_vPosition.z = vPrevPos.z; } } } else { pCurCell = pLastCell->Link( nSide ); } } if( pCurCell->GetType() == NavigationCell::CT_PROP ) CrossMove.m_vPosition.y = pCurCell->GetPlane()->SolveForY( CrossMove.m_vPosition.x, CrossMove.m_vPosition.z ); else CrossMove.m_vPosition.y = CDnWorld::GetInstance().GetHeight( CrossMove.m_vPosition ); } } m_hActor->ProcessCollision( EtVector3(CrossMove.m_vPosition - vPrevPos) ); } } #endif // 결빙은 버튼 연타로 풀어낼 수 있다. void CDnFreezingBlow::Process( LOCAL_TIME LocalTime, float fDelta ) { CDnBlow::Process( LocalTime, fDelta ); // 셋 중 하나라도 NULL 이면 보스 필터링에 걸린 것이므로 실제로 효과 처리를 하지 않으므로 패스. if( NULL == m_pFrameStopBlow ) return; #ifndef _GAMESERVER if( CDnLocalPlayerActor::s_hLocalActor == m_hActor ) { // TODO: 좌우 입력 받을 때 마다 시간 줄여줌. 결과적으로 서버로도 보내줘야 함... int iNumComboCount = 0; bool bComboSatisfy = m_ComboCalc.Process( LocalTime, fDelta, &iNumComboCount ); for( int i = 0; i < iNumComboCount; ++i ) m_StateBlow.fDurationTime -= 1.0f; if( bComboSatisfy ) { m_bShake = true; m_ShakeStartTime = LocalTime; //// 캐릭터를 살짝 흔들어줌.. //if( m_bPlayerCharacter && m_hActor ) //{ // m_hActor->SetPosition( ); //} OutputDebug( "[위기탈출] %d 회 콤보 성공, 시간 %2.2f 로 줄어듬 \n", iNumComboCount, m_StateBlow.fDurationTime ); } } if( m_bShake ) _ProcessShake( LocalTime, fDelta ); // 자기 자신의 플레이어 캐릭터만 위기 탈출로 시간을 줄일 수 있으므로 체크해서 서버로 상태효과 끝낸다고 보냄. if( CDnLocalPlayerActor::s_hLocalActor == m_hActor ) { if( m_StateBlow.fDurationTime < 0.2f ) { CDnLocalPlayerActor* pLocalPlayerActor = static_cast(m_hActor.GetPointer()); if (pLocalPlayerActor) pLocalPlayerActor->CmdRemoveStateEffectByServerBlowID( GetServerBlowID() ); } } #endif if( m_pCantMoveBlow ) m_pCantMoveBlow->Process( LocalTime, fDelta ); if( m_pCantActionBlow ) m_pCantActionBlow->Process( LocalTime, fDelta ); } void CDnFreezingBlow::OnEnd( LOCAL_TIME LocalTime, float fDelta ) { if( m_pCantMoveBlow ) m_pCantMoveBlow->OnEnd( LocalTime, fDelta ); if( m_pCantActionBlow ) m_pCantActionBlow->OnEnd( LocalTime, fDelta ); if( m_pFrameStopBlow ) m_pFrameStopBlow->OnEnd( LocalTime, fDelta ); ////////////////////////////////////////////////////////////////////////// // #36294 // 아이스 스피어 스킬 사용시 상태효과 중복 처리때문에 OnBegin되기 전에 // OnEnd로 들어 오는 경우가 있음. 이때 OnBegin되기 전에 m_bIgnoreEffectAction가 false // 상태여서 중복처리 될때마다 OnEnd로 들어와서 SetActionQueue("Stand")호출됨. // CheckBossMonster()로 함수 빼내서 생성자에서 호출되도록 수정해서 // m_bIgnoreEffectAction 값 정상적으로 설정 되도록 수정함. ////////////////////////////////////////////////////////////////////////// // 다운 상태가 아니라면 풀어준다. if( false == m_bIgnoreEffectAction ) { // 이펙트에 지정된 액션을 계속 실행하고 있을 경우에 Stand 로 풀어주도록 한다. #ifdef _GAMESERVER if( false == m_hActor->IsDown() && m_hActor->IsProcessSkill() == false ) { m_hActor->SetActionQueue( "Stand" ); } #else // #37065 이펙트 정보에 액션 이름이 없거나 (퍼니셔 브라스의 묶기) // 이펙트 정보에 정의된 액션을 취하고 있는 경우 결빙 종료시에 Stand 액션 실행. if( NULL == m_pEffectOutputInfo || m_pEffectOutputInfo->strActorActionName.empty() || m_pEffectOutputInfo->strActorActionName == m_hActor->GetCurrentAction() ) { m_hActor->SetActionQueue( "Stand" ); } #endif } #ifndef _GAMESERVER // 플레이어가 걸렸을 경우에 버튼 연타 UI 보여줌 if( dynamic_cast(m_hActor.GetPointer()) ) { GetInterface().ShowStickAniDialog( false ); } if( m_pEffectOutputInfo ) { // #30527 결빙 이펙트도 각 본에 붙게 되었으므로 본에 붙어있는 이펙트들을 모두 찾아서 break 액션을 시켜준다. if( EffectOutputInfo::ATTACH == m_pEffectOutputInfo->iOutputType ) { if( !m_vlBreakActions.empty() && m_hEtcObjectEffect ) m_hEtcObjectEffect->SetActionQueue( m_vlBreakActions.at( _rand() % (int)m_vlBreakActions.size() ).c_str() ); // 깨지는 액션 실행될 때는 링크 끊어줌 TSmartPtrSignalImp::SmartPtrSignalImpStruct *pStruct = (TSmartPtrSignalImp::SmartPtrSignalImpStruct *)m_hActor->TSmartPtrSignalImp::GetStruct( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, GetBlowID() ); if( pStruct ) pStruct->bLinkObject = false; } else if( EffectOutputInfo::DUMMY_BONE_ATTACH == m_pEffectOutputInfo->iOutputType ) { int nNumBone = (int)m_pEffectOutputInfo->vlDummyBoneIndices.size(); for( int nBone = 0; nBone < nNumBone; ++nBone ) { if( m_hActor->TSmartPtrSignalImp::IsExistSignalHandle( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone ) ) { // 깨지는 액션 실행될 때는 링크 끊어줌 TSmartPtrSignalImp::SmartPtrSignalImpStruct *pStruct = (TSmartPtrSignalImp::SmartPtrSignalImpStruct *)m_hActor->TSmartPtrSignalImp::GetStruct( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone ); if( pStruct ) pStruct->bLinkObject = false; DnEtcHandle hEffectHandle = m_hActor->TSmartPtrSignalImp::GetSignalHandle( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone ); if( hEffectHandle ) { if( hEffectHandle->IsExistAction( "Break" ) ) hEffectHandle->SetActionQueue( "Break" ); // Destroy 시그널 반드시 존재해야 객체 소멸됨 else m_hActor->TSmartPtrSignalImp::RemoveSignalHandle( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone ); } else m_hActor->TSmartPtrSignalImp::RemoveSignalHandle( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone ); } } } } // 효과 삭제... 실제로 이펙트를 없애는 건 아니고, 이펙트 자체는 destroy 시그널로 알아서 죽는다. // 하지만 상태효과는 이 시점에서 끝이므로 이펙트를 추가할 때 판단 근거가 되는 스킬 정보를 여기서 지워줘야 한다. // 그렇지 않으면 스킬 정보 계속 남아있어서 이펙트가 추가가 안됨. m_hActor->DetachSEEffectSkillInfo( m_ParentSkillInfo ); _SetDiffuse( 1.0f, 1.0f, 1.0f, 1.0f ); //m_hActor->DetachSEEffect( m_ParentSkillInfo, m_StateBlow.emBlowIndex, GetBlowID(), m_pEffectOutputInfo ); #else #endif OutputDebug( "CDnFreezingBlow::OnEnd BlowID: %d\n", GetBlowID() ); } #ifdef _GAMESERVER float CDnFreezingBlow::OnCalcDamage( float fOriginalDamage, CDnDamageBase::SHitParam& HitParam ) { float fResult = 0.0f; --m_iDurabilityCount; #ifdef PRE_ADD_BUFF_STATE_LIMIT if( m_hActor && m_hActor->GetStateBlow() ) { float fDamageRatioBase = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::Freezing_AddDamageRatio ); float fDamageRatioMaxLimit = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::Freezing_AdddamageRatio_Max ); CDNGameRoom* pGameRoom = static_cast(m_hActor->GetRoom()); if( pGameRoom && pGameRoom->bIsPvPRoom() ) { fDamageRatioBase = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::Freezing_AddDamageRatio_PVP ); fDamageRatioMaxLimit = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::Freezing_AdddamageRatio_PVP_Max ); } DNVector(DnBlowHandle) vlhBlows; m_hActor->GetStateBlow()->GetStateBlowFromBlowIndex( STATE_BLOW::BLOW_041, vlhBlows ); if( vlhBlows.size() > 0 ) { float fDamageTotal = fDamageRatioBase * vlhBlows.size(); if( fDamageTotal > fDamageRatioMaxLimit ) fDamageRatioBase = fDamageRatioMaxLimit / vlhBlows.size(); fResult = fOriginalDamage * fDamageRatioBase; } } #else // 결빙상태에서는 데미지를 1.5배로 먹는다. fResult = fOriginalDamage / 2.0f; #endif // 결빙상태에서는 Freezing 이던 Stand 액션을 하고 있던 간에 맞았다고 hit 류 액션으로 바꾸지 않는다. // hit 류의 CanHit 가 false 이면 계속 맞지 않는 경우가 생긴다. #15675 HitParam.szActionName.clear(); // 막타라면 상태효과 종료. if( m_iDurabilityCount <= 0 ) { SetState( STATE_BLOW::STATE_END ); OutputDebug( "CDnFreezingBlow::OnCalcDamage - Freezing Broken by Attack!\n" ); } // 결빙 상태일때도 데미지를 먹는 것으로 수정. //else //{ // // 결빙 상태에서는 데미지를 먹지 않는다... 이건 무적 상태로 따로 처리해줘야 할라나. // fResult = -fOriginalDamage; //} return fResult; // 결빙에 걸린 대상은 공격 받을 때 30% 확률로 데미지의 100%의 아이스 속성 데미지를 추가로 받는다. // 추가 데미지는 해당 데미지를 최종 공격력으로 연산하여 아이스 내성에 따라 감소된다. // (아이스 내성 1마다 0.3%감소) 방어력에 의한 추가적인 데미지 감소는 없다. // 라고 기획서에 적여 있음. 그대로 구현. //float fResult = fOriginalDamage; //if( _rand(GetRoom()) % 100 < ICE_DAMAGE_ADD_PROBABILITY ) //{ // float fIceElementResist = m_hActor->GetElementDefense( CDnState::Ice ) * 0.003f; // fResult -= fIceElementResist; //} //return fResult; } #else float CDnFreezingBlow::OnCalcDamage( float fOriginalDamage, CDnDamageBase::SHitParam& HitParam ) { --m_iDurabilityCount; // 마지막 타격일 경우엔 데미지를 두 배로 먹는다... 클라에서는 아이스 볼트 맞았을 때도 // 이쪽으로 들어오게 됨.. // 클라에서는 처음에 상태효과 걸렸을 때도 데미지를 먹기 때문에 이쪽으로 호출됨.. // 따라서 2회 맞으면 부서진다고 했을 때 총 3회로 해주어야 결빙 걸리고 두 번 맞고 깨지게 된다. if( m_iDurabilityCount <= 0 ) { SetState( STATE_BLOW::STATE_END ); OutputDebug( "CDnFreezingBlow::OnCalcDamage - Freezing Broken by Attack!\n" ); } return 0.0f; } #endif void CDnFreezingBlow::OnDuplicate( const STATE_BLOW& StateBlowInfo ) { // 결빙 상태효과에서는 중복처리할 때 아무것도 하지 않는다. // 추후 정확한 요구사항이 오면 반영토록 한다. } #if defined(PRE_ADD_PREFIX_SYSTE_RENEW) void CDnFreezingBlow::AddStateEffectValue(const char* szOrigValue, const char* szAddValue, std::string& szNewValue) { char szBuff[128] = {0, }; //파싱에 필요한 변수 선언 std::vector vlTokens[2]; string strArgument[2]; //필요한 값 변수 float fValue[2] = { 0.0f, }; int nCountValue[2] = { 0, }; ////////////////////////////////////////////////////////////////////////// //첫번째 문자열 파싱. strArgument[0] = szOrigValue; TokenizeA( strArgument[0], vlTokens[0], ";" ); if( vlTokens[0].size() == 2 ) { fValue[0] = (float)atof( vlTokens[0][0].c_str() ); nCountValue[0] = atoi( vlTokens[0][1].c_str() ); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //두번째 문자열 파싱 strArgument[1] = szAddValue; TokenizeA( strArgument[1], vlTokens[1], ";" ); if( vlTokens[1].size() == 2 ) { fValue[1] = (float)atof( vlTokens[1][0].c_str() ); nCountValue[1] = atoi( vlTokens[1][1].c_str() ); } ////////////////////////////////////////////////////////////////////////// //두 값을 더한다. float fResultValue = fValue[0] + fValue[1]; int nResultCount = (nCountValue[0] + nCountValue[1]); sprintf_s(szBuff, "%f;%d", fResultValue, nResultCount); szNewValue = szBuff; } void CDnFreezingBlow::RemoveStateEffectValue(const char* szOrigValue, const char* szAddValue, std::string& szNewValue) { char szBuff[128] = {0, }; //파싱에 필요한 변수 선언 std::vector vlTokens[2]; string strArgument[2]; //필요한 값 변수 float fValue[2] = { 0.0f, }; int nCountValue[2] = { 0, }; ////////////////////////////////////////////////////////////////////////// //첫번째 문자열 파싱. strArgument[0] = szOrigValue; TokenizeA( strArgument[0], vlTokens[0], ";" ); if( vlTokens[0].size() == 2 ) { fValue[0] = (float)atof( vlTokens[0][0].c_str() ); nCountValue[0] = atoi( vlTokens[0][1].c_str() ); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //두번째 문자열 파싱 strArgument[1] = szAddValue; TokenizeA( strArgument[1], vlTokens[1], ";" ); if( vlTokens[1].size() == 2 ) { fValue[1] = (float)atof( vlTokens[1][0].c_str() ); nCountValue[1] = atoi( vlTokens[1][1].c_str() ); } ////////////////////////////////////////////////////////////////////////// //두 값을 더한다. float fResultValue = fValue[0] - fValue[1]; int nResultCount = (nCountValue[0] - nCountValue[1]); sprintf_s(szBuff, "%f;%d", fResultValue, nResultCount); szNewValue = szBuff; } #endif // PRE_ADD_PREFIX_SYSTE_RENEW void CDnFreezingBlow::CheckBossMonster() { // #27679 네임드, 보스, 8인 네스트보스에게는 효과는 적용되나 실제로 결빙의 부가효과가 적용되진 않는다. // #28385 네스트에서만 적용됨. if( m_hActor->IsMonsterActor() ) { if( m_bNestMap ) { CDnMonsterActor* pMonsterActor = static_cast(m_hActor.GetPointer()); if( (CDnMonsterState::Boss == pMonsterActor->GetGrade() || CDnMonsterState::BossHP4 == pMonsterActor->GetGrade() || CDnMonsterState::NestBoss == pMonsterActor->GetGrade() || CDnMonsterState::NestBoss8 == pMonsterActor->GetGrade()) ) { SAFE_DELETE( m_pCantMoveBlow ); SAFE_DELETE( m_pCantActionBlow ); SAFE_DELETE( m_pFrameStopBlow ); // 이래야 보스급에선 freezing 액션 실행 안됨. m_bIgnoreEffectAction = true; } } // #51048 상태효과가 있는 경우 네스트 네임드급 몬스터들과 동일한 취급을 받는다. // 독립적으로 상태효과가 박히는 것이므로 맵과 관계 없다. if( m_hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_235) ) { SAFE_DELETE( m_pCantMoveBlow ); SAFE_DELETE( m_pCantActionBlow ); SAFE_DELETE( m_pFrameStopBlow ); m_bIgnoreEffectAction = true; } } } void CDnFreezingBlow::_ForceLoopEnd( LOCAL_TIME LocalTime, float fDelta ) { #ifdef PRE_ADD_ACADEMIC if( CDnActor::Academic != m_hActor->GetActorType() ) return; CEtActionBase::ActionElementStruct* pActionElement = m_hActor->GetElement( m_hActor->GetCurrentAction() ); if( !pActionElement ) return; CEtActionSignal *pSignal = NULL; for( DWORD itr = 0; itr < pActionElement->pVecSignalList.size(); ++itr ) { pSignal = pActionElement->pVecSignalList[itr]; if( STE_FX_LoopEnd == pSignal->GetSignalIndex() || STE_Particle_LoopEnd == pSignal->GetSignalIndex() ) { m_hActor->OnSignal( (SignalTypeEnum)pSignal->GetSignalIndex(), pSignal->GetData(), LocalTime, 0, 0, pSignal->GetSignalListArrayIndex() ); } } #endif } #if defined(PRE_FIX_51048) void CDnFreezingBlow::RemoveDebufAction(LOCAL_TIME LocalTime, float fDelta) { if( m_pCantMoveBlow ) m_pCantMoveBlow->OnEnd( LocalTime, fDelta ); if( m_pCantActionBlow ) m_pCantActionBlow->OnEnd( LocalTime, fDelta ); if( m_pFrameStopBlow ) m_pFrameStopBlow->OnEnd( LocalTime, fDelta ); SAFE_DELETE( m_pCantMoveBlow ); SAFE_DELETE( m_pCantActionBlow ); SAFE_DELETE( m_pFrameStopBlow ); m_bIgnoreEffectAction = true; } #endif // PRE_FIX_51048