797 lines
No EOL
52 KiB
C++
797 lines
No EOL
52 KiB
C++
#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
|