int CAttackGroup::SelectEnemy(int numEnemies, const float3& groupPos) { int enemySelected = -1; float shortestDistanceFound = MY_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(ai->unitIDs[i]))) < shortestDistanceFound); bool b2 = (ai->cheat->GetUnitDef(ai->unitIDs[i]) != NULL); bool b3 = CloakedFix(ai->unitIDs[i]); bool b4 = ai->cheat->GetUnitDef(ai->unitIDs[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(ai->unitIDs[enemySelected]); assert(CloakedFix(ai->unitIDs[enemySelected])); isShooting = true; assert(numUnits >= 0); for (unsigned int i = 0; i < (unsigned int)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(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)); // 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; 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 float3 unitPos = ai->cheat->GetUnitPos(ai->unitIDs[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(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)) && 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? } } }
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); } } } } }
// attack routine (the "find new enemy" part) void CAttackGroup::FindDefenseTarget(float3 groupPosition, int frameNr) { // KLOOTNOTE: numEnemies will be zero if no enemies in LOS or radar when // non-ccb callback used, rely on AttackHandler to pick "global" targets // and on this function for "local" ones // int numEnemies = ai->ccb->GetEnemyUnits(unitArray); int numEnemies = ai->cb->GetEnemyUnitsInRadarAndLos(&ai->unitIDs[0]); if (numEnemies > 0) { std::vector<float3> enemyPositions; enemyPositions.reserve(numEnemies); // make a vector with the positions of all enemies for (int i = 0; i < numEnemies; i++) { if (ai->unitIDs[i] != -1) { const UnitDef* enemy_ud = ai->ccb->GetUnitDef(ai->unitIDs[i]); float3 enemyPos = ai->ccb->GetUnitPos(ai->unitIDs[i]); // store enemy position if unit not cloaked and not an aircraft if (ai->cb->GetUnitDef(ai->unitIDs[i]) != NULL && CloakedFix(ai->unitIDs[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 (ai->unitIDs[i] != -1) { float3 enemyPos = ai->ccb->GetUnitPos(ai->unitIDs[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); const float costToTarget = ai->pather->FindBestPathToRadius(pathToTarget, groupPosition, THREATRES * SQUARE_SIZE, closestBaseSpot); // TODO: GetKBaseMeans() for support of multiple islands/movetypes // TODO: this doesn't need to be 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 } }
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 } }
void CAttackGroup::Update() { int frameNr = ai -> cb -> GetCurrentFrame(); // L("AG: Update-start. isShooting:" << isShooting << " isMoving:" << isMoving << " frame:" << frameNr << " groupID:" << groupID << " path size:" << pathToTarget.size()); // variable used as a varying "constant" for determining how often a part of the update scheme is done int frameSpread = 30; unsigned int numUnits = units.size(); if (!numUnits) { // L("updated an empty AttackGroup!"); return; } float3 groupPosition = this -> 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)) { // L("AG: isShooting start"); this -> isShooting = false; // get all enemies within attack range: float range = this -> highestAttackRange + 100.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); 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); // selecting one of the enemies 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 = groupPosition.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; } } // 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 // 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 if (enemySelected != -1) { // L("AG:isShooting: YES"); float3 enemyPos = ai -> cheat -> GetUnitPos(unitArray[enemySelected]); assert(CloakedFix(unitArray[enemySelected])); this -> isShooting = true; for (unsigned int i = 0; i < numUnits; i++) { int unit = units[i]; // does our unit exist and is it not currently maneuvering? if ((ai -> cb -> GetUnitDef(unit) != NULL) && (ai -> MyUnits[unit] -> maneuverCounter-- <= 0)) { // TODO: add a routine finding the best(not just closest) target, but for now just fire away // TODO: some cases, force fire on position // TODO: add canattack ai -> MyUnits[unit] -> Attack(unitArray[enemySelected]); // 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)); bool b5 = ai -> cb -> GetUnitDef(unit) -> 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; // two things: 1. don't need a path, just a position and 2. it should avoid other immediate friendly units and/or immediate enemy units+radius // maybe include the height parameter in the search? probably not possible // TODO: OBS doesn't 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? 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; // 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(ceilf(max((float) UNIT_MIN_MANEUVER_TIME / frameSpread, (dist / ai -> MyUnits[unit] -> def() -> speed)))); ai -> MyUnits[unit] -> Move(moveHere); } } 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); } } 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 } } } } if (frameNr % 30 == 0 && !defending) { // 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); // from pos to circle center ai -> cb -> SetFigureColor(GetGroupID() + 6000, 0, 0, 0, 1.0f); // L("AG update: done drawing stuff"); } 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"); 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]; // if we aren't there yet if (groupPosition.distance2D(pathToTarget[pathMaxIndex]) > GROUP_DESTINATION_SLACK) { 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 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 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); } // TODO: give a spring group the order instead of each unit } else { AIHCAddMapPoint amp; amp.label = "case-sua"; amp.pos = groupPosition; } } } // 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"); 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); this -> ClearTarget(); } // L("AG: done updating move stuff"); } // stuck fix stuff. disabled because the spring one works now and that's 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 won't 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 isn't 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(); // findbestpath to perifery of circle around mypos, distance 2x resolution or somesuch // don't reset counter, that would make it loop manuever attempts. vector<float3> tempPath; float3 upos = u -> pos(); if (tempPath.size() > 0) { float3 moveHere = tempPath.back(); u -> Move(moveHere); } } } } } }