DragonNest/GameCommon/DnChainAttackBlow.cpp
Cussrro 47f7895977 Revert "修复编码问题"
This reverts commit 9e69c01767.
2024-12-21 10:04:04 +08:00

520 lines
17 KiB
C++

#include "StdAfx.h"
#include "DnChainAttackBlow.h"
#include "DnProjectile.h"
#include "EtActionSignal.h"
#include <float.h>
#if _GAMESERVER
#include "DnGameServerManager.h"
#endif
#ifdef _CLIENT
#include "DnHideMonsterActor.h"
#include "DnPetActor.h"
#endif
#if !defined( USE_BOOST_MEMPOOL )
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
#endif // #if !defined( USE_BOOST_MEMPOOL )
CDnChainAttackBlow::CDnChainAttackBlow( DnActorHandle hActor, const char* szValue ) : CDnBlow( hActor ),
m_iMaxCount( 0 ),
m_fRange( 0.0f ),
m_fHitApplyPercent( -1.0f ),
m_iRootAttackerTeam( 0 )
{
#ifndef _GAMESERVER
ZeroMemory( &m_ProjectileSignalInfo, sizeof(m_ProjectileSignalInfo) );
#endif
m_StateBlow.emBlowIndex = STATE_BLOW::BLOW_060;
SetValue( szValue );
m_fValue = (float)atof(szValue);
string strValue( szValue );
int iSemiColonIndex = (int)strValue.find_first_of( ';', 0 );
bool bValidArgument = (string::npos != iSemiColonIndex);
_ASSERT( bValidArgument && "체인공격 상태효과 효과 수치가 잘못 되었습니다. 세미콜론을 찾을 수가 없음" );
if( bValidArgument )
{
string strRange = strValue.substr( 0, iSemiColonIndex );
int iOffset = iSemiColonIndex+1;
iSemiColonIndex = (int)strValue.find_first_of( ';', iOffset );
string strMaxCount = strValue.substr( iOffset, iSemiColonIndex-iOffset );
iOffset = iSemiColonIndex+1;
iSemiColonIndex = (int)strValue.find_first_of( ';', iOffset );
string strHitApplyPercent = strValue.substr( iOffset, iSemiColonIndex-iOffset );
// 인자로 받을 게 좀 많다. 범위;최대히트횟수;히트파라메터hitpercent
m_fRange = (float)atoi( strRange.c_str() );
m_iMaxCount = (int)atoi( strMaxCount.c_str() );
m_fHitApplyPercent = (float)atof( strHitApplyPercent.c_str() );
}
}
CDnChainAttackBlow::~CDnChainAttackBlow(void)
{
}
void CDnChainAttackBlow::OnBegin( LOCAL_TIME LocalTime, float fDelta )
{
OutputDebug( "[%x] CDnChainAttackBlow::OnBegin, Value: %2.2f\n", this, m_fValue );
}
void CDnChainAttackBlow::Process( LOCAL_TIME LocalTime, float fDelta )
{
CDnBlow::Process( LocalTime, fDelta );
//// 디버그를 위해 타임 갭을 둔다.
//m_fElapsedTime += fDelta;
//if( 1.0f > m_fElapsedTime )
// return;
// Note: 공격 받은 프로젝타일 정보로 그대로 아군에게 공격. 서버와 클라 독립적으로 실행한다.
// 렉이 생기면 실제 클라와 맞는 위치가 좀 다를 수는 있다.
// 공격할 때는 원래 공격했던 액터의 공격력을 감안해서 날려야 함.
// 정해진 거리 안에 아군이 오면 프로젝타일 날림. 본인은 상태효과 제거.
DNVector(DnActorHandle) vlActorsInRange;
#if _GAMESERVER
// 처음 나가는 체인 공격이라면 전체 카운트를 기록해 둔다.
if( -1 == m_ParentSkillInfo.iLeaveCount )
m_ParentSkillInfo.iLeaveCount = m_iMaxCount;
// 카운트 다 되면 더 이상 전파되지 않고 여기서 끝.
if( m_ParentSkillInfo.iLeaveCount <= 0 )
{
SetState( STATE_BLOW::STATE_END );
return;
}
CDnActor::ScanActor( m_hActor->GetRoom(), *m_hActor->GetPosition(), m_fRange, vlActorsInRange );
float fShortestDistanceSQ = FLT_MAX;
DnActorHandle hActorToAttack;
DWORD dwNumActors = (DWORD)vlActorsInRange.size();
for( DWORD dwActor = 0; dwActor < dwNumActors; ++dwActor )
{
DnActorHandle hActor = vlActorsInRange.at( dwActor );
if (!hActor)
continue;
if( false == (hActor->IsShow() && hActor->IsProcess()) )
continue;
if (hActor->IsDie() || hActor->IsNpcActor() )
continue;
// 직전에 나에게 상태효과 넘겨줬던 액터한테는 다시 주지 않는다.
if( m_ParentSkillInfo.hPrevAttacker != hActor )
{
// #30643 나 이외엔 다 적이다~~~
if( m_iRootAttackerTeam != hActor->GetTeam() &&
hActor != m_hActor )
{
EtVector3 vDistance = (*m_hActor->GetPosition()) - (*hActor->GetPosition());
float fLengthSQ = EtVec3LengthSq( &vDistance );
if( fLengthSQ < fShortestDistanceSQ )
{
fShortestDistanceSQ = fLengthSQ;
hActorToAttack = hActor;
}
}
}
}
// 01/23 m_pProjectileSignalInfo 가 NULL 인 경우가 있어서 덤프나서 우선 NULL 체크만 해둔다.
// 2010.3.19 게임 서버 덤프 의심 나는 부분 수정.
if( hActorToAttack && m_hRootAttacker )
{
ProjectileStruct* pProjectileSignalInfo = &m_ProjectileSignalInfo;
// 프로젝타일 발사 관련 기타 설정들.
DnSkillHandle hSkill = m_hRootAttacker->FindSkill( m_ParentSkillInfo.iSkillID );
// 호밍으로 타겟을 설정토록 정해준다.
pProjectileSignalInfo->nOrbitType = CDnProjectile::Homing;
pProjectileSignalInfo->nTargetType = CDnProjectile::Target;
pProjectileSignalInfo->VelocityType = CDnProjectile::Accell;
pProjectileSignalInfo->fSpeed = 1000.0f;
pProjectileSignalInfo->nValidTime = 5000;
//CDnProjectile* pProjectile = CDnProjectile::CreateProjectile( m_hActor->GetRoom(), hActorToAttack,
// *m_hActor->GetMatEx(), pProjectileSignalInfo.get() );
MatrixEx Cross = *m_hActor->GetMatEx();
Cross.m_vPosition.y += m_hActor->GetHeight() / 2.0f;
CDnProjectile *pProjectile = new CDnProjectile( GetRoom(), m_hRootAttacker );
pProjectile->SetPierce( pProjectileSignalInfo->bPierce == TRUE ? true : false );
pProjectile->SetMaxHitCount( pProjectileSignalInfo->nMaxHitCount );
if( pProjectileSignalInfo->nWeaponTableID > 0 )
{
if( pProjectileSignalInfo->nProjectileIndex != -1 )
{
DnWeaponHandle hWeapon = CDnWeapon::GetSmartPtr( (CMultiRoom*)g_pGameServerManager->GetRootRoom(), pProjectileSignalInfo->nProjectileIndex );
if( hWeapon ) *(CDnWeapon*)pProjectile = *hWeapon.GetPointer();
}
}
int nLength = pProjectile->GetWeaponLength();
if( pProjectileSignalInfo->bIncludeMainWeaponLength )
{
DnWeaponHandle hWeapon = m_hActor->GetWeapon(0);
if ( hWeapon )
nLength += hWeapon->GetWeaponLength();
}
pProjectile->SetWeaponLength( nLength );
pProjectile->SetWeaponType( (CDnWeapon::WeaponTypeEnum)( pProjectile->GetWeaponType() | CDnWeapon::Projectile ) );
pProjectile->SetSpeed( pProjectileSignalInfo->fSpeed );
pProjectile->SetTargetPosition( *hActorToAttack->GetPosition() );
pProjectile->SetTargetActor( hActorToAttack );
pProjectile->Initialize( Cross, static_cast<CDnProjectile::OrbitTypeEnum>(pProjectileSignalInfo->nOrbitType),
static_cast<CDnProjectile::DestroyOrbitTypeEnum>(pProjectileSignalInfo->nDestroyOrbitType),
static_cast<CDnProjectile::TargetTypeEnum>(pProjectileSignalInfo->nTargetType) );
pProjectile->SetValidTime( pProjectileSignalInfo->nValidTime );
pProjectile->SetVelocityType( static_cast<CDnProjectile::VelocityTypeEnum>(pProjectileSignalInfo->VelocityType) );
pProjectile->SetParentSkill( hSkill );
m_ParentSkillInfo.hPrevAttacker = m_hActor;
m_ParentSkillInfo.iLeaveCount -= 1; // 설정되어있는 카운트 하나씩 줄인다.
pProjectile->SetParentSkillInfo( m_ParentSkillInfo );
pProjectile->FromSkill( true );
pProjectile->SetHitApplyPercent( m_fHitApplyPercent );
int iNumSE = hSkill->GetStateEffectCount();
for( int i = 0; i < iNumSE; ++i )
{
const CDnSkill::StateEffectStruct* pStateEffect = hSkill->GetStateEffectFromIndex( i );
if( pStateEffect->ApplyType == CDnSkill::ApplySelf )
continue;
CDnSkill::StateEffectStruct SE = *pStateEffect;
// 체인 공격 상태효과는 남은 시간 적절히 빼서 프로젝타일에 상태효과 실어서 보냄.
if( STATE_BLOW::BLOW_060 == pStateEffect->nID )
SE.nDurationTime = int(GetDurationTime() * 1000);
pProjectile->AddStateEffect( SE );
}
// 프로젝타일에 원래 쏜 녀석의 공격력 정보를 적절히 담아서 셋팅.
boost::shared_ptr<CDnState> pActorStateSnapshotToPass = boost::shared_ptr<CDnState>(new CDnState);
*pActorStateSnapshotToPass = m_RootAttackerState;
pProjectile->SetShooterStateSnapshot( pActorStateSnapshotToPass );
pProjectile->PostInitialize();
#if defined(PRE_FIX_65287)
float fFinalDamageRate = 0.0f;
if (m_hRootAttacker && m_hRootAttacker->IsAppliedThisStateBlow(STATE_BLOW::BLOW_050))
{
DNVector(DnBlowHandle) vlhBlows;
m_hRootAttacker->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_050, vlhBlows );
int iNumBlow = (int)vlhBlows.size();
for( int i = 0; i < iNumBlow; ++i )
{
fFinalDamageRate += vlhBlows[i]->GetFloatValue();
}
}
pProjectile->SetShooterFinalDamageRate(fFinalDamageRate);
#endif // PRE_FIX_65287
// 가까운 아군에게 프로젝타일을 날린 후 상태효과 종료.
SetState( STATE_BLOW::STATE_END );
#else
CDnActor::ScanActor( *m_hActor->GetPosition(), m_fRange, vlActorsInRange );
float fShortestDistanceSQ = FLT_MAX;
DnActorHandle hActorToAttack;
DWORD dwNumActors = (DWORD)vlActorsInRange.size();
for( DWORD dwActor = 0; dwActor < dwNumActors; ++dwActor )
{
DnActorHandle hActor = vlActorsInRange.at( dwActor );
if (!hActor )
continue;
if (hActor->IsDie() || hActor->IsNpcActor() )
continue;
// #32115 특정 목적으로 안보이는 몬스터로 생성한 액터는 패스.
if( hActor->IsMonsterActor() )
{
if( NULL != dynamic_cast<CDnHideMonsterActor*>(hActor.GetPointer()) )
continue;
}
//서버에서는 펫 생성 안됨..
if ( NULL != dynamic_cast<CDnPetActor*>(hActor.GetPointer()))
continue;
// 직전에 나에게 상태효과 넘겨줬던 액터한테는 다시 주지 않는다.
if( m_hPrevAttacker != hActor )
{
//스킬 시전자와 팀이 다르면 전이 되도록한다.
if( m_ParentSkillInfo.hSkillUser && hActor->GetTeam() != m_ParentSkillInfo.hSkillUser->GetTeam() &&
hActor != m_hActor )
{
EtVector3 vDistance = (*m_hActor->GetPosition()) - (*hActor->GetPosition());
float fLengthSQ = EtVec3LengthSq( &vDistance );
if( fLengthSQ < fShortestDistanceSQ )
{
fShortestDistanceSQ = fLengthSQ;
hActorToAttack = hActor;
}
}
}
}
if( hActorToAttack && m_hRootAttacker )
{
// 프로젝타일 발사 관련 기타 설정들.
DnSkillHandle hSkill = m_hRootAttacker->FindSkill( m_ParentSkillInfo.iSkillID );
// 호밍으로 타겟을 설정토록 정해준다.
m_ProjectileSignalInfo.nOrbitType = CDnProjectile::Homing;
m_ProjectileSignalInfo.nTargetType = CDnProjectile::Target;
m_ProjectileSignalInfo.VelocityType = CDnProjectile::Accell;
m_ProjectileSignalInfo.fSpeed = 20000.0f;
m_ProjectileSignalInfo.nValidTime = 5000;
CDnProjectile* pProjectile = CDnProjectile::CreateProjectile( m_hRootAttacker,
*m_hActor->GetMatEx(), &m_ProjectileSignalInfo,
EtVector3( 0.0f, 0.0f, 0.0f), hActorToAttack, NULL, false );
if( pProjectile )
{
pProjectile->SetTargetActor( hActorToAttack );
pProjectile->SetChainShooter( m_hActor );
}
// 가까운 아군에게 프로젝타일을 날린 후 상태효과 종료.
SetState( STATE_BLOW::STATE_END );
#endif
}
}
void CDnChainAttackBlow::OnEnd( LOCAL_TIME LocalTime, float fDelta )
{
#if _GAMESERVER
OutputDebug( "[%x] CDnChainAttackBlow::OnEnd, LeaveCount: %d\n", this, m_ParentSkillInfo.iLeaveCount );
#else
OutputDebug( "[%x] CDnChainAttackBlow::OnEnd", this );
#endif
}
// 체인 상태효과는 중복 무시함.
void CDnChainAttackBlow::OnDuplicate( const STATE_BLOW& StateBlowInfo )
{
}
#if !defined(SW_ADD_CHAINATTACK_STATEEFFECT_20091029_jhk8211 )
#if _GAMESERVER
void CDnChainAttackBlow::WriteAdditionalPacket( void )
{
//if( m_bWroteAdditionalPacket )
//{
_ASSERT( m_ParentSkillInfo.hSkillUser );
m_hRootAttacker = m_ParentSkillInfo.hSkillUser;
m_iRootAttackerTeam = m_hRootAttacker->GetTeam();
// 가장 처음 공격한 공격자 액터의 unique id
DWORD dwRootAttackerActorUniqueID = m_hRootAttacker->GetUniqueID();
m_pPacketStream->Write( &dwRootAttackerActorUniqueID, sizeof(DWORD) );
DWORD dwPrevAttackerActorUniqueID = m_ParentSkillInfo.hPrevAttacker ? m_ParentSkillInfo.hPrevAttacker->GetUniqueID() : UINT_MAX;
m_pPacketStream->Write( &dwPrevAttackerActorUniqueID, sizeof(DWORD) );
// 프로젝타일 시그널을 클라이언트에서 찾는데 필요한 고유 정보들.
//m_pPacketStream->Write( &m_iShootActionIndex, sizeof(int) );
//m_pPacketStream->Write( &m_iProjectileSignalArrayIndex, sizeof(int) );
m_pPacketStream->Write( &m_ParentSkillInfo.iProjectileShootActionIndex, sizeof(int) );
m_pPacketStream->Write( &m_ParentSkillInfo.iProjectileSignalArrayIndex, sizeof(int) );
// m_bWroteAdditionalPacket = true;
//}
}
#else
void CDnChainAttackBlow::ReadyForChainAttack( DWORD dwRootAttackerActorID, DWORD dwPrevAttackerActorID, int iActionIndex, int iProjectileSignalArrayIndex )
{
// 액터를 찾고 액션을 찾고 프로젝타일 인덱스를 찾아서 프로젝타일 시그널 정보 셋팅.
m_hRootAttacker = CDnActor::FindActorFromUniqueID( dwRootAttackerActorID );
m_hPrevAttacker = CDnActor::FindActorFromUniqueID( dwPrevAttackerActorID );
if( m_hRootAttacker )
{
bool bFound = false;
/*
CEtActionBase::ActionElementStruct* pActionElement = m_hRootAttacker->GetElement( iActionIndex );
DWORD dwNumSignalList = (DWORD)pActionElement->pVecSignalList.size();
for( DWORD dwSignal = 0; dwSignal < dwNumSignalList; ++dwSignal )
{
CEtActionSignal* pSignal = pActionElement->pVecSignalList.at( dwSignal );
if( pSignal->GetSignalListArrayIndex() == iProjectileSignalArrayIndex )
{
m_ProjectileSignalInfo = *(static_cast<ProjectileStruct*>(pSignal->GetData()));
bFound = true;
break;
}
}
*/
CEtActionSignal *pSignal = m_hRootAttacker->GetSignal( iActionIndex, iProjectileSignalArrayIndex );
if( pSignal && pSignal->GetSignalIndex() == STE_Projectile ) {
#ifdef PRE_FIX_MEMOPT_SIGNALH
CopyShallow_ProjectileStruct(m_ProjectileSignalInfo, static_cast<ProjectileStruct*>(pSignal->GetData()));
#else
m_ProjectileSignalInfo = *(static_cast<ProjectileStruct*>(pSignal->GetData()));
#endif
bFound = true;
}
_ASSERT( bFound && "CDnChainAttackBlow::SetProjectileSignal() -> 프로젝타일 시그널을 찾을 수 없음. 서버 클라 액션파일이 다를 수 있습니다!" );
if( false == bFound )
{
SetState( STATE_BLOW::STATE_END );
OutputDebug( "CDnChainAttackBlow::SetProjectileSignal Failed!! - Can't found Projectile Signal\n" );
}
}
else
{
// 처음 공격자 액터를 찾지 못했으므로 상태효과 제거.
SetState( STATE_BLOW::STATE_END );
OutputDebug( "CDnChainAttackBlow::SetProjectileSignal Failed!! - Can't found Root Attacker Actor Handle\n" );
}
}
void CDnChainAttackBlow::OnReceiveAddPacket( CPacketCompressStream& PacketStream )
{
// 체인 공격 상태효과라면 추가적으로 날아온 패킷을 처리한다.
DWORD dwRootAttackerActorUniqueID = UINT_MAX;
DWORD dwPrevAttackerActorUniqueID = UINT_MAX;
int iShootActionIndex = -1;
int iProjectileSignalArrayIndex = -1;
PacketStream.Read( &dwRootAttackerActorUniqueID, sizeof(DWORD) );
PacketStream.Read( &dwPrevAttackerActorUniqueID, sizeof(DWORD) );
PacketStream.Read( &iShootActionIndex, sizeof(int) );
PacketStream.Read( &iProjectileSignalArrayIndex, sizeof(int) );
ReadyForChainAttack( dwRootAttackerActorUniqueID, dwPrevAttackerActorUniqueID,
iShootActionIndex, iProjectileSignalArrayIndex );
}
#endif
#endif
#if defined(PRE_ADD_PREFIX_SYSTE_RENEW)
void CDnChainAttackBlow::AddStateEffectValue(const char* szOrigValue, const char* szAddValue, std::string& szNewValue)
{
char szBuff[128] = {0, };
//파싱에 필요한 변수 선언
std::vector<string> vlTokens[2];
string strArgument[2];
//필요한 변수
float fRange[2] = {0.0f, };
int iMaxCount[2] = {0, };
float fHitPercent[2] = {0.0f, };
//////////////////////////////////////////////////////////////////////////
//첫번째 문자열 파싱.
strArgument[0] = szOrigValue;
TokenizeA( strArgument[0], vlTokens[0], ";" );
if( vlTokens[0].size() == 3 ) {
fRange[0] = (float)atof( vlTokens[0][0].c_str() );
iMaxCount[0] = atoi( vlTokens[0][1].c_str() );
fHitPercent[0] = (float)atof( vlTokens[0][2].c_str() );
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//두번째 문자열 파싱.
strArgument[1] = szAddValue;
TokenizeA( strArgument[1], vlTokens[1], ";" );
if( vlTokens[1].size() == 3 ) {
fRange[1] = (float)atof( vlTokens[1][0].c_str() );
iMaxCount[1] = atoi( vlTokens[1][1].c_str() );
fHitPercent[1] = (float)atof( vlTokens[1][2].c_str() );
}
//////////////////////////////////////////////////////////////////////////
float fResultRange = max(fRange[0], fRange[1]);
int iResultMaxCount = iMaxCount[0] + iMaxCount[1];
float fResultHitPercent = fHitPercent[0] + fHitPercent[1];
sprintf_s(szBuff, "%f;%d;%f", fResultRange, iResultMaxCount, fResultHitPercent);
szNewValue = szBuff;
}
void CDnChainAttackBlow::RemoveStateEffectValue(const char* szOrigValue, const char* szAddValue, std::string& szNewValue)
{
char szBuff[128] = {0, };
//파싱에 필요한 변수 선언
std::vector<string> vlTokens[2];
string strArgument[2];
//필요한 변수
float fRange[2] = {0.0f, };
int iMaxCount[2] = {0, };
float fHitPercent[2] = {0.0f, };
//////////////////////////////////////////////////////////////////////////
//첫번째 문자열 파싱.
strArgument[0] = szOrigValue;
TokenizeA( strArgument[0], vlTokens[0], ";" );
if( vlTokens[0].size() == 3 ) {
fRange[0] = (float)atof( vlTokens[0][0].c_str() );
iMaxCount[0] = atoi( vlTokens[0][1].c_str() );
fHitPercent[0] = (float)atof( vlTokens[0][2].c_str() );
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//두번째 문자열 파싱.
strArgument[1] = szAddValue;
TokenizeA( strArgument[1], vlTokens[1], ";" );
if( vlTokens[1].size() == 3 ) {
fRange[1] = (float)atof( vlTokens[1][0].c_str() );
iMaxCount[1] = atoi( vlTokens[1][1].c_str() );
fHitPercent[1] = (float)atof( vlTokens[1][2].c_str() );
}
//////////////////////////////////////////////////////////////////////////
float fResultRange = min(fRange[0], fRange[1]);
int iResultMaxCount = iMaxCount[0] - iMaxCount[1];
float fResultHitPercent = fHitPercent[0] - fHitPercent[1];
sprintf_s(szBuff, "%f;%d;%f", fResultRange, iResultMaxCount, fResultHitPercent);
szNewValue = szBuff;
}
#endif // PRE_ADD_PREFIX_SYSTE_RENEW