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); } } } } }