📄 attackgroup.cpp
字号:
vector<float3> enemyPositions;
enemyPositions.reserve(numEnemies);
// make a vector with the positions of all enemies
for (int i = 0; i < numEnemies; i++) {
if( unitArray[i] != -1) {
const UnitDef* enemy_ud = ai->cheat->GetUnitDef(unitArray[i]);
float3 enemyPos = ai->cheat->GetUnitPos(unitArray[i]);
// store enemy position if unit not cloaked and not an aircraft
if (ai->cb->GetUnitDef(unitArray[i]) != NULL && this->CloakedFix(unitArray[i]) && !enemy_ud->canfly) {
// TODO: remove currently cloaked units
// TODO: remove units not reachable by my unit type and position
enemyPositions.push_back(enemyPos);
}
}
}
// if ALL units are cloaked or aircraft, get their positions anyway
if (enemyPositions.size() == 0) {
for (int i = 0; i < numEnemies; i++) {
if (unitArray[i] != -1) {
float3 enemyPos = ai->cheat->GetUnitPos(unitArray[i]);
enemyPositions.push_back(enemyPos);
}
}
}
// find path to general enemy position
pathToTarget.clear();
float costToTarget = ai->pather->FindBestPath(&pathToTarget, &groupPosition, lowestAttackRange, &enemyPositions);
if (costToTarget < 0.001f && pathToTarget.size() <= 2) {
// cost of zero means something is in range, isShooting will take care of it
isMoving = false;
}
else {
isMoving = true;
this->pathIterator = 0;
}
}
else {
// attempt to path back to base if there are no targets
// KLOOTNOTE: this branch is now purposely never taken
// (might not be the best idea to leave units lingering
// around however)
return;
pathToTarget.clear();
float3 closestBaseSpot = ai->ah->GetClosestBaseSpot(groupPosition);
float costToTarget = ai->pather->FindBestPathToRadius(&pathToTarget, &groupPosition, THREATRES * 8, &closestBaseSpot);
// TODO: GetKBaseMeans() for support of multiple islands/movetypes
// TODO: this doesn't need to be to radius
if (costToTarget == 0 && pathToTarget.size() <= 2) {
isMoving = false;
if (ai->ah->DistanceToBase(groupPosition) > 500) {
// we could not path back to closest base spot
}
}
else {
isMoving = true;
this->pathIterator = 0;
}
}
if (!isShooting && !isMoving) {
// no accessible enemies and we're idling
}
}
bool CAttackGroup::NeedsNewTarget() {
return (defending && !isShooting && !isMoving);
}
void CAttackGroup::ClearTarget() {
this->isMoving = false;
this->defending = true;
this->attackPosition = ZEROVECTOR;
this->attackRadius = 0.0f;
this->pathToTarget.clear();
// to avoid getting a new target the first frame after arrival
this->isShooting = true;
}
// called from CAttackHandler::Update()
void CAttackGroup::Update(int frameNr) {
int frameSpread = 30;
unsigned int numUnits = units.size();
if (!numUnits) {
// empty attack group, nothing to update
return;
}
float3 groupPosition = GetGroupPos();
if (groupPosition == ERRORVECTOR)
return;
// this part of the code checks for nearby enemies and does focus fire/maneuvering
if ((frameNr % frameSpread) == ((groupID * 4) % frameSpread)) {
isShooting = false;
// get all enemies within attack range
float range = highestAttackRange + 100.0f;
int numEnemies = ai->cheat->GetEnemyUnits(unitArray, groupPosition, range);
if (numEnemies > 0) {
// select one of the enemies
int enemySelected = SelectEnemy(numEnemies, groupPosition);
// for every unit, pathfind to enemy perifery (with personal range - 10) then
// if distance to that last point in the path is < 10 or cost is 0, attack
if (enemySelected != -1) {
AttackEnemy(enemySelected, numUnits, range, frameSpread);
}
}
}
if (pathToTarget.size() >= 2) {
// AssignToTarget() was called for this group so
// we have an attack position and path, just move
// (movement may be slow due to spreading of orders)
if (!isShooting && isMoving && (frameNr % 60 == (groupID * 5) % frameSpread)) {
MoveAlongPath(groupPosition, numUnits);
}
} else {
// find something to attack within visual and radar LOS if AssignToTarget() wasn't called
if ((defending && !isShooting && !isMoving) && (frameNr % 60 == groupID % 60)) {
FindDefenseTarget(groupPosition, frameNr);
}
}
}
int CAttackGroup::SelectEnemy(int numEnemies, const float3& groupPos) {
int enemySelected = -1;
float shortestDistanceFound = FLT_MAX;
float temp;
for (int i = 0; i < numEnemies; i++) {
// my range not considered in picking the closest one
// TODO: is it air? is it cloaked?
bool b1 = ((temp = groupPos.distance2D(ai->cheat->GetUnitPos(unitArray[i]))) < shortestDistanceFound);
bool b2 = (ai->cheat->GetUnitDef(unitArray[i]) != NULL);
bool b3 = CloakedFix(unitArray[i]);
bool b4 = ai->cheat->GetUnitDef(unitArray[i])->canfly;
if (b1 && b2 && b3 && !b4) {
enemySelected = i;
shortestDistanceFound = temp;
}
}
return enemySelected;
}
void CAttackGroup::AttackEnemy(int enemySelected, int numUnits, float range, int frameSpread) {
float3 enemyPos = ai->cheat->GetUnitPos(unitArray[enemySelected]);
assert(CloakedFix(unitArray[enemySelected]));
isShooting = true;
for (unsigned int i = 0; i < numUnits; i++) {
int unit = units[i];
const UnitDef* udef = ai->cb->GetUnitDef(unit);
// does our unit exist and is it not currently maneuvering?
if (udef && (ai->MyUnits[unit]->maneuverCounter-- <= 0)) {
// TODO: add a routine finding best (not just closest) target
// TODO: in some cases, force-fire on position
// TODO: add canAttack
ai->MyUnits[unit]->Attack(unitArray[enemySelected]);
// TODO: this should be the max-range of the lowest-ranged weapon
// the unit has assuming you want to rush in with the heavy stuff
assert(range >= ai->cb->GetUnitMaxRange(unit));
// SINGLE UNIT MANEUVERING: testing the possibility of retreating to max
// range if target is too close, EXCEPT FOR FLAMETHROWER-EQUIPPED units
float3 myPos = ai->cb->GetUnitPos(unit);
float maxRange = ai->ut->GetMaxRange(udef);
float losDiff = (maxRange - udef->losRadius);
// float myRange = (losDiff > 0.0f)? (maxRange + udef->losRadius) * 0.5f: maxRange;
float myRange = (losDiff > 0.0f)? maxRange * 0.75f: maxRange;
bool b5 = udef->canfly;
bool b6 = myPos.y < (ai->cb->GetElevation(myPos.x, myPos.z) + 25);
bool b7 = (myRange - UNIT_MIN_MANEUVER_RANGE_DELTA) > myPos.distance2D(enemyPos);
// is it air, or air that's landed
if (!b5 || (b6 && b7)) {
bool debug1 = true;
bool debug2 = false;
vector<float3> tempPath;
// note 1: we don't need a path, just a position
// note 2: should avoid other immediate friendly units and/or immediate enemy units + radius
// maybe include the height parameter in the search? probably not possible
// doesn't this mean pathing might happen every second? outer limit should harsher than inner
float3 unitPos = ai->cheat->GetUnitPos(unitArray[enemySelected]);
float dist = ai->pather->FindBestPathToRadius(&tempPath, &myPos, myRange, &unitPos);
if (tempPath.size() > 0) {
float3 moveHere = tempPath.back();
dist = myPos.distance2D(moveHere);
// TODO: Penetrators are now broken
// is the position between the proposed destination and the
// enemy higher than the average of mine and his height?
float v1 = ((moveHere.y + enemyPos.y) / 2.0f) + UNIT_MAX_MANEUVER_HEIGHT_DIFFERENCE_UP;
float v2 = ai->cb->GetElevation((moveHere.x + enemyPos.x) / 2, (moveHere.z + enemyPos.z) / 2);
bool losHack = v1 > v2;
float a = (float) UNIT_MIN_MANEUVER_TIME / frameSpread;
float b = (dist / ai->MyUnits[unit]->def()->speed);
float c = ceilf(max(a, b));
// assume the pathfinder returns correct Y values
// REMEMBER that this will suck for planes
if (dist > max((UNIT_MIN_MANEUVER_RANGE_PERCENTAGE * myRange), float(UNIT_MIN_MANEUVER_DISTANCE)) && losHack) {
debug2 = true;
ai->MyUnits[unit]->maneuverCounter = int(c);
ai->MyUnits[unit]->Move(moveHere);
}
}
if (debug1 && !debug2) {
// pathfinder run but path not used?
}
}
else if (!udef->canfly || myPos.y < (ai->cb->GetElevation(myPos.x, myPos.z) + 25)) {
// this unit is an air unit
}
}
else {
// OUR unit is dead?
}
}
}
// give move orders to units along previously generated pathToTarget
void CAttackGroup::MoveAlongPath(float3& groupPosition, int numUnits) {
const int maxStepsAhead = 8;
int pathMaxIndex = (int) pathToTarget.size() - 1;
int step1 = min(pathIterator + maxStepsAhead / 2, pathMaxIndex);
int step2 = min(pathIterator + maxStepsAhead, pathMaxIndex);
float3 moveToHereFirst = pathToTarget[step1];
float3 moveToHere = pathToTarget[step2];
// if we aren't there yet
if (groupPosition.distance2D(pathToTarget[pathMaxIndex]) > GROUP_DESTINATION_SLACK) {
// TODO: give a group the order instead of each unit
for (unsigned int i = 0; i < numUnits; i++) {
int unit = units[i];
if (ai->cb->GetUnitDef(unit) != NULL) {
// TODO: when they are near target, change this so they eg. line up
// while some are here and some aren't, there's also something that
// should be done with the units in front that are given the same
// order+shiftorder and skittle around back and forth meanwhile if
// the single unit isn't there yet
if (ai->cb->GetUnitPos(unit).distance2D(pathToTarget[pathMaxIndex]) > UNIT_DESTINATION_SLACK) {
ai->MyUnits[unit]->Move(moveToHereFirst);
if (moveToHere != moveToHereFirst) {
ai->MyUnits[unit]->MoveShift(moveToHere);
}
}
}
}
// if group is as close as the pathiterator-indicated target
// is to the end of the path, increase pathIterator
pathIterator = 0;
float3 endOfPathPos = pathToTarget[pathMaxIndex];
float groupDistanceToEnemy = groupPosition.distance2D(endOfPathPos);
float pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos);
int increment = maxStepsAhead / 2;
while (groupDistanceToEnemy <= pathIteratorTargetDistanceToEnemy && pathIterator < pathMaxIndex) {
pathIterator = min(pathIterator + increment, pathMaxIndex);
pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos);
}
pathIterator = min(pathIterator, pathMaxIndex);
}
else {
// group thinks it has arrived at the destination
this->ClearTarget();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -