#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 #include "LuaVM.h" //#include "Util.h" #include "SAvatarStateMachine.h" #include "SMonsterStateMachine.h" #include "SSkillDB.h" #include "SSkillStageType.h" #include "SGameUtil.h" #include #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(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(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::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(); } }