// 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::UpdateKMeans() { const int arrowDuration = 300; {//want local variable definitions //get positions of all friendly units and put them in a vector (completed buildings only) int numFriendlies = 0; vector<float3> friendlyPositions; int friendlies[MAXUNITS]; numFriendlies = ai->cb->GetFriendlyUnits(friendlies); for (int i = 0; i < numFriendlies; i++) { int unit = friendlies[i]; CUNIT* u = ai->MyUnits[unit]; //its a building, it has hp, and its mine (0) if (this->UnitBuildingFilter(u->def()) && this->UnitReadyFilter(unit) && u->owner() == 0) { friendlyPositions.push_back(u->pos()); } } //hack to make it at least 1 unit, should only happen when you have no base: if (friendlyPositions.size() < 1) { //it has to be a proper position, unless there are no proper positions. if (numFriendlies > 0 && ai->cb->GetUnitDef(friendlies[0]) && ai->MyUnits[friendlies[0]]->owner() == 0) friendlyPositions.push_back(ai->cb->GetUnitPos(friendlies[0])); else friendlyPositions.push_back(float3(RANDINT % (ai->cb->GetMapWidth()*8), 1000, RANDINT % (ai->cb->GetMapHeight()*8))); //when everything is dead } //calculate a new K. change the formula to adjust max K, needs to be 1 minimum. this->kMeansK = int(min((float)(KMEANS_BASE_MAX_K), 1.0f + sqrtf((float)numFriendlies+0.01f))); //iterate k-means algo over these positions and move the means this->kMeansBase = KMeansIteration(this->kMeansBase, friendlyPositions, this->kMeansK); //now, draw these means on the map int lineWidth = 25; int lineWidth2 = 3; // for (int i = 0; i < kMeansK; i++) { // //ai->cb->CreateLineFigure(kMeansBase[i]+float3(0,400,0), kMeansBase[i], lineWidth, 1, arrowDuration, RANDINT % 59549847); // } } //update enemy position k-means //get positions of all enemy units and put them in a vector (completed buildings only) int numEnemies = 0; vector<float3> enemyPositions; int enemies[MAXUNITS]; numEnemies = ai->cheat->GetEnemyUnits(enemies); for (int i = 0; i < numEnemies; i++) { const UnitDef *ud = ai->cheat->GetUnitDef(enemies[i]); if (this->UnitBuildingFilter(ud)) { // && this->UnitReadyFilter(unit)) { enemyPositions.push_back(ai->cheat->GetUnitPos(enemies[i])); // //L("AttackHandler debug: added enemy building position for k-means " << i); } } //hack to make it at least 1 unit, should only happen when you have no base: if (enemyPositions.size() < 1) { //it has to be a proper position, unless there are no proper positions. if (numEnemies > 0 && ai->cheat->GetUnitDef(enemies[0])) enemyPositions.push_back(ai->cheat->GetUnitPos(enemies[0])); else enemyPositions.push_back(float3(RANDINT % (ai->cb->GetMapWidth()*8), 1000, RANDINT % (ai->cb->GetMapHeight()*8))); //when everything is dead } //calculate a new K. change the formula to adjust max K, needs to be 1 minimum. this->kMeansEnemyK = int(min(float(KMEANS_ENEMY_MAX_K), 1.0f + sqrtf((float)numEnemies+0.01f))); // //L("AttackHandler: doing k-means k:" << kMeansK << " numPositions=" << numFriendlies); //iterate k-means algo over these positions and move the means this->kMeansEnemyBase = KMeansIteration(this->kMeansEnemyBase, enemyPositions, this->kMeansEnemyK); //now, draw these means on the map int lineWidth = 25; // for (int i = 0; i < kMeansEnemyK; i++) { // //L("AttackHandler debug: painting k-means for enemy nr " << i); // //ai->cb->CreateLineFigure(kMeansEnemyBase[i]+float3(300,300,0), kMeansEnemyBase[i], lineWidth, 1, arrowDuration, (RANDINT % 49584985)); // } //base k-means and enemy base k-means are updated. //approach: add up (max - distance) to enemies vector<float> proximity; proximity.resize(kMeansK, 0.0000001f); const float mapDiagonal = sqrt(pow((float)ai->cb->GetMapHeight()*8,2) + pow((float)ai->cb->GetMapWidth()*8,2) + 1.0f); for (int f = 0; f < kMeansK; f++) { for (int e = 0; e < kMeansEnemyK; e++) { proximity[f] += mapDiagonal - kMeansBase[f].distance2D(kMeansEnemyBase[e]); } } //sort kMeans by the proximity score float3 tempPos; float temp; for (int i = 1; i < kMeansK; i++) { //how many are completed for (int j = 0; j < i; j++) { //compare to / switch with if (proximity[i] > proximity[j]) { //switch tempPos = kMeansBase[i]; kMeansBase[i] = kMeansBase[j]; kMeansBase[j] = tempPos; temp = proximity[i]; proximity[i] = proximity[j]; proximity[j] = temp; } } } //okay, so now we have a kMeans list sorted by distance to enemies, 0 being risky and k being safest. //now, draw these means on the map for (int i = 1; i < kMeansK; i++) { //ai->cb->CreateLineFigure(kMeansBase[i-1]+float3(0,100,0), kMeansBase[i]+float3(0,100,0), lineWidth, 1, arrowDuration, (RANDINT % 59549847)); } }
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 } }