📄 attackhandler.cpp
字号:
proximity[i] = proximity[j];
proximity[j] = temp;
}
}
}
// now we have a kMeans list sorted by distance
// to enemies, 0 being risky and k being safest
}
bool CAttackHandler::UnitGroundAttackFilter(int unit) {
CUNIT u = *ai->MyUnits[unit];
bool result = ((u.def() != NULL) && (u.def()->canmove) && (u.category() == CAT_G_ATTACK));
return result;
}
bool CAttackHandler::UnitBuildingFilter(const UnitDef *ud) {
bool result = ((ud != NULL) && (ud->speed <= 0));
return result;
}
bool CAttackHandler::UnitReadyFilter(int unit) {
CUNIT u = *ai->MyUnits[unit];
bool result = ((u.def() != NULL) && (!ai->cb->UnitBeingBuilt(unit)) && ((ai->cb->GetUnitHealth(unit)) > (ai->cb->GetUnitMaxHealth(unit) * 0.8f)));
return result;
}
void CAttackHandler::AirAttack(int currentFrame) {
int numEnemies = ai->cheat->GetEnemyUnits(unitArray);
int bestTargetID = -1;
float bestTargetCost = -1.0f;
for (int i = 0; i < numEnemies; i++) {
int enemyID = unitArray[i];
const UnitDef* udef = (enemyID >= 0)? ai->cheat->GetUnitDef(enemyID): 0;
if (udef) {
float mCost = udef->metalCost;
float eCost = udef->energyCost;
float baseCost = mCost + eCost * 0.1f;
bool isStaticTarget = (udef->speed < 0.1f);
float targetCost = isStaticTarget? baseCost: baseCost * 0.01f;
if (targetCost > bestTargetCost) {
bestTargetID = enemyID;
bestTargetCost = targetCost;
}
}
}
if (bestTargetID != -1) {
// attack en-masse, regardless of AA
for (list<int>::iterator it = armedAirUnits.begin(); it != armedAirUnits.end(); it++) {
CUNIT* u = ai->MyUnits[*it];
u->Attack(bestTargetID);
}
airIsAttacking = true;
airTarget = bestTargetID;
}
}
void CAttackHandler::AirPatrol(int currentFrame) {
// get and make up some outer base perimeter
// points for air patrol route updates (if we
// aren't attacking)
vector<float3> outerMeans;
const int numClusters = 3;
outerMeans.reserve(numClusters);
if (kMeansK > 1) {
// offset the outermost one
int counter = (kMeansK / 8);
for (int i = 0; i < numClusters; i++) {
outerMeans.push_back(kMeansBase[counter]);
if (counter < kMeansK - 1)
counter++;
}
} else {
// there is just 1 k-means cluster and we need three
for (int i = 0; i < numClusters; i++) {
outerMeans.push_back(kMeansBase[0] + float3(250 * i, 0, 0));
}
}
if (outerMeans.size() < numClusters) {
// there were two kMeansK clusters?
return;
}
for (list<int>::iterator it = armedAirUnits.begin(); it != armedAirUnits.end(); it++) {
CUNIT* u = ai->MyUnits[*it];
// do this first in case we are in the enemy base
u->Move(outerMeans[0] + float3(0, 50, 0));
for (int i = 0; i < outerMeans.size(); i++) {
u->PatrolShift(outerMeans[i]);
}
}
airPatrolOrdersGiven = true;
}
void CAttackHandler::UpdateAir(int currentFrame) {
if (armedAirUnits.size() == 0)
return;
if (airIsAttacking) {
if (airTarget == -1) {
// we are attacking an invalid target
airIsAttacking = false;
} else {
// if we are attacking but our target is dead
if (ai->cheat->GetUnitHealth(airTarget) <= 0.0f) {
airIsAttacking = false;
airTarget = -1;
}
}
}
if (!airIsAttacking) {
if (armedAirUnits.size() >= 16) {
// start or continue attacking
// if we have 16 or more armed
// planes and no target
AirAttack(currentFrame);
} else {
// return to base
airIsAttacking = false;
airTarget = -1;
if (!airPatrolOrdersGiven) {
AirPatrol(currentFrame);
}
}
}
if (currentFrame % 1800 == 0) {
// clear patrol orders every 60 seconds
airPatrolOrdersGiven = false;
}
if (!airPatrolOrdersGiven && !airIsAttacking) {
AirPatrol(currentFrame);
}
}
void CAttackHandler::UpdateSea(int currentFrame) {
// TODO
}
void CAttackHandler::UpdateNukeSilos(int currentFrame) {
if ((currentFrame % 300) == 0 && ai->uh->NukeSilos.size() > 0) {
std::vector<std::pair<int, float> > potentialTargets;
GetNukeSiloTargets(potentialTargets);
for (std::list<NukeSilo>::iterator i = ai->uh->NukeSilos.begin(); i != ai->uh->NukeSilos.end(); i++) {
NukeSilo* silo = &*i;
if (silo->numNukesReady > 0) {
int targetID = PickNukeSiloTarget(potentialTargets);
if (targetID != -1) {
ai->MyUnits[silo->id]->Attack(targetID);
}
}
}
}
}
// pick a nuke-silo target from a vector of potential ones
// (if there are more than MAX_NUKE_SILOS/2 targets to choose
// from, pick one of the first <MAX_NUKE_SILOS/2>, else pick
// from the full size of the vector)
int CAttackHandler::PickNukeSiloTarget(std::vector<std::pair<int, float> >& potentialTargets) {
int s = potentialTargets.size();
int n = ((s > (MAX_NUKE_SILOS >> 1))? (MAX_NUKE_SILOS >> 1): s);
return ((s > 0)? potentialTargets[RANDINT % n].first: -1);
}
inline bool ComparePairs(const std::pair<int, float>& l, const std::pair<int, float>& r) {
return (l.second > r.second);
}
// sort all enemy targets in decreasing order by unit value
void CAttackHandler::GetNukeSiloTargets(std::vector<std::pair<int, float> >& potentialTargets) {
int numEnemies = ai->cheat->GetEnemyUnits(unitArray);
float minTargetValue = 500.0f;
std::vector<std::pair<int, float> > staticTargets;
std::vector<std::pair<int, float> > mobileTargets;
for (int i = 0; i < numEnemies; i++) {
int targetID = unitArray[i];
const UnitDef* udef = ai->cheat->GetUnitDef(targetID);
if (udef) {
float mCost = ai->cheat->GetUnitDef(targetID)->metalCost;
float eCost = ai->cheat->GetUnitDef(targetID)->energyCost;
float targetValue = mCost + eCost * 0.1f;
bool isMobileTarget = (udef->speed > 0.0f);
if (targetValue > minTargetValue) {
// don't waste nukes on radar towers
if (isMobileTarget) {
mobileTargets.push_back(std::make_pair(targetID, targetValue));
} else {
staticTargets.push_back(std::make_pair(targetID, targetValue));
}
}
}
}
std::sort(staticTargets.begin(), staticTargets.end(), &ComparePairs);
std::sort(mobileTargets.begin(), mobileTargets.end(), &ComparePairs);
// copy over all static targets
for (int i = 0; i < staticTargets.size(); i++) {
potentialTargets.push_back(staticTargets[i]);
}
// if there weren't any static targets
// then copy over all the mobile ones
if (staticTargets.size() == 0) {
for (int i = 0; i < mobileTargets.size(); i++) {
potentialTargets.push_back(mobileTargets[i]);
}
}
}
void CAttackHandler::AssignTarget(CAttackGroup* group_in) {
// get all enemies on map
int numEnemies = ai->cheat->GetEnemyUnits(unitArray);
if (numEnemies) {
vector<int> allEligibleEnemies;
allEligibleEnemies.reserve(numEnemies);
// make a vector with the positions of all
// non-air and non-cloaked (non-dead) enemies
for (int i = 0; i < numEnemies; i++) {
if (unitArray[i] != -1) {
const UnitDef* ud = ai->cheat->GetUnitDef(unitArray[i]);
if (ud) {
bool canFly = ud->canfly;
bool isCloaked = ud->canCloak && ud->startCloaked;
bool goodPos = !(ai->cheat->GetUnitPos(unitArray[i]) == ZEROVECTOR);
if (!canFly && !isCloaked && goodPos) {
allEligibleEnemies.push_back(unitArray[i]);
}
}
}
}
vector<int> availableEnemies;
vector<float3> enemyPositions;
availableEnemies.reserve(allEligibleEnemies.size());
// make a list of all enemies already assigned to (non-defending) groups
list<int> takenEnemies;
for (list<CAttackGroup>::iterator groupIt = attackGroups.begin(); groupIt != attackGroups.end(); groupIt++) {
if ((!groupIt->defending) && (groupIt->GetGroupID() != group_in->GetGroupID())) {
list<int> assignedEnemies = groupIt->GetAssignedEnemies();
takenEnemies.merge(assignedEnemies);
}
}
// filter out assigned enemies from eligible enemies
for (vector<int>::iterator enemy = allEligibleEnemies.begin(); enemy != allEligibleEnemies.end(); enemy++) {
int enemyID = *enemy;
bool taken = false;
for (list<int>::iterator it = takenEnemies.begin(); it != takenEnemies.end(); it++) {
if (*it == enemyID) {
taken = true;
break;
}
}
if (!taken) {
availableEnemies.push_back(enemyID);
enemyPositions.push_back(ai->cheat->GetUnitPos(enemyID));
}
}
if (availableEnemies.size() == 0)
return;
// find cheapest (best) target for this group
vector<float3> pathToTarget;
float3 groupPos = group_in->GetGroupPos();
ai->pather->micropather->SetMapData(ai->pather->MoveArrays[group_in->GetWorstMoveType()],
&ai->tm->ThreatArray.front(),
ai->tm->ThreatMapWidth,
ai->tm->ThreatMapHeight);
// pick an enemy position and path to it
// KLOOTNOTE: should be more like KAI 0.23 by passing group DPS to FindBestPath()
ai->pather->FindBestPath(&pathToTarget, &groupPos, THREATRES * 8, &enemyPositions);
if (pathToTarget.size() > 2) {
const int ATTACKED_AREA_RADIUS = 800;
int lastIndex = pathToTarget.size() - 1;
float3 endPos = pathToTarget[lastIndex];
// get all enemies surrounding endpoint of found path
int enemiesInArea = ai->cheat->GetEnemyUnits(unitArray, endPos, ATTACKED_AREA_RADIUS);
float powerOfEnemies = 0.000001;
// calculate combined "firepower" of armed enemies near endpoint
for (int i = 0; i < enemiesInArea; i++) {
if (ai->cheat->GetUnitDef(unitArray[i])->weapons.size() > 0) {
powerOfEnemies += ai->cheat->GetUnitPower(unitArray[i]);
}
}
if ((enemiesInArea > 0) && group_in->Size() >= 4 && (group_in->Power() > powerOfEnemies * 1.25f)) {
// assign target to this group
group_in->AssignTarget(pathToTarget, pathToTarget.back(), ATTACKED_AREA_RADIUS);
}
else {
// group too weak, forget about this target
group_in->ClearTarget();
}
}
}
}
void CAttackHandler::AssignTargets(int frameNr) {
if (frameNr % 120 == 0) {
// for each attack-group check whether it needs new target, if so assign one
for (list<CAttackGroup>::iterator it = attackGroups.begin(); it != attackGroups.end(); it++) {
CAttackGroup* group = &(*it);
// force group target updates every 300 frames
if (group->NeedsNewTarget() || frameNr % 300 == 0) {
AssignTarget(group);
}
}
}
}
void CAttackHandler::CombineGroups(void) {
bool removedSomething = false;
// pick a group A
for (list<CAttackGroup>::iterator groupA = attackGroups.begin(); groupA != attackGroups.end(); groupA++) {
// if it is defending
if (groupA->defending) {
int groupAid = groupA->GetGroupID();
float3 groupApos = groupA->GetGroupPos();
// look for other groups that are defending
for (list<CAttackGroup>::iterator groupB = attackGroups.begin(); groupB != attackGroups.end(); groupB++) {
// if they are close, combine
float3 groupBpos = groupB->GetGroupPos();
int groupBid = groupB->GetGroupID();
if ((groupB->defending) && (groupAid != groupBid) && (groupApos.distance2D(groupBpos) < 1500)) {
vector<int>* bUnits = groupB->GetAllUnits();
for (vector<int>::iterator groupBunit = bUnits->begin(); groupBunit != bUnits->end(); groupBunit++) {
groupA->AddUnit(*groupBunit);
}
this->attackGroups.erase(groupB);
removedSomething = true;
break;
}
}
}
if (removedSomething)
break;
}
}
void CAttackHandler::Update(int frameNr) {
int frameSpread = 300;
if (frameNr < 2)
UpdateKMeans();
// set map data here so it doesn't have to be done
// in each group (movement map PATHTOUSE is hack)
ai->pather->micropather->SetMapData(ai->pather->MoveArrays[PATHTOUSE], &ai->tm->ThreatArray.front(), ai->tm->ThreatMapWidth, ai->tm->ThreatMapHeight);
// calculate and draw k-means for the base perimeters every 10 seconds
if (frameNr % frameSpread == 0) {
UpdateKMeans();
int num = ai->uh->NumIdleUnits(CAT_G_ATTACK);
for (int i = 0; i < num; i++) {
int unit = ai->uh->GetIU(CAT_G_ATTACK);
if (PlaceIdleUnit(unit) && !ai->cb->GetUnitDef(unit)->canfly) {
ai->uh->IdleUnitRemove(unit);
}
}
}
// check for stuck units in each attack group every second
if (frameNr % 30 == 0) {
for (list<CAttackGroup>::iterator it = attackGroups.begin(); it != attackGroups.end(); it++) {
int stuckUnit = it->PopStuckUnit();
if (stuckUnit != -1 && ai->cb->GetUnitDef(stuckUnit) != NULL) {
pair<int, float3> foo;
foo.first = stuckUnit;
foo.second = ai->cb->GetUnitPos(stuckUnit);
stuckUnits.push_back(foo);
// popped a stuck unit from attack group it->GetGroupID()
ai->MyUnits[stuckUnit]->Stop();
ai->MyUnits[stuckUnit]->groupID = STUCK_GROUP_ID;
}
// if attack group now empty then kill it
if (it->Size() == 0) {
attackGroups.erase(it);
break;
}
}
}
// combine groups that are defending and too weak to attack anything
if (frameNr % frameSpread == 0) {
CombineGroups();
}
// check if we have any new units, add them to a
// nearby defending group of less than 16 units
if (frameNr % 30 == 0 && units.size() > 0) {
CAttackGroup* existingGroup = NULL;
for (list<CAttackGroup>::iterator it = attackGroups.begin(); it != attackGroups.end(); it++) {
if (it->Size() < 16 && it->defending && this->DistanceToBase(it->GetGroupPos()) < 300) {
existingGroup = &(*it);
// KLOOTNOTE: pick the first valid group, not the last
break;
}
}
if (existingGroup != NULL) {
// add all new units to found group
for (list<int>::iterator it = units.begin(); it != units.end(); it++) {
int unit = *it;
if (ai->cb->GetUnitDef(unit) != NULL) {
existingGroup->AddUnit(unit);
}
}
units.clear();
}
else {
// no suitable group found, make new defending one
newGroupID++;
CAttackGroup newGroup(ai, newGroupID);
newGroup.defending = true;
for (list<int>::iterator it = units.begin(); it != units.end(); it++) {
int unit = *it;
if (ai->cb->GetUnitDef(unit) != NULL) {
newGroup.AddUnit(unit);
}
}
units.clear();
attackGroups.push_back(newGroup);
}
}
// do basic attack group formation from defense units
UpdateAir(frameNr);
UpdateSea(frameNr);
UpdateNukeSilos(frameNr);
AssignTargets(frameNr);
// update current groups
for (list<CAttackGroup>::iterator it = attackGroups.begin(); it != attackGroups.end(); it++) {
it->Update(frameNr);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -