void TacticalAI::SelectTargetOpportunity() { // NON-COMBATANTS do not pick targets of opportunity: if (ship->GetIFF() == 0) return; SimObject* potential_target = 0; // pick the closest combatant ship with a different IFF code: double target_dist = ship->Design()->commit_range; SimObject* ward = ship_ai->GetWard(); // FRIGATES are primarily anti-air platforms, but may // also attack smaller starships: if (ship->Class() == Ship::CORVETTE || ship->Class() == Ship::FRIGATE) { Ship* current_ship_target = 0; Shot* current_shot_target = 0; // if we are escorting a larger warship, it is good to attack // the same target as our ward: if (ward) { Ship* s = (Ship*) ward; if (s->Class() > ship->Class()) { SimObject* obj = s->GetTarget(); if (obj && obj->Type() == SimObject::SIM_SHIP) { current_ship_target = (Ship*) obj; target_dist = (ship->Location() - obj->Location()).length(); } } } ListIter<Contact> contact = ship->ContactList(); while (++contact) { Ship* c_ship = contact->GetShip(); Shot* c_shot = contact->GetShot(); if (!c_ship && !c_shot) continue; int c_iff = contact->GetIFF(ship); bool rogue = c_ship && c_ship->IsRogue(); bool tgt_ok = c_iff > 0 && c_iff != ship->GetIFF() && c_iff < 1000; if (rogue || tgt_ok) { if (c_ship && c_ship != ship && !c_ship->InTransition()) { if (c_ship->Class() < Ship::DESTROYER || (c_ship->Class() >= Ship::MINE && c_ship->Class() <= Ship::DEFSAT)) { // found an enemy, check distance: double dist = (ship->Location() - c_ship->Location()).length(); if (dist < 0.75 * target_dist && (!current_ship_target || c_ship->Class() <= current_ship_target->Class())) { current_ship_target = c_ship; target_dist = dist; } } } else if (c_shot) { // found an enemy shot, is there enough time to engage? if (c_shot->GetEta() < 3) continue; // found an enemy shot, check distance: double dist = (ship->Location() - c_shot->Location()).length(); if (!current_shot_target) { current_shot_target = c_shot; target_dist = dist; } // is this shot a better target than the one we've found? else { Ship* ward = ship_ai->GetWard(); if ((c_shot->IsTracking(ward) || c_shot->IsTracking(ship)) && (!current_shot_target->IsTracking(ward) || !current_shot_target->IsTracking(ship))) { current_shot_target = c_shot; target_dist = dist; } else if (dist < target_dist) { current_shot_target = c_shot; target_dist = dist; } } } } } if (current_shot_target) potential_target = current_shot_target; else potential_target = current_ship_target; } // ALL OTHER SHIP CLASSES ignore fighters and only engage // other starships: else { List<Ship> ward_threats; ListIter<Contact> contact = ship->ContactList(); while (++contact) { Ship* c_ship = contact->GetShip(); if (!c_ship) continue; int c_iff = contact->GetIFF(ship); bool rogue = c_ship->IsRogue(); bool tgt_ok = c_ship != ship && c_iff > 0 && c_iff != ship->GetIFF() && !c_ship->InTransition(); if (rogue || tgt_ok) { if (c_ship->IsStarship() || c_ship->IsStatic()) { // found an enemy, check distance: double dist = (ship->Location() - c_ship->Location()).length(); if (dist < 0.75 * target_dist) { potential_target = c_ship; target_dist = dist; } if (ward && c_ship->IsTracking(ward)) { ward_threats.append(c_ship); } } } } // if this ship is protecting a ward, // prefer targets that are threatening that ward: if (potential_target && ward_threats.size() && !ward_threats.contains((Ship*)potential_target)) { target_dist *= 2; ListIter<Ship> iter = ward_threats; while (++iter) { Ship* threat = iter.value(); double dist = (ward->Location() - threat->Location()).length(); if (dist < target_dist) { potential_target = threat; target_dist = dist; } } } } if (ship->Class() != Ship::CARRIER && ship->Class() != Ship::SWACS) ship_ai->SetTarget(potential_target); }
void TacticalAI::SelectTarget() { if (!ship) { roe = NONE; return; } // unarmed vessels should never engage an enemy: if (ship->Weapons().size() < 1) roe = NONE; SimObject* target = ship_ai->GetTarget(); SimObject* ward = ship_ai->GetWard(); // if not allowed to engage, drop and return: if (roe == NONE) { if (target) ship_ai->DropTarget(); return; } // if we have abandoned our ward, drop and return: if (ward && roe != AGRESSIVE) { double d = (ward->Location() - ship->Location()).length(); double safe_zone = 50e3; if (target) { if (ship->IsStarship()) safe_zone = 100e3; if (d > safe_zone) { ship_ai->DropTarget(); return; } } else { if (d > safe_zone) { return; } } } // already have a target, keep it: if (target) { if (target->Life()) { CheckTarget(); // frigates need to be ready to abandon ship-type targets // in favor of drone-type targets, others should just go // with what they have: if (ship->Class() != Ship::CORVETTE && ship->Class() != Ship::FRIGATE) return; // in case the check decided to drop the target: target = ship_ai->GetTarget(); } // if the old target is dead, forget it: else { ship_ai->DropTarget(); target = 0; } } // if not allowed to acquire, forget it: if (ship_ai->DropTime() > 0) return; if (roe == DIRECTED) { if (target && target->Type() == SimObject::SIM_SHIP) SelectTargetDirected((Ship*) target); else if (navpt && navpt->GetTarget() && navpt->GetTarget()->Type() == SimObject::SIM_SHIP) SelectTargetDirected((Ship*) navpt->GetTarget()); else SelectTargetDirected(); } else { SelectTargetOpportunity(); // don't switch one ship target for another... if (ship->Class() == Ship::CORVETTE || ship->Class() == Ship::FRIGATE) { SimObject* potential_target = ship_ai->GetTarget(); if (target && potential_target && target != potential_target) { if (target->Type() == SimObject::SIM_SHIP && potential_target->Type() == SimObject::SIM_SHIP) { ship_ai->SetTarget(target); } } } } }