//------------------------------------------------------------------------------ // SkillState.cpp // (C) 2005 Webzen Sunstudio //------------------------------------------------------------------------- #include "SunClientPrecompiledHeader.h" #include "Character.h" #include "Statebase.h" #include "SkillState.h" #include "SkillInfoParser.h" #include "SkillEffectInfoParser.h" #include "GameFunc.h" #include "SkillEffectFunc.h" #include "CastDialog.h" #include "Player.h" #include "Hero.h" #include "CooltimeManager.h" #include "ProjectileManagerEx.h" #include "InterfaceManager.h" #include "HeroActionInput.h" #include "GameFunc.h" #include "KeyQueueManager.h" #include "GlobalFunc.h" #include "GameFramework.h" #include "Monster.h" #include "ObjectManager.h" #include #include "CloneManager.h" #include "clone.h" #include "AppearanceManager.h" #include "Camera.h" #include "SceneBase.h" #include "SkillEffectFunc.h" //------------------------------------------------------------------------------ /** */ StateSkill::StateSkill() : m_dwSkillCode(0) , m_dwSkillClassCode(0) , m_fRet(0.0f) , m_iHitCount(0) , m_iHitProcessed(0) , m_isCastAni(FALSE) { m_hCastHandle[0] = INVALID_HANDLE_VALUE; m_hCastHandle[1] = INVALID_HANDLE_VALUE; } //------------------------------------------------------------------------------ /** */ StateSkill::~StateSkill() { ReleaseCastingEffect(); } //------------------------------------------------------------------------------ /** »óŰ¡ ½ÃÀÛ ÇÒ¶§ °øÅëÀÇ Ã³¸®¸¦ ÇÑ´Ù.-Áö¿Ï Ãß°¡ */ STATECHANGE::TYPE StateSkill::OnStart(Object* pOwner, DWORD CurTime) { Character* pCharacter = (Character *)pOwner; if( pCharacter != NULL ) { if( pCharacter->IsMyHero() == TRUE ) { if( SendSkillPacket() == FALSE ) { assert(!"½ºÅ³»ç¿ë ÆÐŶ Àü¼Û½ÇÆÐ"); return STATECHANGE::FAILED; } // ¿ÀÇÁ¶óÀΠó¸® if( GENERALPARAM->IsNet() == FALSE ) { if( util::Timer* pTimer = xGetCooltimeManager()->GetSkillCooltime(m_dwSkillCode) ) { pTimer->Reset(); } } } ProcessStartAttackSerial(pCharacter); ProcessStartAnimation(pCharacter); ProcessStartDirection(pCharacter); ProcessStartSkillEffect(pCharacter); return STATECHANGE::SUCCESS; } return STATECHANGE::FAILED; } void StateSkill::ProcessStartAttackSerial(Character* pCharacter) { if( pCharacter->IsMyHero() == TRUE ) { pCharacter->SetCurrentAttackSerial(pCharacter->GetNextAttackSerial()); } else { DWORD dwAttackSerial = pCharacter->GetNextActionAttackSerial(); pCharacter->SetCurrentAttackSerial(dwAttackSerial); pCharacter->SetNextAttackSerial(dwAttackSerial); } // ¸ó½ºÅÍÀÇ ½ºÅ³ º¸À̽º¸¦ »ç¿ëÇÑ´Ù pCharacter->OnUseSkill(); } void StateSkill::ProcessStartAnimation(Character* pCharacter) { if( SkillScriptInfo* pSkillInfo = GetSkillInfo() ) { //ij½ºÆÃ ½ºÅ³ÀÎ °æ¿ì if( pSkillInfo->IsCastingSkill() == true ) { // ¾Ö´Ï¸ÞÀÌ¼Ç ½ÃÀÛ pCharacter->SetAnimation( pSkillInfo->GetCastingAnimationID(), TRUE ); m_isCastAni = TRUE; // ij½ºÆÃ ½Ã°£À» ±¸ÇÑ´Ù. m_dwTotalCastTime = pSkillInfo->GetCastingTime(); // ÇöÀç´Â ij½ºÆÃ °³³ä ÀÚü°¡ ¾ø¾îÁ³´Ù //// Ç÷¹À̾îÀÎ °æ¿ì¿¡´Â º¸³Ê½º ij½ºÆÃ ¼Óµµ·Ñ Àû¿ë //if( pCharacter->IsKindOfObject(PLAYER_OBJECT)) //{ // Player* pPlayer= (Player*)pCharacter; // if( PlayerAttributes* pAtt = pPlayer->GetPlayerAttribute() ) // { // int iBonusCastingTime = pAtt->GetBonusCastingTime(); // float fBonusCastingTimeRatio = pAtt->GetRatioBonusCastingTime() / 100.f; // fBonusCastingTimeRatio = max(-1,fBonusCastingTimeRatio); // int iInterval = (int)m_dwTotalCastTime + iBonusCastingTime; // m_dwTotalCastTime = max( 0, iInterval ); // m_dwTotalCastTime = (WORD)( m_dwTotalCastTime * ( 1 + fBonusCastingTimeRatio ) ); // } //} // ÀÚ½ÅÀÌ »ç¿ëÇÑ °æ¿ì ij½ºÆÃ UIµîÀ» Ç¥½Ã if( pCharacter == g_pHero ) { g_InterfaceManager.ShowWindow(InterfaceManager::DIALOG_CASTING, TRUE); //ij½ºÆÃ Ãë¼Ò À̺¥Æ® »ðÀÔ g_KeyQueueManager.PushKeyMsg( eDoType_cast_cancel, GlobalFunc::DoCastCancel ); g_pHero->SetCast( m_isCastAni ); } //OnEnd°¡ È£Ã⠾ȵɶ§°¡ ÀÖÀ»±î.... if (m_hCastHandle[0] != INVALID_HANDLE_VALUE ) { g_pEffectManager->RemoveEffectPtr( m_hCastHandle[0] ); } m_hCastHandle[0] = SkillEffectFunc::CreateCastingSkillEffect( m_dwSkillClassCode, pCharacter ); } else { // ij½ºÆÃÀÌ ¾ø´Â Áï½Ã »ç¿ë ½ºÅ³ StartSkillAnimation( pCharacter ); } } } void StateSkill::ProcessStartDirection(Character* pCharacter) { // ½ºÅ³¿¡ µû¶ó ij¸¯ÅÍ ¹æÇâÀ» Ʋ¾îÁØ´Ù. if( SkillScriptInfo* pSkillInfo = GetSkillInfo() ) { if ( pSkillInfo->m_byTarget == SKILL_TARGET_ENEMY || pSkillInfo->m_byTarget == SKILL_TARGET_FRIEND || pSkillInfo->m_byTarget == SKILL_TARGET_FRIEND_CORPSE || pSkillInfo->m_byTarget == SKILL_TARGET_REACHABLE_ENEMY || pSkillInfo->m_byTarget == SKILL_TARGET_REACHABLE_FRIEND || pSkillInfo->m_byTarget == SKILL_TARGET_ENEMY_PLAYER || pSkillInfo->m_byTarget == SKILL_TARGET_ENEMY_AND_ME ) { if( Character* pTarget = (Character*) pCharacter->GetTargetObject() ) { pCharacter->SetDirection( pTarget->GetPosition() ); } } else if( pSkillInfo->m_byTarget == SKILL_TARGET_AREA ) { WzVector vTarget = pCharacter->GetCurrentAction()->SKILL.vTargePos; pCharacter->SetDirection( vTarget ); } } } void StateSkill::ProcessStartSkillEffect(Character* pCharacter) { if( SkillScriptInfo* pSkillInfo = GetSkillInfo() ) { // ÀÌÆåÆ® // Casting BottomEffect if( m_hCastHandle[1] != INVALID_HANDLE_VALUE ) { g_pEffectManager->RemoveEffectPtr( m_hCastHandle[1] ); } m_hCastHandle[1] = SkillEffectFunc::CreateCastingSkillBottomEffect( m_dwSkillClassCode, pCharacter ); // Start Effect m_dwStartProjectileKey = SkillEffectFunc::CreateStartSkillEffect( m_dwSkillClassCode, pCharacter ); // Script Effect if( pSkillInfo->m_szSkillMasterEffectName ) { if( StrnCmp( pSkillInfo->m_szSkillMasterEffectName , _T("NULL") , MAX_SKILLNAME_LENGTH ) != 0 ) { GlobalFunc::CreateEffectBeUpToOption( StrToWzID(pSkillInfo->m_szSkillMasterEffectName), pCharacter->GetBonePos(1), pCharacter->GetScale(), pCharacter->GetObjectKey()); } } } } void StateSkill::ProcessAnimationEvent(Character* pCharacter, DWORD CurTime, DWORD dwTick ) { // Ÿ°ÝÁ¡ üũ if ( m_event.m_iNumCustom != 0 ) { for (int i = 0; i < m_event.m_iNumCustom; ++i) { if( m_event.m_Custom[i].m_iaParam3[0] == 1 ) { ++m_iHitCount; // Custom Event½Ã¿¡ ÀÌÆåÆ®¸¦ Ãâ·Â SkillEffectFunc::ProcessSkillHitEffect( pCharacter, m_dwSkillClassCode, m_iHitCount ); SkillEffectFunc::ProcessSkillEffect( pCharacter, m_dwSkillClassCode, m_iHitCount ); // OnProcessCustomEvent¿¡ ÇϵåÄÚµù µÈ ÀÛ¾÷À» ¼öÇà ÇÒ ¼ö ÀÖµµ·Ï. // ³¯¾Æ°¡´Â ¹ß»çü°¡ Ÿ°ÝÀ» ó¸®ÇÏ´Â °æ¿ì¿¡´Â FALSE¸¦ ¸®ÅÏÇØ¾ßÇϰí, ÀÌ °æ¿ì Ÿ°Ý 󸮴 ÇÏÁö¾Ê´Â´Ù. (¹ß»çü°¡ ÇÒ °ÍÀÌ´Ù.) if( OnProcessCustemEvent( m_iHitCount, pCharacter, CurTime, dwTick ) == TRUE ) { pCharacter->ProcessAttackResult_Group( pCharacter->GetCurrentAttackSerial() ); } } } } } //------------------------------------------------------------------------------ /** ¼ø¼ö °¡»ó ÇÔ¼ö ¿´À¸³ª Ưº°ÇÑ ¿¬ÃâÀÌ ¾ø´ÂÇÑ StateSkill¸¦ ÀνºÅϽºÈ­ ÇÒ°ÍÀÓ-Áö¿Ï */ STATEPROCESS::TYPE StateSkill::OnProcess(Object* pOwner, DWORD CurTime,DWORD dwTick) { EnterProcess(pOwner,CurTime,dwTick); ProcessAnimationEvent((Character*)pOwner, CurTime, dwTick ); return LeaveProcess( pOwner, CurTime, dwTick ); } //------------------------------------------------------------------------------ /** »óŰ¡ ³¡³¯¶§ °øÅëÀÇ Ã³¸®¸¦ ÇÑ´Ù.-Áö¿Ï Ãß°¡ */ STATECHANGE::TYPE StateSkill::OnEnd(Object* pOwner, DWORD CurTime) { Character* character = static_cast(pOwner); if (character != NULL) { character->SetAttackStandTime(ATTACK_STAND_TIME); } return STATECHANGE::SUCCESS; } //------------------------------------------------------------------------------ /** ÃʱâÈ­ ó¸® ²À ¿©±â¼­ ÇÒ°Í */ void StateSkill::Initialize(Object* pOwner) { Character* pCharacter = (Character*)pOwner; m_iHitProcessed = 0; m_iHitCount = 0; m_fRet = 0; m_isCastAni = FALSE; m_dwTotalCastTime = 0; m_dwCurCastTime = 0; m_pSkillScript = NULL; m_dwSkillCode = pCharacter->GetCurrentAction()->SKILL.dwSkillID; ZeroMemory(&m_event,sizeof(WzEvent)); m_hCastHandle[0] = INVALID_HANDLE_VALUE; m_hCastHandle[1] = INVALID_HANDLE_VALUE; if( SkillScriptInfo* pSkillScriptInfo = SkillInfoParser::Instance()->GetSkillInfo( m_dwSkillCode ) ) { m_pSkillScript = pSkillScriptInfo; m_dwSkillClassCode = pSkillScriptInfo->m_SkillClassCode; } else { if( g_pHero == pOwner ) { if( FILE* fp = fopen("SkillCrashLog.txt","wt") ) { fprintf(fp,"Hero SkillCode ÀÌ»ó SkillCode : %d", m_dwSkillCode); fclose(fp); } } else { if( FILE* fp = fopen("SkillCrashLog.txt","wt") ) { fprintf(fp,"Character SkillCode ÀÌ»ó SkillCode : %d", m_dwSkillCode); fclose(fp); } } assert(false); } } //------------------------------------------------------------------------------ /** °­Á¦ Á¾·á½Ã ÇÊ¿äÇÑ ÀϵéÀ» ÇÑ´Ù. */ void StateSkill::OnForceChange(Object* pOwner) { StateBase::OnForceChange(pOwner); //½ÃÀÛ ÀÌÆåÆ®¸¦ Á¦°Å ÇÑ´Ù. if(m_dwStartProjectileKey) { g_ProjectileManagerEx.DeleteProjectile(m_dwStartProjectileKey); m_dwStartProjectileKey = 0; } Character* pCharacter = static_cast(pOwner); if (pCharacter) { pCharacter->ProcessAllRemainActionResult(); } } //------------------------------------------------------------------------------ /** ÇÁ·Î¼¼½º°¡ ³¡³¯¶§ÀÇ °øÅëÀÇ Ã³¸® OnEndÀÇ °æ¿ì ÀÚ½Ä Å¬·¡½º¿¡¼­ È£ÃâÀÌ ¾ÈµÇ´Â °æ¿ì°¡ ÀÖÀ¸³ª FinalizeÀÇ °æ¿ì Ç×»ó È£Ãâ ÇÑ´Ù. ÇØÁ¦°¡ ÇÊ¿äÇѰ͵é ó¸® */ void StateSkill::Finalize(Object* pOwner) { Character* pCharacter = (Character*)pOwner; if(m_hCastHandle[0]!=INVALID_HANDLE_VALUE) { g_pEffectManager->RemoveEffectPtr(m_hCastHandle[0]); m_hCastHandle[0]=INVALID_HANDLE_VALUE; } if(m_hCastHandle[1]!=INVALID_HANDLE_VALUE) { g_pEffectManager->RemoveEffectPtr(m_hCastHandle[1]); m_hCastHandle[1]=INVALID_HANDLE_VALUE; } pCharacter->EndTrail(0); pCharacter->EndTrail(1); if( pCharacter == g_pHero ) { g_pHero->SetCast(m_isCastAni); g_KeyQueueManager.DeleteMsg(eDoType_cast_cancel); CastDialog * pCastDlg = (CastDialog * )g_InterfaceManager.GetDialog(InterfaceManager::DIALOG_CASTING); if(pCastDlg) { pCastDlg->SetText(""); pCastDlg->ShowWindow(FALSE); } } if( SkillScriptInfo* pInfo = GetSkillInfo() ) { SkillEffectFunc::CreateSkillEventEffect( pInfo->m_SkillClassCode, pCharacter, cSkillEventEffectInfoData::EVENT_TYPE_END ); // //ÀÌÆåÆ® ½ºÅ©¸³Æ®°¡ ÀÖÀ»¶§ // if( SkillEffectInfo* pEffectInfo = SkillEffectInfoParser::Instance()->GetSkillEffectInfo(pInfo->m_SkillClassCode) ) // { // // // ÁöÁ¤µÈ ¹«±â°¡ ¾Æ´Ï¶ó¸é ÀÌÆåÆ®¸¦ Ç¥½ÃÇÏÁö ¾ÊÀ½ // Player* pPlayer = NULL; // if(pCharacter->IsKindOfObject(PLAYER_OBJECT)) // { // pPlayer = (Player*)pCharacter; // // if( (0 != pEffectInfo->m_byWeaponType) && // (pPlayer->GetWeaponKind() != pEffectInfo->m_byWeaponType) ) // { // return; // } // } // // if(pEffectInfo->m_EffectInfo[eEffect_Type_End].m_wzidEffectCode.id != StrToWzID("NULL")) // { // SkillEffectFunc::AddSkillEffectProjectile( pCharacter, &pEffectInfo->m_EffectInfo[eEffect_Type_End], TRUE ); // } //} } //if( GENERALPARAM->IsNet()==FALSE) // _TestRecvDamageSkillEffect(pCharacter); } //------------------------------------------------------------------------------ /** ÇÁ·Î¼¼½º°¡ ½ÃÀÛÇÒ¶§ÀÇ °øÅë 󸮸¦ ÇÑ´Ù. */ VOID StateSkill::EnterProcess(Object* pOwner, DWORD CurTime,DWORD dwTick) { Character* pCharacter = (Character*)pOwner; pCharacter->ProcessInterpolation(dwTick); // ³»Ä³¸¯ÅÍ À϶§ °ÔÀÌÁö ±×¸®±â if(m_isCastAni)//ij½ºÆ® ¿¡´Ï°¡ ÀÖ´Ù¸é { m_dwCurCastTime+=dwTick; if(m_dwCurCastTime>=m_dwTotalCastTime)//ij½ºÆ® ½Ã°£ÀÌ ´ÙµÆ´Ù¸é { m_dwCurCastTime=m_dwTotalCastTime; StartSkillAnimation( pCharacter ); m_isCastAni=FALSE;//ij½ºÆ®¸¦ ²ô°í ´ÙÀ½ ¿¡´Ï·Î ³Ñ¾î°£´Ù. ReleaseCastingEffect(); } WzVector wzVisualPos = pCharacter->GetVisiblePos(); WzVector wzGaugePos = wzVisualPos; wzGaugePos.z += 2.5f; float fRate = (float)m_dwCurCastTime / (float)m_dwTotalCastTime; if (fRate < 0.0f) { fRate = 0.0f; } else if (fRate > 1.0f) { fRate = 1.0f; } if(pCharacter==g_pHero) { CastDialog* pCastDlg = (CastDialog * )g_InterfaceManager.GetDialog(InterfaceManager::DIALOG_CASTING); pCastDlg->SetGage(fRate*100.0f); } } else if(pCharacter==g_pHero) { g_InterfaceManager.ShowWindow(InterfaceManager::DIALOG_CASTING,FALSE); } pCharacter->ProcessAnimation(dwTick,&m_event); // #8392 / ¼ÛÂùÁ¾ / ½ºÅ³StateÀº ¶óÀÌ´õ Animation Rate¸¦ ¾È¾²°í Ç÷¹À̾îÀÇ Animation Rate¸¦ ¾²µµ·Ï ¼öÁ¤ m_fRet = pCharacter->current_ani_progress_rate(); } //------------------------------------------------------------------------------ /** ÇÁ·Î¼¼½º°¡ ³¡³¯¶§ÀÇ °øÅë 󸮸¦ ÇÑ´Ù.-Áö¿Ï Ãß°¡ */ STATEPROCESS::TYPE StateSkill::LeaveProcess(Object* pOwner, DWORD CurTime,DWORD dwTick) { Character* pCharacter = (Character*)pOwner; // Á¾·á~ if (m_fRet >= 100.0f)//¿¡´Ï¸ÞÀÌ¼Ç ÁøÃ´µµ°¡ 100%À϶§ { if (pCharacter->ProcessNextAction()) { return STATEPROCESS::SUSPEND; } else { return STATEPROCESS::END; } } return STATEPROCESS::HOLD; } // ¸ñÇ¥Á¡±îÁö À̵¿. BOOL StateSkill::ProcessMove( Character *pCharacter, float fMoveSpeed, DWORD dwTick) { SUNPROCESSPROFILE_BEGIN(_T("PathFinding")); if (pCharacter->GetPathExplorer()->IsMoving()) { return g_pSunTerrain->PathExp_Progress(pCharacter->GetPathExplorer(), fMoveSpeed * dwTick, NULL); } SUNPROCESSPROFILE_END(_T("PathFinding")); return FALSE; } BOOL StateSkill::StartSkillAnimation( Character* pCharacter, BOOL bLoop ) { SkillScriptInfo* pSkillInfo = GetSkillInfo(); if( pSkillInfo == NULL ) return FALSE; assert(pCharacter->GetCurrentAction()->ActionID == ACTION_SKILL); const PLAYER_ACTION* pAction = pCharacter->GetCurrentAction(); WzID animationID = 0; // ¹«±â¿¡ »ó°ü¾øÀ¸¸é if ( pSkillInfo->m_iWeaponDefine[0] == -1 || pCharacter->IsKindOfObject(MONSTER_OBJECT)) { animationID = pSkillInfo->m_wzidAnimationCode[0].id; } else { // ÀåÂøµÈ ¹«±âÁ¾·ù¿Í ºñ±³ÇØ ¾Ë¸ÂÀº ¾Ö´Ï¸ÞÀ̼ÇÀ» ã´Â´Ù. DWORD dwWeaponKind = pCharacter->GetWeaponKind(); BOOL bFind = FALSE; animationID = pSkillInfo->m_wzidAnimationCode[0].id; for (int a = 0; a < 4; a++) { if ( pSkillInfo->m_iWeaponDefine[a] == dwWeaponKind ) { bFind = TRUE; animationID = pSkillInfo->m_wzidAnimationCode[a].id; break; } } } // ½ºÅ³¾Ö´Ï¸ÞÀ̼ÇÀÌ Á¤»óÀûÀ¸·Î ³ª¿Àµµ·Ï Blend AnimationÀ» ²¨ÁØ´Ù! pCharacter->StopBlendAnimation(); pCharacter->SetAnimation(animationID, bLoop); #ifdef _DEBUG TCHAR szText[INTERFACE_STRING_LENGTH]; TCHAR szMessage[INTERFACE_STRING_LENGTH]; if (pCharacter->GetBasePart()->GetWzUnit() != NULL) { // ½ºÅ³ ¾Ö´Ï¸ÞÀ̼ÇÀÌ µé¾îÀÖ³ª üũ. if( WzAnimationInfo* pAniInfo = pCharacter->GetAnimationInfo(animationID) ) { if (pAniInfo->m_wiAction != animationID) { if (pCharacter->IsKindOfObject(PLAYER_OBJECT)) { char *pAni = (char *)&animationID; // ½ºÅ³ ¾Ö´Ï¸ÞÀ̼ÇÀÌ ¾ø½À´Ï´Ù. g_InterfaceManager.GetInterfaceString( eST_NOT_FOUND_SKILL_ANIMATION, szText, INTERFACE_STRING_LENGTH ); _stprintf( szMessage, _T("%c%c%c%c %s"), pAni[0], pAni[1], pAni[2], pAni[3], szText ); Player *pPlayer = (Player *)pCharacter; pPlayer->AddChatMessage(szMessage); } } } else { if (pCharacter->IsKindOfObject(PLAYER_OBJECT)) { char *pAni = (char *)&animationID; // ½ºÅ³ ¾Ö´Ï¸ÞÀ̼ÇÀÌ ¾ø½À´Ï´Ù. g_InterfaceManager.GetInterfaceString( eST_NOT_FOUND_SKILL_ANIMATION, szText, INTERFACE_STRING_LENGTH ); _stprintf( szMessage, _T("%c%c%c%c %s"), pAni[0], pAni[1], pAni[2], pAni[3], szText ); Player *pPlayer = (Player *)pCharacter; pPlayer->AddChatMessage(szMessage); } } } #endif return TRUE; } BOOL StateSkill::SendSkillPacket() { return g_pHero->SendUseSkillPacket( m_dwSkillCode ); } BOOL StateSkill::CheckEvent() { // Ÿ°ÝÁ¡ üũ if ( m_event.m_iNumCustom == 0 ) return FALSE; for (int i = 0; i < m_event.m_iNumCustom; ++i) { if( m_event.m_Custom[i].m_iaParam3[0] == 1 ) { ++m_iHitCount; //OnAnimationEvent( m_iHitCount ); return TRUE; } } return FALSE; } BOOL StateSkill::ProcessHit( Character* pCharacter, int HitCount) { assert(HitCount > 0); if (CheckNotProcessedHitEvent(HitCount)) { //SkillEffectFunc::ProcessSkillProjectile( pCharacter, m_dwSkillClassCode, GetSkillInfo(), HitCount ); SkillEffectFunc::ProcessSkillHitEffect( pCharacter, m_dwSkillClassCode, HitCount ); SkillEffectFunc::ProcessSkillEffect( pCharacter, m_dwSkillClassCode, HitCount ); ++m_iHitProcessed; if (pCharacter->ProcessAttackResult_Group(pCharacter->GetCurrentAttackSerial())) { return TRUE; } } return FALSE; } BOOL StateSkill::CheckNotProcessedHitEvent(int HitCount) { if (m_iHitProcessed < HitCount) { if (m_iHitCount > (HitCount - 1)) { return TRUE; } } return FALSE; } void StateSkill::ReleaseCastingEffect() { if(m_hCastHandle[0]!=INVALID_HANDLE_VALUE) { g_pEffectManager->RemoveEffectPtr(m_hCastHandle[0]); m_hCastHandle[0]=INVALID_HANDLE_VALUE; } if(m_hCastHandle[1]!=INVALID_HANDLE_VALUE) { g_pEffectManager->RemoveEffectPtr(m_hCastHandle[1]); m_hCastHandle[1]=INVALID_HANDLE_VALUE; } } WzVector StateSkill::GetTargetNearPosition( const WzVector& StartPosition, Character* pTarget, float Range ) { WzVector TargetPosition; if( pTarget != NULL ) { TargetPosition = pTarget->GetPosition(); } else { TargetPosition = StartPosition; } WzVector vDistance = TargetPosition - StartPosition; float fDistance = VectorLength(&vDistance); if( fDistance < Range ) { // ÀÌ¹Ì »ç°Å¸® À̳»¸é Á¦ÀÚ¸® TargetPosition = StartPosition; } else { WzVector Direction; VectorNormalize(&Direction,&vDistance); TargetPosition = TargetPosition - (Direction * Range); } return TargetPosition; }