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(); }
// 공격 대상을 찾아서 m_idTarget에 세팅한다. BOOL CAIMonster2::Search() { CMover *pMover = GetMover(); CWorld *pWorld = GetWorld(); CModelObject *pModel = (CModelObject *)pMover->GetModel(); FLOAT fRadius = pMover->GetRadiusXZ(); // this의 반지름 FLOAT fRadiusSq = fRadius * fRadius; // 반지름Sq버전. if( m_idTarget == NULL_ID && // 공격대상이 정해지지 않았고 (m_vTarget.x == 0 && m_vTarget.y == 0 && m_vTarget.z == 0) ) // 공격위치도 정해지지 않았다. { // 어떤 쉐리를 공격할까....? 를 선정함. if( SelectTarget() == FALSE ) return FALSE; } D3DXVECTOR3 vTarget; FLOAT fDistSq = 0; // 공격지점과 this의 거리. CMover *pTarget = NULL; if( m_idTarget != NULL_ID ) // 타겟오브젝이 있을때 { pTarget = prj.GetMover( m_idTarget ); if( IsValidObj(pTarget) ) vTarget = pTarget->GetPos(); // 공격좌표는 타겟무버의 좌표 else { m_idTarget = NULL_ID; return FALSE; // 타겟이 거시기 하면 걍리턴. } } else if( m_vTarget.x && m_vTarget.y && m_vTarget.z ) // 공격 좌표로 설정되어 있을때. { vTarget = m_vTarget; } else { return FALSE; // 타겟이 거시기 하면 걍리턴. } D3DXVECTOR3 vDist = vTarget - pMover->GetPos(); fDistSq = D3DXVec3LengthSq( &vDist ); // 목표지점까지는 거리. m_fAttackRange = fRadius; // 얼마나 근접해야하는가? 디폴트로 반지름 길이. if( fDistSq < fRadiusSq * 4.0f ) // 근거리면. { DWORD dwNum = xRandom( 100 ); // 0 ~ 99까지으 난수. if( dwNum < 85 ) { if( xRandom( 2 ) ) m_nAttackType = CAT_NORMAL; // 앞발로 밟기. else m_nAttackType = CAT_NORMAL2; m_fAttackRange = 5.0f; // 딱붙어서 밟아야 한다. ?? } else { m_nAttackType = CAT_QUAKEDOUBLE; // 두손으로 내려치기 - 스턴 } } else if( fDistSq < fRadiusSq * 6.0f ) // 반지름의 x배 이하는 쏘기. { DWORD dwNum = xRandom( 100 ); // 0 ~ 99까지으 난수. // 반지름 3배거리 이상이면 원거리. if( dwNum < 60 ) { m_nAttackType = CAT_QUAKE_ONE; // 하늘에서 돌 떨어뜨리기 m_fAttackRange = 15.0f; // xx미터 까지 접근하자. ?? } else { m_idTarget = NULL_ID; return FALSE; } } else { m_nAttackType = CAT_NORMAL; // 앞발로 밟기. m_fAttackRange = fRadius; //10.0f; } return TRUE; }
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; }