bool ZBrain::FindTarget() { MUID uidTarget = MUID(0,0); float fDist = FLT_MAX; for ( ZCharacterManager::iterator itor = ZGetCharacterManager()->begin(); itor != ZGetCharacterManager()->end(); ++itor) { // 죽은 놈은 관심없다. ZCharacter* pCharacter = (*itor).second; if ( pCharacter->IsDie()) continue; // 제외 목록에 들어간 캐릭터는 건너뜀 if ( ZGetGame()->IsExceptedFromNpcTargetting( pCharacter)) continue; // 거리를 구한다. float dist = MagnitudeSq( pCharacter->GetPosition() - m_pBody->GetPosition()); // 더 가까운 놈이면 이놈을 타겟으로 정한다. if ( dist < fDist) { fDist = dist; uidTarget = pCharacter->GetUID(); } } m_uidTarget = uidTarget; if ( uidTarget == MUID(0,0)) return false; return true; }
ZOBJECTHITTEST ZActor::HitTest(const rvector& origin, const rvector& to, float fTime, rvector *pOutPos) { rvector footpos,actor_dir; if(!GetHistory(&footpos,&actor_dir,fTime)) return ZOH_NONE; if (m_pNPCInfo->bColPick) { rvector dir = to - origin; Normalize(dir); RPickInfo pickinfo; memset(&pickinfo,0,sizeof(RPickInfo)); if (m_pVMesh->Pick(origin, dir, &pickinfo)) { *pOutPos = pickinfo.vOut; if ((pickinfo.parts == eq_parts_head) || (pickinfo.parts == eq_parts_face)) { return ZOH_HEAD; } else { return ZOH_BODY; } } } else { rvector headpos = footpos; if (m_pVMesh) { headpos.z += m_Collision.fHeight - 5.0f; } rvector ap,cp; float fDist=GetDistanceBetweenLineSegment(origin,to,footpos,headpos,&ap,&cp); float fDistToThis=Magnitude(origin-cp); if(fDist < (m_Collision.fRadius-5.0f)) { rvector dir = to - origin; Normalize(dir); rvector ap2cp = ap - cp; float fap2cpsq = MagnitudeSq(ap2cp); float fdiff = sqrt(m_Collision.fRadius*m_Collision.fRadius - fap2cpsq); if(pOutPos) *pOutPos = ap-dir*fdiff;; return ZOH_BODY; } } return ZOH_NONE; }
static rmatrix MakeSaneWorldMatrix(const v3& pos, v3 dir, v3 up) { #ifdef _DEBUG if (!IS_EQ(MagnitudeSq(dir), 1)) { DMLog("dir %f\n", Magnitude(dir)); if (IS_EQ(MagnitudeSq(dir), 0)) dir = { 1, 0, 0 }; else Normalize(dir); } if (!IS_EQ(MagnitudeSq(up), 1)) { DMLog("up %f\n", Magnitude(up)); if (IS_EQ(MagnitudeSq(up), 0)) up = { 1, 0, 0 }; else Normalize(up); } #endif auto mat = GetIdentityMatrix(); auto right = Normalized(CrossProduct(dir, up)); up = Normalized(CrossProduct(right, dir)); v3 basis[] = { right, dir, up }; for (size_t i{}; i < 3; ++i) for (size_t j{}; j < 3; ++j) mat.m[i][j] = basis[i][j]; SetTransPos(mat, pos); return mat; }
bool ZBrain::EscapeFromStuckIn(list<rvector>& wayPointList) { // 길찾기 코드의 허점이 드러나는 맵 지점들이 있다.. 그런 곳에서는 몹이 이동하지를 못한다 // 그런곳을 탈출하기 위해 땜빵을 한다. true 리턴하면 웨이포인트를 여기서 지정했다는 의미. DWORD currTime = timeGetTime(); // 오랜시간 같은 곳에 멈춰있다면 아예 워프해버린다 if (currTime - m_dwExPositionTimeForWarp > 2000) { rvector diff = m_exPositionForWarp - m_pBody->GetPosition(); ResetStuckInStateForWarp(); if (MagnitudeSq(diff) < 100) { OutputDebugString("NPC NEED WARP....\n"); RNavigationMesh* pNavMesh = ZGetGame()->GetWorld()->GetBsp()->GetNavigationMesh(); if (pNavMesh) { // 근방의 랜덤지점을 정한다 // 랜덤 방향 얻기 float angle = (rand() % (314*2)) * 0.01f; D3DXMATRIX matRot; D3DXMatrixRotationZ(&matRot, angle); rvector dir(200, 0, 0); // 이동할 거리 dir = dir * matRot; rvector newpos = m_pBody->GetPosition() + dir; // 가장 가까운 네비게이션노드의 센터로 옮긴다 (네비게이션노드가 크게 잡혀 있는 맵에선 워프가 심하게 눈에 띌수 있음..) RNavigationNode* pNavNode = pNavMesh->FindClosestNode(newpos); if (pNavNode) { m_pBody->SetPosition( pNavNode->CenterVertex()); OutputDebugString("NPC WARP DONE!\n"); return false; } } } } // 짧은시간 같은 곳에 멈춰있다면 앞으로 한발짝 정도 움직여서 탈출시도 if (currTime - m_dwExPositionTime > 1000) { rvector diff = m_exPosition - m_pBody->GetPosition(); ResetStuckInState(); if (MagnitudeSq(diff) < 100) { wayPointList.clear(); // 기본적으로 앞쪽으로 방향을 잡되 좌우로 랜덤하게 방향을 준다 rvector dir = m_pBody->GetDirection(); rmatrix matRot; D3DXMatrixRotationZ(&matRot, (rand()%314 - 157) * 0.01f); // 3.14 즉 반바퀴 범위 내에서 방향을 틀게 함 Normalize(dir); dir *= m_pBody->GetCollRadius() * 0.8f; wayPointList.push_back(m_pBody->GetPosition() + dir); PushWayPointsToTask(); return true; } } return false; }
void ZBrain::ProcessBuildPath( float fDelta) { // Update timer if ( !m_PathFindingTimer.Update( fDelta)) return; // Check status ZTASK_ID nTaskID = m_pBody->m_TaskManager.GetCurrTaskID(); if ( (nTaskID == ZTID_ATTACK_MELEE) || (nTaskID == ZTID_ATTACK_RANGE) || (nTaskID == ZTID_ROTATE_TO_DIR) || (nTaskID == ZTID_SKILL)) return; // 맵에 끼었다면 벗어난다 // 워프해서 끼임을 탈출하는 건 서바이벌일때만으로 제한한다 (워프는 눈에 크게 띄고 조악한 해결법이다. 기존 퀘스트에서 없던 몹워프가 일어나 유저 불만이 있음) if (ZGetGameTypeManager()->IsSurvivalOnly( ZGetGame()->GetMatch()->GetMatchType())) { if (EscapeFromStuckIn(m_WayPointList)) return; } // Get target ZObject* pTarget = GetTarget(); if ( !pTarget) { m_pBody->m_TaskManager.Clear(); m_pBody->Stop(); return; } // 원거리 공격이거나 우호적이면 넘 가까이 다가가지 않고 바라만 본다. if ( ( m_Behavior.GetOffenseType() == ZOFFENSETYPE_RANGE) || m_Behavior.IsFriendly()) { // 거리를 구한다. float dist = MagnitudeSq( pTarget->GetPosition() - m_pBody->GetPosition()); bool bStop = false; // Friendly type if ( m_Behavior.IsFriendly()) { if ( dist < m_fDistForcedIn) bStop = true; } // Else type else { // 직선 거리를 본다. if ( ( dist > DIST_FORCEDIN) && (dist < m_fDistIn)) { // 직선 거리는 가까운데 높이가 많이 차이가 나는지 본다. dist = pTarget->GetPosition().z - m_pBody->GetPosition().z; // 높이가 넘 많이 차이 안나면 정지 if ( (dist > -DIST_HEIGHT) && (dist < DIST_HEIGHT)) bStop = true; } } // Stop if ( bStop) { // 볼 수 있는 위치여야지 정지가 가능하다. 만약 안보인다면 뛰어가서 근접공격을 하도록 한다 if ( m_pBody->CanSee( pTarget) && m_pBody->CanAttackRange( pTarget)) { m_pBody->Stop(); m_pBody->m_TaskManager.Clear(); return; } } } // Make path RNavigationMesh* pNavMesh = ZGetGame()->GetWorld()->GetBsp()->GetNavigationMesh(); if ( pNavMesh == NULL) return; // Make navigation path rvector tarpos = pTarget->GetPosition(); if ( !pNavMesh->BuildNavigationPath( m_pBody->GetPosition(), tarpos)) return; m_WayPointList.clear(); for ( list<rvector>::iterator itor = pNavMesh->GetWaypointList().begin(); itor != pNavMesh->GetWaypointList().end(); ++itor) m_WayPointList.push_back( (*itor)); AdjustWayPointWithBound(m_WayPointList, pNavMesh); PushWayPointsToTask(); }
bool ZBrain::GetUseableSkill( int *pnSkill, MUID *puidTarget, rvector *pTargetPosition) { // Get skill module ZModule_Skills *pmod = (ZModule_Skills *)m_pBody->GetModule(ZMID_SKILLS); if ( !pmod) return false; // Set value if ( puidTarget) (*puidTarget) = MUID(0,0); if (pTargetPosition) (*pTargetPosition) = rvector(0.0f,0.0f,0.0f); // Check skills for ( int i = 0; i < pmod->GetSkillCount(); i++) { ZSkill *pSkill = pmod->GetSkill( i); // Check cool time if ( !pSkill->IsReady()) continue; // Get skill description ZSkillDesc *pDesc = pmod->GetSkill( i)->GetDesc(); // 스킬의 적용 대상이 아군인 경우... if ( pDesc->IsAlliedTarget()) { // 효과가 있는 대상중 가까이 있는 걸 찾는다. float fDist = DIST_OUT; ZObject *pAlliedTarget = NULL; for ( ZObjectManager::iterator itor = ZGetObjectManager()->begin(); itor != ZGetObjectManager()->end(); ++itor) { ZObject *pObject = itor->second; // 죽은 놈은 넘어간다 if ( pObject->IsDie()) continue; // 적이면 넘어간다 if ( ZGetGame()->CanAttack(m_pBody,pObject)) continue; // 자기 자신이면 넘어간다 if ( pObject == m_pBody) continue; // Get distance float dist = MagnitudeSq( pObject->GetPosition() - m_pBody->GetPosition()); if ( pSkill->IsUsable( pObject) && ( dist < fDist)) { fDist = dist; pAlliedTarget = pObject; } } // 만약 대상이 없으면 자기 자신한테라도 스킬을 건다. if ( ( pAlliedTarget == NULL) && ( pSkill->IsUsable( m_pBody))) pAlliedTarget = m_pBody; if (pAlliedTarget) { if ( pnSkill) *pnSkill = i; if ( puidTarget) *puidTarget = pAlliedTarget->GetUID(); if ( pTargetPosition) *pTargetPosition = pAlliedTarget->GetCenterPos(); return true; } } // 스킬의 적용 대상이 적군인 경우... else { ZObject* pTarget = GetTarget(); if ( pTarget == NULL) continue; // Check useable if ( !pSkill->IsUsable( pTarget)) continue; // Get pick info ZPICKINFO pickinfo; memset( &pickinfo, 0, sizeof( ZPICKINFO)); // Check picking rvector pos, tarpos, dir; // 적과 나의 몸통 실린더에서 가슴 정도의 높이 지점끼리 피킹 쏴본다.. pos = m_pBody->GetPosition() + rvector( 0, 0, m_pBody->GetCollHeight()*0.5f*0.8f); // 가슴께로 낮춰주려고 *0.8 tarpos = pTarget->GetPosition() + rvector( 0, 0, pTarget->GetCollHeight()*0.5f*0.8f); dir = tarpos - pos; Normalize( dir); const DWORD dwPickPassFlag = RM_FLAG_ADDITIVE | RM_FLAG_HIDE | RM_FLAG_PASSROCKET | RM_FLAG_PASSBULLET; if ( ZGetGame()->Pick( m_pBody, pos, dir, &pickinfo, dwPickPassFlag)) { if ( pickinfo.pObject) { if ( pnSkill) *pnSkill = i; if ( puidTarget) *puidTarget = pTarget->GetUID(); if ( pTargetPosition) *pTargetPosition = pTarget->GetCenterPos(); return true; } } } } return false; }