Ejemplo n.º 1
0
/**
 @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
Ejemplo n.º 2
0
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 );
} 
Ejemplo n.º 3
0
/**
 @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;
}
Ejemplo n.º 4
0
/**
 @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
Ejemplo n.º 5
0
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 );
        }

    }
}
Ejemplo n.º 6
0
/**
 @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;
	}
}
Ejemplo n.º 7
0
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() );
	}
Ejemplo n.º 8
0
//
//
// 클라이언트용
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 );
	}
}
Ejemplo n.º 9
0
//
//		오브젝트를 움직이는데 필요한 메시지를 발생
//
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
}
Ejemplo n.º 11
0
/**
 @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 );
}
Ejemplo n.º 12
0
/**
 @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
Ejemplo n.º 13
0
/**
 @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