DragonNest/GameCommon/DnFreezingBlow.cpp
2024-12-19 09:48:26 +08:00

797 lines
No EOL
52 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

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

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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<BYTE> 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<CDnLocalPlayerActor*>(m_hActor.GetPointer()) ? true : false;
#endif
AddCallBackType( SB_ONCALCDAMAGE );
if (m_pCantActionBlow)
m_pCantActionBlow->SetActionWhenCancelAttack( "Freezing" );
}
#ifdef _GAMESERVER
CDNGameRoom* pGameRoom = static_cast<CDNGameRoom*>(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<std::string> 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<CDnLocalPlayerActor*>(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<CDnLocalPlayerActor*>(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<DnEtcHandle, EtcObjectSignalStruct>::SmartPtrSignalImpStruct *pStruct =
(TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::SmartPtrSignalImpStruct *)m_hActor->TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::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<DnEtcHandle, EtcObjectSignalStruct>::IsExistSignalHandle( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone ) )
{
// 깨지는 액션 실행될 때는 링크 끊어줌
TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::SmartPtrSignalImpStruct *pStruct =
(TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::SmartPtrSignalImpStruct *)m_hActor->TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::GetStruct( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone );
if( pStruct )
pStruct->bLinkObject = false;
DnEtcHandle hEffectHandle = m_hActor->TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::GetSignalHandle( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone );
if( hEffectHandle )
{
if( hEffectHandle->IsExistAction( "Break" ) )
hEffectHandle->SetActionQueue( "Break" ); // Destroy 시그널 반드시 존재해야 객체 소멸됨
else
m_hActor->TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::RemoveSignalHandle( STATEBLOWEFFECT_ETCOFFSET + m_StateBlow.emBlowIndex, (m_nBlowID*100)+nBone );
}
else
m_hActor->TSmartPtrSignalImp<DnEtcHandle, EtcObjectSignalStruct>::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<CDNGameRoom*>(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<string> 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<string> 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<CDnMonsterActor*>(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