Example #1
0
//
// nDmgCnt : 일반적으론 0 : 지속데미지를 사용할경우에 0이 아닌값이 들어온다.
//
void CSfx::DamageToTarget( int nDmgCnt, float fDmgAngle, float fDmgPower, int nMaxDmgCnt )
{
	CMover* pObjSrc = (CMover*)prj.GetCtrl( m_idSrc );
	CCtrl* pObjDest = prj.GetCtrl( m_idDest );
	
	if( IsInvalidObj(pObjSrc) )		return;		// 지금은 걍 리턴하지만 이렇게 실패한경우는 m_idSfxHit을 Clear해주는작업이 필요하다.
	if(	IsInvalidObj(pObjDest) )	return;

	if( pObjDest->GetType() == OT_MOVER )
	{
		CMover* pMover = (CMover*) pObjDest;
#ifdef __CLIENT
		PLAYSND( pMover->GetProp()->dwSndDmg2, &pMover->GetPos() );	// 마법류 맞을때 타격음.	
#endif 

#ifdef __CLIENT
		// 쏜놈이 플레이어이거나 / 쏜놈은 플레이어가 아닌데 맞은놈이 플레이어일경우 전송
		if( pObjSrc->IsActiveMover() || (pObjSrc->IsPlayer() == FALSE && pObjDest->IsActiveObj()) )
		{
			pMover->SetDmgCnt( 10 );	// 발사체 맞아도 이제 흔들린다,
			g_DPlay.SendSfxHit( m_idSfxHit, m_nMagicPower, m_dwSkill, pObjSrc->GetId(), nDmgCnt, fDmgAngle, fDmgPower );
			if( nMaxDmgCnt == 1 )	// 한방짜리 데미지만 id를 클리어 함.
				m_idSfxHit = 0;		// 0으로 해놔야 this가 삭제될때 SendSfxClear를 또 보내지 않는다.
		}
#endif	// __CLIENT
	}
}
void CAIMonster2::InitAI()
{
	CMover* pMover = GetMover();
	MoverProp* pProperty = pMover->GetProp();
	ASSERT( pProperty );
	if( pProperty->dwAI >= AII_VER2_TYPE0 )
		m_dwFsmType = pProperty->dwAI - AII_VER2_TYPE0;		
	
	m_vPosBegin    = pMover->GetPos();
	m_fAttackRange = pMover->GetRadiusXZ();		
}
Example #3
0
// 싸이킥 월
void CCommonCtrl::_ProcessWall( void )
{
	if( m_nCount == 0 )
	{
	#ifdef __CLIENT		
		m_pSfxModel = new CSfxModel;
		m_pSfxModel2 = new CSfxModel;
		m_pSfxModel->SetSfx( "sfx_sklpsypsychicwall02" );
		m_pSfxModel2->SetSfx( "sfx_sklpsypsychicwall04" );
	#endif
	}

	D3DXVECTOR3	vPos = GetPos();

#ifndef __CLIENT
	CObj* pObj;
	BOOL bApply;
#endif //__CLIENT

	int nRange = 4;
	// 일반적으로 fDepth가 가장 길기때문에 검사 영역은 fDepth로 했다. 
	float fDepth = 3;
	
	if( fDepth <= 4.0f )		nRange = 4;
	else if( fDepth <= 8.0f )	nRange = 8;
	else if( fDepth <= 16.0f )	nRange = 16;
	else						nRange = 32;

#ifdef __WORLDSERVER
	CMover *pAttacker = prj.GetMover( m_idAttacker );
	
	if( IsInvalidObj( pAttacker ) )		// 일단 어태커가 사라지면 컨트롤도 사라지게 하자.
	{
		DestroyWall();
		return;
	}
	int nMin = m_pAddSkillProp->dwAbilityMin + (pAttacker->GetLevel() + (pAttacker->GetInt() / 10) * (int)m_pAddSkillProp->dwSkillLvl);
	int nMax = m_pAddSkillProp->dwAbilityMax + (pAttacker->GetLevel() + (pAttacker->GetInt() / 10) * (int)m_pAddSkillProp->dwSkillLvl);
	int nDamage = xRandom( nMin, nMax );
#if __VER >= 9	// __SKILL_0706
	int nMinPVP	= m_pAddSkillProp->dwAbilityMinPVP + ( pAttacker->GetLevel() + ( pAttacker->GetInt() / 10 ) * (int)m_pAddSkillProp->dwSkillLvl );
	int nMaxPVP		= m_pAddSkillProp->dwAbilityMaxPVP + ( pAttacker->GetLevel() + ( pAttacker->GetInt() / 10 ) * (int)m_pAddSkillProp->dwSkillLvl );
	int nDamagePVP	= xRandom( nMinPVP, nMaxPVP );
#endif	// __SKILL_0706

	int nHitPoint = 0;
	int nTargetHP = 0;
	
	FOR_LINKMAP( GetWorld(), vPos, pObj, nRange, CObj::linkDynamic, GetLayer() )
	{
		bApply = FALSE;
		if( pObj->GetType() == OT_MOVER )				// 대상이 무버일때만.
		{
			CMover *pTarget = (CMover *)pObj;
			if( pTarget->IsPeaceful() == FALSE )		// NPC가 아닌경우만 적용
				bApply = TRUE;
#if __VER >= 8 //	#ifdef	__JHMA_VER_8_5_1			 // 8.5차 경비병 범위스킬 공격효과 불가로 수정 World
			if( pAttacker->IsPlayer() && pAttacker->IsChaotic() == FALSE && pTarget->GetProp()->dwClass == RANK_GUARD )
				bApply = FALSE;
#endif //			#endif // __JHMA_VER_8_5_1			 // 8.5차 경비병 범위스킬 공격효과 불가로 수정 World	
			if( bApply )
			{
				if( IsValidObj( pTarget ) && pTarget->IsLive() )
				{
					if( pObj->IsRangeObj( vPos, 1.0f ) )
					{
						if( IsValidObj(pAttacker) )
						{
							nTargetHP = pTarget->GetHitPoint();
							nHitPoint = nTargetHP - nDamage;
							if( nHitPoint > 0 )
							{
								pTarget->m_nHitPoint = nHitPoint;
								g_UserMng.AddDamage( pTarget, pAttacker->GetId(), nDamage, AF_GENERIC );
							}
							else
							{
								pAttacker->SubExperience( pTarget );		// pTarget를 죽이고 난후의 m_pAttacker 경험치 처리.
								pTarget->DropItemByDied( pAttacker );				// 몬스터였다면 아이템 드랍.
								pAttacker->m_nAtkCnt = 0;					// 타겟을 죽였으면 공격자의 어택카운트 클리어
								pTarget->DoDie( pAttacker );				// pTarget 죽어라. 
								pTarget->m_nHitPoint = 0;
							}
						}
						m_nLife ++;		// 부딪힐때마다 카운트 올라감
						if( m_nLife >= (int)(m_pAddSkillProp->dwSkillLvl / 2) )
							DestroyWall();
						
						// 뒤로 밀리기 처리.
#if __VER >= 10	// __AI_0711
						if( pTarget->IsRank( RANK_MIDBOSS ) == FALSE )
#endif	// __AI_0711
						{
							FLOAT fPushAngle = pTarget->GetAngle() + 180.0f;
							FLOAT fPower = 0.825f;
							AngleToVectorXZ( &pTarget->m_pActMover->m_vDeltaE, fPushAngle, fPower );
							g_UserMng.AddPushPower( pTarget, pTarget->GetPos(), pTarget->GetAngle(), fPushAngle, fPower );
						}
					}
				}
			}
		}
	}
BOOL CAIMonster2::BeginAttack()
{
	CMover	*pMover = GetMover();

	OBJMSG		dwMsg = OBJMSG_NONE;
	DWORD		dwItemID = 0;
	MoverProp	*pProp = pMover->GetProp();

	// 추격하여 도착하면 선택되었던 공격방식을 적용시킨다.
	switch( m_nAttackType )
	{
	case CAT_NORMAL:		dwMsg = OBJMSG_ATK1;	dwItemID = pProp->dwAtk1;	break;
	case CAT_NORMAL2:		dwMsg = OBJMSG_ATK2;	dwItemID = pProp->dwAtk1;	break;
	case CAT_QUAKEDOUBLE:	dwMsg = OBJMSG_ATK3;	dwItemID = pProp->dwAtk3;	break;
	case CAT_QUAKE_ONE:		dwMsg = OBJMSG_ATK4;	dwItemID = pProp->dwAtk2;	break;
	default:
		ASSERT(0);
	}

	if( dwMsg == OBJMSG_NONE )
		return FALSE;

	if( m_idTarget == NULL_ID )
		return FALSE;

//	LPMODELELEM lpModelElem = prj.m_modelMng.GetModelElem( OT_MOVER, pMover->GetIndex() );
//	if( lpModelElem == NULL )
//		return FALSE;
//	if( lpModelElem->m_nMax 
	dwMsg = OBJMSG_ATK1;

	int nResult = pMover->DoAttackMelee( m_idTarget, dwMsg, dwItemID );
	if( nResult )
	{
		CMover *pTarget = prj.GetMover( m_idTarget );			
		// 이벤트 메세지
		// 보스몬스터가 유저에게 말을 한다.
		switch( m_nAttackType )
		{
		case CAT_QUAKEDOUBLE:
			{
				if( pTarget )
				{
					g_UserMng.AddWorldShout( pMover->GetName(), prj.GetText(TID_GAME_BOSS_BIGMUSCLE_MSG_04),
						pTarget->GetPos(), pTarget->GetWorld() );
				}
			}
			break;
		case CAT_QUAKE_ONE:
			{
				if( pTarget )
				{
					TCHAR szChar[128] = { 0 };
					sprintf( szChar, prj.GetText(TID_GAME_BOSS_BIGMUSCLE_MSG_05), pTarget->GetName() );
					g_UserMng.AddWorldShout( pMover->GetName(), szChar,
						pTarget->GetPos(), pTarget->GetWorld() );
				}
			}
			break;
		}					

		return TRUE;
	} 

	return FALSE;
}
BOOL CAIMonster2::SelectTarget()
{
	CMover	*pMover = GetMover();
	CWorld	*pWorld = GetWorld();
	int		nAttackFirstRange = pMover->GetProp()->m_nAttackFirstRange;

	FLOAT fRadius = pMover->GetRadiusXZ();		// this의 반지름
	FLOAT fRadiusSq = fRadius * fRadius;		// 반지름Sq버전.
	
	CMover *pLastAttacker = prj.GetMover( m_idLastAttacker );
	if( IsValidObj( pLastAttacker ) && pLastAttacker->IsDie() )
	{
		m_idLastAttacker = NULL_ID;
		pLastAttacker = NULL;
	}
	
	if( pLastAttacker == NULL )			// LastAttacker가 없어졌으면 타겟 다시 잡을 수 있도록 하자.
	{
		m_idLastAttacker = NULL_ID;
	} 
	else
	{		
		D3DXVECTOR3 vDist = pLastAttacker->GetPos() - pMover->GetPos();
		FLOAT fDistSq = D3DXVec3LengthSq( &vDist );		// 목표지점까지의 거리.
		if( fDistSq >= fRadiusSq * 10.0f )				// 라스트어태커가 내 반지름의 10배이상 떨어져있으면
		{
			// 타겟 포기
			m_idLastAttacker = NULL_ID;
			pLastAttacker = NULL;
		}
	}

	m_idTarget = NULL_ID;
	m_vTarget.x = m_vTarget.y = m_vTarget.z = 0;	// 일단 이건 안쓰는걸로 하자.

	if( m_idLastAttacker == NULL_ID )		// 아직 날 때린쉐리가 없다.
	{
		CMover* pTarget = NULL;
		pTarget = ScanTarget( pMover, nAttackFirstRange, JOB_ALL );		
		if( pTarget )
		{
			if( pMover->IsFlyingNPC() == pTarget->m_pActMover->IsFly() )	// 위상이 같으면 OK
				m_idTarget = pTarget->GetId();
			else
				return FALSE;		
		} 
		else
			return FALSE;
	} 
	else
	{
		// 날 때린 쉐리가 있다.
		DWORD dwNum = xRandom( 100 );		// 0 ~ 99까지으 난수.
		DWORD dwAggroRate = 50;
		
		if( IsValidObj( pLastAttacker ) )
		{
			if( pLastAttacker->GetJob() == JOB_MERCENARY )		// 마지막으로 날때린 쉐리가 머서면 어그로 좀더 주자.
				dwAggroRate = 70;
		}

		if( dwNum < dwAggroRate )		
		{
			// dwAggroRate% 확률로 마지막으로 날 때린넘 공격.
			m_idTarget = m_idLastAttacker;		// 날 공격한 쉐리를 타겟으로 지정하자.
		} 
		else if( dwNum < 75 )
		{
			// 50미터 반경내에서 가장 쎈넘을 잡자.
			CMover *pTarget = ScanTargetStrong( pMover, (float)( nAttackFirstRange ) );
			if( pTarget )
			{
				// this가 비행형 몬스터거나 || 타겟이 비행중이 아닐때만 공격.
				if( pMover->IsFlyingNPC() == pTarget->m_pActMover->IsFly() )	
					m_idTarget = pTarget->GetId();
				else
					m_idTarget = m_idLastAttacker;		// 타겟이 공격하기가 여의치 않으면 마지막으로 때린쉐리 공격하자.
			} 
			else
				m_idTarget = m_idLastAttacker;		// 타겟이 공격하기가 여의치 않으면 마지막으로 때린쉐리 공격하자.
		} 
		else if( dwNum < 100 )
		{
			// 오버힐하는 어시를 죽이자.
			CMover *pTarget = ScanTargetOverHealer( pMover, (float)( nAttackFirstRange ) );
			if( pTarget )
			{
				// this가 비행형 몬스터거나 || 타겟이 비행중이 아닐때만 공격.
				if( pMover->IsFlyingNPC() == pTarget->m_pActMover->IsFly() )	
					m_idTarget = pTarget->GetId();
				else
					m_idTarget = m_idLastAttacker;		// 타겟이 공격하기가 여의치 않으면 마지막으로 때린쉐리 공격하자.
			} 
			else
				m_idTarget = m_idLastAttacker;		// 타겟이 공격하기가 여의치 않으면 마지막으로 때린쉐리 공격하자.
		}
	}

	return TRUE;
}