// 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 = std::min(pathIterator + maxStepsAhead / 2, pathMaxIndex); int step2 = std::min(pathIterator + maxStepsAhead, pathMaxIndex); const float3& moveToHereFirst = pathToTarget[step1]; const 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 assert(numUnits >= 0); for (unsigned int i = 0; i < (unsigned int)numUnits; i++) { CUNIT* unit = ai->GetUnit(units[i]); if (ai->cb->GetUnitDef(unit->uid) != 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 ((unit->pos()).distance2D(pathToTarget[pathMaxIndex]) > UNIT_DESTINATION_SLACK) { unit->Move(moveToHereFirst); if (moveToHere != moveToHereFirst) { 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 = std::min(pathIterator + increment, pathMaxIndex); pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos); } pathIterator = std::min(pathIterator, pathMaxIndex); } else { // group thinks it has arrived at the destination this->ClearTarget(); } }
void CAttackHandler::UpdateAir() { if (airUnits.size() == 0) return; /* if enemy is dead, attacking = false every blue moon, do an air raid on most valuable thingy */ assert(!(airIsAttacking && airTarget == -1)); if (airIsAttacking && (airUnits.size() == 0 || ai->cheat->GetUnitDef(airTarget) == NULL)) { airTarget = -1; airIsAttacking = false; } if (ai->cb->GetCurrentFrame() % (60*30*5) == 0 //5 mins || (ai->cb->GetCurrentFrame() % (30*30) == 0 && airUnits.size() > 8)) { //30 secs && 8+ units //L("AH: trying to attack with air units."); int numOfEnemies = ai->cheat->GetEnemyUnits(unitArray); int bestID = -1; float bestFound = -1.0; for (int i = 0; i < numOfEnemies; i++) { int enemy = unitArray[i]; if (enemy != -1 && ai->cheat->GetUnitDef(enemy) != NULL && ai->cheat->GetUnitDef(enemy)->metalCost > bestFound) { bestID = enemy; bestFound = ai->cheat->GetUnitDef(enemy)->metalCost; } } //L("AH: selected the enemy: " << bestID); if (bestID != -1 && ai->cheat->GetUnitDef(bestID)) { //give the order for (list<int>::iterator it = airUnits.begin(); it != airUnits.end(); it++) { CUNIT* u = ai->MyUnits[*it]; u->Attack(bestID); } airIsAttacking = true; airTarget = bestID; ai->cb->SendTextMsg("AH: air group is attacking", 0); } } //TODO: units currently being built. (while the others are off attacking) if (ai->cb->GetCurrentFrame() % 1800 == 0) { airPatrolOrdersGiven = false; } if (!airPatrolOrdersGiven && !airIsAttacking) { //L("AH: updating air patrol routes"); // assert(false); //get / make up some outer base perimeter points vector<float3> outerMeans; const int num = 3; outerMeans.reserve(num); if (kMeansK > 1) { int counter = 0; counter += kMeansK / 8; //offsetting the outermost one for (int i = 0; i < num; i++) { outerMeans.push_back(kMeansBase[counter]); if (counter < kMeansK-1) counter++; } } else { //theres just 1 kmeans and we need three for (int i = 0; i < num; i++) { outerMeans.push_back(kMeansBase[0] + float3(250*i, 0, 0)); } } assert(outerMeans.size() == num); //give the patrol orders to the outer means for (list<int>::iterator it = airUnits.begin(); it != airUnits.end(); it++) { CUNIT* u = ai->MyUnits[*it]; u->Move(outerMeans[0] + float3(0,50,0)); //do this first in case theyre in the enemy base for (int i = 0; i < num; i++) { u->PatrolShift(outerMeans[i]); } } airPatrolOrdersGiven = true; } }
void CAttackGroup::AttackEnemy(int enemySelected, int numUnits, float range, int frameSpread) { const float3& enemyPos = ai->ccb->GetUnitPos(ai->unitIDs[enemySelected]); assert(CloakedFix(ai->unitIDs[enemySelected])); isShooting = true; assert(numUnits >= 0); for (unsigned int i = 0; i < (unsigned int)numUnits; i++) { CUNIT* unit = ai->GetUnit(units[i]); const UnitDef* udef = ai->cb->GetUnitDef(unit->uid); if (udef == NULL || unit->maneuverCounter-- > 0) { // our unit does not exist (?) or is it currently maneuvering continue; } // TODO: add a routine finding best (not just closest) target // TODO: in some cases, force-fire on position // TODO: add canAttack unit->Attack(ai->unitIDs[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->uid)); // 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->uid); const float maxRange = ai->ut->GetMaxRange(udef); const float losDiff = (maxRange - udef->losRadius); // const float myRange = (losDiff > 0.0f)? (maxRange + udef->losRadius) * 0.5f: maxRange; const float myRange = (losDiff > 0.0f)? maxRange * 0.75f: maxRange; const bool b6 = (myPos.y < (ai->cb->GetElevation(myPos.x, myPos.z) + 25.0f)); const bool b7 = (myRange - UNIT_MIN_MANEUVER_RANGE_DELTA) > myPos.distance2D(enemyPos); if (!udef->canfly || (b6 && b7)) { std::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 const float3 unitPos = ai->ccb->GetUnitPos(ai->unitIDs[enemySelected]); //!! TODO: //!! for deterministic reproduction, should be able to //!! set the random seed for the RNG's to a fixed value ai->pather->FindBestPathToRadius(tempPath, myPos, myRange, unitPos); if (!tempPath.empty()) { const float3& moveHere = tempPath.back(); const float 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? const float v1 = ((moveHere.y + enemyPos.y) * 0.5f) + UNIT_MAX_MANEUVER_HEIGHT_DIFFERENCE_UP; const float v2 = ai->cb->GetElevation((moveHere.x + enemyPos.x) * 0.5f, (moveHere.z + enemyPos.z) * 0.5f); if (v1 <= v2) { // nope continue; } const float a = float(UNIT_MIN_MANEUVER_TIME) / frameSpread; const float b = (dist / unit->def()->speed); const float c = ceilf(std::max(a, b)); // assume the pathfinder returns correct Y values // REMEMBER that this will suck for planes if (dist > std::max((UNIT_MIN_MANEUVER_RANGE_PERCENTAGE * myRange), float(UNIT_MIN_MANEUVER_DISTANCE))) { unit->maneuverCounter = int(c); unit->Move(moveHere); } } } } }
void CAttackGroup::Update() { int frameNr = ai->cb->GetCurrentFrame(); ////L("AG: Update-start. isShooting:" << isShooting << " isMoving:" << isMoving << " frame:" << frameNr << " groupID:" << groupID << " path size:" << pathToTarget.size()); int numUnits = (int)units.size(); if(!numUnits) { //L("updated an empty AttackGroup!"); return; } int frameSpread; // variable used as a varying "constant" for determining how often a part of the update scheme is done float3 groupPosition = this->GetGroupPos(); if(groupPosition == ERRORVECTOR) return; //debug line: // //ai->cb->CreateLineFigure(groupPosition + float3(0,100,50), groupPosition + float3(5+(unitCounter*20),100,50),20,0,2,groupID+1500); //sets the isShooting variable. - this part of the code checks for nearby enemies and does focus fire/maneuvering frameSpread = 30; if (/*myEnemy == -1 && */frameNr % frameSpread == (groupID*4) % frameSpread) { // //L("AG: isShooting start"); this->isShooting = false; // int enemies[MAXUNITS]; //get all enemies within attack range: float range = this->highestAttackRange + 100.0f;//(this->highestAttackRange+200.0f)*2.0f; int numEnemies = ai->cheat->GetEnemyUnits(unitArray, groupPosition, range); // //L("this is the isShooting setting procedure, numEnemies=" << numEnemies << " range checked: " << range << " and the position of the group is (" << groupPosition.x << " " << groupPosition.y << " " << groupPosition.z << ") numUnits:" << numUnits); //assert(numEnemies > 0); if(numEnemies > 0) { ////L("this is the isShooting setting procedure, numEnemies=" << numEnemies << " range checked: " << range << " and the position of the group is (" << groupPosition.x << " " << groupPosition.y << " " << groupPosition.z << ") numUnits:" << numUnits); // //L("this is the isShooting setting and numEnemies was more than 0 : " << numEnemies); //ai->cb->SendTextMsg("attackgroup : Im not sure that this happens, ever", 0); //selecting one of the enemies int enemySelected = -1; float shortestDistanceFound = FLT_MAX; float temp; //bool closestSoFarIsBuilding = false; shoot units first //float3 enemyPos;// = ai->cheat->GetUnitPos(enemies[enemySelected]); int xSize = 40; int lineGroupID = groupID+5000; // int heightOffset = 10; for (int i = 0; i < numEnemies; i++) { //my range not considered in picking the closest one //TODO: is it air? is it cloaked? if (((temp = groupPosition.distance2D(ai->cheat->GetUnitPos(unitArray[i]))) < shortestDistanceFound) && ai->cheat->GetUnitDef(unitArray[i]) != NULL && CloakedFix(unitArray[i]) && !ai->cheat->GetUnitDef(unitArray[i])->canfly) { enemySelected = i; shortestDistanceFound = temp; } // enemyPos = ai->cheat->GetUnitPos(unitArray[i]); // //ai->cb->CreateLineFigure(enemyPos + float3(-xSize,heightOffset,0), enemyPos + float3(xSize,heightOffset,0), 5, 0, frameSpread/2, 6000+(groupID*6000 + i)); // //ai->cb->CreateLineFigure(enemyPos + float3(0,heightOffset,-xSize), enemyPos + float3(0,heightOffset,xSize), 5, 0, frameSpread/2, 6000+(groupID*6000 + numEnemies + i)); } // enemyPos = ai->cheat->GetUnitPos(unitArray[enemySelected]); //X marks the target // //ai->cb->CreateLineFigure(enemyPos + float3(-xSize,heightOffset,-xSize), enemyPos + float3(xSize,heightOffset,xSize), 5, 0, frameSpread, lineGroupID); // //ai->cb->CreateLineFigure(enemyPos + float3(-xSize,heightOffset,xSize), enemyPos + float3(xSize,heightOffset,-xSize), 5, 0, frameSpread, lineGroupID); //tiny line from there to groupPosition // //ai->cb->CreateLineFigure(enemyPos, groupPosition, 5, 0, frameSpread, lineGroupID); //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 // hack: just attack it ////L("index of the best enemy: " << enemySelected << " its distance: " << shortestDistanceFound << " its id:" << enemies[enemySelected] << " humanName:" << ai->cheat->GetUnitDef(enemies[enemySelected])->humanName); //TODO: can enemy id be 0 //GIVING ATTACK ORDER: if (enemySelected != -1) { //L("AG:isShooting: YES"); float3 enemyPos = ai->cheat->GetUnitPos(unitArray[enemySelected]); assert (CloakedFix(unitArray[enemySelected])); this->isShooting = true; for (int i = 0; i < numUnits; i++) { int unit = units[i]; //does our unit exist and its not currently maneuvering if (ai->cb->GetUnitDef(unit) != NULL && ai->MyUnits[unit]->maneuverCounter-- <= 0) { //add a routine finding the best(not just closest) target, but for now just fire away //TODO: add canattack ai->MyUnits[unit]->Attack(unitArray[enemySelected]); //TODO: some cases, force fire on position //assert(!ai->cheat->GetUnitDef(unitArray[enemySelected])->canfly); //TODO: this should be the max range of the weapon the unit has with the lowest range //assuming you want to rush in with the heavy stuff, that is assert(range >= ai->cb->GetUnitMaxRange(unit)); //SINGLE UNIT MANEUVERING: //testing the possibility of retreating to max range if target is too close float3 myPos = ai->cb->GetUnitPos(unit); float myRange = this->ai->ut->GetMaxRange(ai->cb->GetUnitDef(unit)); //is it air, or air thats landed if(!ai->cb->GetUnitDef(unit)->canfly || myPos.y < ai->cb->GetElevation(myPos.x, myPos.z) + 25 && (myRange - UNIT_MIN_MANEUVER_RANGE_DELTA) > myPos.distance2D(enemyPos)) { bool debug1 = true; bool debug2 = false; // ai->cb->SendTextMsg("range diff makes it good to maneuver && im not air", 0); vector<float3> tempPath; //two things: 1. dont need a path, just a position and 2. it should avoid other immediate friendly units and/or immediate enemy units+radius //needs to be a point ON the perifery circle, not INSIDE it - fixed //maybe include the height parameter in the search? probably not possible //TODO: OBS doesnt this mean pathing might happen every second? outer limit should be more harsh 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. kind of. //is the position between the proposed destination and the enemy higher than the average of mine and his height? bool losHack = ((moveHere.y + enemyPos.y)/2.0f) + UNIT_MAX_MANEUVER_HEIGHT_DIFFERENCE_UP > ai->cb->GetElevation((moveHere.x+enemyPos.x)/2, (moveHere.z+enemyPos.z)/2); //im here assuming the pathfinder returns correct Y values if (dist > max((UNIT_MIN_MANEUVER_RANGE_PERCENTAGE*myRange), float(UNIT_MIN_MANEUVER_DISTANCE)) && losHack) { //(enemyPos.y - moveHere.y) < UNIT_MAX_MANEUVER_HEIGHT_DIFFERENCE_UP) { debug2 = true; //ai->cb->SendTextMsg("AG: maneuvering", 0); //Draw where it would move: //ai->cb->CreateLineFigure(myPos, moveHere, 80, 1, frameSpread, RANDINT%1000000); ai->MyUnits[unit]->maneuverCounter = int(ceilf(max((float)UNIT_MIN_MANEUVER_TIME/frameSpread, (dist/ai->MyUnits[unit]->def()->speed)))); //L("AG maneuver debug, maneuverCounter set to " << ai->MyUnits[unit]->maneuverCounter << " and dist:" << dist << " speed:" << ai->MyUnits[unit]->def()->speed << " frameSpread:" << frameSpread << " dist/speed:" << (dist/ai->MyUnits[unit]->def()->speed) << " its a " << ai->MyUnits[unit]->def()->humanName); ai->MyUnits[unit]->Move(moveHere); //REMEMBER that this will suck for planes (which is why i wont add them to this group) } } if (debug1 && !debug2) { //L("AG: maneuver: pathfinder run but path not used"); } } else if (!ai->cb->GetUnitDef(unit)->canfly || myPos.y < ai->cb->GetElevation(myPos.x, myPos.z) + 25){ //L("AttackGroup: this unit is an air unit: " << ai->cb->GetUnitDef(unit)->humanName); }//endif our unit exists && we arent currently maneuvering } else { ////L("isShooting: OUR unit is dead. or something. Shouldnt happen ever. or maneuvering."); } //if cant attack, stop with the others, assuming someone was able to attack something } } } // //L("AG: isShooting end"); } if (frameNr % 30 == 0 && !defending) { //drawing the target region: //L("AG update: drawing target region (attacking)"); int heightOffset = 50; vector<float3> circleHack; float diagonalHack = attackRadius * (2.0f/3.0f); circleHack.resize(8, attackPosition); circleHack[0] += float3(-diagonalHack,heightOffset,-diagonalHack); circleHack[1] += float3(0,heightOffset,-attackRadius); circleHack[2] += float3(diagonalHack,heightOffset,-diagonalHack); circleHack[3] += float3(attackRadius,heightOffset,0); circleHack[4] += float3(diagonalHack,heightOffset,diagonalHack); circleHack[5] += float3(0,heightOffset,attackRadius); circleHack[6] += float3(-diagonalHack,heightOffset,diagonalHack); circleHack[7] += float3(-attackRadius,heightOffset,0); for(int i = 0; i < 8; i++) { //ai->cb->CreateLineFigure(circleHack[i], circleHack[(i+1)%8], 5, 0, 300, GetGroupID()+6000); } //from pos to circle center //ai->cb->CreateLineFigure(groupPosition+float3(0,heightOffset,0), attackPosition+float3(0,heightOffset,0), 5, 1, 30, GetGroupID()+6000); ai->cb->SetFigureColor(GetGroupID() + 6000, 0, 0, 0, 1.0f); //L("AG update: done drawing stuff"); } //TODO: if arrived and not isShooting, set defending? already done in ismoving-end if (defending && !isShooting && !isMoving && frameNr % 60 == groupID % 60) { FindDefenseTarget(groupPosition); } // GIVE MOVE ORDERS TO UNITS ALONG PATHTOTARGET frameSpread = 60; if (!isShooting && isMoving && frameNr % frameSpread == (groupID*5) % frameSpread) { //L("AG: start of move order part before loop"); //do the moving const int maxStepsAhead = 8; int pathMaxIndex = (int)pathToTarget.size()-1; int step1 = min(this->pathIterator + maxStepsAhead/2, pathMaxIndex); int step2 = min(this->pathIterator + maxStepsAhead, pathMaxIndex); //L("AG: picking stuff out of pathToTarget"); //L("AG: pathtotarget size: " << pathToTarget.size()); float3 moveToHereFirst = this->pathToTarget[step1]; float3 moveToHere = this->pathToTarget[step2]; //drawing destination // //ai->cb->CreateLineFigure(groupPosition + float3(0,50,0), pathToTarget[pathMaxIndex] + float3(0,50,0),8,1,frameSpread,groupID+1200); //L("AG: move order still before loop"); //if we aint there yet if (groupPosition.distance2D(pathToTarget[pathMaxIndex]) > GROUP_DESTINATION_SLACK) { for(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 line up or something while some are here and some arent. attack checker will take over soon. //theres 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 (groupPosition.distance2D(ai->cheat->GetUnitPos(myEnemy)) > this->highestAttackRange) { //if the single unit aint there yet if (ai->cb->GetUnitPos(unit).distance2D(pathToTarget[pathMaxIndex]) > UNIT_DESTINATION_SLACK) { ai->MyUnits[unit]->Move(moveToHereFirst); // //ai->cb->CreateLineFigure(ai->cb->GetUnitPos(unit) + float3(0,50,0), moveToHereFirst + float3(0,50,0),15,1,10,groupID+50000); if (moveToHere != moveToHereFirst) { ai->MyUnits[unit]->MoveShift(moveToHere); // //ai->cb->CreateLineFigure(moveToHereFirst + float3(0,50,0), moveToHere + float3(0,50,0),15,1,10,groupID+50000); } //TODO: give a spring group the order instead of each unit //draw thin line from unit to groupPos //ai->cb->CreateLineFigure(ai->cb->GetUnitPos(unit) + float3(0,50,0), groupPosition + float3(0,50,0),4,0,frameSpread,groupID+500); } else { AIHCAddMapPoint amp; amp.label = "case-sua"; amp.pos = groupPosition; // //ai->cb->HandleCommand(&); } } } //if group is as close as the pathiterator-indicated target is to the end of the path, increase pathIterator //L("AG: after move order, about to adjust stuff"); /* float3 endOfPathPos = pathToTarget[pathMaxIndex]; float groupDistanceToEnemy = groupPosition.distance2D(endOfPathPos); float pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos); if (groupDistanceToEnemy <= pathIteratorTargetDistanceToEnemy) { this->pathIterator = min(pathIterator + maxStepsAhead/2, pathMaxIndex); } if (groupDistanceToEnemy > (pathIteratorTargetDistanceToEnemy*3.0f)) { //L("AG: path iterator reset bug thing"); //this->pathIterator = min(pathIterator + maxStepsAhead/2, pathMaxIndex); this->pathIterator = 1; } */ 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 { //L("AG: group thinks it has arrived at the destination:" << groupID); // AIHCAddMapPoint amp; // amp.label = "group thinks it has arrived at the destination."; // amp.pos = groupPosition; // //ai->cb->HandleCommand(&); this->ClearTarget(); } //L("AG: done updating move stuff (yay)"); }//endif move //stuck fix stuff. disabled because the spring one works now and thats not taken into consideration yet. frameSpread = 60; if (false && isMoving && !isShooting && frameNr % frameSpread == 0) { //L("AG: unit stuck checking start"); //stuck unit counter update. (at a 60 frame modulo) //TODO: if one unit has completed the path, it wont be given any new orders, but this will still happen //so there might be false positives here :< for (vector<int>::iterator it = units.begin(); it != units.end(); it++) { int unit = *it; if (ai->cb->GetUnitDef(unit)){ CUNIT *u = ai->MyUnits[unit]; float distanceMovedSinceLastUpdate = ai->cb->GetUnitPos(unit).distance2D(u->earlierPosition); if (distanceMovedSinceLastUpdate < UNIT_STUCK_MOVE_DISTANCE) { u->stuckCounter++; } else { //it moved so it isnt stuck u->stuckCounter = 0; } u->earlierPosition = u->pos(); //hack for slight maneuvering around buildings, spring fails at this sometimes if (u->stuckCounter == UNIT_STUCK_COUNTER_MANEUVER_LIMIT) { //L("AG: attempting unit unstuck manuevering for " << unit << " at pos " << u->pos().x << " " << u->pos().y << " " << u->pos().z); AIHCAddMapPoint amp; amp.label = "stuck-fix"; amp.pos = u->pos(); //ai->cb->HandleCommand(&); //findbestpath to perifery of circle around mypos, distance 2x resolution or somesuch //dont reset counter, that would make it loop manuever attempts. vector<float3> tempPath; float3 upos = u->pos(); float dist = ai->pather->FindBestPathToRadius(&tempPath, &upos, UNIT_STUCK_MANEUVER_DISTANCE, &upos); //500 = hack //float dist = ai->pather->FindBestPathToRadius(&tempPath, &ai->cb->GetUnitPos(unit), myRange, &ai->cheat->GetUnitPos(enemies[enemySelected])); if (tempPath.size() > 0) { float3 moveHere = tempPath.back(); //ai->cb->CreateLineFigure(u->pos(), moveHere, 14, 1, frameSpread, RANDINT%1000000); u->Move(moveHere); } } } }//endfor unstuck } }