Files
Leviathan/Client/Game/game/Player/SGameMob.cpp
T
2026-06-01 12:46:52 +02:00

1224 lines
38 KiB
C++

#include "stdafx.h"
#include ".\sgamemob.h"
#include "GameDefine.h"
#include "KSeqAvatarEx.h"
#include "KSeqAvatar.h"
#include "SMotionDB.h"
#include "SGameMessage.h"
#include "SMonsterDB.h"
#include "SGameWork.h"
#include "SBasicStat.h"
//#include "Util.h"
#include "SGameAniType.h"
#include "SAvatarProperty.h"
#include <toolkit/XStringUtil.h>
#include "LuaVM.h"
//#include "Util.h"
#include "SAvatarStateMachine.h"
#include "SMonsterStateMachine.h"
#include "SSkillDB.h"
#include "SSkillStageType.h"
#include "SGameUtil.h"
#include <toolkit/bits_scramble.h>
#include "SDebug_Util.h"
const float MOB_LEAVE_TIME = 15000.f; //12초 후에 서버에서 Leave 메시지 날아옴 ( 클라도 그와 비슷하게 처리하자 ( 15초 정도로 ) )
bool SGameMob::s_bForceRender = true;
SGameMob::SGameMob(ENC_INT nMobID) : SGameAvatarEx(nMobID)
, m_nEffectMode( EFFECT_NONE )
, m_dwDeadTime( 0 )
, m_vFixedView( 0.f, 0.f, 0.f )
, m_bIgnoreDamageMotion( false )
, m_bIsUseSkill_complete( true )
, m_fPlayRate( DEFAULT_ANI_PLAY_RATE )
, m_fStandardWalkPlayRate( DEFAULT_ANI_PLAY_RATE )
, m_fStandardRunPlayRate( DEFAULT_ANI_PLAY_RATE )
, m_fWalkSpeed( (float)DEF_SPEED )
, m_fMobLeaveTime( MOB_LEAVE_TIME )
, m_bShadowOff( false )
, m_dwIdleTime( 0 ) // hunee 2008.03.06 몬스터 IDLE 모션 동작 구현
{
K3DMatrixIdentity( m_RotMat );
m_pStateVM = new SLocalMonsterStateMachine; //상태 머신 임시로 만듬
m_pStateVM->SetReceiver( this );
m_pMonsterInfo = GetMonsterDB().GetMonsterData( m_pProperty->ContentID );
if( m_pMonsterInfo )
{
m_fStandardWalkPlayRate = (float)m_pMonsterInfo->standard_walk_speed / 7.0f;
m_fStandardRunPlayRate = (float)m_pMonsterInfo->standard_run_speed / 7.0f;
m_fWalkSpeed = (float)((m_pMonsterInfo->standard_walk_speed + m_pMonsterInfo->standard_run_speed) / 2.0f) / 7.0f;
SetScale( m_pMonsterInfo->scale );
switch( m_pMonsterInfo->fight_type )
{
case 0:
m_nMobType = FIELD_MOB;
break;
case 1:
m_nMobType = FIELD_MOB;
break;
case 2:
m_nMobType = CORE_MOB;
break;
case 3:
m_nMobType = FIELD_MOB;
m_bIgnoreDamageMotion = true;
break;
}
switch( m_pMonsterInfo->monster_type )
{
case MonsterBase::MONSTER_FIELD_MID_BOSS:
case MonsterBase::MONSTER_DUNGEON_MID_BOSS:
case MonsterBase::MONSTER_RANK_C_BOSS:
case MonsterBase::MONSTER_RANK_B_BOSS:
case MonsterBase::MONSTER_RANK_A_BOSS:
case MonsterBase::MONSTER_RANK_S_BOSS:
{
m_bFootsteps = true;
}
break;
}
}
m_bUseRot = false;
m_fTargetRoll = 1.0f;
m_fPresentRoll = 1.0f;
}
SGameMob::~SGameMob()
{
Destroy();
}
int SGameMob::GetNameID()
{
if( m_pMonsterInfo ) return m_pMonsterInfo->name_id;
return 0;
}
int SGameMob::GetSlantType()
{
if( m_pMonsterInfo ) return m_pMonsterInfo->slant_type;
return 0;
}
bool SGameMob::Render( unsigned long uRenderBitVector, class KViewportObject** ppViewportList, int nViewportCount)
{
if( GetForceRender() == false ) return false;
if( m_bShadowOff )
{
if( m_xRenderFlag.IsOn( AV_SHADOW ) )
m_xRenderFlag.Off( AV_SHADOW );
}
return SGameAvatarEx::Render( uRenderBitVector, ppViewportList, nViewportCount);
}
AR_UNIT SGameMob::GetSize()
{
return GetMonsterDB().GetSize( m_pProperty->ContentID() );
}
AR_UNIT SGameMob::GetScale()
{
// TODO : m_fScale을 왜 곱하는 걸까?
// return GetMonsterDB().GetScale( m_pProperty->ContentID() ) * m_fScale;
return m_fScale;
}
void SGameMob::SetStatus( unsigned status )
{
SGameAvatarEx::SetStatus( status );
//다른거 처리 할게 있을까?
if( (GetStatus() & TS_ENTER::PlayerInfo::FLAG_BATTLE_MODE) )
SetAttackMode();
}
void SGameMob::SetMountMode()
{
if( m_pStateVM )
m_pStateVM->SetMode( SObjectStateMachine::MODE_MOUNT );
}
void SGameMob::SetAttackMode()
{
if( m_pStateVM )
m_pStateVM->SetMode( SObjectStateMachine::MODE_ATTACK );
}
void SGameMob::SetNormalMode()
{
if( m_pStateVM )
m_pStateVM->SetMode( SObjectStateMachine::MODE_NORMAL );
}
bool SGameMob::Process( DWORD time, unsigned long uProcessBitVector )
{
//Load 검사?
if( !m_bIsActivated || !m_bIsInit ) return false;
SGameAvatarEx::Process( time, uProcessBitVector );
_processIdle(); // hunee 2008.03.06 몬스터 IDLE 모션 동작 구현
if( !m_vCastSkillList.empty() )
CheckWorkDel( m_vCastSkillList );
if( !m_vFireSkillList.empty() )
CheckWorkDel( m_vFireSkillList );
for( unsigned int i(0); m_vCastSkillList.size()>i; i++ )
m_vCastSkillList[i]->Process( m_dwTime );
for( unsigned int i(0); m_vFireSkillList.size()>i; i++ )
m_vFireSkillList[i]->Process( m_dwTime );
if( m_nEffectMode == EFFECT_01 )
{
if( (time - m_dwEffectStartTime) >= m_dwEffectKeepTime )
{
m_nEffectMode = EFFECT_NONE;
m_dwEffectStartTime = 0;
m_dwEffectKeepTime = 0;
}
else
{ //흔들기
K3DMatrix mat;
static float fDel = 0.3f;
K3DMatrixRotationZ( m_RotMat, fDel );
fDel = -fDel;
mat = m_RotMat;
mat._41 = GetTransform()->_41;
mat._42 = GetTransform()->_42;
mat._43 = GetTransform()->_43;
m_pSeqAvatar->SetTransform( mat );
}
/* if( m_nEffectMode == 1 )
{
static K3DVector vUpVector = K3DVector( 0.f, 0.f, 1.f );
K3DVector vPos = m_vOriPos;
static float fvibrate = 10.f;
if( (m_dwTime - m_dwEffectStartTime) >= m_dwEffectKeepTime )
{
m_nEffectMode = 0;
m_dwEffectStartTime = 0;
m_dwEffectKeepTime = 0;
vUpVector = K3DVector( 0.f, 0.f, 1.f );
vPos = m_vOriPos;
fvibrate = 0.f;
}
else
{
K3DVector vViewVector = GetViewVector();
K3DVectorCross( &vUpVector, &vViewVector, &vUpVector );
vPos += vUpVector * fvibrate;
vPos.z = GetTransform()->_43;
m_pSeqAvatar->SetPosition( vPos.x, vPos.y, vPos.z );
vUpVector.z = -vUpVector.z;
vPos = m_vOriPos;
fvibrate -= -0.1f;
if( fvibrate <= 0.f ) fvibrate = 0.f;
}
}*/
}
if( m_dwStartTime == 0 ) m_dwStartTime = time;
if( IsDead() && m_dwDeadTime == 0 )
{
if( !IsPlaying() ) //죽는 모션이 끝나고 나서 Visible처리
{
int nBoneCnt = 0;
DWORD dwMinTime = 0, dwMaxTime = 0;
DWORD dwCurAniTime = 0;
GetCurrentAnimationInfo( nBoneCnt, dwMinTime, dwMaxTime, dwCurAniTime );
float fMaxTime = (float)dwMaxTime / 160.0f;
float fMinTime = (float)dwMinTime / 160.0f;
fMaxTime = ((fMaxTime * 4.8f) / 100.0f ) * 1000.0f;
fMinTime = ((fMinTime * 4.8f) / 100.0f ) * 1000.0f;
float fAnitime = fMaxTime - fMinTime;
m_fMobLeaveTime = MOB_LEAVE_TIME - fAnitime; //죽는 모션 시간만큼 LeaveTime에서 뺀다
//죽었다.
m_dwDeadTime = m_dwTime;
}
//아브흐바만 예외 처리
//GetContentID() <- 본 서버 용
//GetInnContentID() <- 테스트 서버 용
if( GetInnContentID() == bits_scramble< int, 3 >( 9050013 ) )
{
m_bShadowOff = true;
}
}
if( m_bPlayOverride )
{
// CheckPendingSeq() == false면 Pending된 Seq가 없다는 뜻
if( m_pSeqAvatar && !m_pSeqAvatar->CheckPendingSeq() )
{
FindNewEvent();
m_bPlayOverride = false;
}
}
if( IsLive() )
{
if( m_fVisibility < 1.f )
{
m_fVisibility = (float(m_dwTime - m_dwStartTime)/3000.f);
if( m_fVisibility >= 1.f ) m_fVisibility = 1.f;
SetVisibility( m_fVisibility );
// _oprint( "Live Visibility : %f\n", m_fVisibility );
}
}
else
{
if( m_dwDeadTime == 0 ) return true;
if( m_nMobType == CORE_MOB ) return true;
if( m_fVisibility > 0.01f && ((float)(m_dwTime-m_dwDeadTime) < m_fMobLeaveTime) )
{
float fValue = 1.f - (m_dwTime-m_dwDeadTime)/m_fMobLeaveTime;
// _oprint( "Dead Visibility : %f %d-%d=%d\n", fValue, m_dwTime, m_dwStartTime, m_dwTime-m_dwDeadTime );
if( m_fVisibility > fValue )
{
m_fVisibility = fValue;
if( m_fVisibility < 0.02f ) m_fVisibility = 0.02f;
SetVisibility( m_fVisibility );
// _oprint( "Dead Visibility : %f\n", m_fVisibility );
}
}
}
return true;
}
void SGameMob::ItemDrop()
{
for( unsigned int i(0); m_arItemHandleList.size()>i; i++ )
DropItem( m_arItemHandleList[i] );
m_arItemHandleList.clear();
}
//떨굴 Item 핸들
void SGameMob::SetDropItemInfo( AR_HANDLE itemhandle )
{
m_arItemHandleList.push_back( itemhandle );
}
void SGameMob::NPlayAnimation( int nType , int nAniType, float fPlayRate )
{
// _oprint( "Mob Ani : %d\n", nType );
SGameAvatarEx::NPlayAnimation( nType , nAniType, fPlayRate );
}
void SGameMob::Damage()
{
// _oprint( "Damage()\n" );
//이펙트 설정
m_nEffectMode = EFFECT_01;
m_dwEffectStartTime = m_dwTime;
m_dwEffectKeepTime = 2000;
_damage();
if( m_bIgnoreDamageMotion ) return;
if( IsUsingSkill() ) return;
//이미 공격 중이면, 모션 플레이 안됨.
if( GetAttackAniLock() )
return;
////이동 중, 공격 중 에는 영향 안 받는다.
if( GetCurrAnimationID() == ANI_WALK ||
GetCurrAnimationID() == ANI_RUN ||
GetCurrAnimationID() == ANI_DEAD01 )
return;
// hunee 2009.03.06 몬스터 IDLE 모션 동작 구현
if( GetCurrAnimationID() == ANI_DEFAULT01 || GetCurrAnimationID() == ANI_IDLE )
NPlayAnimation( ANI_DAMAGE01, SEQTYPE_NORMAL );
if( GetCurrAnimationID() == ANI_DAMAGE01 && !IsPlaying() )
{
NPlayAnimation( ANI_DAMAGE01, SEQTYPE_NORMAL );
}
else if( GetCurrAnimationID() == ANI_DAMAGE02 && !IsPlaying() )
{
NPlayAnimation( ANI_DAMAGE02, SEQTYPE_NORMAL );
}
/*
// sonador 1.8.12 몬스터 피격모션2 추가
#if defined( _PROC_DAMAGE02 ) // sonador 1.8.14 몬스터 피격모션2 삭제
if( m_pStateVM->GetMode() == SObjectStateMachine::MODE_ATTACK )
{
if( GetCurrAnimationID() != ANI_DAMAGE02 )
NPlayAnimation( ANI_DAMAGE02 );
}
else if( m_pStateVM->GetMode() == SObjectStateMachine::MODE_NORMAL )
{
if( GetCurrAnimationID() != ANI_DAMAGE01 )
NPlayAnimation( ANI_DAMAGE01 );
}
#else
// sonador 1.8.7 몬스터 공격중 피격 연출 오류 수정
NPlayAnimation( ANI_DAMAGE01 );
#endif*/
}
void SGameMob::Default( bool bForce/*=false*/ )
{
if( m_pStateVM == NULL ) return;
if( IsDead() )
return;
if( CurrentlyAnimationIsAttack() && IsPlaying() )
return;
if( m_pStateVM->GetMode() == SObjectStateMachine::MODE_ATTACK )
{
if( GetCurrAnimationID() != ANI_DEFAULT02 )
NPlayAnimation( ANI_DEFAULT02, SEQTYPE_LOOP );
}
else if( m_pStateVM->GetMode() == SObjectStateMachine::MODE_NORMAL )
{ //
_resetIdleTime(); // hunee 2008.03.06 몬스터 IDLE 모션 동작 구현
if( GetCurrAnimationID() != ANI_DEFAULT01 )
NPlayAnimation( ANI_DEFAULT01, SEQTYPE_LOOP );
}
// hunee 2008.03.06 몬스터 IDLE 모션 동작 구현
else //if( m_pStateVM->GetMode() == SObjectStateMachine::MODE_MOUNT )
{
if( GetCurrAnimationID() != ANI_DEFAULT01 )
NPlayAnimation( ANI_DEFAULT01, SEQTYPE_LOOP );
}
}
void SGameMob::Walk( bool bLoop )
{
if( GetCurrAnimationID() != ANI_WALK || m_fCurPlayRate != m_fPlayRate )
NPlayAnimation( ANI_WALK, SEQTYPE_LOOP, m_fPlayRate );
}
void SGameMob::Run( bool bAutoRun, int nAniType )
{
//if( GetCurrAnimationID() != nAniType || m_fCurPlayRate != m_fPlayRate )
if( GetCurrAnimationID() != nAniType )
NPlayAnimation( nAniType, SEQTYPE_LOOP, m_fPlayRate );
}
// sonaodr 1.8.13 아바타 IDLE 모션 동작 구현
void SGameMob::_processIdle()
{
if( GetCurrAnimationID() == ANI_IDLE )
{
if( !IsPlaying() )
NPlayAnimation( ANI_DEFAULT01, SEQTYPE_LOOP );
}
else if( GetCurrAnimationID() == ANI_DEFAULT01 )
{
int timeDiff = m_dwTime - m_dwIdleTime;
int rand = XFastRandom( 1, 10000000 );
if( rand < (int)timeDiff )
{
NPlayAnimation( ANI_IDLE, SEQTYPE_NORMAL );
_resetIdleTime();
}
}
}
void SGameMob::OnNetInput( struct SGameMessage * pMsg )
{
switch(pMsg->nType)
{
case MSG_MOVE :
{
SMSG_MOVE* pMoveAck = static_cast<SMSG_MOVE*>(pMsg);
if( pMoveAck->speed == 0 )
{
//달리다 죽은 것이므로 Ani 처리는 스킵한다.
return;
}
//보통 일 경우만 이동
if(pMoveAck->speed < m_fWalkSpeed)
{
CalcCreatureWalkPlayRate( (float)pMoveAck->speed );
Walk();
}
else
{
CalcCreatureRunPlayRate( (float)pMoveAck->speed );
Run(); //Speed 재설정..
}
m_speed = pMoveAck->speed;
SetViewVector( pMoveAck->pos_target ); //몸통 방향 바꾸기
break;
}
case MSG_ATTACK :
{
SMSG_ATTACK* pAttackMsg = static_cast<SMSG_ATTACK*>(pMsg);
if( pAttackMsg->attacker_handle != GetArID() )
return;
if( pAttackMsg->m_vAttackInfoList.empty() ) return;
SetViewVector( pAttackMsg->pos_target ); //몸통 방향 바꾸기
float fPlayRate = Attack( pAttackMsg->target_handle, GetCurPosWithChangeDir(), pAttackMsg->attack_speed, ANI_ATTACK01 );
SetAttackAniLock( true );
//타겟 핸들, 현재 이벤트 키, 이벤트 전체 길이, 현재 시간, 공격 스피드
SWorkAttackEx * pAttackEx = new SWorkAttackEx( this, pAttackMsg->target_handle, fPlayRate );
pAttackEx->SetEventHandle( m_dwTime, pAttackMsg->attack_speed,
m_pCurEventHandle00_effect,
m_pCurEventHandle00_sound,
m_pCurEventHandle00_motion_effect,
m_pCurEventHandle01,
m_pCurEventHandle02,
m_pCurEventHandle03,
m_pCurEventHandle04,
m_pCurEventHandle05,
m_pCurEventHandle06 );
pAttackEx->SetDamegeMsg( pAttackMsg );
m_vAttackEventList.push_back( pAttackEx );
//공격 이펙트는 나올 필요가 없다.
SetEventHandleNull();
break; //공격 시작
}
// 2010.07.16 - prodongi
case MSG_STATUS_CHANGE:
{
SMSG_STATUS_CHANGE * status = (SMSG_STATUS_CHANGE*)pMsg;
if(status->status & TS_ENTER::MonsterInfo::FLAG_DEAD)
{
recvDead();
if( m_pStateVM )
{
m_pStateVM->OnNetInput( pMsg );
}
}
}
break;
default:
{
if( m_pStateVM )
{
m_pStateVM->OnNetInput( pMsg );
return;
}
}
break;
}
}
void SGameMob::SetViewVectorAngle( float fAngle )
{
K3DVector vCur = GetCurPosWithChangeDir();
//방향 설정
K3DMatrix mat, matRot, matTrans;
K3DMatrixIdentity(matRot);
K3DMatrixIdentity(matTrans);
//무작위 회전
/* float fRandom = (float)rand() / (float)RAND_MAX;
fAngle = ((fRandom * (float)fabs(360.0f-1.0f))+1.0f);*/
//북쪽이 기준이다
K3DMatrixRotationZ( matRot, (fAngle+270.f) * ( (D3DX_PI * 2.f) / 360.f ) );
GetHeight( vCur.x, vCur.y, vCur.z,m_wCurTile );
K3DMatrixTranslation( matTrans, vCur.x, vCur.y, vCur.z );
K3DMatrixMultiply( mat, matTrans, matRot );
SetTransform(mat);
//북쪽이 기준
K3DVectorTransform( m_vFixedView, K3DVector( 0.f, -1.f, 0.f ), matRot );
K3DVector vPos = *GetPosition();
vPos += m_vFixedView * 5.0f;
m_fTargetRoll = m_fPresentRoll = atan2( vPos.y-GetPosition()->y, vPos.x-GetPosition()->x );
}
//void SGameMob::SetMobType( int nFightType )
//{
// switch( nFightType )
// {
// case 0: m_nMobType = FIELD_MOB; break;
// case 1: m_nMobType = FIELD_MOB; break;
// case 2: m_nMobType = CORE_MOB; break;
// }
//}
void SGameMob::CalcCreatureRunPlayRate( float fSpeed )
{
//#ifdef _DEBUG
m_fPlayRate = DEFAULT_ANI_PLAY_RATE * ( (fSpeed / m_fStandardRunPlayRate) / m_fScale );
//#else
// m_fPlayRate = DEFAULT_ANI_PLAY_RATE;
//#endif
}
void SGameMob::CalcCreatureWalkPlayRate( float fSpeed )
{
//#ifdef _DEBUG
m_fPlayRate = DEFAULT_ANI_PLAY_RATE * ( (fSpeed / m_fStandardWalkPlayRate) / m_fScale );
//#else
// m_fPlayRate = DEFAULT_ANI_PLAY_RATE;
//#endif
}
void SGameMob::OnChangeState( SObjectState::ID stateid, const SStateInfo &info )
{
//상태가 바뀌었다.. 애니메이션 바꾸자, Work 기반으로
switch( stateid )
{
case SObjectState::STATE_CAST :
{
m_bIsUseSkill_complete = false;
_SKILL_FX* pSkillFXDB = GetSkillStageDB().GetSkillStageData( info.nSkillID );
_oprint( "스킬 캐스팅 시작! [스킬ID:%d]\n", info.nSkillID );
if( !pSkillFXDB )
{
_oprint( "스킬 연출 데이타 없음!!! [스킬ID:%d]\n", info.nSkillID );
return;
}
_SKILL_FX* pSkillFX = new _SKILL_FX;
*pSkillFX = *pSkillFXDB;
float fRate = 1.f;
if( pSkillFX->nCasting_Type_Id != 1 && (pSkillFX->nCasting_Start_Motion_Id !=0 || pSkillFX->nCasting_Middle_Motion_Id != 0) )
{
pSkillFX->nCasting_Start_Motion_Id = 110;
pSkillFX->nCasting_Middle_Motion_Id = 111;
pSkillFX->nFire_Motion_Id = 112;
std::string strAniKey;
int nBoneCount = 0;
DWORD dwMinTime, dwMaxTime, totalTime;
dwMinTime = 0;
dwMaxTime = 0;
totalTime = 0;
if( pSkillFX->nCasting_Start_Motion_Id != 0 )
{
strAniKey = GetAniKey( GetObjType(), pSkillFX->nCasting_Start_Motion_Id, true );
if( m_pSeqAvatar )
m_pSeqAvatar->GetFrameInfo( ANIPART_BIPED, strAniKey.c_str(), nBoneCount, dwMinTime, dwMaxTime );
totalTime = dwMaxTime;
}
if( pSkillFX->nCasting_Middle_Motion_Id )
{
strAniKey = GetAniKey( GetObjType(), pSkillFX->nCasting_Middle_Motion_Id, true );
if( m_pSeqAvatar )
m_pSeqAvatar->GetFrameInfo( ANIPART_BIPED, strAniKey.c_str(), nBoneCount, dwMinTime, dwMaxTime );
totalTime += dwMaxTime;
}
float fAttack = GetAttackSpeed( info.dwSpeed*10 );
//기본 배율
fRate = GetAniPlayRate( fAttack, totalTime, false );
if (!ExistAnimation(pSkillFX->nCasting_Start_Motion_Id) || !ExistAnimation(pSkillFX->nCasting_Middle_Motion_Id) || !ExistAnimation(pSkillFX->nFire_Motion_Id))
{
pSkillFX->nCasting_Start_Motion_Id = ANI_ATTACK01;
pSkillFX->nCasting_Middle_Motion_Id = ANI_ATTACK01;
pSkillFX->nFire_Motion_Id = ANI_ATTACK01;
}
// 위에서 계산한 배율은 잘못되었음 배율 다시계산 ㄱㄱ
SkillBaseEx* pSkillData = GetSkillDB().GetSkillData( info.nSkillID );
if ( pSkillData && pSkillData->GetCastDelay(1) > FLT_EPSILON ) // 0보다 크면
{
if ( pSkillFX->nCasting_Start_Motion_Id == ANI_ATTACK01)
{
DWORD aniLength = GetAnimationLength(pSkillFX->nCasting_Start_Motion_Id);
if (aniLength > 0)
{
float aniLenSec = aniLength / 4800.0f; // 초단위로 바꿈
float delay = (pSkillData->GetCastDelay(1) + pSkillData->delay_common) / 100.0f; // 초단위로 바꿈 ; 원래 1/100초 단위
fRate = ( aniLenSec / delay );
}
}
else
{
if (totalTime > 0)
{
float aniLenSec = totalTime / 4800.0f; // 초단위로 바꿈
float delayToFire = pSkillData->GetCastDelay(1) / 100.0f; // 초단위로 바꿈 ; 원래 1/100초 단위
fRate = ( aniLenSec / delayToFire );
}
}
}
}
else if( pSkillFX->nCasting_Type_Id == 1 )
{
pSkillFX->nCasting_Start_Motion_Id = 70;
pSkillFX->nCasting_Middle_Motion_Id = 80;
}
SNewSkill * pSkill_Cast = NULL;
SGameAvatarEx * pTarget = NULL;
if( info.hTarget )
{
pTarget = (SGameAvatarEx *)GetGameObject( info.hTarget );
}
switch( pSkillFX->nStage_Type_Id )
{
case SS_TYPE_001 : { SWorkSummonCall * pSkill = new SWorkSummonCall( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_002 : { SWorkSummonReCall * pSkill = new SWorkSummonReCall( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_101 : { SWorkWeaponSwingHit * pSkill = new SWorkWeaponSwingHit( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_111 : { SWorkShieldSwingHit * pSkill = new SWorkShieldSwingHit( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_121 : { SWorkWeaponSwing_3Motion * pSkill = new SWorkWeaponSwing_3Motion( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_122 : { SWorkWeaponSwing_ContinuousHits * pSkill = new SWorkWeaponSwing_ContinuousHits( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_123 : { SWorkWeaponSwing_Zeal * pSkill = new SWorkWeaponSwing_Zeal( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_151 : { SWorkWeaponSwing_Leech * pSkill = new SWorkWeaponSwing_Leech( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_201 : { SWorkBowShoot* pSkill = new SWorkBowShoot( this, pSkillFX ); pSkill_Cast = pSkill; fRate = 1.f; } break;
case SS_TYPE_212 : { SWorkBowShoot* pSkill = new SWorkBowShoot( this, pSkillFX ); pSkill_Cast = pSkill; fRate = 1.f; } break;
case SS_TYPE_251 : { SWorkBowShoot* pSkill = new SWorkBowShoot( this, pSkillFX ); pSkill_Cast = pSkill; fRate = 1.f; } break;
case SS_TYPE_301 : { SWorkSelf * pSkill = new SWorkSelf( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_401 :
case SS_TYPE_404 :
{ SWorkMotion * pSkill = new SWorkMotion( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_402 : { SWorkMotion_Leech * pSkill = new SWorkMotion_Leech( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_403 : { SWorkMotion_SummonMotion_Leech * pSkill = new SWorkMotion_SummonMotion_Leech( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_411 : { SWorkMotion_FXMotion * pSkill = new SWorkMotion_FXMotion( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_412 : { SWorkMotion_FXMotionMultiple * pSkill = new SWorkMotion_FXMotionMultiple( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_501 : { SWorkShoot_FXShoot * pSkill = new SWorkShoot_FXShoot( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_502 : { SWorkShoot_FXShootMultiple * pSkill = new SWorkShoot_FXShootMultiple( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_503 : { SWorkShoot_FXShootPenetrate * pSkill = new SWorkShoot_FXShootPenetrate( this, pSkillFX ); pSkill_Cast = pSkill; } break;
/// 2011.03.23 - prodongi
case SS_TYPE_504 : { SWorkShoot_FXShootChain * pSkill = new SWorkShoot_FXShootChain( this, pSkillFX ); pSkill_Cast = pSkill; } break;
case SS_TYPE_601 : { SWorkGround_FXContinue * pSkill = new SWorkGround_FXContinue( this, pSkillFX ); pSkill_Cast = pSkill; } break;
default:
{
_oprint( "구현 보류 중인 스킬입니다.기획팀에 사용 여부를 확인 후, 프로그램 팀에 구현 여부를 확인 바랍니다.\n" );
// assert( 0 && "구현 보류 중인 스킬입니다.기획팀에 사용 여부를 확인 후, 프로그램 팀에 구현 여부를 확인 바랍니다.\n" );
}
break;
}
_oprint("nCasting_Type_Id = %d \n", pSkillFX->nCasting_Type_Id);
if( pSkill_Cast )
{ //변경 되었으면, PlayRate 수정
if( fRate != 1.f && pSkillFX->nCasting_Type_Id != 1 ) //마법류는 PlayRate 수정 하지 않는다.
{
pSkill_Cast->SetPlayRate( fRate ); // by 정동섭. fRate/4.8이 아니라 그냥 fRate 인 듯???
}
else if (pSkillFX->nCasting_Type_Id == 1)
{
}
if( pTarget )
{
pSkill_Cast->SetTarget( info.hTarget, pTarget );
// 2010.05.25 몹은 시체 흡수 같은 스킬이 안나온다고 합니다만, 적용은 해 놓습니다.- prodongi
//흡수체 스킬류
if(pSkillFX->nStage_Type_Id == SS_TYPE_402 )
{
//대상이 삭제될수 있으이 보류시킨다
pTarget->SetReservation(true);
}
}
pSkill_Cast->SetStartTime( m_dwTime );
pSkill_Cast->SetSkillMode( SNewSkill::SKILLMODE_CAST );
m_vCastSkillList.push_back( pSkill_Cast );
m_nWeaponSwing_RepeatMotion = 0;
if ((pSkillFX->nCasting_Type_Id == 4) && (pSkillFX->nCasting_Start_Motion_Id == 110)) // 밀리스킬일경우
{
pSkill_Cast->SetIsMeleeSkill(true);
}
}
else
SAFE_DELETE( pSkillFX );
break; // 캐스팅
}
case SObjectState::STATE_FIRE :
{
//SkillResult가 0이면 Fire실패 Default모션으로
if(info.vSkillResult.empty())
{
m_bIsUseSkill_complete = true;
for( unsigned int i(0); m_vCastSkillList.size()>i; i++ )
m_vCastSkillList[i]->ForceEnd();
SetAniLock( false );
Default();
return;
}
_SKILL_FX* pSkillFXDB = GetSkillStageDB().GetSkillStageData( info.nSkillID );
if( !pSkillFXDB )
{
_oprint( "스킬 연출 데이타 없음!!! [스킬ID:%d]\n", info.nSkillID );
return;
}
_SKILL_FX* pSkillFX = new _SKILL_FX;
*pSkillFX = *pSkillFXDB;
if( pSkillFX->nCasting_Type_Id == 1 )
pSkillFX->nFire_Motion_Id = 90;
else
pSkillFX->nFire_Motion_Id = 112;
if (!ExistAnimation(pSkillFX->nFire_Motion_Id))
{
pSkillFX->nFire_Motion_Id = ANI_ATTACK01;
}
SNewSkill * pSkill_Fire = NULL;
SGameAvatarEx * pTarget = NULL;
if( info.hTarget )
{
pTarget = (SGameAvatarEx *)GetGameObject( info.hTarget );
}
switch( pSkillFX->nStage_Type_Id )
{
case SS_TYPE_001 : { SWorkSummonCall * pSkill = new SWorkSummonCall( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_002 : { SWorkSummonReCall * pSkill = new SWorkSummonReCall( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_101 : { SWorkWeaponSwingHit * pSkill = new SWorkWeaponSwingHit( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_111 : { SWorkShieldSwingHit * pSkill = new SWorkShieldSwingHit( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_121 : { SWorkWeaponSwing_3Motion * pSkill = new SWorkWeaponSwing_3Motion( this, pSkillFX ); pSkill_Fire = pSkill; pSkill->SetCurHitCount( m_nWeaponSwing_RepeatMotion ); } break;
case SS_TYPE_122 : { SWorkWeaponSwing_ContinuousHits * pSkill = new SWorkWeaponSwing_ContinuousHits( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_123 : { SWorkWeaponSwing_Zeal * pSkill = new SWorkWeaponSwing_Zeal( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_151 : { SWorkWeaponSwing_Leech * pSkill = new SWorkWeaponSwing_Leech( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_201 : { SWorkBowShoot* pSkill = new SWorkBowShoot( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_212 : { SWorkBowShoot* pSkill = new SWorkBowShoot( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_251 : { SWorkBowShoot* pSkill = new SWorkBowShoot( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_301 : { SWorkSelf * pSkill = new SWorkSelf( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_401 :
case SS_TYPE_404 :
{ SWorkMotion * pSkill = new SWorkMotion( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_402 : { SWorkMotion_Leech * pSkill = new SWorkMotion_Leech( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_403 : { SWorkMotion_SummonMotion_Leech * pSkill = new SWorkMotion_SummonMotion_Leech( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_411 : { SWorkMotion_FXMotion * pSkill = new SWorkMotion_FXMotion( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_412 : { SWorkMotion_FXMotionMultiple * pSkill = new SWorkMotion_FXMotionMultiple( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_501 : { SWorkShoot_FXShoot * pSkill = new SWorkShoot_FXShoot( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_502 : { SWorkShoot_FXShootMultiple * pSkill = new SWorkShoot_FXShootMultiple( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_503 : { SWorkShoot_FXShootPenetrate * pSkill = new SWorkShoot_FXShootPenetrate( this, pSkillFX ); pSkill_Fire = pSkill; } break;
/// 2011.03.23 - prodongi
case SS_TYPE_504 : { SWorkShoot_FXShootChain * pSkill = new SWorkShoot_FXShootChain( this, pSkillFX ); pSkill_Fire = pSkill; } break;
case SS_TYPE_601 : { SWorkGround_FXContinue * pSkill = new SWorkGround_FXContinue( this, pSkillFX ); pSkill_Fire = pSkill; } break;
default:
{
_oprint( "구현 보류 중인 스킬입니다.기획팀에 사용 여부를 확인 후, 프로그램 팀에 구현 여부를 확인 바랍니다.\n" );
// assert( 0 && "구현 보류 중인 스킬입니다.기획팀에 사용 여부를 확인 후, 프로그램 팀에 구현 여부를 확인 바랍니다.\n" );
}
break;
}
if ((pSkillFX->nCasting_Type_Id == 4) && (pSkillFX->nFire_Motion_Id == 112)) // 밀리스킬일경우
{
pSkill_Fire->SetIsMeleeSkill(true);
}
// 스킬의 fire 애니메이션 속도를 SkillBase::delay_common에 때려맞추는 코드 (날림...) by 정동섭 2009.3.30
if (pSkill_Fire != NULL)
{
SkillBaseEx* pSkillData = GetSkillDB().GetSkillData( info.nSkillID );
if ( pSkillData && pSkillData->delay_common > FLT_EPSILON ) // 0보다 크면
{
DWORD aniLength = GetAnimationLength(pSkillFX->nFire_Motion_Id);
if (aniLength > 0)
{
float aniLenSec = aniLength / 4800.0f; // 초단위로 바꿈
float delayAfterFire = pSkillData->delay_common / 100.0f; // 초단위로 바꿈 ; 원래 1/100초 단위
pSkill_Fire->SetPlayRateAfterFire( aniLenSec / delayAfterFire );
}
}
}
if( pSkillFX->nStage_Type_Id == SS_TYPE_121 )
{
//최대 연타 수 ( DB에 정의됨 )
if( m_nWeaponSwing_RepeatMotion < pSkillFXDB->fStage_Data[9] )
++m_nWeaponSwing_RepeatMotion;
else
m_nWeaponSwing_RepeatMotion = 0;
}
else
m_nWeaponSwing_RepeatMotion = 0;
if( pSkill_Fire )
{
//SAction에서 데미지 처리하고 있기 때문에 데미지 표시가 2번뜬다
switch( pSkillFX->nStage_Type_Id )
{
case SS_TYPE_001 :
case SS_TYPE_002 :
case SS_TYPE_101 :
case SS_TYPE_111 :
case SS_TYPE_121 :
case SS_TYPE_122 :
case SS_TYPE_123 : pSkill_Fire->SetDamegeInfo( &info );
break;
default:
pSkill_Fire->SetActionSkillEvent( &info );
break;
}
//돌고 있는 케스팅은 모두 강제 끝
for( unsigned int i(0); m_vCastSkillList.size()>i; i++ )
m_vCastSkillList[i]->ForceEnd();
if( pSkillFX->nCasting_Type_Id == 1 ) //마법류 스킬이면
{
//5번 타인에게 사용하는 해로운 스킬
pSkill_Fire->SetFireMotionID( 90 ); //나쁜 스킬 설정
//1번 소환/역소환 스킬이면
if( pSkillFX->nStage_Type_Id == 1 || pSkillFX->nStage_Type_Id == 2 )
pSkill_Fire->SetFireMotionID( 94 ); //소환 스킬은 없을듯
else
{
SkillBaseEx * pSkill = GetSkillDB().GetSkillData( info.nSkillID );
if( pSkill )
{
//2번 자신에게만 주는 이로운 스킬
if( (pSkill->GetSkillTargetType() == 1 || pSkill->GetSkillTargetType() == 4 || pSkill->GetSkillTargetType() == 31 ) && info.hTarget == GetArID() )
{
pSkill_Fire->SetFireMotionID( 90 );
}
else
{
//3번 자신의 주변에 영향을 주는 스킬
if( info.hTarget == GetArID() )
pSkill_Fire->SetFireMotionID( 90 );
else
{
//4번 타인에게 사용하는 이로운 스킬
if( !pSkill->IsHarmful() )
pSkill_Fire->SetFireMotionID( 90 );
}
}
}
}
}
if( pTarget )
pSkill_Fire->SetTarget( info.hTarget, pTarget );
pSkill_Fire->SetStartTime( m_dwTime );
pSkill_Fire->SetSkillMode( SNewSkill::SKILLMODE_FIRE );
m_vFireSkillList.push_back( pSkill_Fire );
}
else
{
if( pSkillFX->nStage_Type_Id != SS_TYPE_171 )
{
SAFE_DELETE( pSkillFX );
return;
}
}
//RUSH, KNOCK_BACK, DAMAGE 처리
if( pSkillFX->nStage_Type_Id != SS_TYPE_171 ) return;
std::vector<SkillResult>::const_iterator it;
for( it = info.vSkillResult.begin(); it != info.vSkillResult.end(); it++ )
{
_SKILL_FX* ptSkillFX = new _SKILL_FX;
*ptSkillFX = *pSkillFXDB;
if( SkillResult::RUSH == (*it).GetType() )
{
if( (*it).rush.bResult )
{
SWorkRush_Shove* pRushShove = new SWorkRush_Shove( this, ptSkillFX, &info );
m_vFireSkillList.push_back( pRushShove );
return;
}
}
else if( SkillResult::DAMAGE_WITH_KNOCK_BACK == (*it).GetType() )
{
SWorkKnockBack* pWorkKnockBack = new SWorkKnockBack( this, ptSkillFX, &(*it) );
pWorkKnockBack->SetStartTime( m_dwTime );
m_vFireSkillList.push_back( pWorkKnockBack );
}
else if( SkillResult::DAMAGE == (*it).GetType() )
{
SChargeAttack* pChargerAttack = new SChargeAttack( this, ptSkillFX, &(*it) );
pChargerAttack->SetStartTime( m_dwTime );
m_vFireSkillList.push_back( pChargerAttack );
}
}
}
break; // 캐스팅
case SObjectState::STATE_SKILL_END :
{
m_bIsUseSkill_complete = true;
//_oprint( "SKILL - SObjectState::STATE_SKILL_END\n" );
for( unsigned int i(0); m_vCastSkillList.size()>i; i++ )
m_vCastSkillList[i]->ForceEnd();
// for( unsigned int i(0); m_vFireSkillList.size()>i; i++ )
// m_vFireSkillList[i]->ForceEnd();
break;
}
case SObjectState::STATE_CAST_CANCEL :
{
m_bIsUseSkill_complete = true;
//_oprint( "SKILL - SObjectState::STATE_CAST_CANCEL\n" );
for( unsigned int i(0); m_vCastSkillList.size()>i; i++ )
m_vCastSkillList[i]->ForceEnd();
// for( unsigned int i(0); m_vFireSkillList.size()>i; i++ )
// m_vFireSkillList[i]->ForceEnd();
Default();
break;
}
}
}
// 죽을때 모션이 두개이기 때문에 모션 선택하는 부분을 따로 함수로 뺐다.
void SGameMob::EnterDeadAnimation()
{
// 몹이 2가지 모션으로 랜덤하게 죽는다. 2009.03.27 by 정동섭
const char * pAni = NULL;
// 몹이면
if( GetInnObjType() == TS_ENTER::GAME_MOB )
{
//몹디비에서 02번 죽는 모션의 이름을 꺼내본다
_MONSTER_INFO_FILE * pMonster = GetMonsterDB().GetMonsterData( GetInnContentID() );
if( pMonster )
pAni = GetMonsterMotionSetDB().GetAni( pMonster->motion_file_id, ANI_DEAD02 );
else
{
_oprint( "GetAni Key Fail - Mob : %d\n", GetInnContentID() );
}
std::string strAniKey;
KSeqObject* ani_dead02 = NULL;
if (pAni != NULL)
{
CStringUtil::GetStrKey( pAni, '_', strAniKey );
// 여기까지 해서 죽는 모션의 이름을 꺼내본뒤에
// 아래에서 아바타의 BIPED 부분에서 그 애니메이션이 있는지 꺼내본다
if (m_pSeqAvatar != NULL)
ani_dead02 = m_pSeqAvatar->getAvatarPart(ANIPART_BIPED)->GetAnimation( strAniKey.c_str() );
}
if (ani_dead02 == NULL) // 없으면 닥치고 1번모션
{
NPlayAnimation( ANI_DEAD01, SEQTYPE_NORMAL );
}
else // 있으면
{
int prob = rand() % 100; // /주사위 100
if (prob >= 50) // 50~99면 01번죽는모션
NPlayAnimation( ANI_DEAD01, SEQTYPE_NORMAL );
else // 0~49면 02번 죽는 모션
NPlayAnimation( ANI_DEAD02, SEQTYPE_NORMAL );
}
}
else
{
NPlayAnimation( ANI_DEAD01, SEQTYPE_NORMAL );
}
}
bool SGameMob::ExistAnimation( int ani_id)
{
const char * pAni = NULL;
if( GetInnObjType() == TS_ENTER::GAME_MOB)
{
//몹디비에서 02번 죽는 모션의 이름을 꺼내본다
_MONSTER_INFO_FILE * pMonster = GetMonsterDB().GetMonsterData( GetInnContentID() );
if( pMonster )
pAni = GetMonsterMotionSetDB().GetAni( pMonster->motion_file_id, ani_id );
if (pAni != NULL)
{
std::string strAniKey;
KSeqObject* ani = NULL;
CStringUtil::GetStrKey( pAni, '_', strAniKey );
// 여기까지 해서 죽는 모션의 이름을 꺼내본뒤에
// 아래에서 아바타의 BIPED 부분에서 그 애니메이션이 있는지 꺼내본다
if (m_pSeqAvatar != NULL)
ani = m_pSeqAvatar->getAvatarPart(ANIPART_BIPED)->GetAnimation( strAniKey.c_str() );
if ( ani == NULL)
return false;
else
return true;
}
else
return false;
}
return true;
}
// ToDo : 가상함수로 만들어서 AvatarEx의 하위클래스들에 맞게 바꿔야 함.. 지금은 몹디비만 보는 중
int SGameMob::GetAnimationLength(int ani_id)
{
const char * pAni = NULL;
if( GetInnObjType() == TS_ENTER::GAME_MOB)
{
//몹디비에서 02번 죽는 모션의 이름을 꺼내본다
_MONSTER_INFO_FILE * pMonster = GetMonsterDB().GetMonsterData( GetInnContentID() );
if( pMonster )
pAni = GetMonsterMotionSetDB().GetAni( pMonster->motion_file_id, ani_id );
std::string strAniKey;
KSeqObject* new_ani = NULL;
if (pAni != NULL)
{
CStringUtil::GetStrKey( pAni, '_', strAniKey );
// 여기까지 해서 죽는 모션의 이름을 꺼내본뒤에
// 아래에서 아바타의 BIPED 부분에서 그 애니메이션이 있는지 꺼내본다
if (m_pSeqAvatar != NULL)
new_ani = m_pSeqAvatar->getAvatarPart(ANIPART_BIPED)->GetAnimation( strAniKey.c_str() );
}
if (new_ani == NULL) // 없으면 -1
{
return -1;
}
else // 있으면
{
return new_ani->GetInterval().GetLength();
}
}
return -1;
}
void SGameMob::RefreshTextureGroup( vec_cobset* pCobSet )
{
if ( pCobSet == NULL || m_pSeqAvatar == NULL || pCobSet->empty() ) return;
assert( GetInnObjType() == TS_ENTER::GAME_MOB );
_MONSTER_INFO_FILE* pMonster = GetMonsterDB().GetMonsterData( GetInnContentID() );
if( pMonster == NULL ) return;
int texture_group_index = pMonster->texture_group; // sonador 1.8.10 아바타 텍스쳐 그룹 적용(페이스 컷 포함)
if (texture_group_index > 0)
{
// Alucard enabling MTE
EnableMTE(true);
}
_RefreshTextureGroup( texture_group_index, pCobSet );
}
// 2010.07.16 - prodongi
void SGameMob::SetHP( int nHP, bool IsEnter )
{
if( IsEnter ) //공격 받은 후~
{
if( m_pProperty )
m_pProperty->HP = nHP;
//if( GetHP() <= 0 )
if (nHP <= 0)
{
m_bIsDead = true; //시체 엔터된 경우
NPlayAnimation( ANI_DEAD01, SEQTYPE_LASTPAGE );
SetAnimationPos( SEQPOS_END );
SetEventHandleNull();
if( m_pStateVM ) m_pStateVM->Dead();
}
return;
}
else
{
if( m_pProperty )
m_pProperty->HP = nHP;
}
}
void SGameMob::recvDead()
{
SGameAvatarEx::Dead();
}
void SGameMob::Dead()
{
// 원래는 recvDead()에서 드랍 아이템을 처리하는게 맞으나
// drop 패킷이 recvDead()호출 후에 날라오기 때문에 여기에서 drop을 처리
if( GetObjType() == TS_ENTER::GAME_MOB )
{
ItemDrop();
}
}