/** @brief pCenter(or vCenter)를 중심으로 radius범위내의 유닛들을 얻어 어레이에 담는다. @return 얻어낸 타겟수 @param pCenter 중심타겟. null이면 vCenter를 사용한다. @param vCenter 중심좌표. pCenter가 있다면 사용되지 않는다. @param pixelRadius 픽셀단위 반지름. @param bitSideFilter 검색해야할 진영 @param numCost 총 코스트값. 각 유닛은 크기 코스트가 있으며 타겟이 검색될때마다 numCost에서 차감한다. 0이되면 더이상 담지 않는다. 차감한 코스값이 마이너스가 될수는 없다. 0은 코스트에 관계없이 무조건 타겟1개만 검색. -1은 무효값. @param bIncludeCenter 중심타겟(pCenter가 있을경우)을 대상에 포함할지 말지. pCenter가 없다면 이 옵션은 무시된다. */ int XEObjMngWithType::GetListUnitRadius2( XVector<XSPUnit> *pOutAry, XEBaseWorldObj *pCenter, const XE::VEC2& vCenter, float pixelRadius, BIT bitSideFilter, int numCost, bool bIncludeCenter, BIT bitFlag ) const // 생존필터 { if( numCost < 0 ) return 0; // XVector<XSPUnit> aryInNew; // pOutAry에 이미 타겟이 있는채로 들어왔을때 그것은 제한 리스트 XVector<XSPUnit> aryIn = *pOutAry; // 기존타겟에 반경검사타겟까지 포함. int costSum = 0; for( auto spUnit : m_listUnits ) { if( spUnit->IsDestroy() ) continue; XBREAK( spUnit == nullptr ); if( (spUnit->GetCamp() & bitSideFilter) == 0 ) continue; if( (bitFlag & XSKILL::xTF_LIVE) == 0 && spUnit->IsLive() ) continue; if( (bitFlag & XSKILL::xTF_DEAD) == 0 && spUnit->IsDead() ) continue; // 시전대상을 포함하지 않는 조건일때 유닛이 시전대상이면 스킵 if( pCenter && pCenter->GetsnObj() == spUnit->GetsnObj() // spUnit이 중심타겟일때 && bIncludeCenter == false ) // 중심타겟안포함 옵션이면 스킵한다. continue; if( bitFlag & XSKILL::xTF_DIFF_SQUAD ) { // spUnit이 이미 찾은 유닛과 같은 부대면 스킵 bool bExisted = false; for( auto& spFinded : aryIn ) { if( spUnit->GetpSquadObj()->GetsnSquadObj() == spFinded->GetpSquadObj()->GetsnSquadObj() ) { bExisted = true; break; } } if( bExisted ) continue; } const XE::VEC2 vDist = spUnit->GetvwPos().ToVec2() - vCenter; const float distsq = vDist.Lengthsq(); // 코스트와 관계없이 일단 범위안에 들어가는 타겟은 모두 담은후 거기서 다시 코스트에 따라 랜덤으로 꺼낸다. if( distsq <= pixelRadius * pixelRadius ) { aryIn.Add( spUnit ); aryInNew.Add( spUnit ); costSum += spUnit->GetSizeCost(); // 배열에 들어간 유닛들의 토탈코스트 } // in radius } // for // 목표코스트를 다 채우지 못했거나 딱맞게 채웠을때는 전체 리스트에서 랜덤으로 뽑을필요가 없으므로 그냥 검색된 리스트를 모두 리턴한다. if( costSum <= numCost ) { *pOutAry = aryIn; return pOutAry->Size(); } int currCost = numCost; // XVector<XSPUnit> ary = *pOutAry; // pOutAry->Clear(); // 영역안에 들어온 타겟들을 대상으로 다시 코스트에 따라 실제 타겟을 선정한다. while( aryInNew.Size() > 0 ) { auto spUnit = aryInNew.PopFromRandom(); if( numCost == 0 ) { if( pOutAry->Size() == 0 ) pOutAry->Add( spUnit ); return 1; } else { const auto costUnit = spUnit->GetSizeCost(); if( costUnit <= currCost ) { // 유닛 코스트가 남은 코스트를 깔수 있을때만. currCost -= costUnit; XBREAK( currCost < 0 ); pOutAry->Add( spUnit ); if( currCost == 0 ) // 코스트를 다 채웠으면 리턴. return pOutAry->Size(); } } } return pOutAry->size(); }