2564 lines
No EOL
162 KiB
C++
2564 lines
No EOL
162 KiB
C++
|
||
#include "StdAfx.h"
|
||
#include "DnMonsterActor.h"
|
||
#include "DnWorld.h"
|
||
#include "DnWeapon.h"
|
||
#include "MAAiBase.h"
|
||
#include "DnTableDB.h"
|
||
#include "DnDropItem.h"
|
||
#include "DnPartyTask.h"
|
||
#include "DnGameTask.h"
|
||
#include "TaskManager.h"
|
||
#include "DnItemTask.h"
|
||
#include "DnBlow.h"
|
||
#include "DNGameRoom.h"
|
||
#include "DNUserSession.h"
|
||
#include "DnWorldTrapProp.h"
|
||
#include "DnStateBlow.h"
|
||
#include "navigationcell.h"
|
||
#include "navigationmesh.h"
|
||
#include "navigationpath.h"
|
||
#include "DnPlayerActor.h"
|
||
#include "DNLogConnection.h"
|
||
#include "MAWalkMovementNav.h"
|
||
#include "DNMissionSystem.h"
|
||
#include "DnProjectile.h"
|
||
#include "MAAiScript.h"
|
||
#include "DNMonsterAggroSystem.h"
|
||
#include "GameSendPacket.h"
|
||
#include "MAScanner.h"
|
||
#include "MasterRewardSystem.h"
|
||
#include "DnBlockBlow.h"
|
||
#include "DnParryBlow.h"
|
||
#include "DnCannonMonsterActor.h"
|
||
#if defined(PRE_ADD_WEEKLYEVENT)
|
||
#include "DNGameDataManager.h"
|
||
#endif
|
||
|
||
int CDnMonsterActor::s_nPositionRevisionTime = 3000;
|
||
|
||
CDnMonsterActor::CDnMonsterActor( CMultiRoom *pRoom, int nClassID )
|
||
:CDnActor( pRoom, nClassID )
|
||
,m_nMonsterClassID(0)
|
||
,m_AIDifficult( Dungeon::Difficulty::Easy )
|
||
,m_pszCanBumpActionName(NULL)
|
||
{
|
||
CDnActionBase::Initialize( this );
|
||
m_nDestroyTime = 0;
|
||
m_bTimeMonster = false;
|
||
m_fScale = 1.f;
|
||
m_nMonsterWeightTableID = -1;
|
||
m_eElementType = ElementEnum_Amount;
|
||
m_bNoDamage = false;
|
||
m_LastSendMoveMsg = 0;
|
||
m_fRotateResistance = 1.f;
|
||
m_nPartyComboCount = 0;
|
||
m_nPartyComboDelay = 0;
|
||
m_bIsTriggerMonster = false;
|
||
m_iTriggerRandomSeed = 0;
|
||
m_hProp = CDnWorldProp::Identity();
|
||
m_uiForcePositionRevisionTick = 0;
|
||
m_uiPrevForcePositionRevisionTick = 0;
|
||
m_bEnableDropItem = true;
|
||
m_nBirthAreaHandle = -1;
|
||
m_dwSummonerActorID = 0;
|
||
m_bSuicideWhenSummonerDie = false;
|
||
m_bFollowSummonerStage = false;
|
||
m_iSummonGroupID = 0;
|
||
m_fLimitSummonerDistanceSQ = 0.0f;
|
||
m_bSummoned = false;
|
||
m_bReCreatedFollowStageMonster = false;
|
||
|
||
#ifdef PRE_ADD_MONSTER_CATCH
|
||
m_iCatchedActorActionIndex = 0;
|
||
#endif // #ifdef PRE_ADD_MONSTER_CATCH
|
||
|
||
#if defined( PRE_FIX_MOVEBACK )
|
||
m_bNearMoveBack = false;
|
||
m_bPrevMoveBack = false;
|
||
#endif
|
||
|
||
m_nAutoRecallRange = 0;
|
||
|
||
#if defined(PRE_FIX_51048)
|
||
m_EnablePassiveStateEffect = false;
|
||
#endif // PRE_FIX_51048
|
||
|
||
m_bChangeAxisOnFinishAction = false;
|
||
m_isPuppetSummonMonster = false;
|
||
#ifdef PRE_MOD_DARKLAIR_RECONNECT
|
||
m_nEventAreaUniqueID = -1;
|
||
#endif // PRE_MOD_DARKLAIR_RECONNECT
|
||
#ifdef PRE_ADD_TRANSFORM_MONSTER_ACTOR
|
||
m_nSwapActorID = -1;
|
||
#endif
|
||
}
|
||
|
||
CDnMonsterActor::~CDnMonsterActor()
|
||
{
|
||
//몬스터 오라 스킬 취소.
|
||
if( IsEnabledAuraSkill() )
|
||
OnSkillAura( m_hAuraSkill, false );
|
||
|
||
SAFE_DELETE_VEC( m_VecDropItemList );
|
||
}
|
||
|
||
bool CDnMonsterActor::Initialize()
|
||
{
|
||
CalcMonsterWeightIndex();
|
||
|
||
CDnMonsterState::Initialize( m_nClassID );
|
||
MAAiReceiver::Initialize( m_nMonsterClassID, GetAIFileName().c_str() );
|
||
CDnActor::Initialize();
|
||
|
||
if( m_fWeight == 0.f ) m_fRevisionWeight = 0.f;
|
||
else m_fRevisionWeight = m_fWeight + ( ( m_fScale - 1.f ) * CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::ScaleWeightValue ) );
|
||
|
||
GenerationDropItem();
|
||
|
||
if( m_hObject ) {
|
||
m_hObject->SetCollisionGroup( COLLISION_GROUP_DYNAMIC( 1 ) );
|
||
m_hObject->SetTargetCollisionGroup( COLLISION_GROUP_STATIC( 1 ) | COLLISION_GROUP_DYNAMIC( 2 ) );
|
||
}
|
||
|
||
DNTableFileFormat* pSox = GetDNTable( CDnTableDB::TMONSTER );
|
||
int nStrElementType = pSox->GetFieldFromLablePtr( m_nMonsterClassID, "_Element_Str_Type" )->GetInteger();
|
||
if( nStrElementType != -1 )
|
||
m_eElementType = (ElementEnum)nStrElementType;
|
||
|
||
_ASSERT( m_pAggroSystem == NULL );
|
||
m_pAggroSystem = new CDNMonsterAggroSystem( GetActorHandle() );
|
||
_ASSERT( m_pAggroSystem != NULL );
|
||
|
||
return true;
|
||
}
|
||
|
||
void CDnMonsterActor::ProcessLook( LOCAL_TIME LocalTime, float fDelta )
|
||
{
|
||
if( bIsAILook() && bIsTurnOnAILook() )
|
||
{
|
||
Look( *m_pAi->GetAILook() );
|
||
}
|
||
else
|
||
{
|
||
bool bNaviMode = IsNaviMode();
|
||
// bool bLockTarget = IsSignalRange( STE_LockTargetLook );
|
||
|
||
#if defined( PRE_MOD_LOCK_TARGET_LOOK )
|
||
if( false == bNaviMode && 0 != m_nLockLookEventArea && true == m_bLockLookTarget )
|
||
{
|
||
if( GetLookTarget() )
|
||
ResetLook();
|
||
if( NULL != GetGameRoom() && NULL != GetGameRoom()->GetWorld() )
|
||
{
|
||
std::vector<CEtWorldEventArea *> vecArea;
|
||
GetGameRoom()->GetWorld()->FindEventAreaFromCreateUniqueID( m_nLockLookEventArea, &vecArea );
|
||
|
||
if( false == vecArea.empty() )
|
||
{
|
||
EtVector3 vAreaPosition = vecArea[0]->GetOBB()->Center;
|
||
EtVector3 * vMonsterPosition = GetPosition();
|
||
|
||
EtVector2 vDir;
|
||
vDir.x = vAreaPosition.x - vMonsterPosition->x;
|
||
vDir.y = vAreaPosition.z - vMonsterPosition->z;
|
||
EtVec2Normalize( &vDir, &vDir );
|
||
|
||
CmdLook( vDir );
|
||
}
|
||
}
|
||
}
|
||
else
|
||
#endif // #if defined( PRE_MOD_LOCK_TARGET_LOOK )
|
||
if( !bNaviMode && m_bLockLookTarget && GetAggroTarget() )
|
||
{
|
||
if( !GetLookTarget() && GetLookTarget() != GetAggroTarget() )
|
||
LookTarget( GetAggroTarget() );
|
||
}
|
||
else
|
||
{
|
||
if( GetLookTarget() )
|
||
ResetLook();
|
||
}
|
||
}
|
||
if( !IsSignalRange( STE_RotateResistance ) ) m_fRotateResistance = 1.f;
|
||
}
|
||
|
||
void CDnMonsterActor::Process( LOCAL_TIME LocalTime, float fDelta )
|
||
{
|
||
m_bLockLookTarget = false;
|
||
m_pszCanBumpActionName = NULL;
|
||
m_uiPrevForcePositionRevisionTick = m_uiForcePositionRevisionTick;
|
||
m_uiForcePositionRevisionTick = 0;
|
||
|
||
#if defined( PRE_MOD_LOCK_TARGET_LOOK )
|
||
m_nLockLookEventArea = 0;
|
||
#endif // #if defined( PRE_MOD_LOCK_TARGET_LOOK )
|
||
|
||
CDnActor::Process( LocalTime, fDelta );
|
||
PROFILE_TIME_TEST_BLOCK_START( "CDnMonsterActor::Process" );
|
||
ProcessLook( LocalTime, fDelta );
|
||
SetMoveVectorX( m_Cross.m_vXAxis );
|
||
SetMoveVectorZ( m_Cross.m_vZAxis );
|
||
|
||
if ( m_bTimeMonster && !IsDie() )
|
||
{
|
||
m_nDestroyTime -= (LOCAL_TIME)(fDelta*1000.0f);
|
||
|
||
if( m_nDestroyTime <= 0 )
|
||
CmdSuicide( false, false );
|
||
}
|
||
|
||
// 소환자가 죽었을 때 같이 죽도록 되어있다면 체크해서 같이 죽는다.
|
||
if( m_bSuicideWhenSummonerDie )
|
||
{
|
||
// 소환자의 액터 객체가 사라졌거나 죽었을 때..
|
||
if( !m_hSummonerPlayerActor ||
|
||
m_hSummonerPlayerActor->IsDie() )
|
||
{
|
||
CmdSuicide( false, false );
|
||
}
|
||
}
|
||
|
||
// 일정 거리 이상 벌어지면 죽게 되어있다면 그렇게 처리한다.
|
||
if( 0.0f < m_fLimitSummonerDistanceSQ )
|
||
{
|
||
if( m_hSummonerPlayerActor && false == m_hSummonerPlayerActor->IsDie() )
|
||
{
|
||
EtVector3 vSummonerPos = *m_hSummonerPlayerActor->GetPosition();
|
||
float fNowDistanceSQ = EtVec3LengthSq( &(vSummonerPos - *GetPosition()) );
|
||
if( m_fLimitSummonerDistanceSQ < fNowDistanceSQ )
|
||
{
|
||
CmdSuicide( false, false );
|
||
}
|
||
}
|
||
}
|
||
|
||
ProcessPositionRevision( fDelta );
|
||
ProcessPartyCombo( LocalTime, fDelta );
|
||
|
||
#ifdef PRE_ADD_MONSTER_CATCH
|
||
ProcessCatchActor( LocalTime, fDelta );
|
||
#endif // #ifdef PRE_ADD_MONSTER_CATCH
|
||
|
||
Process_AutoRecallRange();
|
||
|
||
PROFILE_TIME_TEST_BLOCK_END();
|
||
}
|
||
|
||
void CDnMonsterActor::ProcessAI(LOCAL_TIME LocalTime, float fDelta)
|
||
{
|
||
if( MATransAction::GetGameRoom() )
|
||
{
|
||
MAAiReceiver::Process( LocalTime, fDelta );
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::OnDamage( CDnDamageBase *pHitter, SHitParam &HitParam, HitStruct *pHitStruct )
|
||
{
|
||
int nSeed = CRandom::Seed(GetRoom());
|
||
_srand( GetRoom(), nSeed );
|
||
INT64 nTemp = GetHP();
|
||
|
||
CDnActor::OnDamage( pHitter, HitParam, pHitStruct );
|
||
INT64 nDamage = nTemp - GetHP();
|
||
|
||
switch( pHitter->GetDamageObjectType() )
|
||
{
|
||
case DamageObjectTypeEnum::Actor:
|
||
{
|
||
// Aggro Process
|
||
//DnActorHandle hActor = dynamic_cast<CDnActor*>(pHitter)->GetMySmartPtr();
|
||
DnActorHandle hActor = pHitter->GetActorHandle();
|
||
|
||
if( m_pAggroSystem )
|
||
m_pAggroSystem->OnDamageAggro( hActor, HitParam, (int)nDamage );
|
||
|
||
ResetCustomAction();
|
||
// 화살같은경우 무기가 플에이되면서 발사되기 때문에 Hit 시 Idle 를 해준다.
|
||
if( GetWeapon() ) {
|
||
if( GetWeapon()->GetElementIndex( "Idle" ) != -1 )
|
||
GetWeapon()->SetActionQueue( "Idle" );
|
||
}
|
||
if( HitParam.HitType != CDnWeapon::Defense )
|
||
OnPartyCombo( hActor, pHitStruct->nPartyComboDelay );
|
||
}
|
||
break;
|
||
case DamageObjectTypeEnum::Prop:
|
||
break;
|
||
}
|
||
RequestDamage( pHitter, nSeed, nDamage );
|
||
}
|
||
|
||
void CDnMonsterActor::OnDie( DnActorHandle hHitter )
|
||
{
|
||
CDnActor::OnDie( hHitter );
|
||
CDnPartyTask *pTask = (CDnPartyTask *)CTaskManager::GetInstance(GetRoom()).GetTask( "PartyTask" );
|
||
if( !pTask ) return;
|
||
|
||
#ifdef PRE_FIX_REMOVE_AURA_ONDIE
|
||
if (IsEnabledAuraSkill())
|
||
OnSkillAura(m_hAuraSkill, false);
|
||
#endif
|
||
|
||
if( m_hSummonerPlayerActor )
|
||
{
|
||
_ASSERT( m_hSummonerPlayerActor->IsPlayerActor() );
|
||
if( m_hSummonerPlayerActor->IsPlayerActor() )
|
||
{
|
||
CDnPlayerActor* pPlayerActor = static_cast<CDnPlayerActor*>(m_hSummonerPlayerActor.GetPointer());
|
||
pPlayerActor->OnDieSummonedMonster( GetMySmartPtr() );
|
||
}
|
||
}
|
||
|
||
CDnPlayerActor* pMasterPlayerActor = NULL;
|
||
if (hHitter)
|
||
{
|
||
if (hHitter->IsPlayerActor())
|
||
pMasterPlayerActor = static_cast<CDnPlayerActor*>(hHitter.GetPointer());
|
||
else if (hHitter->IsMonsterActor())
|
||
{
|
||
CDnMonsterActor* pMonster = static_cast<CDnMonsterActor*>(hHitter.GetPointer());
|
||
if (pMonster && pMonster->IsCannonMonsterActor())
|
||
{
|
||
CDnCannonMonsterActor* pCannonMonster = static_cast<CDnCannonMonsterActor*>(hHitter.GetPointer());
|
||
if (pCannonMonster && pCannonMonster->GetMasterPlayerActor() && pCannonMonster->GetMasterPlayerActor()->IsPlayerActor())
|
||
pMasterPlayerActor = static_cast<CDnPlayerActor*>(pCannonMonster->GetMasterPlayerActor().GetPointer());
|
||
}
|
||
}
|
||
}
|
||
|
||
int nPartyCount = pTask->GetRoom()->GetUserCount() - pTask->GetRoom()->GetGMCount();
|
||
|
||
// 경험치 분배해주시고. 돈도 Add 해주시고..
|
||
// float fValue = ( ( 1.f + ( 0.9f * ( nPartyCount - 1 ) ) ) / nPartyCount );
|
||
float fExpPenalty;
|
||
float fExp;
|
||
int nValue;
|
||
float fCompleteExp;
|
||
float fItemExp = 0.f;
|
||
float fGuildRewardExp = 0.f;
|
||
|
||
float fDeadDurabilityRevision = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::MonsterDeadDurabilityRevision );
|
||
int nDeadDurability = (int)( ( GetDeadDurability() * ( 1.f + ( fDeadDurabilityRevision * ( nPartyCount - 1 ) ) ) ) / nPartyCount );
|
||
int nFinalDeadDurability = nDeadDurability;
|
||
for( int i=0; i<nPartyCount; i++ )
|
||
{
|
||
CDNUserSession *pSession = pTask->GetRoom()->GetUserData(i);
|
||
if( !pSession )
|
||
continue;
|
||
// 운영자난입은 경험치를 먹지 않게 해준다.
|
||
if( pSession->bIsGMTrace() )
|
||
continue;
|
||
|
||
DnActorHandle hActor = pSession->GetActorHandle();
|
||
if( !hActor ) continue;
|
||
|
||
pSession->GetEventSystem()->OnEvent( EventSystem::OnKillMonster2, 1, EventSystem::MonsterID, GetMonsterClassID() );
|
||
|
||
// 사망시 경험치
|
||
nValue = hActor->GetLevel() - GetLevel();
|
||
fExpPenalty = 1.f - ( ( pow( max( nValue - 1, 0.f ), CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::ExpPenaltyValue1 ) ) ) *
|
||
CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::ExpPenaltyValue2 ) );
|
||
fExpPenalty = max( fExpPenalty, CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::ExpPenaltyMin ) );
|
||
|
||
float fExpPartyBonus = CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::ExpPartyBonus );
|
||
fExp = GetDeadExperience() * ( 1.f + ( fExpPartyBonus * ( nPartyCount - 1 ) ) ) / nPartyCount * fExpPenalty;
|
||
float fEventBonusExp = pTask->GetRoom()->GetEventExpWhenMonsterDie(fExp, pSession->GetFriendBonus(), pSession->GetClassID(), pSession->GetUserJob());
|
||
#ifdef PRE_ADD_BEGINNERGUILD
|
||
fEventBonusExp += (pTask->GetRoom()->GetPartyStructData().bPartyBeginnerGuild == true && pSession->CheckBegginerGuild()) ? (float)((fExp * (float)((float)(BeginnerGuild::Common::PartyBonusRate)/100)) + 0.5f) : 0;
|
||
#endif //#ifdef PRE_ADD_BEGINNERGUILD
|
||
|
||
#if defined(PRE_ADD_TOTAL_LEVEL_SKILL)
|
||
float fIncExpRate = 0.0f;
|
||
if (hActor && hActor->IsAppliedThisStateBlow(STATE_BLOW::BLOW_255))
|
||
{
|
||
DNVector(DnBlowHandle) vlBlows;
|
||
hActor->GatherAppliedStateBlowByBlowIndex(STATE_BLOW::BLOW_255, vlBlows);
|
||
{
|
||
int nCount = (int)vlBlows.size();
|
||
for (int i = 0; i < nCount; ++i)
|
||
{
|
||
DnBlowHandle hBlow = vlBlows[i];
|
||
if (hBlow && hBlow->IsEnd() == false)
|
||
{
|
||
fIncExpRate += hBlow->GetFloatValue();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
float fAddBlowExp = fExp * fIncExpRate;
|
||
fEventBonusExp += fAddBlowExp;
|
||
#endif // PRE_ADD_TOTAL_LEVEL_SKILL
|
||
|
||
#if defined(PRE_ADD_WEEKLYEVENT)
|
||
if (CDnWorld::GetInstance(GetRoom()).GetMapSubType() != EWorldEnum::MapSubTypeNest){
|
||
int nThreadID = GetGameRoom()->GetServerID();
|
||
|
||
if (pTask->GetRoom()->GetPartyUpkeepCount() > 0){
|
||
float fEventValue = g_pDataManager->GetWeeklyEventValuef(WeeklyEvent::Player, pSession->GetClassID(), WeeklyEvent::Event_6, nThreadID);
|
||
if (fEventValue != 0)
|
||
fEventBonusExp += fEventValue;
|
||
}
|
||
|
||
if (pSession->GetFriendBonus()){
|
||
float fEventValue = g_pDataManager->GetWeeklyEventValuef(WeeklyEvent::Player, pSession->GetClassID(), WeeklyEvent::Event_7, nThreadID);
|
||
if (fEventValue != 0)
|
||
fEventBonusExp += fEventValue;
|
||
}
|
||
}
|
||
#endif // #if defined(PRE_ADD_WEEKLYEVENT)
|
||
|
||
// 완료시 경험치 처리후 사망 판정을 진행한다.
|
||
fCompleteExp = GetCompleteExperience() * ( 1.f + ( fExpPartyBonus * ( nPartyCount - 1 ) ) ) / nPartyCount * fExpPenalty;
|
||
|
||
CDnPlayerActor *pPlayer = static_cast<CDnPlayerActor *>(hActor.GetPointer());
|
||
|
||
if( pPlayer->GetAddExp() > 0.f )
|
||
fItemExp = GetDeadExperience()*pPlayer->GetAddExp();
|
||
float fGuildCompleteExp = 0.f;
|
||
int nGuildRewardValue = pSession->GetGuildRewardItemValue(GUILDREWARDEFFECT_TYPE_EXTRAEXP);
|
||
if( nGuildRewardValue > 0 )
|
||
{
|
||
fGuildRewardExp = (float)(GetDeadExperience() * (nGuildRewardValue * 0.01));
|
||
fGuildCompleteExp = (float)(fCompleteExp * (nGuildRewardValue * 0.01));
|
||
}
|
||
else
|
||
fGuildRewardExp = 0;
|
||
|
||
fCompleteExp += ( (fCompleteExp*pPlayer->GetAddExp()) + fGuildCompleteExp );
|
||
pPlayer->AddCompleteExperience((int)fCompleteExp);
|
||
|
||
if (hActor->IsDie())
|
||
continue;
|
||
|
||
MasterSystem::CRewardSystem* pMasterRewardSystem = pSession->GetGameRoom()->GetMasterRewardSystem();
|
||
if( pMasterRewardSystem )
|
||
{
|
||
float fBonusRate = pMasterRewardSystem->GetExpRewardRate( pSession );
|
||
if( fBonusRate > 0.f )
|
||
{
|
||
fEventBonusExp += (fExp*fBonusRate);
|
||
int iMasterAddExp = 0;
|
||
iMasterAddExp = pMasterRewardSystem->GetMasterSystemAddExp(pSession, fExp, false);
|
||
fEventBonusExp += iMasterAddExp;
|
||
#if defined( _WORK )
|
||
WCHAR wszBuf[MAX_PATH];
|
||
wsprintf( wszBuf, L"[사제시스템] 추가 경험치 %d, 스승:%d", static_cast<int>(fExp*fBonusRate), iMasterAddExp );
|
||
pSession->SendDebugChat( wszBuf );
|
||
#endif // #if defined( _WORK )
|
||
}
|
||
}
|
||
if( pSession->GetPeriodExpItemRate() > 0 )
|
||
{
|
||
fEventBonusExp += (fExp* (float)(pSession->GetPeriodExpItemRate()/100.0f));
|
||
#if defined( _WORK )
|
||
WCHAR wszBuf[MAX_PATH];
|
||
wsprintf( wszBuf, L"[경험치추가아이템] 추가 경험치 %d", static_cast<int>(fExp* (float)(pSession->GetPeriodExpItemRate()/100.0f)) );
|
||
pSession->SendDebugChat( wszBuf );
|
||
#endif // #if defined( _WORK )
|
||
}
|
||
float fPcBangExp = 0.0f;
|
||
if (pSession->IsPCBang() && pSession->GetPcBangBonusExp() > 0 && fExp > 0.f )
|
||
fPcBangExp = (float)((fExp * (float)((float)(pSession->GetPcBangBonusExp())/100)) + 0.5f);
|
||
|
||
float fPromotionBonusExp = 0.0f;
|
||
if( fExp > 0.f )
|
||
fPromotionBonusExp = (float)((fExp * (float)((float)(pSession->GetPromotionValue(PROMOTIONTYPE_MONSTERKILL))/100)) + 0.5f);
|
||
|
||
float fVIPExp = 0.0f;
|
||
#if defined(PRE_ADD_VIP)
|
||
if (pSession->IsVIP() && pSession->GetVIPBonusExp() > 0 && fExp > 0.f )
|
||
fVIPExp = (float)((fExp * (float)((float)(pSession->GetVIPBonusExp())/100)) + 0.5f);
|
||
#endif // #if defined(PRE_ADD_VIP)
|
||
|
||
TExpData ExpData;
|
||
ExpData.set( fExp, fEventBonusExp, fPcBangExp, fVIPExp, fPromotionBonusExp, fItemExp, fGuildRewardExp );
|
||
#if defined( PRE_USA_FATIGUE )
|
||
pPlayer->CmdAddExperience( ExpData, DBDNWorldDef::CharacterExpChangeCode::DungeonMonster, pPlayer->GetUserSession()->GetPartyID() );
|
||
#else
|
||
pPlayer->CmdAddExperience( ExpData, DBDNWorldDef::CharacterExpChangeCode::Dungeon, pPlayer->GetUserSession()->GetPartyID() );
|
||
#endif // #if defined( PRE_USA_FATIGUE )
|
||
|
||
// 만랩 보너스 경험치입니다. 반드시 위에 추가되면 여기도 더해서 해주세요.
|
||
pPlayer->UpdateMaxLevelGainExperience( (int)( fExp + fEventBonusExp + fPcBangExp ) );
|
||
|
||
int nGuildRewardItemValue = pSession->GetGuildRewardItemValue(GUILDREWARDEFFECT_TYPE_REDUCEDURABILITYRATIO);
|
||
if( nGuildRewardItemValue > 0 )
|
||
nFinalDeadDurability -= (int)(nFinalDeadDurability * nGuildRewardItemValue * 0.01);
|
||
// 파티원 내구도 감소
|
||
pPlayer->OnDecreaseEquipDurability( nFinalDeadDurability, false );
|
||
|
||
if (pMasterPlayerActor)
|
||
{
|
||
pSession->GetEventSystem()->OnEvent( EventSystem::OnKillMonster, 3,
|
||
EventSystem::MonsterID, GetMonsterClassID(),
|
||
EventSystem::MonsterGrade, GetGrade(),
|
||
EventSystem::MonsterRaceID, GetRaceID());
|
||
}
|
||
}
|
||
// pTask->GetRoom()->AddCompleteExperience( GetCompleteExperience() );
|
||
|
||
if( m_bEnableDropItem )
|
||
DropItems();
|
||
|
||
if( IsSlowByDie() ) {
|
||
CDnGameTask *pGameTask = (CDnGameTask *)CTaskManager::GetInstance(GetRoom()).GetTask( "GameTask" );
|
||
if( pGameTask ) pGameTask->RequestChangeGameSpeed( 0.2f, 3000 );
|
||
}
|
||
|
||
if (m_pAggroSystem)
|
||
{
|
||
int wholeAggro = m_pAggroSystem->GetAggroSum();
|
||
|
||
std::list<CDNAggroSystem::AggroStruct>& aggroList = m_pAggroSystem->GetAggroList();
|
||
std::list<CDNAggroSystem::AggroStruct>::iterator iter = aggroList.begin();
|
||
for( ; iter!=aggroList.end() ; ++iter)
|
||
{
|
||
CDNAggroSystem::AggroStruct& curAggro = *iter;
|
||
if (curAggro.hActor && curAggro.hActor->IsPlayerActor())
|
||
{
|
||
CDnPlayerActor* pActor = static_cast<CDnPlayerActor*>(curAggro.hActor.GetPointer());
|
||
pActor->UpdateAssistScore(curAggro.iAggro, wholeAggro);
|
||
}
|
||
}
|
||
}
|
||
if (GetGameRoom())
|
||
{
|
||
GetGameRoom()->OnDie(GetActorHandle(), hHitter);
|
||
}
|
||
|
||
#ifdef PRE_ADD_MONSTER_CATCH
|
||
if( false == m_vlCatchedActors.empty() )
|
||
{
|
||
ReleaseAllActor();
|
||
}
|
||
#endif // #ifdef PRE_ADD_MONSTER_CATCH
|
||
}
|
||
|
||
void CDnMonsterActor::ProcessDie( LOCAL_TIME LocalTime, float fDelta )
|
||
{
|
||
CDnActor::ProcessDie( LocalTime, fDelta );
|
||
}
|
||
|
||
void CDnMonsterActor::DropItems()
|
||
{
|
||
DNVector(CDnItem::RealDropItemStruct) vRealDropItemList;
|
||
vRealDropItemList.reserve( m_VecDropItemList.size() );
|
||
|
||
int iPercent = HackPenanty::Common::MaxRate - GetGameRoom()->GetHackPenalty();
|
||
for( DWORD i=0; i<m_VecDropItemList.size(); i++ )
|
||
{
|
||
CDnItem::DropItemStruct *pStruct = &m_VecDropItemList[i];
|
||
int nRotate = (int)( ( ( pStruct->nSeed % 360 ) / (float)m_VecDropItemList.size() ) * i );
|
||
|
||
if( _rand(GetRoom())%HackPenanty::Common::MaxRate < iPercent )
|
||
{
|
||
vRealDropItemList.push_back( CDnItem::RealDropItemStruct(*pStruct, nRotate) );
|
||
}
|
||
else
|
||
{
|
||
g_Log.Log(LogType::_DROPITEMPENALTY, L"[%d] RoomID=%d HackPenalty=%d ItemID=%d Miss!!!\n", g_Config.nManagedID, GetGameRoom()->GetRoomID(), GetGameRoom()->GetHackPenalty(), pStruct->nItemID );
|
||
}
|
||
}
|
||
|
||
#if defined( PRE_FATIGUE_DROPITEM_PENALTY )
|
||
if( CDnWorld::GetInstance(GetRoom()).GetMapSubType() == EWorldEnum::MapSubTypeNone)
|
||
{
|
||
int iFatigueDropPercent = GetGameRoom()->GetFatigueDropRate();
|
||
if( iFatigueDropPercent < 100 )
|
||
{
|
||
for( UINT i=0 ; i<vRealDropItemList.size() ; ++i )
|
||
{
|
||
if( _roomrand(GetRoom())%100 >= iFatigueDropPercent )
|
||
{
|
||
vRealDropItemList.erase( vRealDropItemList.begin()+i );
|
||
--i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif // #if defined( PRE_FATIGUE_DROPITEM_PENALTY )
|
||
|
||
EtVector3 vPos = *GetPosition();
|
||
|
||
for( DWORD i=0 ; i<vRealDropItemList.size() ; ++i )
|
||
{
|
||
CDnItem::RealDropItemStruct* pStruct = &vRealDropItemList[i];
|
||
#if defined(PRE_ADD_STAGE_CLEAR_ENCHANT_REWARD)
|
||
CDnDropItem::DropItem( GetRoom(), vPos, pStruct->dwUniqueID, pStruct->nItemID, pStruct->nSeed, pStruct->cOption, pStruct->nCount, pStruct->nRotate, -1, pStruct->nEnchantID );
|
||
#else // #if defined(PRE_ADD_STAGE_CLEAR_ENCHANT_REWARD)
|
||
CDnDropItem::DropItem( GetRoom(), vPos, pStruct->dwUniqueID, pStruct->nItemID, pStruct->nSeed, pStruct->cOption, pStruct->nCount, pStruct->nRotate );
|
||
#endif // #if defined(PRE_ADD_STAGE_CLEAR_ENCHANT_REWARD)
|
||
}
|
||
|
||
if( !vRealDropItemList.empty() )
|
||
{
|
||
for( DWORD i=0; i<GetGameRoom()->GetUserCount(); i++ )
|
||
{
|
||
CDNUserSession* pGameSession = GetGameRoom()->GetUserData(i);
|
||
if( pGameSession && pGameSession->GetState() == SESSION_STATE_GAME_PLAY )
|
||
SendGameDropItemList( pGameSession, vPos, vRealDropItemList );
|
||
}
|
||
}
|
||
|
||
SAFE_DELETE_VEC( m_VecDropItemList );
|
||
}
|
||
|
||
void CDnMonsterActor::OnDrop( float fCurVelocity )
|
||
{
|
||
if( IsAir() ) {
|
||
if( !IsHit() ) {
|
||
char szStr[64];
|
||
sprintf_s( szStr, "%s_Landing", GetCurrentAction() );
|
||
if( IsExistAction( szStr ) )
|
||
{
|
||
SetActionQueue( szStr, 0, 2.f, 0.f, true, false );
|
||
|
||
// 스킬 사용중일땐 체인액션 셋팅해준다.
|
||
if( m_hProcessSkill )
|
||
{
|
||
m_hProcessSkill->AddUseActionName( szStr );
|
||
m_hProcessSkill->OnChainInput( szStr );
|
||
}
|
||
}
|
||
|
||
SetMovable( false );
|
||
}
|
||
else if( !IsDie() ) {
|
||
std::string szAction;
|
||
float fBlendFrame = 2.f;
|
||
// 떨어지는 속도가 10이상이면 bigBounce로 한번 더 띄어준다.
|
||
if( fCurVelocity < -6.f && m_HitParam.vVelocity.y != 0.f && abs(m_HitParam.vVelocity.y) > 0.1f ) {
|
||
if( m_HitParam.vVelocity.y > 0.f ) {
|
||
m_HitParam.vVelocity.y *= 0.6f;
|
||
SetVelocityY( m_HitParam.vVelocity.y );
|
||
}
|
||
else { // 가속도가 처음부터 바닥으로 향해있는 경우에는 뒤집어줘야한다.
|
||
m_HitParam.vVelocity.y *= -0.6f;
|
||
if( m_HitParam.vResistance.y > 0.f )
|
||
m_HitParam.vResistance.y *= -1.f;
|
||
SetVelocityY( m_HitParam.vVelocity.y );
|
||
|
||
if( m_HitParam.vVelocity.y > 0 && m_HitParam.vResistance.y <= 0 )
|
||
SetResistanceY( -15.0f );
|
||
else
|
||
SetResistanceY( m_HitParam.vResistance.y );
|
||
|
||
}
|
||
szAction = "Hit_AirBounce";
|
||
}
|
||
else {
|
||
szAction = "Down_SmallBounce";
|
||
fBlendFrame = 0.f;
|
||
}
|
||
SetActionQueue( szAction.c_str(), 0, fBlendFrame, 0.f, true, false );
|
||
}
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::OnStop( EtVector3 &vPosition )
|
||
{
|
||
if( IsDie() ) return;
|
||
if( IsMove() ) CmdStop( "Stand" );
|
||
|
||
if( m_pAi ) m_pAi->OnStop(vPosition);
|
||
}
|
||
|
||
void CDnMonsterActor::OnBeginNaviMode()
|
||
{
|
||
if( IsDie() ) return;
|
||
std::string strPrevAction = m_hActor->GetCurrentAction();
|
||
|
||
bool bPrevIsMove = false;
|
||
if( IsMove() && !IsNaviMode() )
|
||
{
|
||
bPrevIsMove = true;
|
||
CmdStop( "Stand" );
|
||
}
|
||
|
||
if( m_pAi )
|
||
m_pAi->OnBeginNaviMode( strPrevAction.c_str(), bPrevIsMove );
|
||
}
|
||
|
||
void CDnMonsterActor::CmdMove( EtVector3 &vPos, const char *szActionName, int nLoopCount, float fBlendFrame )
|
||
{
|
||
if( !IsMovable() ) return;
|
||
|
||
MovePos( vPos, true );
|
||
if( SetActionQueue( szActionName, nLoopCount, fBlendFrame, 0.f, true ) == false ) return;
|
||
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
int nActionIndex = GetElementIndex( szActionName );
|
||
Stream.Write( &nActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &vPos, sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &EtVec3toVec2( *GetMoveVectorZ() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( &EtVec3toVec2( *GetLookDir() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( &nLoopCount, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
|
||
Send( eActor::SC_CMDMOVE, &Stream );
|
||
|
||
ResetPositionRevision();
|
||
}
|
||
|
||
void CDnMonsterActor::CmdMove( DnActorHandle hActor, float fMinDistance, const char *szActionName, int nLoopCount, float fBlendFrame )
|
||
{
|
||
if( !IsMovable() )
|
||
return;
|
||
|
||
bool bIsNaviMode = m_hActor->IsNaviMode();
|
||
MoveTarget( hActor, fMinDistance );
|
||
if( SetActionQueue( szActionName, nLoopCount, fBlendFrame, 0.f, !bIsNaviMode ) == false )
|
||
return;
|
||
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
int nActionIndex = GetElementIndex( szActionName );
|
||
DWORD dwUniqueID = hActor->GetUniqueID();
|
||
Stream.Write( &nActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( &dwUniqueID, sizeof(DWORD) );
|
||
Stream.Write( &fMinDistance, sizeof(float), CPacketCompressStream::FLOAT_SHORT, 1.f );
|
||
Stream.Write( &EtVec3toVec2( *GetMoveVectorZ() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( &EtVec3toVec2( *GetLookDir() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &nLoopCount, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
|
||
Send( eActor::SC_CMDMOVETARGET, &Stream );
|
||
|
||
ResetPositionRevision();
|
||
}
|
||
|
||
bool CDnMonsterActor::_bIsCheckVaildPosition( EtVector3& vTargetPos )
|
||
{
|
||
// 타겟 네비게이션 범위 검사
|
||
CEtWorldGrid* pGrid = INSTANCE(CDnWorld).GetGrid();
|
||
if( IsMovable() && pGrid )
|
||
{
|
||
NavigationMesh* pNavMesh = pGrid->GetNavMesh( vTargetPos );
|
||
if( pNavMesh )
|
||
{
|
||
NavigationCell* pCurCell = pNavMesh->FindClosestCell( vTargetPos );
|
||
if( pCurCell && pCurCell->IsPointInCellCollumn( vTargetPos ) )
|
||
return true;
|
||
}
|
||
}
|
||
|
||
if( !IsDie() ) CmdStop( "Stand" );
|
||
return false;
|
||
}
|
||
|
||
void CDnMonsterActor::_SendCmdMoveNavi( EtVector3& vTargetPos, float fMinDistance, const char* szActionName, int nLoopCount )
|
||
{
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
int nActionIndex = GetElementIndex( szActionName );
|
||
Stream.Write( &nActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( &vTargetPos, sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &fMinDistance, sizeof(float), CPacketCompressStream::FLOAT_SHORT, 1.f );
|
||
Stream.Write( &EtVec3toVec2( *GetMoveVectorZ() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( &EtVec3toVec2( *GetLookDir() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &nLoopCount, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
|
||
Send( eActor::SC_CMDMOVETARGET_NAVI, &Stream );
|
||
}
|
||
|
||
void CDnMonsterActor::CmdMoveNavi( DnActorHandle hActor, float fMinDistance, const char *szActionName, int nLoopCount, float fBlendFrame )
|
||
{
|
||
// 타겟이 네비게이션 밖에 있는지 검사
|
||
if( !_bIsCheckVaildPosition( *hActor->GetPosition() ) )
|
||
return;
|
||
//
|
||
bool bAlreadyNaviMode = m_hActor->IsNaviMode();
|
||
MoveTargetNavi( hActor, fMinDistance, szActionName );
|
||
if( !m_hActor->IsNaviMode() )
|
||
return CmdStop( "Stand" );
|
||
if( (bAlreadyNaviMode && GetWayPointSize() <= 2) )
|
||
return CmdStop( "Stand" );
|
||
if( SetActionQueue( szActionName, -1, fBlendFrame, 0.f, !bAlreadyNaviMode ) == false )
|
||
return;
|
||
// 패킷
|
||
_SendCmdMoveNavi( *hActor->GetPosition(), fMinDistance, szActionName, nLoopCount );
|
||
|
||
ResetPositionRevision();
|
||
}
|
||
|
||
void CDnMonsterActor::CmdMoveNavi( EtVector3& vTargetPos, float fMinDistance, const char *szActionName, int nLoopCount, float fBlendFrame )
|
||
{
|
||
// 타겟이 네비게이션 밖에 있는지 검사
|
||
if( !_bIsCheckVaildPosition( vTargetPos ) )
|
||
return;
|
||
//
|
||
bool bAlreadyNaviMode = m_hActor->IsNaviMode();
|
||
MoveTargetNavi( vTargetPos, fMinDistance, szActionName );
|
||
if( !m_hActor->IsNaviMode() )
|
||
return CmdStop( "Stand" );
|
||
if( SetActionQueue( szActionName, -1, fBlendFrame, 0.f, !bAlreadyNaviMode ) == false )
|
||
return;
|
||
// 패킷
|
||
_SendCmdMoveNavi( vTargetPos, fMinDistance, szActionName, nLoopCount );
|
||
|
||
ResetPositionRevision();
|
||
}
|
||
|
||
void CDnMonsterActor::CmdStop( const char *szActionName, int nLoopCount, float fBlendFrame, float fStartFrame )
|
||
{
|
||
if( SetActionQueue( szActionName, nLoopCount, fBlendFrame, fStartFrame, true ) == false ) return;
|
||
|
||
ResetMove();
|
||
ResetLook();
|
||
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
int nActionIndex = GetElementIndex( szActionName );
|
||
Stream.Write( &nActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( &nLoopCount, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
Stream.Write( GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
|
||
Send( eActor::SC_CMDSTOP, &Stream );
|
||
|
||
ResetPositionRevision();
|
||
}
|
||
|
||
void CDnMonsterActor::CmdAction( const char *szActionName, int nLoopCount /*= 0*/, float fBlendFrame /*= 3.f*/,
|
||
bool bCheckOverlapAction /*= true*/, bool bFromStateBlow /*= false */, bool bSkillChain/* = false*/ )
|
||
{
|
||
// mp 소모 스킬 사용 불가 상태효과가 몬스터에게는 attack state 있는 액션을 사용치 못하도록 처리됨. (#13032)
|
||
if( m_pStateBlow->IsApplied( STATE_BLOW::BLOW_078 ) )
|
||
{
|
||
if( IsAttack( szActionName ) )
|
||
return;
|
||
}
|
||
|
||
if( SetActionQueue( szActionName, nLoopCount, fBlendFrame, 0.f, bCheckOverlapAction ) == false )
|
||
return;
|
||
|
||
int nActionIndex = GetElementIndex( szActionName );
|
||
bool bAILook = bIsAILook();
|
||
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
Stream.Write( &nActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( &nLoopCount, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
Stream.Write( &fBlendFrame, sizeof(float), CPacketCompressStream::FLOAT_SHORT, 1.f );
|
||
Stream.Write( &bAILook, sizeof(bool) );
|
||
if( bAILook )
|
||
Stream.Write( m_pAi->GetAILook(), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
else
|
||
Stream.Write( &EtVec3toVec2( *GetLookDir() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &bFromStateBlow, sizeof(bool) );
|
||
|
||
#if defined( PRE_FIX_MOVEBACK )
|
||
bool bNearMoveBack = IsPrevMoveBack();
|
||
Stream.Write( &bNearMoveBack, sizeof(bool));
|
||
#endif
|
||
|
||
Send( eActor::SC_CMDACTION, &Stream );
|
||
|
||
ResetPositionRevision();
|
||
}
|
||
|
||
void CDnMonsterActor::CmdLook( EtVector2 &vVec, bool bForce )
|
||
{
|
||
Look( vVec, bForce );
|
||
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
Stream.Write( &vVec, sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( &bForce, sizeof(bool) );
|
||
|
||
Send( eActor::SC_CMDLOOK, &Stream );
|
||
}
|
||
|
||
void CDnMonsterActor::CmdLook( DnActorHandle hActor, bool bLock )
|
||
{
|
||
if( bLock ) LookTarget( hActor );
|
||
else {
|
||
if( !hActor ) ResetLook();
|
||
else {
|
||
EtVector2 vVec;
|
||
vVec.x = hActor->GetPosition()->x - GetPosition()->x;
|
||
vVec.y = hActor->GetPosition()->z - GetPosition()->z;
|
||
EtVec2Normalize( &vVec, &vVec );
|
||
Look( vVec );
|
||
}
|
||
}
|
||
|
||
BYTE pBuffer[128];
|
||
CMemoryStream Stream( pBuffer, 128 );
|
||
|
||
DWORD dwUniqueID = ( hActor ) ? hActor->GetUniqueID() : -1;
|
||
Stream.Write( &dwUniqueID, sizeof(DWORD) );
|
||
Stream.Write( &bLock, sizeof(bool) );
|
||
|
||
Send( eActor::SC_CMDLOOKTARGET, &Stream );
|
||
}
|
||
|
||
int CDnMonsterActor::CmdAddStateEffect( const CDnSkill::SkillInfo* pParentSkill, STATE_BLOW::emBLOW_INDEX emBlowIndex, int nDurationTime,
|
||
const char *szParam, bool bOnPlayerInit/* = false*/, bool bCheckCanBegin/* = true*/ , bool bEternity /* = false */ )
|
||
{
|
||
int iID = CDnActor::CmdAddStateEffect( pParentSkill, emBlowIndex, nDurationTime, szParam, bOnPlayerInit, bCheckCanBegin , bEternity );
|
||
if( -1 == iID )
|
||
return -1;
|
||
|
||
DnBlowHandle hAddedBlow = m_pStateBlow->GetStateBlowFromID( iID );
|
||
|
||
const CPacketCompressStream* pPacketStream = hAddedBlow->GetPacketStream( szParam, false );
|
||
Send( eActor::SC_CMDADDSTATEEFFECT, const_cast<CPacketCompressStream*>(pPacketStream) );
|
||
|
||
return iID;
|
||
}
|
||
|
||
void CDnMonsterActor::CmdRemoveStateEffect( STATE_BLOW::emBLOW_INDEX emBlowIndex, bool bRemoveFromServerToo/* = true*/ )
|
||
{
|
||
if( m_pStateBlow->IsApplied( emBlowIndex ) )
|
||
{
|
||
if( bRemoveFromServerToo )
|
||
CDnActor::CmdRemoveStateEffect( emBlowIndex );
|
||
|
||
SendRemoveStateEffect( emBlowIndex );
|
||
}
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::SendRemoveStateEffect( STATE_BLOW::emBLOW_INDEX emBlowIndex )
|
||
{
|
||
BYTE pBuffer[32];
|
||
CPacketCompressStream Stream( pBuffer, 32 );
|
||
Stream.Write( &emBlowIndex, sizeof(STATE_BLOW::emBLOW_INDEX) );
|
||
|
||
Send( eActor::SC_CMDREMOVESTATEEFFECT, &Stream );
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::CmdSuicide( bool bDropItem, bool bDropExp )
|
||
{
|
||
if( IsDie() ) return;
|
||
SetDestroy();
|
||
|
||
if( m_hSummonerPlayerActor )
|
||
{
|
||
_ASSERT( m_hSummonerPlayerActor->IsPlayerActor() );
|
||
if( m_hSummonerPlayerActor->IsPlayerActor() )
|
||
{
|
||
CDnPlayerActor* pPlayerActor = static_cast<CDnPlayerActor*>(m_hSummonerPlayerActor.GetPointer());
|
||
pPlayerActor->OnDieSummonedMonster( GetMySmartPtr() );
|
||
}
|
||
}
|
||
|
||
if( bDropExp ) {
|
||
EnableDropItem( bDropItem );
|
||
OnDie( DnActorHandle() );
|
||
}
|
||
else {
|
||
if( bDropItem ) DropItems();
|
||
}
|
||
|
||
BYTE pBuffer[32];
|
||
CPacketCompressStream Stream( pBuffer, 32 );
|
||
Stream.Write( &bDropItem, sizeof(bool) );
|
||
Stream.Write( &bDropExp, sizeof(bool) );
|
||
Send( eActor::SC_CMDSUICIDE, &Stream );
|
||
}
|
||
|
||
void CDnMonsterActor::CmdMixedAction( const char *szActionBone, const char *szMaintenanceBone, const char *szActionName, float fFrame, float fBlendFrame )
|
||
{
|
||
int nActionIndex = GetElementIndex( szActionName );
|
||
int nMaintenanceBoneIndex = GetBoneIndex( szMaintenanceBone );
|
||
int nActionBoneIndex = GetBoneIndex( szActionBone );
|
||
|
||
int nBlendAniIndex = m_nAniIndex;
|
||
|
||
if( nActionIndex == -1 ) {
|
||
assert(0);
|
||
}
|
||
|
||
if( nActionBoneIndex == -1 || nMaintenanceBoneIndex == -1 )
|
||
return;
|
||
|
||
BYTE pBuffer[128] = {0,};
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
Stream.Write( &nActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( &nActionBoneIndex, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
Stream.Write( &nMaintenanceBoneIndex, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
Stream.Write( &fFrame, sizeof(float), CPacketCompressStream::FLOAT_SHORT, 10.f );
|
||
Stream.Write( &fBlendFrame, sizeof(float), CPacketCompressStream::FLOAT_SHORT, 10.f );
|
||
|
||
Send( eActor::SC_CMDMIXEDACTION, &Stream );
|
||
|
||
|
||
}
|
||
|
||
void CDnMonsterActor::CmdWarp( EtVector3 &vPos, EtVector2 &vLook, CDNUserSession* pGameSession, bool bCheckPlayerFollowSummonedMonster/*=false*/ )
|
||
{
|
||
CDnActor::CmdWarp( vPos, vLook, pGameSession, bCheckPlayerFollowSummonedMonster );
|
||
|
||
MAWalkMovementNav *pMovement = dynamic_cast<MAWalkMovementNav *>(GetMovement());
|
||
if( pMovement ) pMovement->ValidateCurCell();
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::GenerationDropItem()
|
||
{
|
||
m_VecDropItemList.clear();
|
||
|
||
if( m_nItemDropGroupTableID < 1 ) return;
|
||
|
||
int nExtendDropRate = 0;
|
||
GetGameRoom()->GetExtendDropRateIgnoreTime(nExtendDropRate);
|
||
#if defined(PRE_ADD_WORLD_EVENT)
|
||
#else
|
||
#if defined(PRE_ADD_WEEKLYEVENT)
|
||
if (CDnWorld::GetInstance(GetRoom()).GetMapSubType() != EWorldEnum::MapSubTypeNest){
|
||
int nThreadID = GetGameRoom()->GetServerID();
|
||
|
||
int nEventValue = g_pDataManager->GetWeeklyEventValue(0, 0, WeeklyEvent::Event_10, nThreadID);
|
||
if (nEventValue != 0)
|
||
nExtendDropRate += nEventValue;
|
||
}
|
||
#endif // #if defined(PRE_ADD_WEEKLYEVENT)
|
||
#endif //#if defined(PRE_ADD_WORLD_EVENT)
|
||
#if defined( PRE_ADD_NEWCOMEBACK )
|
||
if( GetGameRoom() )
|
||
{
|
||
if( GetGameRoom()->GetTaskMng() )
|
||
{
|
||
CDnPartyTask* pPartyTask = (CDnPartyTask*)(GetGameRoom()->GetTaskMng()->GetTask("PartyTask"));
|
||
if( pPartyTask )
|
||
{
|
||
float fBlowValue = pPartyTask->GetPlayerDropUpBlowValue();
|
||
if( fBlowValue > 0 )
|
||
{
|
||
fBlowValue = fBlowValue * 100;
|
||
nExtendDropRate += (int)fBlowValue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
#if defined( PRE_ADD_STAGE_WEIGHT )
|
||
if( GetGameRoom() && GetGameRoom()->GetTaskMng() )
|
||
{
|
||
CDnGameTask* pGameTask = static_cast<CDnGameTask*>(GetGameRoom()->GetTaskMng()->GetTask("GameTask"));
|
||
if( pGameTask )
|
||
{
|
||
const TStageWeightData * pStageWeightData = pGameTask->GetStageWeightData();
|
||
if( pStageWeightData )
|
||
{
|
||
float fStageWeightBonus = pStageWeightData->fItemDropRate;
|
||
fStageWeightBonus = fStageWeightBonus * 100;
|
||
nExtendDropRate += (int)fStageWeightBonus;
|
||
}
|
||
}
|
||
}
|
||
#endif // #if defined( PRE_ADD_STAGE_WEIGHT )
|
||
|
||
float fCalcDropCount = ((float)((float)nExtendDropRate/100) + 1.0f);
|
||
for (int h = 0; 0 < fCalcDropCount; h++)
|
||
{
|
||
CDnDropItem::CalcDropItemList( GetRoom(), m_AIDifficult, m_nItemDropGroupTableID, m_VecDropItemList );
|
||
|
||
for( DWORD i=0; i<m_VecDropItemList.size(); i++ ) {
|
||
#if defined(PRE_ADD_STAGE_CLEAR_ENCHANT_REWARD)
|
||
if( CDnDropItem::PreInitializeItem( GetRoom(), m_VecDropItemList[i].nItemID, m_VecDropItemList[i].nEnchantID ) == false )
|
||
#else // #if defined(PRE_ADD_STAGE_CLEAR_ENCHANT_REWARD)
|
||
if( CDnDropItem::PreInitializeItem( GetRoom(), m_VecDropItemList[i].nItemID ) == false )
|
||
#endif // #if defined(PRE_ADD_STAGE_CLEAR_ENCHANT_REWARD)
|
||
{
|
||
m_VecDropItemList.erase( m_VecDropItemList.begin() + i );
|
||
i--;
|
||
}
|
||
}
|
||
|
||
fCalcDropCount -= 1.0f;
|
||
if (h >= 4 || (fCalcDropCount < 1.0f && ((float)(_rand(GetRoom())%100)/100) > fCalcDropCount))
|
||
break; //4개이상(이벤트3개)은 불가하게 조절, 1보다 작은 값이 남은경우 확율계산하여 한던 더돌지 판단
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::SetGenerationArea( SOBB &Box )
|
||
{
|
||
m_GenerationArea = Box;
|
||
}
|
||
|
||
SOBB *CDnMonsterActor::GetGenerationArea()
|
||
{
|
||
return &m_GenerationArea;
|
||
}
|
||
|
||
void CDnMonsterActor::SyncClassTime( LOCAL_TIME LocalTime )
|
||
{
|
||
MAActorRenderBase::m_LocalTime = LocalTime;
|
||
|
||
CDnActor::SyncClassTime( LocalTime );
|
||
}
|
||
|
||
void CDnMonsterActor::OnChangeAction( const char *szPrevAction )
|
||
{
|
||
if( m_pAi )
|
||
m_pAi->OnChangeAction( szPrevAction );
|
||
|
||
#ifdef PRE_ADD_AURA_FOR_MONSTER
|
||
_CheckActionWithProcessPassiveActionSkill(szPrevAction);
|
||
#endif
|
||
|
||
}
|
||
|
||
void CDnMonsterActor::_CheckActionWithProcessPassiveActionSkill( const char* szPrevAction )
|
||
{
|
||
// 같은 액션 반복중이면 패스
|
||
if( szPrevAction && m_nPrevActionIndex == m_nActionIndex )
|
||
return;
|
||
|
||
// if instantly passive skill, then cancel skill. ( ex) archer's spinkick)
|
||
// because state effect must deactivate when change to another attack action.
|
||
if( m_hProcessSkill )
|
||
{
|
||
m_setUseActionName.clear();
|
||
m_setUseActionName.insert( szPrevAction );
|
||
|
||
// 현재 액션이 prev 액션의 next 액션이라면 스킬이 이어지는 것으로 본다.
|
||
// 이전 액션이 현재 진행중인 스킬에서 사용하는 액션이었고 현재 액션이 이전 액션의 next 액션이 아니라면
|
||
// 패시브 스킬이 끝난 것으로 판단한다.
|
||
ActionElementStruct* pElement = GetElement( szPrevAction );
|
||
bool bIsNextAction = false;
|
||
if( pElement )
|
||
{
|
||
// #25154 기본 스탠드 액션은 스킬에서 지정된 next 액션이 이어지는 것으로 보지 않는다.
|
||
// 오라 스킬 액션이 끝나고 이 함수가 호출되었을 때 현재 액션이 Stand 로 되어있는데 해당 시점에서
|
||
// m_hProcessSkill 이 스킬이 끝난 것으로 판단되어서 NULL 로 되어야 한다.
|
||
// m_hProcessSkill 이 남아있으면 다른 스킬 썼을 때 강제로 onend 될 수 있기 때문에 안됨.
|
||
// 따라서 bIsNextAction 이 false 가 되고 m_hProcessSkill->IsUseSkillActionNames() 함수 내부에서
|
||
// 스킬 액션이 종료된 것으로 판단되어야 한다.
|
||
bIsNextAction = ((pElement->szNextActionName != "Stand") && (pElement->szNextActionName == GetCurrentAction()));
|
||
}
|
||
|
||
if( false == bIsNextAction &&
|
||
m_hProcessSkill->IsUseActionNames( m_setUseActionName ) )
|
||
{
|
||
if( (m_hProcessSkill->GetSkillType() == CDnSkill::Passive || m_hProcessSkill->GetSkillType() == CDnSkill::AutoPassive) &&
|
||
m_hProcessSkill->GetDurationType() == CDnSkill::Instantly )
|
||
{
|
||
// 패시브 스킬이 체인 입력이 들어왔을 때를 체크한다. 한번 체크되는 순간 체인 입력 플래그는 초기화된다.
|
||
// 체인입력되는 순간 액션의 길이만큼 패시브 스킬 사용 길이가 늘어난다.
|
||
// 이렇게 플래그와 시간 둘 다 같이 사용해야 패시브 스킬의 연속 체인이 가능해진다.
|
||
if( false == m_hProcessSkill->CheckChainingPassiveSkill() )
|
||
{
|
||
m_hProcessSkill->OnEnd( MAActorRenderBase::m_LocalTime, 0.0f );
|
||
m_hProcessSkill.Identity();
|
||
}
|
||
}
|
||
else
|
||
// Note 한기: m_hProcessSkill 스마트 포인터는 오라 스킬 사용하는 액션이 재생되는 동안은 유효해야
|
||
// 게임 서버에서 CDnPlayerActor::CmdStop() 쪽에서 걸러지기 때문에 겜 서버에서 해당 액션 시그널이 끝까지
|
||
// 처리됨. 따라서 CDnActor::OnChangeAction 쪽에서 ProcessAction 을 Identity 시킴.
|
||
if( IsEnabledAuraSkill() && m_hProcessSkill->IsAuraOn() )
|
||
{
|
||
m_hProcessSkill.Identity();
|
||
ClearSelfStateSignalBlowQueue(); // 오라 스킬의 자기 자신에게 적용하는 상태효과 타이밍 시그널에 보내주는 큐 초기화 시킴. 안그럼 다른 스킬에 영향을 준다.
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::OnFinishAction(const char* szPrevAction, LOCAL_TIME time)
|
||
{
|
||
if( m_bChangeAxisOnFinishAction )
|
||
{
|
||
EtVector2 vView = EtVec3toVec2( *GetLookDir() );
|
||
vView *= -1.f;
|
||
Look( vView, true );
|
||
m_bChangeAxisOnFinishAction = false;
|
||
}
|
||
|
||
if( m_pAi )
|
||
m_pAi->OnFinishAction(szPrevAction, time);
|
||
}
|
||
|
||
void CDnMonsterActor::ResetActor()
|
||
{
|
||
if( m_pAi )
|
||
m_pAi->ResetDelay();
|
||
}
|
||
|
||
void CDnMonsterActor::OnBeginStateBlow( DnBlowHandle hBlow )
|
||
{
|
||
CDnActor::OnBeginStateBlow( hBlow );
|
||
|
||
if( m_pAggroSystem )
|
||
m_pAggroSystem->OnStateBlowAggro( hBlow );
|
||
}
|
||
|
||
// MASkillUser
|
||
#ifdef PRE_FIX_GAMESERVER_OPTIMIZE
|
||
void CDnMonsterActor::OnAddSkill( DnSkillHandle hSkill, bool isInitialize/* = false*/ )
|
||
{
|
||
// 패시브 이면서 버프고 상태효과 Self 로 붙어있는 스킬은 곧바로 적용시켜 줌..
|
||
bool bPassiveBuff = false;
|
||
|
||
bPassiveBuff = ApplyPassiveSkill( hSkill, isInitialize );
|
||
|
||
if( bPassiveBuff )
|
||
m_vlhSelfPassiveBlowSkill.push_back( hSkill );
|
||
}
|
||
|
||
bool CDnMonsterActor::AddSkill( int nSkillTableID, int nLevel/* = 1*/, int iSkillLevelApplyType/* = CDnSkill::PVE*/ )
|
||
{
|
||
if( IsExistSkill( nSkillTableID, nLevel ) ) return false;
|
||
if( !MASkillUser::IsValidActor() ) return false;
|
||
if( GetRoom() == NULL ) return false;
|
||
|
||
DnSkillHandle hSkill = CDnSkill::CreateMonsterSkill( GetMySmartPtr(), nSkillTableID, nLevel );
|
||
if( !hSkill ) return false;
|
||
|
||
#ifdef PRE_ADD_AURA_FOR_MONSTER
|
||
CDnSkill::DurationTypeEnum eDurationType = hSkill->GetDurationType();
|
||
switch(eDurationType)
|
||
{
|
||
case CDnSkill::Aura:
|
||
m_vlhAuraSkills.push_back(hSkill);
|
||
}
|
||
#endif
|
||
|
||
#ifndef PRE_FIX_SKILLLIST
|
||
m_vlhSkillList.push_back( hSkill );
|
||
m_vbSelfAllocList.push_back( true );
|
||
#else
|
||
AddSkillObject( S_SKILL_OBJECT(hSkill, true) );
|
||
#endif // #ifndef PRE_FIX_SKILLLIST
|
||
|
||
if( hSkill->GetActor() )
|
||
hSkill->SetHasActor( GetMySmartPtr() );
|
||
|
||
OnAddSkill( hSkill );
|
||
|
||
return true;
|
||
}
|
||
#endif // #ifdef PRE_FIX_GAMESERVER_OPTIMIZE
|
||
|
||
bool CDnMonsterActor::ExecuteSkill( DnSkillHandle hSkill, LOCAL_TIME LocalTime, float fDelta )
|
||
{
|
||
if ( CDnActor::ExecuteSkill(hSkill, LocalTime, fDelta) == false )
|
||
return false;
|
||
|
||
BYTE pBuffer[128] = {0,};
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
int nSkillID = hSkill->GetClassID();
|
||
char cLevel = hSkill->GetLevel();
|
||
|
||
Stream.Write( &nSkillID, sizeof(int) );
|
||
Stream.Write( &cLevel, sizeof(char) );
|
||
Stream.Write( &EtVec3toVec2( *GetLookDir() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
Stream.Write( GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
#if defined( PRE_ADD_ACADEMIC )
|
||
int iSummonerSkillID = 0;
|
||
DnSkillHandle hSummonerSkill = FindSkill( nSkillID );
|
||
if( hSummonerSkill )
|
||
iSummonerSkillID = hSummonerSkill->GetSummonerDecreaseSPSkillID();
|
||
Stream.Write( &iSummonerSkillID, sizeof(int) );
|
||
#endif // #if defined( PRE_ADD_ACADEMIC )
|
||
|
||
Send( eActor::SC_USESKILL, &Stream );
|
||
|
||
ResetPositionRevision();
|
||
|
||
return true;
|
||
}
|
||
|
||
CDnSkill::UsingResult CDnMonsterActor::UseSkill( int nSkillTableID, bool bCheckValid/* = true*/, bool bAutoUseFromServer/* = false*/, int nLuaSkillIndex/*=-1*/ )
|
||
{
|
||
CDnSkill::UsingResult Result = MASkillUser::UseSkill( nSkillTableID, bCheckValid, bAutoUseFromServer );
|
||
if( Result == CDnSkill::UsingResult::Success && nLuaSkillIndex >= 0 )
|
||
{
|
||
std::map<int,int>::iterator itor = m_mUseSkillCount.find( nLuaSkillIndex );
|
||
if( itor == m_mUseSkillCount.end() )
|
||
{
|
||
m_mUseSkillCount.insert( std::make_pair(nLuaSkillIndex,1) );
|
||
}
|
||
else
|
||
{
|
||
++(*itor).second;
|
||
}
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
int CDnMonsterActor::GetUseSkillCount( int iLuaSkillIndex )
|
||
{
|
||
std::map<int,int>::iterator itor = m_mUseSkillCount.find( iLuaSkillIndex );
|
||
if( itor != m_mUseSkillCount.end() )
|
||
return (*itor).second;
|
||
|
||
return 0;
|
||
}
|
||
|
||
void CDnMonsterActor::SetScale( float fValue )
|
||
{
|
||
m_fScale = fValue;
|
||
if( m_hObject ) {
|
||
m_hObject->SetCollisionScale( m_fScale );
|
||
}
|
||
MAActorRenderBase::SetScale( EtVector3( m_fScale, m_fScale, m_fScale ) );
|
||
|
||
if( m_fWeight == 0.f ) m_fRevisionWeight = 0.f;
|
||
else m_fRevisionWeight = m_fWeight + ( ( m_fScale - 1.f ) * CGlobalWeightTable::GetInstance().GetValue( CGlobalWeightTable::ScaleWeightValue ) );
|
||
}
|
||
|
||
float CDnMonsterActor::GetWeight()
|
||
{
|
||
return m_fRevisionWeight;
|
||
}
|
||
|
||
float CDnMonsterActor::GetThreatRange()
|
||
{
|
||
_ASSERT( dynamic_cast<CDNMonsterAggroSystem*>(m_pAggroSystem) );
|
||
return m_pAggroSystem ? static_cast<CDNMonsterAggroSystem*>(m_pAggroSystem)->GetThreatRange() : 0.f;
|
||
}
|
||
|
||
float CDnMonsterActor::GetCognizanceThreatRange()
|
||
{
|
||
_ASSERT( dynamic_cast<CDNMonsterAggroSystem*>(m_pAggroSystem) );
|
||
return m_pAggroSystem ? static_cast<CDNMonsterAggroSystem*>(m_pAggroSystem)->GetCognizanceThreatRange() : 0.f;
|
||
}
|
||
|
||
float CDnMonsterActor::GetCognizanceThreatRangeSq()
|
||
{
|
||
_ASSERT( dynamic_cast<CDNMonsterAggroSystem*>(m_pAggroSystem) );
|
||
return m_pAggroSystem ? static_cast<CDNMonsterAggroSystem*>(m_pAggroSystem)->GetCognizanceThreatRangeSq() : 0.f;
|
||
}
|
||
|
||
std::string CDnMonsterActor::GetAIFileName()
|
||
{
|
||
DNTableFileFormat* pSox = NULL;
|
||
int nItemID = 0;
|
||
|
||
if( m_nMonsterWeightTableID == -1 )
|
||
{
|
||
pSox = GetDNTable( CDnTableDB::TMONSTER );
|
||
nItemID = m_nMonsterClassID;
|
||
}
|
||
else
|
||
{
|
||
pSox = GetDNTable( CDnTableDB::TMONSTERWEIGHT );
|
||
nItemID = m_nMonsterWeightTableID;
|
||
}
|
||
|
||
if( !pSox || !pSox->IsExistItem( nItemID ) )
|
||
return std::string("");
|
||
std::string szName = pSox->GetFieldFromLablePtr( nItemID, "_CustomAI" )->GetString();
|
||
|
||
return szName;
|
||
}
|
||
|
||
void CDnMonsterActor::CalcMonsterWeightIndex()
|
||
{
|
||
if( m_nMonsterClassID == 0 ) return;
|
||
DNTableFileFormat* pSox = GetDNTable( CDnTableDB::TMONSTERWEIGHT );
|
||
if( !pSox )
|
||
{
|
||
g_Log.Log( LogType::_FILELOG, L"MonsterWeightTable.ext failed\r\n");
|
||
return;
|
||
}
|
||
|
||
std::vector<int> nVecList;
|
||
pSox->GetItemIDListFromField( "_MonsterTableIndex", m_nMonsterClassID, nVecList );
|
||
|
||
int nDifficulty;
|
||
for( DWORD i=0; i<nVecList.size(); i++ ) {
|
||
nDifficulty = pSox->GetFieldFromLablePtr( nVecList[i], "_Difficulty" )->GetInteger();
|
||
if( nDifficulty == m_AIDifficult ) {
|
||
m_nMonsterWeightTableID = nVecList[i];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::SetNaviDestination( SOBB* pOBB, UINT uiMoveFrontRate )
|
||
{
|
||
if( m_pAi )
|
||
m_pAi->OnInitNaviDestination( pOBB, uiMoveFrontRate );
|
||
else
|
||
_DANGER_POINT();
|
||
}
|
||
|
||
int CDnMonsterActor::GetWaitOrderCount( int iSkillID )
|
||
{
|
||
std::map<int,int>::iterator itor = m_mOrderCount.find( iSkillID );
|
||
if( itor == m_mOrderCount.end() )
|
||
return 0;
|
||
return (*itor).second;
|
||
}
|
||
|
||
void CDnMonsterActor::AddWaitOrderCount( int iSkillID )
|
||
{
|
||
std::map<int,int>::iterator itor = m_mOrderCount.find( iSkillID );
|
||
if( itor == m_mOrderCount.end() )
|
||
{
|
||
m_mOrderCount.insert( std::make_pair(iSkillID,1) );
|
||
return;
|
||
}
|
||
++(*itor).second;
|
||
}
|
||
|
||
void CDnMonsterActor::DelWaitOrderCount( int iSkillID )
|
||
{
|
||
std::map<int,int>::iterator itor = m_mOrderCount.find( iSkillID );
|
||
if( itor == m_mOrderCount.end() )
|
||
return;
|
||
if( (*itor).second > 0 )
|
||
--(*itor).second;
|
||
}
|
||
|
||
bool CDnMonsterActor::OnAINonTarget()
|
||
{
|
||
// Walk_Font 액션 검사
|
||
if( !IsExistAction( "Walk_Front") )
|
||
return true;
|
||
|
||
if ( !IsHit() && IsMove() )
|
||
CmdStop( "Stand", 0, g_fBendFrame );
|
||
|
||
if ( !IsMovable() )
|
||
return false;
|
||
|
||
// 어슬렁 거린다.
|
||
EtVector3 *pvPos = m_hActor->GetPosition();
|
||
EtVector3 vTemp;
|
||
vTemp = *pvPos;
|
||
vTemp.y = 0.f;
|
||
vTemp.x += cos( EtToRadian( _rand(m_hActor->GetRoom())%360 ) ) * ( 300 + _rand(m_hActor->GetRoom())%200 );
|
||
vTemp.z += sin( EtToRadian( _rand(m_hActor->GetRoom())%360 ) ) * ( 300 + _rand(m_hActor->GetRoom())%200 );
|
||
|
||
SOBB Box = *GetGenerationArea();
|
||
Box.Extent[1] = 1000000.f;
|
||
if( !Box.IsInside( vTemp ) )
|
||
{
|
||
vTemp = Box.Center;
|
||
vTemp -= Box.Axis[0] * Box.Extent[0];
|
||
vTemp -= Box.Axis[2] * Box.Extent[2];
|
||
|
||
int iModValue = static_cast<int>(Box.Extent[0] * 2.f);
|
||
if( iModValue > 0 ) vTemp += Box.Axis[0] * (float)( _rand(m_hActor->GetRoom())%iModValue );
|
||
else vTemp += Box.Axis[0];
|
||
|
||
iModValue = static_cast<int>(Box.Extent[2] * 2.f);
|
||
if( iModValue ) vTemp += Box.Axis[2] * (float)( _rand(m_hActor->GetRoom())%iModValue );
|
||
else vTemp += Box.Axis[2];
|
||
|
||
vTemp.y = CDnWorld::GetInstance(m_hActor->GetRoom()).GetHeight( vTemp );
|
||
vTemp += Box.Axis[1] * Box.Extent[1];
|
||
}
|
||
|
||
CmdMove( vTemp, "Walk_Front", -1, g_fBendFrame );
|
||
|
||
return true;
|
||
}
|
||
|
||
void CDnMonsterActor::OnBumpWall()
|
||
{
|
||
if( !m_pszCanBumpActionName )
|
||
return;
|
||
|
||
CmdAction( m_pszCanBumpActionName, 0, g_fBendFrame );
|
||
}
|
||
|
||
// 패킷 보내기
|
||
// 이 부분은 CDnProjectile::GetPacketStream() 을 쓰지 않고 액션 인덱스와 시그널 인덱스 기준으로 패킷을 보내서
|
||
// 클라이언트가 발사체 시그널 정보를 찾아 생성하게 되어있는데 일단 그대로 둡니다.
|
||
// 현재는 문제될 것이 없지만 추후에 서버에서 결정해서 보내줘야할 데이터가 생긴다든지 할때 수정해야 합니다. - 한기.
|
||
void CDnMonsterActor::SendProjectile( CDnProjectile *pProjectile, ProjectileStruct* pStruct, MatrixEx& LocalCross, int iSignalIndex )
|
||
{
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
DWORD dwVal = pProjectile->GetUniqueID();
|
||
Stream.Write( &dwVal, sizeof(DWORD) );
|
||
Stream.Write( &LocalCross.m_vPosition, sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &LocalCross.m_vXAxis, sizeof(EtVector3), CPacketCompressStream::VECTOR3_SHORT );
|
||
Stream.Write( &LocalCross.m_vYAxis, sizeof(EtVector3), CPacketCompressStream::VECTOR3_SHORT );
|
||
Stream.Write( &LocalCross.m_vZAxis, sizeof(EtVector3), CPacketCompressStream::VECTOR3_SHORT );
|
||
|
||
#if defined(PRE_FIX_55378)
|
||
int nActionIndex = m_nActionIndex;
|
||
|
||
std::string szChargerAction = GetChargerAction();
|
||
if (szChargerAction.empty() == false)
|
||
nActionIndex = GetElementIndex(szChargerAction.c_str());
|
||
|
||
Stream.Write( &nActionIndex, sizeof(int) );
|
||
#else
|
||
Stream.Write( &m_nActionIndex, sizeof(int) );
|
||
#endif // PRE_FIX_55378
|
||
|
||
Stream.Write( &iSignalIndex, sizeof(int) );
|
||
|
||
bool bUsedForceDir = pProjectile->IsUsedForceDir();
|
||
Stream.Write( &bUsedForceDir, sizeof(bool) );
|
||
if( bUsedForceDir )
|
||
{
|
||
const EtVector3& vForceDir = pProjectile->GetForceDir();
|
||
Stream.Write( &vForceDir, sizeof(EtVector3), CPacketCompressStream::VECTOR3_SHORT );
|
||
}
|
||
|
||
switch( pStruct->nTargetType )
|
||
{
|
||
case 2: // TargetPosition
|
||
{
|
||
Stream.Write( pProjectile->GetTargetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
break;
|
||
}
|
||
case 3: // Target
|
||
{
|
||
DnActorHandle hTarget = pProjectile->GetTargetActor();
|
||
if( !hTarget )
|
||
hTarget = GetActorHandle();
|
||
|
||
dwVal = hTarget->GetUniqueID();
|
||
Stream.Write( &dwVal, sizeof(DWORD) );
|
||
break;
|
||
}
|
||
}
|
||
|
||
Send( eActor::SC_PROJECTILE, &Stream );
|
||
}
|
||
|
||
|
||
// #15557 이슈 관련. 몬스터가 발사체에서 발사체를 쏘는 경우.
|
||
// 기본적으로 클라이언트의 LocalPlayerActor 가 보내는 내용과 같다.
|
||
// 클라이언트 측에선 파티원이 쏜 발사체와 역시 동일하게 패킷을 처리한다.
|
||
// 따라서 반드시 CDnLocalPlayerActor::OnProjectile() 쪽도 같이 수정해야 함.
|
||
void CDnMonsterActor::SendProjectileFromProjectile( CDnProjectile* pProjectile, int nSignalIndex )
|
||
{
|
||
CPacketCompressStream* pPacketStream = pProjectile->GetPacketStream();
|
||
_ASSERT( pPacketStream );
|
||
|
||
if( pPacketStream )
|
||
Send( eActor::SC_MONSTER_PROJECTILE_FROM_PROJECTILE, pPacketStream );
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::OnSignal( SignalTypeEnum Type, void *pPtr, LOCAL_TIME LocalTime, LOCAL_TIME SignalStartTime, LOCAL_TIME SignalEndTime, int nSignalIndex )
|
||
{
|
||
switch( Type ) {
|
||
case STE_LockTargetLook:
|
||
{
|
||
#if defined( PRE_MOD_LOCK_TARGET_LOOK )
|
||
LockTargetLookStruct * pStruct = static_cast<LockTargetLookStruct*>(pPtr);
|
||
m_nLockLookEventArea = pStruct->LookEeventAreaID;
|
||
#endif // PRE_MOD_LOCK_TARGET_LOOK
|
||
|
||
#if defined( PRE_FIX_MOVEBACK )
|
||
if( !IsNearMoveBack() )
|
||
m_bLockLookTarget = true;
|
||
#else
|
||
m_bLockLookTarget = true;
|
||
#endif
|
||
break;
|
||
}
|
||
case STE_CanBumpWall:
|
||
{
|
||
CanBumpWallStruct* pStruct = static_cast<CanBumpWallStruct*>(pPtr);
|
||
m_pszCanBumpActionName = pStruct->szActionName;
|
||
break;
|
||
}
|
||
case STE_Projectile:
|
||
{
|
||
ProjectileStruct* pStruct = (ProjectileStruct *)pPtr;
|
||
// Multiple타겟이면 추가로 서버에서 Projectile 생성하여 발사
|
||
#if defined (PRE_MOD_AIMULTITARGET)
|
||
if( static_cast<MAAiScript*>(GetAIBase())->m_cMultipleTarget.GetMultipleTarget() > 0 )
|
||
#else
|
||
if( static_cast<MAAiScript*>(GetAIBase())->m_cMultipleTarget.bIsMultipleTarget() )
|
||
#endif
|
||
{
|
||
static_cast<MAAiScript*>(GetAIBase())->m_cMultipleTarget.CreateProjectile( this, static_cast<ProjectileStruct*>(pPtr), nSignalIndex );
|
||
}
|
||
else
|
||
{
|
||
DNVector(DnActorHandle) StigmaActorList;
|
||
if (pStruct->nTargetStateIndex != 0)
|
||
{
|
||
ScanActorByStateIndex(StigmaActorList, STATE_BLOW::BLOW_246);
|
||
}
|
||
|
||
//낙인 대상이 있는 경우만 처리
|
||
if (StigmaActorList.empty() == false)
|
||
{
|
||
int nStigmaActorCount = (int)StigmaActorList.size();
|
||
for (int i = 0; i < nStigmaActorCount; ++i)
|
||
{
|
||
//타겟 액터..
|
||
DnActorHandle hTargetActor = StigmaActorList[i];
|
||
|
||
CDnProjectile* pProjectile = CDnProjectile::CreateProjectile( GetRoom(), GetMySmartPtr(), m_Cross, pStruct, NULL, hTargetActor );
|
||
if( pProjectile == NULL )
|
||
return;
|
||
pProjectile->SetShooterType( GetMySmartPtr(), m_nActionIndex, nSignalIndex );
|
||
|
||
SendProjectile( pProjectile, pStruct, m_Cross, nSignalIndex );
|
||
OnProjectile( pProjectile, pStruct, m_Cross, nSignalIndex );
|
||
OnSkillProjectile( pProjectile );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//#52808 - 추가 요청 (낙인용 발사체 설정이 2인 경우 낙인 대상이 없으면 발사체 생성 안됨.
|
||
if (pStruct->nTargetStateIndex == 2)
|
||
return;
|
||
|
||
CDnProjectile* pProjectile = CDnProjectile::CreateProjectile( GetRoom(), GetMySmartPtr(), m_Cross, pStruct );
|
||
|
||
if( pProjectile == NULL )
|
||
return;
|
||
pProjectile->SetShooterType( GetMySmartPtr(), m_nActionIndex, nSignalIndex );
|
||
|
||
SendProjectile( pProjectile, pStruct, m_Cross, nSignalIndex );
|
||
OnProjectile( pProjectile, pStruct, m_Cross, nSignalIndex );
|
||
OnSkillProjectile( pProjectile );
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case STE_ProjectileTargetPosition:
|
||
{
|
||
if( m_pAi )
|
||
m_pAi->SetProjectileTarget();
|
||
break;
|
||
}
|
||
|
||
case STE_ReserveProjectileTarget:
|
||
{
|
||
if( m_pAi )
|
||
m_pAi->ReservedProjectileTarget();
|
||
|
||
break;
|
||
}
|
||
|
||
case STE_RotateResistance:
|
||
{
|
||
RotateResistanceStruct *pStruct = (RotateResistanceStruct *)pPtr;
|
||
m_fRotateResistance = pStruct->fResistanceRatio;
|
||
break;
|
||
}
|
||
case STE_PositionRevision:
|
||
{
|
||
PositionRevisionStruct* pStruct = reinterpret_cast<PositionRevisionStruct*>(pPtr);
|
||
m_uiForcePositionRevisionTick = pStruct->nRevisionTick;
|
||
return;
|
||
}
|
||
|
||
case STE_SuicideMonster:
|
||
{
|
||
SuicideMonsterStruct* pStruct = (SuicideMonsterStruct*)pPtr;
|
||
// 아이템을 드롭하는가. 여기서 클라로 패킷도 보냄.
|
||
CmdSuicide( pStruct->bDropItem ? true : false, pStruct->bGetExp ? true : false );
|
||
}
|
||
break;
|
||
case STE_TriggerEvent:
|
||
{
|
||
TriggerEventStruct *pStruct = (TriggerEventStruct *)pPtr;
|
||
CDnWorld::GetInstance(GetRoom()).InsertTriggerEventStore( "EventArea", GetBirthAreaHandle() );
|
||
CDnWorld::GetInstance(GetRoom()).InsertTriggerEventStore( "ActorHandle", GetUniqueID() );
|
||
CDnWorld::GetInstance(GetRoom()).InsertTriggerEventStore( "EventID", pStruct->nEventID );
|
||
CDnWorld::GetInstance(GetRoom()).OnTriggerEventCallback( "CDnActor::TriggerEvent", LocalTime, 0.f );
|
||
}
|
||
break;
|
||
case STE_Announce:
|
||
{
|
||
AnnounceStruct* pStruct = reinterpret_cast<AnnounceStruct*>(pPtr);
|
||
|
||
DNVector(DnActorHandle) hScanList;
|
||
GetMAScanner().Scan( MAScanner::eType::MonsterSkillSameTeamExpectMe, m_hActor, 0.f, static_cast<float>(pStruct->nRange), hScanList );
|
||
|
||
for( UINT i=0 ; i<hScanList.size() ; ++i )
|
||
{
|
||
DnActorHandle hActor = hScanList[i];
|
||
if( hActor && hActor->IsMonsterActor() )
|
||
{
|
||
CDnMonsterActor* pMonster = static_cast<CDnMonsterActor*>(hActor.GetPointer());
|
||
if( pMonster->GetMonsterClassID() == pStruct->nTargetMonsterID )
|
||
{
|
||
pMonster->GetAIBase()->NotifyDieAnnounce();
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case STE_NextCustomAction:
|
||
{
|
||
if( !GetAIBase() )
|
||
return;
|
||
|
||
int state = 0;
|
||
float fLength = 0.0f;
|
||
EtVector3* pvPos = GetPosition();
|
||
|
||
MAAiScript* pScript = static_cast<MAAiScript*>(GetAIBase());
|
||
if( pScript->GetTarget() )
|
||
{
|
||
state = pScript->GetTargetDistanceState( pScript->GetTarget() );
|
||
fLength = EtVec3Length( &( *pvPos - *pScript->GetTarget()->GetPosition() ) );
|
||
}
|
||
|
||
if( static_cast<MAAiScript*>(GetAIBase())->GetAIState() == MAAiScript::AT_CustomAction )
|
||
static_cast<MAAiScript*>(GetAIBase())->OnCustomAction( state, fLength, 0 );
|
||
if( static_cast<MAAiScript*>(GetAIBase())->GetAIState() == MAAiScript::AT_UseSkill )
|
||
{
|
||
CancelUsingSkill();
|
||
static_cast<MAAiScript*>(GetAIBase())->OnUseSkill( state, fLength, 0 );
|
||
}
|
||
return;
|
||
}
|
||
|
||
#ifdef PRE_ADD_MONSTER_CATCH
|
||
case STE_CatchActor:
|
||
{
|
||
CatchActor( reinterpret_cast<CatchActorStruct*>(pPtr), nSignalIndex );
|
||
}
|
||
break;
|
||
|
||
case STE_ReleaseActor:
|
||
{
|
||
ReleaseAllActor( /*reinterpret_cast<ReleaseActorStruct*>(pPtr)*/ );
|
||
}
|
||
break;
|
||
#endif // #ifdef PRE_ADD_MONSTER_CATCH
|
||
case STE_ChangeAxis:
|
||
{
|
||
m_bChangeAxisOnFinishAction = true;
|
||
}
|
||
break;
|
||
}
|
||
CDnActor::OnSignal( Type, pPtr, LocalTime, SignalStartTime, SignalEndTime, nSignalIndex );
|
||
}
|
||
|
||
bool CDnMonsterActor::IsHittable( DnActorHandle hHitter, LOCAL_TIME LocalTime, HitStruct *pHitSignal, int iHitUniqueID )
|
||
{
|
||
#if defined(PRE_FIX_61382)
|
||
#else
|
||
//#59347
|
||
//꼭두각시 소환 몬스터인경우 히트 가능 여부를 꼭두각시를 소환한 주인 액터의 Hittable여부를 체크 하도록 한다.
|
||
if ( IsPuppetSummonMonster() && m_hSummonerPlayerActor )
|
||
{
|
||
bool bSummonerPlayerActorHittable = m_hSummonerPlayerActor->IsHittable(hHitter, LocalTime, pHitSignal, iHitUniqueID);
|
||
if (bSummonerPlayerActorHittable == false)
|
||
return false;
|
||
}
|
||
#endif // PRE_FIX_61382
|
||
|
||
if( m_bNoDamage ) return false;
|
||
return CDnActor::IsHittable( hHitter, LocalTime, pHitSignal, iHitUniqueID );
|
||
}
|
||
|
||
void CDnMonsterActor::ProcessPositionRevision( float fDelta )
|
||
{
|
||
if( CheckSignalPositionRevision( fDelta ) || CheckPositionRevision() )
|
||
{
|
||
BYTE pBuffer[128] = { 0, };
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
Stream.Write( GetPosition(), sizeof(EtVector3), CPacketCompressStream::VECTOR3_BIT );
|
||
Stream.Write( &EtVec3toVec2( *GetMoveVectorZ() ), sizeof(EtVector2), CPacketCompressStream::VECTOR2_SHORT );
|
||
|
||
Send( eActor::SC_POSREV, &Stream );
|
||
|
||
ResetPositionRevision();
|
||
}
|
||
}
|
||
|
||
bool CDnMonsterActor::CheckPositionRevision()
|
||
{
|
||
if( m_LastSendMoveMsg == 0 )
|
||
return false;
|
||
|
||
if( !IsMove() )
|
||
{
|
||
m_LastSendMoveMsg = CDnActionBase::m_LocalTime;
|
||
return false;
|
||
}
|
||
|
||
if( CDnActionBase::m_LocalTime - m_LastSendMoveMsg > s_nPositionRevisionTime )
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
bool CDnMonsterActor::CheckSignalPositionRevision( float fDelta )
|
||
{
|
||
if( m_uiForcePositionRevisionTick == 0 )
|
||
return false;
|
||
|
||
if( m_uiPrevForcePositionRevisionTick == 0 )
|
||
{
|
||
//std::cout << GetTickCount() << ":첫번째 강제위치 보정" << std::endl;
|
||
m_fForcePositionRevisionDelta = m_uiForcePositionRevisionTick/1000.f;
|
||
return true;
|
||
}
|
||
|
||
m_fForcePositionRevisionDelta -= fDelta;
|
||
if( m_fForcePositionRevisionDelta <= 0.f )
|
||
{
|
||
m_fForcePositionRevisionDelta = m_uiForcePositionRevisionTick/1000.f;
|
||
//std::cout << GetTickCount() << ":강제위치 보정" << std::endl;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void CDnMonsterActor::ResetPositionRevision()
|
||
{
|
||
m_LastSendMoveMsg = CDnActionBase::m_LocalTime;
|
||
}
|
||
|
||
|
||
float CDnMonsterActor::GetRotateAngleSpeed()
|
||
{
|
||
return CDnMonsterState::GetRotateAngleSpeed() * m_fRotateResistance;
|
||
}
|
||
|
||
void CDnMonsterActor::AttachWeapon( DnWeaponHandle hWeapon, int nEquipIndex, bool bDelete )
|
||
{
|
||
CDnActor::AttachWeapon( hWeapon, nEquipIndex, bDelete );
|
||
// 무기 내구도 제설정
|
||
if( m_nMonsterWeightTableID == -1 ) return;
|
||
if( hWeapon->IsInfinityDurability() || hWeapon->GetDurability() <= 0 ) return;
|
||
|
||
DNTableFileFormat* pWeight = GetDNTable( CDnTableDB::TMONSTERWEIGHT );
|
||
int nDurability = (int)( hWeapon->GetDurability() * pWeight->GetFieldFromLablePtr( m_nMonsterWeightTableID, "_DurabilityWeight" )->GetFloat() );
|
||
hWeapon->SetDurability( nDurability );
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::SetTeam( int nValue )
|
||
{
|
||
if( GetTeam() != nValue ) {
|
||
if( m_pAi ) m_pAi->ResetAggro();
|
||
}
|
||
CDnActor::SetTeam( nValue );
|
||
}
|
||
|
||
void CDnMonsterActor::ProcessPartyCombo( LOCAL_TIME LocalTime, float fDelta )
|
||
{
|
||
if( m_nPartyComboDelay > 0 ) m_nPartyComboDelay -= (int)( fDelta * 1000 );
|
||
if( m_nPartyComboDelay < 0 ) {
|
||
m_nPartyComboCount = 0;
|
||
m_nPartyComboDelay = 0;
|
||
m_hPartyHitActor.Identity();
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::OnPartyCombo( DnActorHandle hHitter, int nComboDelay )
|
||
{
|
||
// #12170 콤보 딜레이 값이 0인 hit 는 콤보 판정에 아무런 영향을 주지 않도록 처리.
|
||
if( 0 == nComboDelay )
|
||
return;
|
||
|
||
if( m_hPartyHitActor == hHitter ) {
|
||
m_nPartyComboDelay = nComboDelay;
|
||
return;
|
||
}
|
||
m_hPartyHitActor = hHitter;
|
||
if( m_nPartyComboDelay > 0 ) {
|
||
m_nPartyComboCount++;
|
||
if( hHitter && hHitter->IsPlayerActor() ) {
|
||
CDnPlayerActor *pPlayer = static_cast<CDnPlayerActor *>(hHitter.GetPointer());
|
||
pPlayer->UpdatePartyCombo( m_nPartyComboCount );
|
||
}
|
||
}
|
||
else {
|
||
m_nPartyComboCount = 0;
|
||
}
|
||
m_nPartyComboDelay = nComboDelay;
|
||
}
|
||
|
||
void CDnMonsterActor::Process_AutoRecallRange()
|
||
{
|
||
if( NULL == m_hSummonerPlayerActor )
|
||
return;
|
||
|
||
if( 0 == m_nAutoRecallRange )
|
||
return;
|
||
|
||
//#55707 현재 공격 상태인 경우 강제 소환 안됨
|
||
bool isAttackState = IsAttack();
|
||
if (isAttackState == true)
|
||
return;
|
||
|
||
EtVector2 vVec;
|
||
vVec.x = m_hSummonerPlayerActor->GetPosition()->x - m_Cross.m_vPosition.x;
|
||
vVec.y = m_hSummonerPlayerActor->GetPosition()->z - m_Cross.m_vPosition.z;
|
||
|
||
float fLength = EtVec2Length(&vVec);
|
||
|
||
if( m_nAutoRecallRange < fLength )
|
||
{
|
||
//#55707 강제 소환시 어그로 리셋 시킴.
|
||
ResetAggro();
|
||
|
||
CmdWarp( *m_hSummonerPlayerActor->GetPosition(), EtVec3toVec2( *m_hSummonerPlayerActor->GetLookDir() ) );
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::ReTransmitSlaveMsg(CDNUserSession* pBreakIntoSession) // 동기맞추기위해 난입 유저에게 보내줄때 사용.
|
||
{
|
||
BYTE pBuffer[32];
|
||
CPacketCompressStream Stream( pBuffer, 32 );
|
||
Stream.Write( &m_dwSummonerActorID, sizeof(DWORD) );
|
||
Stream.Write( &m_bSummoned , sizeof(bool) );
|
||
Stream.Write( &m_nMaxHP, sizeof(INT64) );
|
||
Stream.Write( &m_nHP, sizeof(INT64) );
|
||
Stream.Write( &m_bFollowSummonerStage, sizeof(bool) );
|
||
Stream.Write( &m_bReCreatedFollowStageMonster, sizeof(bool) );
|
||
|
||
#if defined(PRE_FIX_55618)
|
||
//BaseState의 MaxHP/HP/이동 속도 관련 정보를 클라이언트로 보내주고, 이 패킷을 받은 클라이언트는 정보 저장 해놓고
|
||
//더이상 갱신 되지 않도록 m_bCopiedFromSummoner 설정 해준다..
|
||
INT64 nBaseMaxHP = m_BaseState.GetMaxHP();
|
||
float nBaseMaxHPRate = m_BaseState.GetMaxHPRatio();
|
||
|
||
Stream.Write( &nBaseMaxHP, sizeof(INT64) );
|
||
Stream.Write( &nBaseMaxHPRate, sizeof(float) );
|
||
|
||
Stream.Write( &m_nMoveSpeed, sizeof(int) );
|
||
#endif // PRE_FIX_55618
|
||
|
||
Send( pBreakIntoSession , eActor::SC_SLAVE_OF, GetUniqueID() , &Stream );
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::SlaveOf( DWORD dwSummonerActorUniqueID, bool bSummoned/* = false*/, bool bSuicideWhenSummonerDie/* = false*/, bool bFollowSummonerStage/* = false*/, bool bReCreateFollowStageMonster/* = false*/ )
|
||
{
|
||
BYTE pBuffer[32];
|
||
CPacketCompressStream Stream( pBuffer, 32 );
|
||
Stream.Write( &dwSummonerActorUniqueID, sizeof(DWORD) );
|
||
Stream.Write( &bSummoned, sizeof(bool) );
|
||
if( bSummoned )
|
||
{
|
||
Stream.Write( &m_nMaxHP, sizeof(INT64) );
|
||
Stream.Write( &m_nHP, sizeof(INT64) );
|
||
Stream.Write( &bFollowSummonerStage, sizeof(bool) );
|
||
Stream.Write( &bReCreateFollowStageMonster, sizeof(bool) );
|
||
|
||
#if defined(PRE_FIX_55618)
|
||
//BaseState의 MaxHP/HP/이동 속도 관련 정보를 클라이언트로 보내주고, 이 패킷을 받은 클라이언트는 정보 저장 해놓고
|
||
//더이상 갱신 되지 않도록 m_bCopiedFromSummoner 설정 해준다..
|
||
INT64 nBaseMaxHP = m_BaseState.GetMaxHP();
|
||
float nBaseMaxHPRate = m_BaseState.GetMaxHPRatio();
|
||
|
||
Stream.Write( &nBaseMaxHP, sizeof(INT64) );
|
||
Stream.Write( &nBaseMaxHPRate, sizeof(float) );
|
||
|
||
Stream.Write( &m_nMoveSpeed, sizeof(int) );
|
||
#endif // PRE_FIX_55618
|
||
|
||
#if defined(PRE_FIX_61382)
|
||
Stream.Write(&m_isPuppetSummonMonster, sizeof(bool));
|
||
#endif // PRE_FIX_61382
|
||
}
|
||
|
||
Send( eActor::SC_SLAVE_OF, &Stream );
|
||
|
||
m_dwSummonerActorID = dwSummonerActorUniqueID;
|
||
|
||
if( bSummoned )
|
||
{
|
||
DnActorHandle hActor = CDnActor::FindActorFromUniqueID( GetRoom(), dwSummonerActorUniqueID );
|
||
//#53454 꼭두각시 관련 소환 주체가 PlayerActor가 아닌 경우가 생김.
|
||
if( hActor/* && hActor->IsPlayerActor()*/ )
|
||
{
|
||
m_hSummonerPlayerActor = hActor;
|
||
m_bSuicideWhenSummonerDie = bSuicideWhenSummonerDie;
|
||
m_bFollowSummonerStage = bFollowSummonerStage;
|
||
|
||
// 플레이어가 소환하는 몬스터 액터는 pvp/pve 스킬레벨테이블 나뉜것을 적용한다.
|
||
SelectSkillLevelDataType( hActor->GetSelectedSkillLevelDataType(), true );
|
||
}
|
||
}
|
||
|
||
m_bSummoned = bSummoned;
|
||
m_bReCreatedFollowStageMonster = bReCreateFollowStageMonster;
|
||
}
|
||
|
||
// 현재 대포에서만 쓰임.
|
||
void CDnMonsterActor::SlaveRelease( void )
|
||
{
|
||
if( 0 == m_dwSummonerActorID )
|
||
return;
|
||
|
||
BYTE pBuffer[32];
|
||
CPacketCompressStream Stream( pBuffer, 32 );
|
||
Stream.Write( &m_dwSummonerActorID, sizeof(DWORD) );
|
||
Send( eActor::SC_SLAVE_RELEASE, &Stream );
|
||
|
||
if( m_hSummonerPlayerActor )
|
||
{
|
||
m_hSummonerPlayerActor.Identity();
|
||
}
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::OnHitSuccess( LOCAL_TIME LocalTime, DnActorHandle hActor, HitStruct *pStruct )
|
||
{
|
||
// 일반적인 상황에서의 몹은 여기서 할 일이 없으나 렐릭으로 소환된 몹은 소환한 플레이어의 콤보 카운트로 처리해 줘야 함.
|
||
if( m_hSummonerPlayerActor && m_hSummonerPlayerActor->IsPlayerActor() )
|
||
m_hSummonerPlayerActor->OnHitSuccess( LocalTime, hActor, pStruct );
|
||
}
|
||
|
||
void CDnMonsterActor::OnHitFinish( LOCAL_TIME LocalTime, HitStruct *pStruct )
|
||
{
|
||
CDnActor::OnHitFinish( LocalTime, pStruct );
|
||
|
||
if (IsSummonedMonster())
|
||
{
|
||
DnActorHandle hMasterActor = GetSummonerPlayerActor();
|
||
if (hMasterActor)
|
||
{
|
||
int nDieCount = 0;
|
||
for( DWORD i=0; i<m_hVecLastHitList.size(); i++ ) {
|
||
if( m_hVecLastHitList[i] && m_hVecLastHitList[i]->IsDie() ) nDieCount++;
|
||
}
|
||
|
||
hMasterActor->UpdateMissionByMonsterKillCount(nDieCount);
|
||
}
|
||
}
|
||
|
||
if( GetAIBase() )
|
||
GetAIBase()->OnHitFinish( LocalTime, pStruct );
|
||
}
|
||
|
||
void CDnMonsterActor::ResetAggro( void )
|
||
{
|
||
if( m_pAggroSystem )
|
||
m_pAggroSystem->ResetAggro();
|
||
}
|
||
|
||
void CDnMonsterActor::ResetAggro( DnActorHandle hActor )
|
||
{
|
||
if( m_pAggroSystem )
|
||
m_pAggroSystem->ResetAggro( hActor );
|
||
}
|
||
|
||
void CDnMonsterActor::RequestDamageFromStateBlow( DnBlowHandle hFromBlow, int iDamage )
|
||
{
|
||
if( m_bNoDamage ) return;
|
||
CDnActor::RequestDamageFromStateBlow( hFromBlow, iDamage );
|
||
}
|
||
|
||
#ifdef PRE_ADD_MONSTER_CATCH
|
||
void CDnMonsterActor::_CatchThisActor( DnActorHandle hResultActor, int nSignalIndex )
|
||
{
|
||
S_CATCH_ACTOR_INFO CatchActorInfo;
|
||
|
||
CatchActorInfo.hCatchedActor = hResultActor;
|
||
CatchActorInfo.hCatchedActor->SetActionQueue( m_strCatchedActorAction.c_str() );
|
||
m_iCatchedActorActionIndex = CatchActorInfo.hCatchedActor->GetElementIndex( m_strCatchedActorAction.c_str() );
|
||
|
||
// 이동/행동불가 상태효과 추가.
|
||
CatchActorInfo.iCatchCantMoveBlowID = CatchActorInfo.hCatchedActor->CmdAddStateEffect( NULL, STATE_BLOW::BLOW_070, -1, "", false, false );
|
||
CatchActorInfo.iCatchCantActionBlowID = CatchActorInfo.hCatchedActor->CmdAddStateEffect( NULL, STATE_BLOW::BLOW_071, -1, "", false, false );
|
||
|
||
if( CatchActorInfo.hCatchedActor->IsPlayerActor() )
|
||
{
|
||
static_cast<CDnPlayerActor*>(CatchActorInfo.hCatchedActor.GetPointer())->SetCatcherMonsterActor( GetMySmartPtr() );
|
||
}
|
||
|
||
m_vlCatchedActors.push_back( CatchActorInfo );
|
||
|
||
// 클라이언트로 잡았다고 패킷 보냄.
|
||
BYTE pBuffer[ 128 ] = { 0 };
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
DWORD dwCatchedActorID = CatchActorInfo.hCatchedActor->GetUniqueID();
|
||
int nCatchActionIndex = GetCurrentActionIndex();
|
||
int nCatchSignalArrayIndex = nSignalIndex;
|
||
Stream.Write( &dwCatchedActorID, sizeof(DWORD) );
|
||
Stream.Write( &nCatchActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( &nCatchSignalArrayIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
|
||
Send( eActor::SC_CATCH_ACTOR, &Stream );
|
||
}
|
||
|
||
void CDnMonsterActor::CatchActor( CatchActorStruct* pCatchActor, int nSignalIndex )
|
||
{
|
||
// 여러명 잡는 것이 켜져 있지 않다면 한놈만 잡고 있어도 처리하지 않고 넘긴다.
|
||
if( FALSE == pCatchActor->bMultiCatch )
|
||
if( false == m_vlCatchedActors.empty() )
|
||
return;
|
||
|
||
m_strCatchBoneName = pCatchActor->szCatchBoneName;
|
||
m_strTargetActorCatchBoneName = pCatchActor->szTargetActorCatchBoneName;
|
||
|
||
if( pCatchActor->szCatchedActorAction != NULL &&
|
||
0 < strlen(pCatchActor->szCatchedActorAction) )
|
||
{
|
||
m_strCatchedActorAction.assign( pCatchActor->szCatchedActorAction );
|
||
}
|
||
else
|
||
m_strCatchedActorAction.assign( "Hold" );
|
||
|
||
EtVector3 vCatchBonePos( 0.0f, 0.0f, 0.0f );
|
||
GetObjectHandle()->SetCalcAni( true );
|
||
EtMatrix matBoneWorld = GetBoneMatrix( m_strCatchBoneName.c_str() );
|
||
GetObjectHandle()->SetCalcAni( false );
|
||
memcpy_s( &vCatchBonePos, sizeof(EtVector3), &matBoneWorld._41, sizeof(EtVector3) );
|
||
|
||
// 사용하는 본 이름으로 본의 위치를 얻어오고 현재 본 위치 근처에서 시그널에서 설정한 범위보다
|
||
// 거리가 짧은 애가 있는지 확인.
|
||
DNVector( DnActorHandle ) vlhActors;
|
||
ScanActor( GetGameRoom(), vCatchBonePos, pCatchActor->fCatchDistance, vlhActors );
|
||
|
||
// 범위에 높이값만 갖고 체크.
|
||
|
||
float fMinDistanceSQ = FLT_MAX;
|
||
DnActorHandle hResultActor;
|
||
for( int i = 0; i < (int)vlhActors.size(); ++i )
|
||
{
|
||
DnActorHandle hActor = vlhActors.at( i );
|
||
|
||
// 같은 시그널에서 한번 체크가 된 액터는 제외.
|
||
if( m_setCatchCheckedActorIDs.end() != m_setCatchCheckedActorIDs.find( hActor->GetUniqueID() ) )
|
||
continue;
|
||
|
||
// 자기 자신은 패스
|
||
if( GetMySmartPtr() == hActor )
|
||
continue;
|
||
|
||
// 체크가 된 애들은 set 에 곧바로 넣어둠.
|
||
m_setCatchCheckedActorIDs.insert( hActor->GetUniqueID() );
|
||
|
||
// 플레이어 액터만.
|
||
if( false == hActor->IsPlayerActor() )
|
||
continue;
|
||
|
||
// 같은 팀 제외.
|
||
if( GetTeam() == hActor->GetTeam() )
|
||
continue;
|
||
|
||
// CanHit 여부 확인.
|
||
if( FALSE == pCatchActor->bIgnoreCanHit )
|
||
{
|
||
if( false == hActor->CDnActorState::IsHittable() )
|
||
continue;
|
||
|
||
// 무적 상태효과가 켜져 있다면 잡히지 않는다.
|
||
if( hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_099 ) )
|
||
continue;
|
||
}
|
||
|
||
// bMultiCatch 플래그가 꺼져있다면 가장 가까운 녀석을 잡기 처리./
|
||
// 켜져 있다면 범위에만 맞으면 모두 잡기 처리.
|
||
EtVector3 vTargetActorPos = *hActor->GetPosition();
|
||
EtVector3 vDist = vCatchBonePos - vTargetActorPos;
|
||
float fDistanceSQ = EtVec3LengthSq( &vDist );
|
||
if( TRUE == pCatchActor->bMultiCatch ||
|
||
fDistanceSQ < fMinDistanceSQ )
|
||
{
|
||
// 높이도 맞는지 확인.
|
||
bool bHeightMax = vTargetActorPos.y < (pCatchActor->fHeightMax + vCatchBonePos.y);
|
||
bool bHeightMin = (vCatchBonePos.y + pCatchActor->fHeightMin) < vTargetActorPos.y; // HeightMin 값은 음수임.
|
||
if( bHeightMax && bHeightMin )
|
||
{
|
||
bool bCatchFailed = true;
|
||
|
||
// 잡기 실패했을 경우 클라로 보내줄 정보들.
|
||
// 실패한 이유, 0: 패링, 1: 블록, 2: 슈퍼아머로 견딤.
|
||
int iFailedType = -1;
|
||
int iBlowID = 0;
|
||
string strBlockOrParringAction;
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
// 패링 상태효과 무시 여부. 여기선 히트가 아니므로 강제로 관련 상태효과들을 확률 체크.
|
||
bool bPassParring = true;
|
||
if( FALSE == pCatchActor->bIgnoreParring )
|
||
{
|
||
bool bAppliedBlockBlow = hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_030 );
|
||
bool bAppliedParringBlow = hActor->IsAppliedThisStateBlow( STATE_BLOW::BLOW_031 );
|
||
|
||
if( bAppliedBlockBlow || bAppliedParringBlow )
|
||
{
|
||
// 패링이 된 경우엔 패링 액션을 실행시켜줘야 한다..
|
||
// 상태효과쪽에서 HitParam 을 쓰기 때문에 가상으로 만들어서 넘겨준다.
|
||
SHitParam HitParam;
|
||
HitParam.szActionName = m_strCatchedActorAction;
|
||
HitParam.fDamage = 1.0f;// 어차피 여기서 데미지 주는 것은 아니므로 아무값이나 넣으면 됨. 0 이면 블록 상태효과체크에서 패스됨.
|
||
HitParam.bIgnoreParring = false;
|
||
|
||
CDnActorState* pState = static_cast<CDnActorState*>(this);
|
||
bool bSuccess = false;
|
||
|
||
if( bAppliedParringBlow )
|
||
{
|
||
DNVector(DnBlowHandle) vlhBlows;
|
||
hActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_031, vlhBlows );
|
||
if( false == vlhBlows.empty() )
|
||
{
|
||
DnBlowHandle hParringBlow = vlhBlows.front();
|
||
bSuccess = hParringBlow->OnDefenseAttack( GetMySmartPtr(), pState, HitParam, true );
|
||
if( bSuccess )
|
||
{
|
||
iFailedType = 0;
|
||
iBlowID = hParringBlow->GetBlowID();
|
||
}
|
||
}
|
||
}
|
||
|
||
if( (false == bSuccess) && bAppliedBlockBlow )
|
||
{
|
||
DNVector(DnBlowHandle) vlhBlows;
|
||
hActor->GatherAppliedStateBlowByBlowIndex( STATE_BLOW::BLOW_030, vlhBlows );
|
||
if( false == vlhBlows.empty() )
|
||
{
|
||
DnBlowHandle hBlockBlow = vlhBlows.front();
|
||
bSuccess = hBlockBlow->OnDefenseAttack( GetMySmartPtr(), pState, HitParam, true );
|
||
if( bSuccess )
|
||
{
|
||
iFailedType = 1;
|
||
iBlowID = hBlockBlow->GetBlowID();
|
||
}
|
||
}
|
||
}
|
||
|
||
if( bSuccess )
|
||
{
|
||
bPassParring = false;
|
||
// 패링 상태효과나 블록 상태효과에 의해 피격 액션이 변경된 경우 실행하고 넘긴다.
|
||
if( HitParam.szActionName != m_strCatchedActorAction )
|
||
{
|
||
m_hActor->SetActionQueue( HitParam.szActionName.c_str() );
|
||
strBlockOrParringAction = HitParam.szActionName;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 패링이 되지 않았으면 실제로 잡기 처리. 마지막으로 슈퍼아머로 견뎌내는지 체크.
|
||
if( bPassParring )
|
||
{
|
||
if( hActor->CatchCalcSuperArmor( GetMySmartPtr(), pCatchActor->nApplySuperArmorDamage ) )
|
||
{
|
||
// 실제로 슈퍼아머 다되어 잡힌 애.
|
||
// 여러명 잡기 플래그가 켜졌느냐의 여부에 따라 여러명 잡기 처리.
|
||
if( TRUE == pCatchActor->bMultiCatch )
|
||
{
|
||
_CatchThisActor( hActor, nSignalIndex );
|
||
}
|
||
else
|
||
{
|
||
hResultActor = hActor;
|
||
fMinDistanceSQ = fDistanceSQ;
|
||
}
|
||
|
||
bCatchFailed = false;
|
||
}
|
||
else
|
||
{
|
||
iFailedType = 2;
|
||
}
|
||
}
|
||
|
||
// 최종적으로 범위 안에 있는데도 몬스터가 캐릭터 잡기에 실패했을 경우 클라이언트로 알려줘야 한다.
|
||
// 패링이나 블록, 슈퍼아머가 그 이유가 될 수 있음.
|
||
// 블록인 경우에는 클라에서 횟수를 깍아주고, 패링 블록 모두 막는 액션 실행키셔줘야 함.
|
||
if( bCatchFailed )
|
||
{
|
||
BYTE pBuffer[ 128 ] = { 0 };
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
// 실패한 이유, 0: 패링, 1: 블록, 2: 슈퍼아머로 견딤.
|
||
int iFailedType = 0;
|
||
DWORD dwCatchFailedActorID = hActor->GetUniqueID();
|
||
int iParringOrBlockActionIndex = hActor->GetElementIndex( strBlockOrParringAction.c_str() );
|
||
Stream.Write( &iFailedType, sizeof(int), CPacketCompressStream::INTEGER_CHAR );
|
||
Stream.Write( &iBlowID, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
Stream.Write( &dwCatchFailedActorID, sizeof(DWORD) );
|
||
Stream.Write( &iParringOrBlockActionIndex, sizeof(int), CPacketCompressStream::INTEGER_SHORT );
|
||
|
||
Send( eActor::SC_CATCH_ACTOR_FAILED, &Stream );
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
if( hResultActor )
|
||
{
|
||
_CatchThisActor( hResultActor, nSignalIndex );
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::ReleaseThisActor( DnActorHandle hActor )
|
||
{
|
||
vector<S_CATCH_ACTOR_INFO>::iterator iter = m_vlCatchedActors.begin();
|
||
for( iter; iter != m_vlCatchedActors.end(); ++iter )
|
||
{
|
||
S_CATCH_ACTOR_INFO& CatchActorInfo = *iter;
|
||
if( CatchActorInfo.hCatchedActor == hActor )
|
||
{
|
||
ReleaseThisActor( CatchActorInfo );
|
||
m_vlCatchedActors.erase( iter );
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CDnMonsterActor::ReleaseThisActor( S_CATCH_ACTOR_INFO &CatchActorInfo )
|
||
{
|
||
if( CatchActorInfo.hCatchedActor )
|
||
{
|
||
m_setCatchCheckedActorIDs.erase( CatchActorInfo.hCatchedActor->GetUniqueID() );
|
||
|
||
CatchActorInfo.hCatchedActor->CmdRemoveStateEffectFromID( CatchActorInfo.iCatchCantMoveBlowID );
|
||
CatchActorInfo.hCatchedActor->CmdRemoveStateEffectFromID( CatchActorInfo.iCatchCantActionBlowID );
|
||
|
||
// 클라로 놓는다고 패킷 보낸다.
|
||
BYTE pBuffer[ 128 ] = { 0 };
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
DWORD dwCatchActorID = CatchActorInfo.hCatchedActor->GetUniqueID();
|
||
Stream.Write( &dwCatchActorID, sizeof(DWORD) );
|
||
|
||
Send( eActor::SC_RELEASE_ACTOR, &Stream );
|
||
|
||
// TODO: 놓아지는 순간을 알릴 필요가 있다면.. 따로 또 처리하고.
|
||
|
||
if( CatchActorInfo.hCatchedActor->IsPlayerActor() )
|
||
{
|
||
static_cast<CDnPlayerActor*>( CatchActorInfo.hCatchedActor.GetPointer() )->ReleaseCatcherMonsterActor();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void CDnMonsterActor::ReleaseAllActor( void )
|
||
{
|
||
// 체크 셋 비움.
|
||
m_setCatchCheckedActorIDs.clear();
|
||
|
||
// 걸어줬던 상태효과 해제
|
||
for( int i = 0; i < (int)m_vlCatchedActors.size(); ++i )
|
||
{
|
||
S_CATCH_ACTOR_INFO& CatchActorInfo = m_vlCatchedActors.at( i );
|
||
if( CatchActorInfo.hCatchedActor )
|
||
{
|
||
ReleaseThisActor( CatchActorInfo );
|
||
}
|
||
}
|
||
|
||
m_vlCatchedActors.clear();
|
||
}
|
||
|
||
void CDnMonsterActor::ProcessCatchActor( LOCAL_TIME LocalTime, float fDelta )
|
||
{
|
||
vector<S_CATCH_ACTOR_INFO>::iterator iter = m_vlCatchedActors.begin();
|
||
for( iter; iter != m_vlCatchedActors.end(); )
|
||
{
|
||
S_CATCH_ACTOR_INFO& CatchActorInfo = *iter;
|
||
// 잡고 있는 플레이어가 죽었을 때. 놔준다.
|
||
// TODO: 잡고 있는 이 몬스터가 죽었을 때도 놔줘야 한다.
|
||
if( CatchActorInfo.hCatchedActor &&
|
||
false == CatchActorInfo.hCatchedActor->IsDie() &&
|
||
false == IsDie() )
|
||
{
|
||
EtVector3 vCatchBonePos( 0.0f, 0.0f, 0.0f );
|
||
EtMatrix matBoneWorld = GetBoneMatrix( m_strCatchBoneName.c_str() );
|
||
memcpy_s( &vCatchBonePos, sizeof(EtVector3), &matBoneWorld._41, sizeof(EtVector3) );
|
||
|
||
// vCatchBonePos 에 캐릭터 Bip01 을 배치시켜야 한다.
|
||
// 현재 이 몹의 vCatchBonePos 와 잡힐 캐릭터의 Bip01 의 거리를 계산해서
|
||
// 그만큼 위치에 적용시켜준다.
|
||
EtVector3 vPlayerCatchedPointPos( 0.0f, 0.0f, 0.0f );
|
||
CatchActorInfo.hCatchedActor->GetObjectHandle()->SetCalcAni( true );
|
||
matBoneWorld = CatchActorInfo.hCatchedActor->GetBoneMatrix( m_strTargetActorCatchBoneName.c_str() );
|
||
CatchActorInfo.hCatchedActor->GetObjectHandle()->SetCalcAni( false );
|
||
memcpy_s( &vPlayerCatchedPointPos, sizeof(EtVector3), &matBoneWorld._41, sizeof(EtVector3) );
|
||
|
||
EtVector3 vDelta = vCatchBonePos - vPlayerCatchedPointPos;
|
||
EtVector3 vNowCatchedActorPos = *CatchActorInfo.hCatchedActor->GetPosition();
|
||
EtVector3 vCatchedActorPos = vNowCatchedActorPos + vDelta;
|
||
CatchActorInfo.hCatchedActor->SetPosition( vCatchedActorPos );
|
||
|
||
//CatchActorInfo.hCatchedActor->SetActionQueue( m_strCatchedActorAction.c_str() );
|
||
|
||
// 다른 액션을 취하고 있으면 잡기 액션으로 고쳐준다.
|
||
int iCurrentActionIndex = CatchActorInfo.hCatchedActor->GetCurrentActionIndex();
|
||
if( iCurrentActionIndex != m_iCatchedActorActionIndex )
|
||
CatchActorInfo.hCatchedActor->SetActionQueue( m_strCatchedActorAction.c_str() );
|
||
|
||
++iter;
|
||
}
|
||
else
|
||
{
|
||
ReleaseThisActor( CatchActorInfo );
|
||
iter = m_vlCatchedActors.erase( iter );
|
||
}
|
||
}
|
||
|
||
if( false == IsSignalRange( STE_CatchActor ) )
|
||
{
|
||
if( false == m_setCatchCheckedActorIDs.empty() )
|
||
m_setCatchCheckedActorIDs.clear();
|
||
}
|
||
}
|
||
#endif // #ifdef PRE_ADD_MONSTER_CATCH
|
||
|
||
#if defined(PRE_FIX_51048)
|
||
void CDnMonsterActor::AddPassiveStateEffectInfo(PassiveStateEffectInfo& stateEffectInfo)
|
||
{
|
||
m_PassiveStateEffectInfoList.push_back(stateEffectInfo);
|
||
}
|
||
|
||
void CDnMonsterActor::InitPassiveStateEffectInfo()
|
||
{
|
||
m_PassiveStateEffectInfoList.clear();
|
||
}
|
||
|
||
void CDnMonsterActor::ApplyPassiveStateEffect()
|
||
{
|
||
if (m_PassiveStateEffectInfoList.empty())
|
||
return;
|
||
|
||
std::list<PassiveStateEffectInfo>::iterator iter = m_PassiveStateEffectInfoList.begin();
|
||
std::list<PassiveStateEffectInfo>::iterator endIter = m_PassiveStateEffectInfoList.end();
|
||
|
||
for (; iter != endIter; ++iter)
|
||
{
|
||
PassiveStateEffectInfo& passiveStateEffect = (*iter);
|
||
|
||
DnSkillHandle hSkill = FindSkill(passiveStateEffect.pParentSkill->iSkillID);
|
||
if (hSkill)
|
||
{
|
||
// 서버 -> 클라이언트로 패킷 전송을 위해 수정됨
|
||
int iBlowID = CmdAddStateEffect( passiveStateEffect.pParentSkill, passiveStateEffect.emBlowIndex, -1, passiveStateEffect.szParam.c_str(), true ); // Duration Time이 -1 이면 무한 적용임
|
||
OnApplyPassiveSkillBlow( iBlowID );
|
||
|
||
hSkill->SetAppliedPassiveBlows( true );
|
||
}
|
||
}
|
||
}
|
||
|
||
bool CDnMonsterActor::ApplyPassiveSkill( DnSkillHandle hSkill, bool isInitialize/* = false*/ )
|
||
{
|
||
bool bPassiveBuf = false;
|
||
|
||
if( CDnSkill::Passive == hSkill->GetSkillType() )
|
||
{
|
||
int iNumStateEffect = hSkill->GetStateEffectCount();
|
||
for( int i = 0; i < iNumStateEffect; ++i )
|
||
{
|
||
CDnSkill::StateEffectStruct* pSE = hSkill->GetStateEffectFromIndex( i );
|
||
if( CDnSkill::StateEffectApplyType::ApplySelf == pSE->ApplyType )
|
||
{
|
||
if( CDnSkill::DurationTypeEnum::Buff == hSkill->GetDurationType() )
|
||
{
|
||
bPassiveBuf = true;
|
||
if( hSkill->IsSatisfyWeapon() )
|
||
{
|
||
//패킷 순서때문에 상태효과 정보를 리스트에 담아 놓느다.
|
||
if (GetEnablePassiveStateEffectList() == true)
|
||
{
|
||
PassiveStateEffectInfo passiveStateEffectInfo;
|
||
passiveStateEffectInfo.pParentSkill = hSkill->GetInfo();
|
||
passiveStateEffectInfo.emBlowIndex = (STATE_BLOW::emBLOW_INDEX)pSE->nID;
|
||
passiveStateEffectInfo.nDurationTime = -1;
|
||
passiveStateEffectInfo.szParam = pSE->szValue;
|
||
|
||
AddPassiveStateEffectInfo(passiveStateEffectInfo);
|
||
}
|
||
else
|
||
{
|
||
// 서버 -> 클라이언트로 패킷 전송을 위해 수정됨
|
||
int iBlowID = CmdAddStateEffect( hSkill->GetInfo(), (STATE_BLOW::emBLOW_INDEX)pSE->nID, -1, pSE->szValue.c_str(), true ); // Duration Time이 -1 이면 무한 적용임
|
||
OnApplyPassiveSkillBlow( iBlowID );
|
||
|
||
hSkill->SetAppliedPassiveBlows( true );
|
||
}
|
||
|
||
#ifndef _FINAL_BUILD
|
||
char szTemp[256] = { 0, };
|
||
WideCharToMultiByte( CP_ACP, 0, hSkill->GetName(), -1, szTemp, _countof(szTemp), NULL, NULL );
|
||
OutputDebug( "[패시브 버프 스킬 적용됨: %d] \"%s\" 상태효과Index: %d, Value:%s\n", hSkill->GetClassID(), szTemp, pSE->nID, pSE->szValue.c_str() );
|
||
#endif // #ifndef _FINAL_BUILD
|
||
}
|
||
#ifndef _FINAL_BUILD
|
||
else
|
||
{
|
||
char szTemp[256] = { 0, };
|
||
WideCharToMultiByte( CP_ACP, 0, hSkill->GetName(), -1, szTemp, _countof(szTemp), NULL, NULL );
|
||
OutputDebug( "[패시브 버프 스킬 적용안됨: %d] \"%s\" 스킬에 지정된 필요 무기 타입이 맞지 않습니다.\n", hSkill->GetClassID(), szTemp );
|
||
}
|
||
#endif // #ifndef _FINAL_BUILD
|
||
}
|
||
#ifndef _FINAL_BUILD
|
||
else
|
||
{
|
||
char szTemp[256] = { 0, };
|
||
WideCharToMultiByte( CP_ACP, 0, hSkill->GetName(), -1, szTemp, _countof(szTemp), NULL, NULL );
|
||
OutputDebug( "[스킬 데이터 확인 요망: %d] \"%s\" 패시브 버프 스킬로 생각되나 Buff 로 설정되어있지 않아서 적용 안됨\n", hSkill->GetClassID(), szTemp );
|
||
}
|
||
#endif // #ifndef _FINAL_BUILD
|
||
}
|
||
}
|
||
}
|
||
|
||
return bPassiveBuf;
|
||
}
|
||
#endif // PRE_FIX_51048
|
||
|
||
|
||
#if defined(PRE_FIX_61382)
|
||
void CDnMonsterActor::RequestDamageFromStateBlow( DnBlowHandle hFromBlow, int iDamage, CDnDamageBase::SHitParam* pHitParam/* = NULL*/ )
|
||
{
|
||
if (IsPuppetSummonMonster() == true)
|
||
{
|
||
DnActorHandle hOwnerActor = GetSummonerPlayerActor();
|
||
|
||
//데미지 전달 상태효과가 설정 되어 있는 경우만? 데미지 전달 한다.
|
||
if (IsAppliedThisStateBlow(STATE_BLOW::BLOW_051) && hOwnerActor)
|
||
{
|
||
#if defined(PRE_FIX_59347) && !defined(PRE_FIX_67656)
|
||
hOwnerActor->SetApplyPartsDamage(true);
|
||
#endif // PRE_FIX_59347
|
||
|
||
hOwnerActor->RequestDamageFromStateBlow(hFromBlow, iDamage, pHitParam);
|
||
|
||
#if defined(PRE_FIX_59347) && !defined(PRE_FIX_67656)
|
||
hOwnerActor->SetApplyPartsDamage(false);
|
||
#endif // PRE_FIX_59347
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CDnActor::RequestDamageFromStateBlow(hFromBlow, iDamage, pHitParam);
|
||
}
|
||
}
|
||
#endif // PRE_FIX_61382
|
||
|
||
#if defined(PRE_FIX_64312)
|
||
void CDnMonsterActor::SendApplySummonMonsterExSkill(int nBaseSkillID, int nLevel, int nSelectedType, DWORD dwMasterUniqueID, int nMasterExSkillID)
|
||
{
|
||
BYTE pBuffer[128];
|
||
CPacketCompressStream Stream( pBuffer, 128 );
|
||
|
||
Stream.Write(&nBaseSkillID, sizeof(int));
|
||
Stream.Write(&nLevel, sizeof(int));
|
||
Stream.Write(&nSelectedType, sizeof(int));
|
||
Stream.Write(&dwMasterUniqueID, sizeof(DWORD));
|
||
Stream.Write(&nMasterExSkillID, sizeof(int));
|
||
|
||
Send( eActor::SC_APPLY_SUMMON_MONSTER_EX_SKILL, &Stream );
|
||
}
|
||
#endif // PRE_FIX_64312
|