/** @brief Invoke대상에게 pEffect효과를 적용시킨다. @param pInvoker pEffect효과를 발동시킨측. pCaster와는 다르다. @param vIvkPos 발동대상이 좌표인경우 0이아닌값이 들어온다. 이때 pInvokeTarget은 null이된다. @return 효과적용에 성공하면 true를 리턴한다. */ bool XSkillUser::ApplyInvokeEffToIvkTarget( XSkillReceiver* pIvkTarget, // null일수도 있음. const XE::VEC2& vIvkPos, // 발동대상이 좌표형태인경우. const XSkillDat *pDat, const EFFECT *pEffect, XSkillReceiver *pInvoker, bool bCreateSfx, int level, XBuffObj *pBuffObj ) { // 좌표에 값이 있으면 타겟은 널이어야 한다., XBREAK( vIvkPos.IsNotZero() && pIvkTarget != nullptr ); // 발동적용확률이 있다면 확률검사를 통과해야 한다.(XBuffObj에 이것이 있을때는 GetInvokeTarget을 하기전에 수행되었는데 지금은 발동타겟 개별적으로 확률검사를 하도록 바뀜. 이게 맞는거 같아서) bool bOk = XSKILL::DoDiceInvokeApplyRatio( pEffect, level ); if( !bOk ) return false; bool bOrCond = false; // 조건블럭이 없으면 무조건 true if( pEffect->aryInvokeCondition.size() == 0 ) { bOrCond = true; } else // 발동조건 블럭검사. if( pIvkTarget && IsInvokeTargetCondition( pDat, pEffect, pIvkTarget ) ) { bOrCond = true; } if( !bOrCond ) return false; if( pEffect->idInvokeSound ) { OnSkillPlaySound( pEffect->idInvokeSound ); } // 발동타겟에게 실제 효과적용 if( pIvkTarget ) { int retApplied = pIvkTarget->ApplyInvokeEffect( const_cast<XSkillDat*>(pDat), this , pInvoker, pBuffObj , pEffect, level ); // 발동대상이펙트가 있다면 생성해준다. if( bCreateSfx && pEffect->m_invokeTargetEff.IsHave() ) { const float secPlay = 0.f; // 1play. 발동이펙트는 반복플레이가 없음. const XE::VEC2 vZero; CreateSfx( pIvkTarget, pDat, pEffect->m_invokeTargetEff, secPlay, vZero ); } return retApplied != 0; } else if( vIvkPos.IsNotZero() ) { // 발동타겟이 좌표형태 // 발동타겟이 좌표이면 바닥에 놓는 버프객체를 뜻한다. // 발동좌표를 기준타겟좌표로해서 시전한다. 기준타겟좌표로 시전된 스킬은 가상의 리시버객체가 생성되고 거기에 버프가 걸리는 형태. if( XASSERT(!pEffect->strInvokeSkill.empty()) ) { // 발동대상이 좌표형태인경우 그 효과는 반드시 발동스킬로 구현되어야 한다. const ID idCallerBuff = ( pBuffObj ) ? pBuffObj->GetidSkill() : 0; pInvoker->DoInvokeSkill( nullptr, vIvkPos, pDat, pEffect, level, this, idCallerBuff ); } // 발동대상이펙트가 있다면 생성해준다. if( bCreateSfx && pEffect->m_invokeTargetEff.IsHave() ) { const float secPlay = 0.f; // 1play. 발동이펙트는 반복플레이가 없음. CreateSfx( pIvkTarget, pDat, pEffect->m_invokeTargetEff, secPlay, vIvkPos ); } return true; } return false; } // ApplyInvokeEffectToInvokeTarget
void CWndPiercing::OnInitialUpdate() { CWndNeuz::OnInitialUpdate(); CWndInventory* pWndInventory = (CWndInventory*)g_WndMng.CreateApplet( APP_INVENTORY ); CRect rcInventory = pWndInventory->GetWindowRect( TRUE ); CRect rcVendor = GetWindowRect( TRUE ); CPoint ptInventory = rcInventory.TopLeft(); CPoint point; if( ptInventory.x > m_pWndRoot->GetWndRect().Width() / 2 ) point = ptInventory - CPoint( rcVendor.Width(), 0 ); else point = ptInventory + CPoint( rcInventory.Width(), 0 ); Move( point ); LPWNDCTRL pCustom = NULL; pCustom = GetWndCtrl( WIDC_STATIC5 ); m_Rect[0] = pCustom->rect; pCustom = GetWndCtrl( WIDC_STATIC6 ); m_Rect[1] = pCustom->rect; pCustom = GetWndCtrl( WIDC_STATIC7 ); m_Rect[2] = pCustom->rect; CWndStatic* pGoldNum = (CWndStatic*) GetDlgItem( WIDC_STATIC9 ); pGoldNum->AddWndStyle( WSS_MONEY ); if( g_pPlayer ) m_pSfx = CreateSfx( g_Neuz.m_pd3dDevice, XI_INT_INCHANT, g_pPlayer->GetPos(), g_pPlayer->GetId(), g_pPlayer->GetPos(), g_pPlayer->GetId(), -1 ); }
/** @brief 시전대상에게 효과시전 이 함수는 시전대상의 수만큼 불린다. @param pBaseTarget 기준타겟 @param pCastingTarget 시전대상들 */ xtError XSkillUser::CastEffToCastTarget( const XSkillDat *pDat, EFFECT *pEffect, int level, // XSkillReceiver *pBaseTarget, XSkillReceiver *pCastingTarget, const XE::VEC2& vCastTarget, ID idCallerSkill ) { XBREAK( pDat == nullptr ); XBREAK( pEffect == nullptr ); // 시전대상들에게 모두 이펙트를 붙인다. if( pEffect->m_CastTargetEff.IsHave() ) { float secPlay = pEffect->GetDuration( level ); XBREAK( pCastingTarget == nullptr ); // 시전대상이 널인경우도 있을까? CreateSfx( pCastingTarget, pDat, pEffect->m_CastTargetEff, secPlay, vCastTarget ); } if( pEffect->IsDuration() ) { if( pCastingTarget ) { // 지속시간이 있는 버프형 효과를 타겟에게 시전한다. CastEffToCastTargetByBuff( pDat, pEffect, level, pCastingTarget, idCallerSkill ); } } else { // 즉시발동형(지속시간 0) // 시전대상에게 즉시 효과가 발동된다. CastEffToCastTargetByDirect( pDat, pEffect, level, pCastingTarget, vCastTarget ); } if( pEffect->idCastSound ) { OnSkillPlaySound( pEffect->idCastSound ); } return xOK; }
/** @brief 스킬을 사용(Shoot)한다. 발사체일경우는 발사체객체를 생성하고 즉시시전형이면 기준타겟에 바로 스킬을 시전한다. */ xtError XSkillUser::OnShootSkill( XSkillDat *pDat , XSkillReceiver *pBaseTarget , int level , const XE::VEC2& vBaseTarget , ID idCallerSkill ) { if( XBREAK(pDat == nullptr ||( pBaseTarget == nullptr && vBaseTarget.IsZero() ) || (vBaseTarget.IsNotZero() && pBaseTarget != nullptr)) ) return xERR_GENERAL; // auto pDat = pUseSkill->GetpDatMutable(); // 시전자의 스킬동작 타점에 시전자에게 생성되는 이펙트(루핑없음) if( !pDat->GetShootEff().m_strSpr.empty() ) { float secPlay = 0; // once const XE::VEC2 vZero; CreateSfx( GetThisRecv(), pDat, pDat->GetShootEff(), secPlay, vZero ); } // 슈팅타겟이펙트(슈팅시점에 타겟에게 발생한다. 보통 타점을 포함하고 있다)-메테오 if( !pDat->GetShootTargetEff().m_strSpr.empty() ) { float secPlay = 0; // once XSkillSfx *pSfx = pBaseTarget->OnCreateSkillSfxShootTarget( pDat, pBaseTarget, level, pDat->GetShootTargetEff(), secPlay, vBaseTarget ); if( pSfx ) pSfx->RegisterCallback( this, pDat, level, pBaseTarget, vBaseTarget ); // 슈팅타겟이펙트는 여기서 sfx만 생성하고 리턴한다음 sfx의 타점에서 CastSkillToBaseTarget이 호출된다. return xOK; } // 발사체방식인가 if( !pDat->GetstrShootObj().empty() ) { // 타겟까지 날아가서 스킬시전을 한다. 발사체스킬은 반드시 발동스킬로 구현되어야 한다.// 시전자->스킬발동()을 한다 // virtual. 발사체오브젝트를 생성하고 오브젝트 매니저에 등록한다 CreateAndAddToWorldShootObj( pDat, level, pBaseTarget, this, vBaseTarget ); // 여기서 기준타겟의 좌표를 실제로 사용해야함.(따라서 좌표는 이시점에 미리 얻어야 함. } else { // 지금 바로 기준타겟에 스킬을 시전한다. CastSkillToBaseTarget( pDat, level, pBaseTarget, vBaseTarget, idCallerSkill ); } return xOK; } // OnShootSkill
void _sfx_link( std::vector< string >& arg ) { if( arg.size() < 2 ) return; int index = 0; sscanf( arg[1].c_str(), "%d", &index ); if( g_pPlayer ) { CSfx *pSfx = CreateSfx( D3DDEVICE, index, g_pPlayer->GetPos(), g_pPlayer->GetId() ); // 시전동작이기때문에 무조건 자기에게 나타난다. if( pSfx ) { pSfx->SetPartLink( 0 ); // 오른손에 링크. pSfx->SetPartLink( 1 ); } } }
/** @brief 기준타겟에 스킬을 시전한다. 이함수는 슈팅타겟이펙트 사용시 단독으로도 호출될수 있다. */ void XSkillUser::CastSkillToBaseTarget( XSkillDat *pDat , int level , XSkillReceiver *pBaseTarget , const XE::VEC2& vBaseTarget , ID idCallerSkill) { // (기준)타겟이펙트 if( pDat->GetTargetEff().IsHave() ) { // const float secPlay = pDat->GetDuration( ); // once const float secPlay = 0; CreateSfx( pBaseTarget, pDat, pDat->GetTargetEff(), secPlay, vBaseTarget ); } // 스킬이 가진 효과들 기준타겟에게 사용한다. for( auto pEffect : pDat->GetlistEffects() ) { xtError err = UseEffect( pDat, pEffect, level, pBaseTarget, vBaseTarget, idCallerSkill ); if( err != xOK ) return; } }
DWORD CActionMover::OnDamageMsgC( DWORD dwMsg, CMover* pAttacker, DWORD dwAtkFlags, int nParam ) { CMover* pMover = GetMover(); BOOL bValid = IsValidObj( pMover ) && IsValidObj( pAttacker ); if( !bValid || IsState( OBJSTA_DIE_ALL ) ) return 0; if( IsSit() ) // 앉아있다가 맞으면 앉기해제 한다. ResetState( OBJSTA_MOVE_ALL ); SendActMsg( OBJMSG_STAND ); // 날때린놈에 대한 정보를 기록함. if( pMover->IsNPC() && pAttacker->IsPlayer() ) // 맞은놈은 NPC , 어태커가 플레이어 일때만 적용됨 { pMover->m_idAttacker = pAttacker->GetId(); // 날 때린넘이 어떤놈인가를 기록함. pMover->m_idTargeter = pAttacker->GetId(); } pAttacker->m_idLastHitMover = pMover->GetId(); // 어태커가 마지막으로 때렸던넘이 나란걸 기록함. if( (dwAtkFlags & AF_GENERIC) ) { ItemProp* pAttackerProp = pAttacker->GetActiveHandItemProp(); D3DXVECTOR3 vLocal; if( pAttackerProp && pAttackerProp->dwItemKind3 == IK3_YOYO ) { vLocal = pMover->GetPos(); vLocal.y += 1.0f; } else { AngleToVectorXZ( &vLocal, pAttacker->GetAngle(), 1.0f ); vLocal += pMover->GetPos(); //gmpbigsun : 피격자 일반 effect 09_12_17 vLocal.y += 1.0f; // 2006/6/20 xuzhu } if( pAttackerProp && pAttackerProp->dwSfxObj3 != NULL_ID ) CreateSfx( g_Neuz.m_pd3dDevice, pAttackerProp->dwSfxObj3, vLocal ); if( pAttackerProp && pAttackerProp->dwSfxObj5 != NULL_ID ) //gmpbigsun: 공격자 일반 effect 09_12_17 { vLocal = pAttacker->GetPos( ); CreateSfx( g_Neuz.m_pd3dDevice, pAttackerProp->dwSfxObj5, vLocal ); } } else if ( (dwAtkFlags & AF_MONSTER_SP_CLIENT) ) { // hitter ItemProp* pAttackerProp = prj.GetItemProp( nParam >> 16 ); assert( pAttackerProp ); DWORD dwSfxObj = pAttackerProp->dwSfxObj3; // gmpbigsun:특수공격에 이펙트가 있다면 3번사용. if( dwSfxObj != NULL_ID ) CreateSfx( D3DDEVICE, dwSfxObj, pMover->GetPos() ); // attacker dwSfxObj = pAttackerProp->dwSfxObj5; if( NULL_ID != dwSfxObj ) CreateSfx( D3DDEVICE, dwSfxObj, pAttacker->GetPos() ); }
// // // 클라이언트용 void CMover::ProcessMoveArrival( CCtrl *pObj ) { // 클라이언트 처리 if( IsActiveMover() ) { switch( m_oaCmd ) // 목표에 도착한 후의 명령 처리. { case OBJACT_USESKILL: if( pObj->GetType() == OT_MOVER && ( m_SkillTimerStop || m_SkillTimer.TimeOut() ) ) { CWorld *pWorld = GetWorld(); D3DXVECTOR3 vStart = GetPos(); vStart.y += 0.5f; D3DXVECTOR3 vEnd = pObj->GetPos(); vEnd.y += 0.5f; if( pWorld->IntersectObjLine( NULL, vStart, vEnd, FALSE, FALSE ) ) { g_WndMng.PutString( prj.GetText( TID_GAME_BLOCKTARGETING ), NULL, prj.GetTextColor( TID_GAME_BLOCKTARGETING ) ); g_WndMng.m_pWndWorld->SetNextSkill( NEXTSKILL_NONE ); break; } PlayCombatMusic(); int nSkillIdx = GetCmdParam(0); OBJID idTarget = (OBJID)GetCmdParam(1); SKILLUSETYPE sutType = (SKILLUSETYPE)GetCmdParam(2); if( (m_dwReqFlag & REQ_USESKILL) == 0 ) // 응답 요청중일땐 다시 보내선 안된다. { LPSKILL pSkill = GetSkill( 0, nSkillIdx ); // this가 가진 스킬중 nIdx에 해당하는 스킬을 꺼낸다. if( pSkill == NULL ) { Error( "CMD_SetUseSkill : %s skill(%d) not found", m_szName, nSkillIdx ); return; // skill not found } if( pSkill->dwSkill == SI_MAG_MAG_BLINKPOOL ) { CWndWorld* pWndWorld; pWndWorld = (CWndWorld*)g_WndMng.m_pWndWorld; { vStart = GetPos(); vStart.y += 1.0f; vEnd = pWndWorld->m_vTelePos; if( pWorld->IntersectObjLine( NULL, vStart, vEnd, FALSE, FALSE ) ) { g_WndMng.m_pWndWorld->SetNextSkill( NEXTSKILL_NONE ); g_WndMng.PutString( prj.GetText( TID_GAME_BLOCKTARGETING ), NULL, prj.GetTextColor( TID_GAME_BLOCKTARGETING ) ); break; } } if(g_pMoveMark!=NULL) g_pMoveMark->m_pSfxObj->m_nCurFrame=180; CreateSfx(g_Neuz.m_pd3dDevice,XI_GEN_MOVEMARK01,pWndWorld->m_vTelePos); } // 뒤에서 공격가능한 스킬인지 판단한다 // 강탈 스킬은 뒤에서 사용가능(일단 클라에서 판정하자~) if( pSkill->GetProp() && pSkill->GetProp()->dwAtkStyle == AS_BACK ) { D3DXVECTOR3 v3Pos; D3DXVECTOR3 v3PosSrc; D3DXVECTOR3 v3PosDest; // 방향벡터 1 v3PosSrc = pObj->GetPos() - GetPos(); D3DXVec3Normalize( &v3PosSrc, &v3PosSrc ); // 방향벡터 2 AngleToVectorXZ( &v3Pos, pObj->GetAngle(), 3.0f ); v3PosDest = (pObj->GetPos()+v3Pos) - pObj->GetPos(); D3DXVec3Normalize( &v3PosDest, &v3PosDest ); FLOAT fDir = D3DXVec3Dot( &v3PosSrc, &v3PosDest ); // 뒤가 아니면 스킬 사용 불가! if( fDir < 0.3f ) { g_WndMng.PutString( prj.GetText(TID_GAME_NEVERKILLSTOP) ); break; } } #if __VER >= 8 // __S8_PK // 카오에게 좋은 스킬을 사용할때는 Control 키를 눌러야 함 if( g_eLocal.GetState( EVE_PK ) ) { CMover * pMover; pMover = prj.GetMover( idTarget ); if( IsValidObj(pMover) && pMover != g_pPlayer && pMover->IsPlayer() && pMover->IsChaotic() ) if( pSkill->GetProp()->nEvildoing > 0 ) // 좋은 스킬 if( !(GetAsyncKeyState(VK_CONTROL) & 0x8000) ) break; } #endif // __VER >= 8 // __S8_PK TRACE( "OBJACT_USESKILL %d\n", nSkillIdx ); #if __VER >= 8 // __S8_PK BOOL bControl = ((GetAsyncKeyState(VK_CONTROL) & 0x8000)? TRUE:FALSE); g_DPlay.SendUseSkill( 0, nSkillIdx, idTarget, sutType, bControl ); // 목표지점에 도착하면 스킬쓴다고 알림. #else // __VER >= 8 // __S8_PK g_DPlay.SendUseSkill( 0, nSkillIdx, idTarget, sutType ); // 목표지점에 도착하면 스킬쓴다고 알림. #endif // __VER >= 8 // __S8_PK m_dwReqFlag |= REQ_USESKILL; // 응답 요청중 } ClearDestObj(); // 목표에 도달하면 추적을 멈춤. SendActMsg( OBJMSG_STOP ); if( !m_SkillTimerStop ) m_SkillTimer.Reset(); } break; //------------------------------------------ case OBJACT_MELEE_ATTACK: if( pObj->GetType() == OT_MOVER ) { ItemProp *pItemProp = GetActiveHandItemProp(); if( pItemProp && pItemProp->dwItemKind3 == IK3_YOYO ) { CWorld *pWorld = GetWorld(); D3DXVECTOR3 vStart = GetPos(); vStart.y += 0.5f; D3DXVECTOR3 vEnd = pObj->GetPos(); vEnd.y += 0.5f; if( pWorld->IntersectObjLine( NULL, vStart, vEnd, FALSE, FALSE ) ) { g_WndMng.PutString( prj.GetText( TID_GAME_BLOCKTARGETING ), NULL, prj.GetTextColor( TID_GAME_BLOCKTARGETING ) ); break; } } DoAttackMelee( (CMover *)pObj ); // pObj를 일반공격. } break; //--------------------------------------------- case OBJACT_MAGIC_ATTACK: if( pObj->GetType() == OT_MOVER ) { PlayCombatMusic(); OBJID idTarget = GetCmdParam(0); int nMagicPower = GetCmdParam(1); CMover *pTarget = prj.GetMover( idTarget ); // 타겟의 아이디를 포인터로 읽음. if( IsInvalidObj(pTarget) ) break; // 타겟이 거시기한 포인터면 취소시킴. SendActMsg( OBJMSG_STAND ); ClearDestObj(); // 목표에 도달하면 추적을 멈춤. DoAttackMagic( pTarget, nMagicPower ); } break; case OBJACT_RANGE_ATTACK: { if( pObj->GetType() == OT_MOVER ) { PlayCombatMusic(); OBJID idTarget = GetCmdParam(0); int nPower = GetCmdParam(1); CMover *pTarget = prj.GetMover( idTarget ); // 타겟의 아이디를 포인터로 읽음. if( IsInvalidObj(pTarget) ) break; // 타겟이 거시기한 포인터면 취소시킴. SendActMsg( OBJMSG_STAND ); SendActMsg( OBJMSG_STOP_TURN ); ClearDestObj(); // 목표에 도달하면 추적을 멈춤. DoAttackRange( pTarget, nPower, 0 ); // nPower를 dwItemID에 넣는다. } } break; //--------------------------------------------- case OBJACT_USEITEM: ClearDestObj(); // 그외는 목표에 도착하면 멈춤. SendActMsg( OBJMSG_STAND ); SetAngle( GetDegree(pObj->GetPos(), GetPos()) ); // 목표쪽으로 몸을 돌림. break; //--------------------------------------------- case OBJACT_COLLECT: ClearDestObj(); // 그외는 목표에 도착하면 멈춤. SendActMsg( OBJMSG_STOP ); SetAngle( GetDegree(pObj->GetPos(), GetPos()) ); // 목표쪽으로 몸을 돌림. g_DPlay.SendDoCollect( pObj ); // 서버로 보냄. break; //--------------------------------------------- default: ClearDestObj(); // 그외는 목표에 도착하면 멈춤. SendActMsg( OBJMSG_STOP ); break; } SetCmd( OBJACT_NONE ); } else { BOOL bQuery = m_pActMover->IsMove(); ClearDestObj(); // 그외는 목표에 도착하면 멈춤. SendActMsg( OBJMSG_STOP ); OnArrive( pObj->GetId(), 0 ); if( bQuery ) g_DPlay.SendQueryGetDestObj( this ); } }
// // 오브젝트를 움직이는데 필요한 메시지를 발생 // int CWndWorld::ControlGround( DWORD dwMessage, CPoint point ) { bool fCastCancel = false; int nMsg = 0; BOOL bTempKey, bSit; // ,bCombatKey, bFlyKey BOOL bUp, bDown, bLeft, bRight, bSpace, bBoard, bLForward = FALSE, bRForward = FALSE; BOOL bWalk; static BOOL s_bWalk2 = 0; CMover* pMover = CMover::GetActiveMover(); CWndChat* pWndChat = (CWndChat*) g_WndMng.GetApplet( APP_COMMUNICATION_CHAT ); BOOL bWhisper = g_bKeyTable['R']; if( pWndChat && bWhisper ) { if( 0 < strlen( g_Neuz.m_szWhisperName ) ) { CString strWhisper; strWhisper.Format( "/whisper %s ", g_Neuz.m_szWhisperName ); pWndChat->SetFocus(); CWndEditChat* pWndEdit = &pWndChat->m_wndEdit; pWndEdit->SetString( strWhisper ); pWndEdit->SetFocus(); g_bKeyTable['R'] = FALSE; } } // 전진/후진/스톱 CWndBase* pWndBaseFocus = (CWndBase*) g_WndMng.GetFocusWnd(); if( g_Neuz.m_bActiveNeuz == FALSE || ( pWndChat && pWndBaseFocus && pWndBaseFocus == pWndChat ) ) { g_bKeyTable[g_Neuz.Key.chUp] = FALSE;// | m_bRButtonDown; g_bKeyTable[g_Neuz.Key.chLeft] = FALSE; g_bKeyTable['S'] = FALSE; g_bKeyTable['D'] = FALSE; g_bKeyTable['Q'] = FALSE; g_bKeyTable['E'] = FALSE; } bUp = g_bKeyTable[g_Neuz.Key.chUp]; // | m_bRButtonDown; bDown = g_bKeyTable['S']; #ifdef __BS_ADJUST_SYNC //gmpbigsun : 키보드 조작중에는 마우스이동 불가 if( bUp || bDown ) m_bLButtonDown = FALSE; #endif if( bDown ) { g_WndMng.m_bAutoRun = FALSE; } if( bUp ) { m_timerAutoRunPush.Reset(); if( m_nDubleUp == 2 && m_timerAutoRun.TimeOut() == FALSE ) { m_nDubleUp = 3; g_WndMng.m_bAutoRun = TRUE; m_timerAutoRun.Reset(); m_timerAutoRunBlock.Reset(); } else { m_nDubleUp = 1; m_timerAutoRun.Reset(); } if( m_timerAutoRunBlock.TimeOut() ) g_WndMng.m_bAutoRun = FALSE; } else { if( m_timerAutoRunPush.TimeOut() == FALSE ) { if( m_nDubleUp == 1 ) m_nDubleUp = 2; } else { m_nDubleUp = 0; } } if( g_WndMng.m_bAutoRun ) bUp = TRUE; // 좌/우 회전 bLeft = g_bKeyTable[g_Neuz.Key.chLeft]; bRight = g_bKeyTable['D']; bSpace = g_bKeyTable[ VK_SPACE ]; // bCombatKey = g_bKeyTable['C']; // g_bKeyTable['C'] = 0; bBoard = g_bKeyTable['B']; bSit = g_bKeyTable['V']; g_bKeyTable['V'] = 0; /* if( g_Option.m_nInterface == 2 ) { bLForward = g_bKeyTable['Q']; bRForward = g_bKeyTable['E']; if( m_bLButtonDown ) { if( bLeft ) { bLeft = FALSE; bLForward = TRUE; } if( bRight ) { bRight = FALSE; bRForward = TRUE; } } } */ CWorld* pWorld = g_WorldMng.Get(); CRect rect = GetClientRect(); D3DXVECTOR3 vRayEnd; CObj* pFocusObj = pWorld->GetObjFocus(); CActionMover *pAct = pMover->m_pActMover; pAct->m_dwCtrlMsg = 0; if( m_bLButtonDown ) pAct->m_dwCtrlMsg |= CTRLMSG_LDOWN; #if __VER >= 12 // __ITEMCREATEMON_S0602 D3DXVECTOR3 vec3Tri[3]; pWorld->ClientPointToVector( vec3Tri, rect, point, &pWorld->m_matProj, &g_Neuz.m_camera.m_matView, &vRayEnd, TRUE ); g_Neuz.m_vCursorPos = vRayEnd; if( g_Neuz.m_pCreateMonItem ) { if( bUp || bDown || bLeft || bRight || bSpace || m_bLButtonDown ) { BOOL bSendCM = TRUE; if( m_bLButtonDown ) { D3DXVECTOR3 vDist2 = g_pPlayer->GetPos() - g_Neuz.m_vCursorPos; float fDist = D3DXVec3Length( &vDist2 ); // 두좌표간의 거리 if( 15.f < fDist ) { g_WndMng.PutString( prj.GetText( TID_GAME_CREATEMON_F_15 ), NULL, prj.GetTextColor( TID_GAME_CREATEMON_F_15 ) ); bSendCM = FALSE; } if( bSendCM ) { int nAttr = g_pPlayer->GetWorld()->GetHeightAttribute( g_Neuz.m_vCursorPos.x, g_Neuz.m_vCursorPos.z ); // 이동할 위치의 속성 읽음. if( nAttr == HATTR_NOWALK || nAttr == HATTR_NOMOVE || g_pPlayer->IsRegionAttr( RA_SAFETY ) || g_pPlayer->GetWorld()->GetID() == WI_WORLD_GUILDWAR ) // 못 움직이는 곳이거나 안전지역이면 Pass { g_WndMng.PutString( prj.GetText( TID_GAME_CREATEMON_F_AREA ), NULL, prj.GetTextColor( TID_GAME_CREATEMON_F_AREA ) ); bSendCM = FALSE; } else if( g_pPlayer->GetWorld()->GetID() != WI_WORLD_MADRIGAL ) { g_WndMng.PutString( prj.GetText( TID_GAME_CREATEMON_F_AREA ), NULL, prj.GetTextColor( TID_GAME_CREATEMON_F_AREA ) ); bSendCM = FALSE; } if( bSendCM ) { g_DPlay.SendCreateMonster( MAKELONG( ITYPE_ITEM, g_Neuz.m_pCreateMonItem->m_dwObjId ), g_Neuz.m_vCursorPos ); } } m_bLButtonDown = FALSE; } if( bSendCM ) g_Neuz.m_pCreateMonItem = NULL; } } #endif // __ITEMCREATEMON_S0602 //TODO:ata3k님 꼭 고쳐주세요. 왜 그런지 아무도 몰라! // 이동금지 상태가 아닐때만 클릭으로 이동할수 있다. #ifdef __Y_INTERFACE_VER3 bool *bpButton; if( g_Option.m_nInterface == 2 ) bpButton = &m_bLButtonUp; else bpButton = &m_bLButtonDown; if( *bpButton ) #else //__Y_INTERFACE_VER3 if( m_bLButtonDown ) #endif //__Y_INTERFACE_VER3 { #ifdef __Y_INTERFACE_VER3 if( g_Option.m_nInterface == 2 ) { *bpButton = FALSE; if( m_timerLButtonDown.GetLeftTime() > 200 ) return nMsg; } #endif //__Y_INTERFACE_VER3 D3DXVECTOR3 vec3Tri[3]; if( pWorld->ClientPointToVector( vec3Tri, rect, point, &pWorld->m_matProj, &g_Neuz.m_camera.m_matView, &vRayEnd, TRUE ) ) { // 이동 포인트를 얻어 목표 세팅 if( m_bFreeMove ) { // if( m_bLButtonDown ) // 이동금지 상태가 아닐때만 클릭으로 이동할수 있다. { { if( m_pWndGuideSystem && m_pWndGuideSystem->IsVisible()) #if __VER >= 12 // __MOD_TUTORIAL m_pWndGuideSystem->m_Condition.bIsClickOnLand = true; #else m_pWndGuideSystem->SendGuideMessage(GUIDE_EVENT_MOVE); #endif #ifdef __IAOBJ0622 if( GetLastPickObj() && GetLastPickObj()->GetType() == OT_SHIP ) pMover->SetDestPos( (CShip *)GetLastPickObj(), vRayEnd ); else pMover->SetDestPos( vRayEnd ); #else pMover->SetDestPos( vRayEnd ); #endif pMover->m_nCorr = -1; #ifndef __J0823 m_bFreeMove = FALSE; g_DPlay.SendSnapshot( TRUE ); fCastCancel = true; if( g_pMoveMark && g_pMoveMark->m_pSfxObj ) g_pMoveMark->m_pSfxObj->m_nCurFrame = 180; CSfx *pObj = CreateSfx(g_Neuz.m_pd3dDevice,XI_GEN_MOVEMARK01,vRayEnd); D3DXVECTOR3 vVector1 = vec3Tri[2] - vec3Tri[0]; D3DXVECTOR3 vVector2 = vec3Tri[1] - vec3Tri[0]; D3DXVECTOR3 vNormal; D3DXVec3Cross( &vNormal, &vVector1, &vVector2); D3DXVec3Normalize( &vNormal, &vNormal ); D3DXVECTOR3 v3Up = D3DXVECTOR3( 0.0f, -1.0f, 0.0f ); D3DXVECTOR3 v3Cross; FLOAT fDot; FLOAT fTheta; D3DXVec3Cross( &v3Cross, &v3Up, &vNormal ); fDot = D3DXVec3Dot( &v3Up, &vNormal ); fTheta = acos( fDot ); D3DXQUATERNION qDirMap; D3DXQuaternionRotationAxis( &qDirMap, &v3Cross, fTheta ); D3DXVECTOR3 vYPW; QuaternionRotationToYPW( qDirMap, vYPW ); pObj->m_pSfxObj->m_vRotate.x = D3DXToDegree(vYPW.x); pObj->m_pSfxObj->m_vRotate.y = D3DXToDegree(vYPW.y); pObj->m_pSfxObj->m_vRotate.z = D3DXToDegree(vYPW.z); #endif // __J0823 m_objidTracking = NULL_ID; } } } } } //if( !pMover->IsEmptyDestPos() || !pMover->IsEmptyDestObj() ) // return nMsg; #ifdef __Y_INTERFACE_VER3 if( bUp || bDown || bLeft || bRight || bSpace || bLForward || bRForward ) // 이동 키조작이 들어가면 자동공격 멈춤. #else //__Y_INTERFACE_VER3 if( bUp || bDown || bLeft || bRight || bSpace ) // 이동 키조작이 들어가면 자동공격 멈춤. #endif //__Y_INTERFACE_VER3 { if( bUp || bDown ) #if __VER >= 12 // __MOD_TUTORIAL { CWndGuideSystem* pWndGuide = NULL; pWndGuide = (CWndGuideSystem*)GetWndBase( APP_GUIDE ); if(pWndGuide && pWndGuide->IsVisible()) pWndGuide->m_Condition.bIsKeyMove = true; } #else m_pWndGuideSystem->SendGuideMessage(GUIDE_EVENT_KEY_MOVE); #endif m_bAutoAttack = FALSE; g_pPlayer->ClearCmd(); if( !bSpace ) m_objidTracking = NULL_ID; } if( m_objidTracking != NULL_ID ) { CMover* pObjTracking = prj.GetMover( m_objidTracking ); if( pObjTracking ) { D3DXVECTOR3 vDis = pMover->GetPos() - pObjTracking->GetPos(); if( D3DXVec3LengthSq( &vDis ) > 16 ) pMover->SetDestObj( m_objidTracking ); } else m_objidTracking = NULL_ID; } bool fMoved = false; bool fBehavior = false; if( bUp ) { if( pMover->SendActMsg( OBJMSG_FORWARD ) == 1 ) { fMoved = true; fCastCancel = true; } } else if( bDown ) { if( pMover->SendActMsg( OBJMSG_BACKWARD ) == 1 ) { fMoved = true; fCastCancel = true; } } #ifdef __Y_INTERFACE_VER3 else if( bLForward ) { if( pMover->SendActMsg( OBJMSG_LFORWARD ) == 1 ) { fMoved = true; fCastCancel = true; } } else if( bRForward ) { if( pMover->SendActMsg( OBJMSG_RFORWARD ) == 1 ) { fMoved = true; fCastCancel = true; } } #endif //__Y_INTERFACE_VER3 else // if( (bUp == FALSE && s_bUped == TRUE) || (bDown == FALSE && s_bDowned == TRUE) ) // 키를 뗀 순간에만 처리해보자.. if( bUp == FALSE || bDown == FALSE ) { if( pMover->IsEmptyDest() ) { if( pMover->m_pActMover->IsActJump() == FALSE && (pMover->m_pActMover->IsStateFlag( OBJSTAF_SIT ) ) == 0 ) // 앉아있을땐 실행하면 안된다. { if( pMover->SendActMsg( OBJMSG_STAND ) == 1 ) { fMoved = true; // TRACE( "PlayerMoved, " ); } } } } // s_bUped = bUp; // s_bDowned = bDown; if( bLeft ) { if( pMover->SendActMsg( OBJMSG_LTURN ) == 1 ) { fMoved = true; } } else if( bRight ) { if( pMover->SendActMsg( OBJMSG_RTURN ) == 1 ) { fMoved = true; } } else { if( pMover->SendActMsg( OBJMSG_STOP_TURN ) == 1 ) { fMoved = true; // fBehavior = true; } } // jump if( bSpace ) { #if __VER < 12 // __MOD_TUTORIAL if( m_pWndGuideSystem ) m_pWndGuideSystem->SendGuideMessage(GUIDE_EVENT_KEY_JUMP); #endif if( pMover->SendActMsg( OBJMSG_JUMP ) == 1 ) { fBehavior = true; fCastCancel = true; } } if( m_bLButtonDown == TRUE && m_bRButtonDown == TRUE ) { if( m_timerLButtonDown.GetLeftTime() < 500 && m_timerRButtonDown.GetLeftTime() < 500 ) { if( g_pPlayer->SendActMsg( OBJMSG_JUMP ) == 1 ) { fBehavior = true; fCastCancel = true; } } } #ifdef __Y_INTERFACE_VER3 if( g_Option.m_nInterface == 2 ) { if( g_bKeyTable[VK_DIVIDE] || g_bKeyTable[191] ) { bWalk = TRUE; } else { bWalk = FALSE; } } else { bWalk = g_bKeyTable[g_Neuz.Key.chWalk]; } #else //__Y_INTERFACE_VER3 bWalk = g_bKeyTable[g_Neuz.Key.chWalk]; #endif //__Y_INTERFACE_VER3 if( bWalk && !s_bWalk2 ) // 걷기 모드 토글. { if( pMover->m_pActMover->IsStateFlag( OBJSTAF_WALK ) ) { if( pMover->SendActMsg( OBJMSG_MODE_RUN ) == 1 ) { g_WndMng.PutString( prj.GetText( TID_GAME_RUN ), NULL, prj.GetTextColor( TID_GAME_RUN ) , CHATSTY_SYSTEM_CLIENT ); fBehavior = true; } } else { #if __VER < 12 // __MOD_TUTORIAL if(m_pWndGuideSystem) m_pWndGuideSystem->SendGuideMessage(GUIDE_EVENT_KEY_RUN); #endif if( pMover->SendActMsg( OBJMSG_MODE_WALK ) == 1 ) { g_WndMng.PutString( prj.GetText( TID_GAME_WALK ), NULL, prj.GetTextColor( TID_GAME_WALK ) , CHATSTY_SYSTEM_CLIENT ); fBehavior = true; } } } s_bWalk2 = bWalk; if( fMoved || fBehavior ) { g_pPlayer->ClearDest(); #ifdef __J0823 g_DPlay.ClearPlayerDestPos(); #endif // __J0823 } if( fMoved ) g_DPlay.SendPlayerMoved(); if( fBehavior ) g_DPlay.SendPlayerBehavior(); if( g_pPlayer->IsStateMode( STATE_BASEMOTION_MODE ) && fCastCancel ) // 캐스트 취소 { g_DPlay.SendStateModeCancel( STATE_BASEMOTION_MODE, STATEMODE_BASEMOTION_CANCEL ); } // 운영자가 쓰는 키. 서버로부터 좌표받아오기. if( bTempKey = g_bKeyTable[ '8' ] ) { if( !m_bTemp3ed ) { pMover->SendActMsg( OBJMSG_TEMP2 ); // __bTestLOD ^= 1; } } m_bTemp3ed = bTempKey; //----------- 스킬사용. /* static BOOL s_bShift2, s_bKeyC2; BOOL bShift, bKeyC; if( g_Option.m_nInterface == 1 ) // 신버전 인터페이스 방식은 X 가 스킬사용이다. { bShift = g_bKeyTable[ VK_SHIFT ]; bKeyC = g_bKeyTable[ 'C' ]; if( bKeyC ) { int a = 0; } if( (bShift && !s_bShift2) || (bKeyC && !s_bKeyC2) ) { CObj* pTargetObj = CObj::m_pObjHighlight; // 커서를 대고 있던 오브젝트가 하이라이트 오브젝이다. if( pTargetObj ) // 커서를 대고 있던 오브젝트가 있으면 { pWorld->SetObjFocus( pTargetObj ); // 그놈을 셀렉트 하는 동시에. CMover* pMover = (CMover*)pTargetObj; if( pMover->GetType() == OT_MOVER ) m_dwNextSkill = NEXTSKILL_ACTIONSLOT; // 스킬 사용 예약. } else m_dwNextSkill = NEXTSKILL_ACTIONSLOT; // 스킬 사용 예약. } s_bShift2 = bShift; s_bKeyC2 = bKeyC; } */ //------------ 비공정 타기 if( bBoard ) { if( !s_bBoarded ) // 플레이어가 비공정에 올라타있는 상태에서. 탑승키를 누르면. { if( g_pShip == NULL ) { if( g_pPlayer->GetIAObjLink() && g_pPlayer->GetIAObjLink()->GetType() == OT_SHIP && g_pPlayer->GetIAObjLink()->GetIndex() == 3 ) { CShip *pShip = (CShip *)g_pPlayer->GetIAObjLink(); if( pShip->GetMover() == NULL ) // 쥔장이 없는 배일때. { pShip->SetMover( g_pPlayer ); // 쥔장을 g_pPlayer로 설정. g_pShip = pShip; } } } else // 이미 배를 조종하고 있을때 { g_pShip->SetMover( NULL ); g_pShip = NULL; } } } s_bBoarded = bBoard; #ifdef _DEBUG // 디버깅용 키 if( bTempKey = g_bKeyTable[ VK_F2 ] ) { if( !s_bTempKeyed ) { // pMover->SendActMsg( OBJMSG_TEMP ); // g_Option.m_nObjectDetail ++; // if( g_Option.m_nObjectDetail > 2 ) g_Option.m_nObjectDetail = 0; } } s_bTempKeyed = bTempKey; if( bTempKey = g_bKeyTable[ 'F' ] ) { if( !m_bTemp2ed ) { pMover->SendActMsg( OBJMSG_TEMP3 ); } } m_bTemp2ed = bTempKey; #endif return nMsg; }
void CActionMover::ProcessFlyMove( void ) { #ifdef __CLIENT g_nDrift = 0; // 드리프트 플랙 클리어 #endif float fLenSq = D3DXVec3LengthSq( &m_vDelta ); if( fLenSq == 0.0f && (GetStateFlag() & OBJSTAF_ACC ) == 0 ) return; // 멈춤 상태면 리턴 CMover* pMover = m_pMover; FLOAT fAccPwr = m_fAccPower; #ifdef __CLIENT ProcessFlyTracking(); #endif // client // 터보모드 처리 if( (GetStateFlag() & OBJSTAF_TURBO) && (GetStateFlag() & OBJSTAF_ACC) ) // 터보모드 & 전진중 { #ifdef __WORLDSERVER pMover->m_tmAccFuel = (int)( pMover->m_tmAccFuel - (1000.0f / (float)FRAME_PER_SEC) ); // 1/60만큼 깎음 #endif if( pMover->m_tmAccFuel <= 0 ) // 가속연료가 다 떨어지면 { pMover->m_tmAccFuel = 0; SendActMsg( OBJMSG_MODE_TURBO_OFF ); // 터보모드 중지 #ifdef __WORLDSERVER g_UserMng.AddSendActMsg( pMover, OBJMSG_MODE_TURBO_OFF ); #endif } else fAccPwr *= 1.2f; // 가속연료가 남아있다면 터보모드 } #ifdef __CLIENT ProcessFlyParticle( fLenSq ); #endif // 관성처리 if( fAccPwr > 0.0f ) { // 힘벡터 생성 FLOAT fAngX = D3DXToRadian( pMover->GetAngleX() ); FLOAT fAng = D3DXToRadian( pMover->GetAngle() ); FLOAT fDist = cosf(fAngX) * fAccPwr; D3DXVECTOR3 vAcc; vAcc.x = sinf( fAng ) * fDist; vAcc.z = -cosf( fAng ) * fDist; vAcc.y = -sinf( fAngX ) * fAccPwr; // 관성벡터와 추진력벡터가 각도가 50도 이하면 급추진 if( fLenSq > 0.01f ) { D3DXVECTOR3 vDeltaNorm, vAccNorm; D3DXVec3Normalize( &vDeltaNorm, &m_vDelta ); D3DXVec3Normalize( &vAccNorm, &vAcc ); float fDot = D3DXVec3Dot( &vDeltaNorm, &vAccNorm ); if( fDot < 0.633319f ) // 이전코드: cosf(70.0f) 값으로는 대략 50도 { vAcc *= 2.0f; m_vDelta *= 0.985f; #ifdef __CLIENT g_nDrift = 1; if( ! ( pMover->IsMode( TRANSPARENT_MODE ) ) ) // 투명상태가 아닐때만 렌더. { if( (g_nProcessCnt & 3) == 0 ) CreateSfx( g_Neuz.m_pd3dDevice, XI_NAT_DUST_RUN, pMover->GetPos() ); } #endif } } fLenSq = D3DXVec3LengthSq( &m_vDelta ); // 1/60 sec 속도 float fMaxSpeed = 0.3f; if( GetStateFlag() & OBJSTAF_TURBO ) // 터보모드에선 MAX속도가 1.1배 fMaxSpeed *= 1.1f; if( fLenSq < (fMaxSpeed * fMaxSpeed) ) // 일정이상 속도를 넘지 않게 하자. m_vDelta += vAcc; // 관성벡터 += 추진력벡터 } m_vDelta *= (1.0f - FRIC_AIR); // 마찰력에 의한 감소 // raiders - 수치적 안정성을 위해서 적은 수치가 계산되는 것을 피한다. fLenSq = D3DXVec3LengthSq( &m_vDelta ); if( m_fAccPower == 0.0f && fLenSq < 0.0002f * 0.0002f ) { fLenSq = 0; m_vDelta = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); RemoveStateFlag( OBJSTAF_ACC ); // 가속상태 해제 } #ifdef __CLIENT if( pMover->IsActiveMover() ) { g_nFlySpeed = (int)( (sqrt(fLenSq) * 60.0f) * 60.0f * 60.0f ); g_nFlySpeed = (int)( g_nFlySpeed / 200.0f ); } #endif }
/** @brief sfx를 생성한다. @param vPos 값이 있다면 좌표를 기준으로 생성하고 없다면 this를 기준으로 생성한다. */ ID XSkillUser::CreateSfx( XSkillReceiver* pTarget, const XSkillDat *pSkillDat, const xEffSfx& effSfx, float secPlay, const XE::VEC2& vPos ) { return CreateSfx( pTarget, pSkillDat, effSfx.m_strSpr, effSfx.m_idAct, effSfx.m_Point, secPlay, vPos ); }
/** @brief 버프타입의 효과를 시전대상에게 시전한다. */ xtError XSkillUser::CastEffToCastTargetByBuff( const XSkillDat *pDat, EFFECT *pEffect, int level, XSkillReceiver *pCastingTarget, // const XE::VEC2& vPos, ID idCallerSkill ) { XASSERT( pCastingTarget ); // 발사체의 형태로 시전대상에게 날아오다 시전대상이 사라지면 대상이 널이 될수 있음 XBuffObj *pBuffObj = nullptr; bool bCreateBuff = true; // 사용하려는 스킬의 버프객체가 시전대상에 이미 있는지 검사. // 시전자가 다른 같은버프가 중복으로 걸릴수도 있으므로 시전자까지 같아야 한다 pBuffObj = pCastingTarget->FindBuffSkill( pDat->GetidSkill(), this ); if( pBuffObj == nullptr ) { // 시전자도 같고 아이디도 같은 버프는 없다. 그렇다면 // 시전자는 달라도 아이디는 같은 버프는 있는가? pBuffObj = pCastingTarget->FindBuffSkill( pDat->GetidSkill() ); if( pBuffObj ) { // 시전자는 다르고 아이디만 같은 경우 if( pEffect->bDuplicate ) { // 중복생성가능. 새로 버프를 생성시키도록 null로 클리어 한다. pBuffObj = nullptr; } } } // 지속시간형 객체를 생성한다. if( pBuffObj == nullptr ) { // 버프오브젝트만 일단 생성하고 이펙트오브젝트는 시전대상에 맞는것만 따로 추가한다 pBuffObj = CreateSkillBuffObj( this, pCastingTarget, pDat, level, // vPos, idCallerSkill ); XASSERT( pBuffObj ); // 시전대상의 버프리스트에 추가 pCastingTarget->AddSkillRecvObj( pBuffObj ); // 시전대상에게 버프가 추가된 직후 이벤트가 발생한다. pCastingTarget->OnAddSkillRecvObj( pBuffObj, pEffect ); // 지속이펙트생성(효과가 여러개여도 지속이펙트는 1개) if( pEffect->m_PersistEff.IsHave() ) { // const float secPlay = pEffect->GetDuration( level ); // 지속이펙트는 무조건 루핑. const float secPlay = pEffect->GetDuration(level); // 무한으로 돌리고 버프객체측에서 효과가 종료될때 삭제시킨다. const XE::VEC2 vZero; ID idSfx = CreateSfx( pCastingTarget, pDat, pEffect->m_PersistEff, secPlay, vZero ); if( idSfx ) { #ifdef _CLIENT pBuffObj->SetidSfx( idSfx ); #endif // _CLIENT } } // 시전사운드 플레이 if( pEffect->idCastSound ) { OnPlaySoundUse( pEffect->idCastSound ); // virtual } } XBREAK( pBuffObj == nullptr ); // 효과를 추가하고 타이머를 작동시킴 // 이펙트오브젝트를 추가. 이미 효과가 있으면 기존거를 리턴 EFFECT_OBJ *pEffObj = pBuffObj->FindEffect( pEffect ); if( pEffObj == nullptr ) { pEffObj = pBuffObj->AddEffect( pEffect ); // "시전"스크립트 실행 pBuffObj->ExecuteScript( GetThisRecv(), pEffect->scriptCast.c_str() ); // 버프객체에 이벤트 발생 pBuffObj->OnCastedEffect( pCastingTarget, pEffObj ); } // 지속시간 타이머를 셋. 이미 효과가 걸려있으면 타이머만 리셋 if( pEffect->IsDuration() ) { const auto sec = pEffect->GetDuration( level ); pEffObj->timerDuration.Set( sec ); pEffObj->cntDot = 0; } return xOK; } // CastEffToCastTargetByBuff
/** @brief 스킬 최초 사용 pUseSkill을 사용하기 위한 시작함수. 이 함수가 성공하면 스킬모션이 시작된다. 캐스팅 시작이라고 봐도 됨. @param pCurrTarget 현재 this가 잡고 있는 공격타겟. 혹은 직접 터치로 찍은 타겟. 스킬의 타입에 따라서 이 타겟은 안쓰여 질수도 있다. @param vPos 기준타겟이 좌표형태라면 IsZero가 아닌값이 들어온다. */ XSkillUser::xUseSkill XSkillUser::UseSkill( XSkillDat* pDat, int level, XSkillReceiver *pCurrTarget, const XE::VEC2& vPos ) { // 좌표에 값이 있으면 타겟은 널이어야 한다., XBREAK( vPos.IsNotZero() && pCurrTarget != nullptr ); xUseSkill infoUseSkill; if( XBREAK( pDat == nullptr ) ) { infoUseSkill.errCode = xERR_CRITICAL_ERROR; return infoUseSkill; } // auto pDat = pUseSkill->GetpDatMutable(); // 시전자 이펙트(시전시작 시점에 발생되는 이펙트) if( !pDat->GetCasterEff().m_strSpr.empty() ) { const float secPlay = 0.f; // play once const XE::VEC2 vZero; CreateSfx( GetThisRecv(), pDat, pDat->GetCasterEff(), secPlay, vZero ); } infoUseSkill.pDat = pDat; infoUseSkill.level = level; const auto baseTarget = pDat->GetbaseTarget(); // 기준타겟이 자신이면 this로 타겟을 바꾼다. if( baseTarget == xBST_SELF ) infoUseSkill.pBaseTarget = GetThisRecv(); else if( baseTarget == xBST_CURR_TARGET ) { // 현재타겟과 기준타겟우호가 맞지 않으면 일단 상속 클래스에 의뢰한다. const auto bitBaseTarget = pDat->GetbitBaseTarget(); infoUseSkill.pBaseTarget = pCurrTarget; if( pCurrTarget == nullptr || (bitBaseTarget == xfALLY && pCurrTarget->GetCamp().IsFriendly(GetCampUser()) == FALSE) || (bitBaseTarget == xfHOSTILE && pCurrTarget->GetCamp().IsEnemy( GetCampUser() ) == FALSE ) ) { infoUseSkill.pBaseTarget = GetSkillBaseTarget( pDat ); } // 타겟을 그래도 못찾은경우 에러코드 리턴(주변에 적당한 타겟이 없다거나 하면 생길수 있음.) if( infoUseSkill.pBaseTarget == nullptr ) { infoUseSkill.errCode = xERR_MUST_SELECT_TARGET; return infoUseSkill; } } else if( baseTarget == xBST_POSITION ) { if( vPos.IsZero() ) // 좌표가 없다면 하위클래스에 일단 의뢰해본다. infoUseSkill.vTarget = GetSkillBaseTargetPos( pDat ); else infoUseSkill.vTarget = vPos; // 기준타겟을 좌표로 하겠다고 했으면 좌표가 있어야함. if( XBREAK(infoUseSkill.vTarget.IsZero()) ) { infoUseSkill.errCode = xERR_MUST_SELECT_TARGET; return infoUseSkill; } } else // 기준타겟조건 if( baseTarget == xBST_CONDITION ) { #pragma message("=========================================기준타겟조건 쓰는것들 재점검") infoUseSkill.pBaseTarget = GetBaseTargetByCondition( pDat, pDat->GetcondBaseTarget(), pDat->GetrangeBaseTargetCond(), level, pCurrTarget, vPos ); if( infoUseSkill.pBaseTarget == nullptr ) { infoUseSkill.errCode = xOK; return infoUseSkill; } } else { // 하드코딩 } XBREAK( infoUseSkill.pBaseTarget == nullptr && infoUseSkill.vTarget.IsZero() ); infoUseSkill.errCode = xOK; return infoUseSkill; } // UseSkill