1224 lines
38 KiB
C++
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();
|
|
}
|
|
} |