From 3c487c5cd69c53d6fea948643c0a76df03516605 Mon Sep 17 00:00:00 2001 From: Aki Date: Fri, 1 Apr 2022 21:23:39 +0200 Subject: Moved Stars45 to StarsEx --- Stars45/TacticalAI.cpp | 957 ------------------------------------------------- 1 file changed, 957 deletions(-) delete mode 100644 Stars45/TacticalAI.cpp (limited to 'Stars45/TacticalAI.cpp') diff --git a/Stars45/TacticalAI.cpp b/Stars45/TacticalAI.cpp deleted file mode 100644 index 7f4e64d..0000000 --- a/Stars45/TacticalAI.cpp +++ /dev/null @@ -1,957 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Generic Ship Tactical Level AI class -*/ - -#include "TacticalAI.h" -#include "ShipAI.h" -#include "CarrierAI.h" -#include "Ship.h" -#include "ShipDesign.h" -#include "Element.h" -#include "Instruction.h" -#include "RadioMessage.h" -#include "RadioTraffic.h" -#include "Contact.h" -#include "WeaponGroup.h" -#include "Drive.h" -#include "Hangar.h" -#include "Sim.h" -#include "Shot.h" -#include "Drone.h" -#include "StarSystem.h" - -#include "Clock.h" -#include "Random.h" - -// +----------------------------------------------------------------------+ - -static int exec_time_seed = 0; - -// +----------------------------------------------------------------------+ - -TacticalAI::TacticalAI(ShipAI* ai) -: ship(0), ship_ai(0), carrier_ai(0), navpt(0), orders(0), -action(0), threat_level(0), support_level(1), -directed_tgtid(0) -{ - if (ai) { - ship_ai = ai; - ship = ai->GetShip(); - - Sim* sim = Sim::GetSim(); - - if (ship && ship->GetHangar() && ship->GetCommandAILevel() > 0 && - ship != sim->GetPlayerShip()) - carrier_ai = new CarrierAI(ship, ship_ai->GetAILevel()); - } - - agression = 0; - roe = FLEXIBLE; - element_index = 1; - exec_time = exec_time_seed; - exec_time_seed += 17; -} - -TacticalAI::~TacticalAI() -{ - delete carrier_ai; -} - -// +--------------------------------------------------------------------+ - -void -TacticalAI::ExecFrame(double secs) -{ - const int exec_period = 1000; - - if (!ship || !ship_ai) - return; - - navpt = ship->GetNextNavPoint(); - orders = ship->GetRadioOrders(); - - if ((int) Clock::GetInstance()->GameTime() - exec_time > exec_period) { - element_index = ship->GetElementIndex(); - - CheckOrders(); - SelectTarget(); - FindThreat(); - FindSupport(); - - if (element_index > 1) { - int formation = 0; - - if (orders && orders->Formation() >= 0) - formation = orders->Formation(); - - else if (navpt) - formation = navpt->Formation(); - - FindFormationSlot(formation); - } - - ship_ai->SetNavPoint(navpt); - - if (carrier_ai) - carrier_ai->ExecFrame(secs); - - exec_time += exec_period; - } -} - -// +--------------------------------------------------------------------+ - -void -TacticalAI::CheckOrders() -{ - directed_tgtid = 0; - - if (CheckShipOrders()) - return; - - if (CheckFlightPlan()) - return; - - if (CheckObjectives()) - return; -} - -// +--------------------------------------------------------------------+ - -bool -TacticalAI::CheckShipOrders() -{ - return ProcessOrders(); -} - -// +--------------------------------------------------------------------+ - -bool -TacticalAI::CheckObjectives() -{ - bool processed = false; - Ship* ward = 0; - Element* elem = ship->GetElement(); - - if (elem) { - Instruction* obj = elem->GetTargetObjective(); - - if (obj) { - ship_ai->ClearPatrol(); - - if (obj->Action()) { - switch (obj->Action()) { - case Instruction::INTERCEPT: - case Instruction::STRIKE: - case Instruction::ASSAULT: - { - SimObject* tgt = obj->GetTarget(); - if (tgt && tgt->Type() == SimObject::SIM_SHIP) { - roe = DIRECTED; - SelectTargetDirected((Ship*) tgt); - } - } - break; - - case Instruction::DEFEND: - case Instruction::ESCORT: - { - SimObject* tgt = obj->GetTarget(); - if (tgt && tgt->Type() == SimObject::SIM_SHIP) { - roe = DEFENSIVE; - ward = (Ship*) tgt; - } - } - break; - - default: - break; - } - } - - orders = obj; - processed = true; - } - } - - ship_ai->SetWard(ward); - return processed; -} - -// +--------------------------------------------------------------------+ - -bool -TacticalAI::ProcessOrders() -{ - if (ship_ai) - ship_ai->ClearPatrol(); - - if (orders && orders->EMCON() > 0) { - int desired_emcon = orders->EMCON(); - - if (ship_ai && (ship_ai->GetThreat() || ship_ai->GetThreatMissile())) - desired_emcon = 3; - - if (ship->GetEMCON() != desired_emcon) - ship->SetEMCON(desired_emcon); - } - - if (orders && orders->Action()) { - switch (orders->Action()) { - case RadioMessage::ATTACK: - case RadioMessage::BRACKET: - case RadioMessage::IDENTIFY: - { - bool tgt_ok = false; - SimObject* tgt = orders->GetTarget(); - - if (tgt && tgt->Type() == SimObject::SIM_SHIP) { - Ship* tgt_ship = (Ship*) tgt; - - if (CanTarget(tgt_ship)) { - roe = DIRECTED; - SelectTargetDirected((Ship*) tgt); - - ship_ai->SetBracket(orders->Action() == RadioMessage::BRACKET); - ship_ai->SetIdentify(orders->Action() == RadioMessage::IDENTIFY); - ship_ai->SetNavPoint(0); - - tgt_ok = true; - } - } - - if (!tgt_ok) - ClearRadioOrders(); - } - break; - - case RadioMessage::ESCORT: - case RadioMessage::COVER_ME: - { - SimObject* tgt = orders->GetTarget(); - if (tgt && tgt->Type() == SimObject::SIM_SHIP) { - roe = DEFENSIVE; - ship_ai->SetWard((Ship*) tgt); - ship_ai->SetNavPoint(0); - } - else { - ClearRadioOrders(); - } - } - break; - - case RadioMessage::WEP_FREE: - roe = AGRESSIVE; - ship_ai->DropTarget(0.1); - break; - - case RadioMessage::WEP_HOLD: - case RadioMessage::FORM_UP: - roe = NONE; - ship_ai->DropTarget(5); - break; - - case RadioMessage::MOVE_PATROL: - roe = SELF_DEFENSIVE; - ship_ai->SetPatrol(orders->Location()); - ship_ai->SetNavPoint(0); - ship_ai->DropTarget(Random(5, 10)); - break; - - case RadioMessage::RTB: - case RadioMessage::DOCK_WITH: - roe = NONE; - - ship_ai->DropTarget(10); - - if (!ship->GetInbound()) { - RadioMessage* msg = 0; - Ship* controller = ship->GetController(); - - if (orders->Action() == RadioMessage::DOCK_WITH && orders->GetTarget()) { - controller = (Ship*) orders->GetTarget(); - } - - if (!controller) { - Element* elem = ship->GetElement(); - if (elem && elem->GetCommander()) { - Element* cmdr = elem->GetCommander(); - controller = cmdr->GetShip(1); - } - } - - if (controller && controller->GetHangar() && - controller->GetHangar()->CanStow(ship)) { - SimRegion* self_rgn = ship->GetRegion(); - SimRegion* rtb_rgn = controller->GetRegion(); - - if (self_rgn == rtb_rgn) { - double range = Point(controller->Location() - ship->Location()).length(); - - if (range < 50e3) { - msg = new RadioMessage(controller, ship, RadioMessage::CALL_INBOUND); - RadioTraffic::Transmit(msg); - } - } - } - else { - ship->ClearRadioOrders(); - } - - ship_ai->SetNavPoint(0); - } - break; - - case RadioMessage::QUANTUM_TO: - case RadioMessage::FARCAST_TO: - roe = NONE; - ship_ai->DropTarget(10); - break; - - } - - action = orders->Action(); - return true; - } - - // if we had an action before, this must be a "cancel orders" - else if (action) { - ClearRadioOrders(); - } - - return false; -} - -void -TacticalAI::ClearRadioOrders() -{ - action = 0; - roe = FLEXIBLE; - - if (ship_ai) - ship_ai->DropTarget(0.1); - - if (ship) - ship->ClearRadioOrders(); - -} - -// +--------------------------------------------------------------------+ - -bool -TacticalAI::CheckFlightPlan() -{ - Ship* ward = 0; - - // Find next Instruction: - navpt = ship->GetNextNavPoint(); - - roe = FLEXIBLE; - - if (navpt) { - switch (navpt->Action()) { - case Instruction::LAUNCH: - case Instruction::DOCK: - case Instruction::RTB: roe = NONE; - break; - - case Instruction::VECTOR: roe = SELF_DEFENSIVE; - break; - - case Instruction::DEFEND: - case Instruction::ESCORT: roe = DEFENSIVE; - break; - - case Instruction::INTERCEPT: - roe = DIRECTED; - break; - - case Instruction::RECON: - case Instruction::STRIKE: - case Instruction::ASSAULT: roe = DIRECTED; - break; - - case Instruction::PATROL: - case Instruction::SWEEP: roe = FLEXIBLE; - break; - - default: break; - } - - if (roe == DEFENSIVE) { - SimObject* tgt = navpt->GetTarget(); - - if (tgt && tgt->Type() == SimObject::SIM_SHIP) - ward = (Ship*) tgt; - } - - - if (navpt->EMCON() > 0) { - int desired_emcon = navpt->EMCON(); - - if (ship_ai && (ship_ai->GetThreat() || ship_ai->GetThreatMissile())) - desired_emcon = 3; - - if (ship->GetEMCON() != desired_emcon) - ship->SetEMCON(desired_emcon); - } - } - - if (ship_ai) - ship_ai->SetWard(ward); - - return (navpt != 0); -} - -// +--------------------------------------------------------------------+ - -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); - } - } - } - } -} - -// +--------------------------------------------------------------------+ - -void -TacticalAI::SelectTargetDirected(Ship* tgt) -{ - Ship* potential_target = tgt; - - // try to target one of the element's objectives - // (if it shows up in the contact list) - - if (!tgt) { - Element* elem = ship->GetElement(); - - if (elem) { - Instruction* objective = elem->GetTargetObjective(); - - if (objective) { - SimObject* obj_sim_obj = objective->GetTarget(); - Ship* obj_tgt = 0; - - if (obj_sim_obj && obj_sim_obj->Type() == SimObject::SIM_SHIP) - obj_tgt = (Ship*) obj_sim_obj; - - if (obj_tgt) { - ListIter contact = ship->ContactList(); - while (++contact && !potential_target) { - Ship* test = contact->GetShip(); - - if (obj_tgt == test) { - potential_target = test; - } - } - } - } - } - } - - if (!CanTarget(potential_target)) - potential_target = 0; - - ship_ai->SetTarget(potential_target); - - if (tgt && tgt == ship_ai->GetTarget()) - directed_tgtid = tgt->Identity(); - else - directed_tgtid = 0; -} - -// +--------------------------------------------------------------------+ - -bool -TacticalAI::CanTarget(Ship* tgt) -{ - bool result = false; - - if (tgt && !tgt->InTransition()) { - if (tgt->IsRogue() || tgt->GetIFF() != ship->GetIFF()) - result = true; - } - - return result; -} - -// +--------------------------------------------------------------------+ - -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 = 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 ward_threats; - - ListIter 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 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::CheckTarget() -{ - SimObject* tgt = ship_ai->GetTarget(); - - if (!tgt) return; - - if (tgt->GetRegion() != ship->GetRegion()) { - ship_ai->DropTarget(); - return; - } - - if (tgt->Type() == SimObject::SIM_SHIP) { - Ship* target = (Ship*) tgt; - - // has the target joined our side? - if (target->GetIFF() == ship->GetIFF() && !target->IsRogue()) { - ship_ai->DropTarget(); - return; - } - - // is the target already jumping/breaking/dying? - if (target->InTransition()) { - ship_ai->DropTarget(); - return; - } - - // have we been ordered to pursue the target? - if (directed_tgtid) { - if (directed_tgtid != target->Identity()) { - ship_ai->DropTarget(); - } - - return; - } - - // can we catch the target? - if (target->Design()->vlimit <= ship->Design()->vlimit || - ship->Velocity().length() <= ship->Design()->vlimit) - return; - - // is the target now out of range? - WeaponDesign* wep_dsn = ship->GetPrimaryDesign(); - if (!wep_dsn) - return; - - // compute the "give up" range: - double drop_range = 3 * wep_dsn->max_range; - if (drop_range > 0.75 * ship->Design()->commit_range) - drop_range = 0.75 * ship->Design()->commit_range; - - double range = Point(target->Location() - ship->Location()).length(); - if (range < drop_range) - return; - - // is the target closing or separating? - Point delta = (target->Location() + target->Velocity()) - - (ship->Location() + ship->Velocity()); - - if (delta.length() < range) - return; - - ship_ai->DropTarget(); - } - - else if (tgt->Type() == SimObject::SIM_DRONE) { - Drone* drone = (Drone*) tgt; - - // is the target still a threat? - if (drone->GetEta() < 1 || drone->GetTarget() == 0) - ship_ai->DropTarget(); - } -} - -// +--------------------------------------------------------------------+ - -void -TacticalAI::FindThreat() -{ - // pick the closest contact on Threat Warning System: - Ship* threat = 0; - Shot* threat_missile = 0; - Ship* rumor = 0; - double threat_dist = 1e9; - const DWORD THREAT_REACTION_TIME = 1000; // 1 second - - ListIter iter = ship->ContactList(); - - while (++iter) { - Contact* contact = iter.value(); - - if (contact->Threat(ship) && - (Clock::GetInstance()->GameTime() - contact->AcquisitionTime()) > THREAT_REACTION_TIME) { - - if (contact->GetShot()) { - threat_missile = contact->GetShot(); - rumor = (Ship*) threat_missile->Owner(); - } - else { - double rng = contact->Range(ship); - - Ship* c_ship = contact->GetShip(); - if (c_ship && !c_ship->InTransition() && - c_ship->Class() != Ship::FREIGHTER && - c_ship->Class() != Ship::FARCASTER) { - - if (c_ship->GetTarget() == ship) { - if (!threat || c_ship->Class() > threat->Class()) { - threat = c_ship; - threat_dist = 0; - } - } - else if (rng < threat_dist) { - threat = c_ship; - threat_dist = rng; - } - } - } - } - } - - if (rumor && !rumor->InTransition()) { - iter.reset(); - - while (++iter) { - if (iter->GetShip() == rumor) { - rumor = 0; - ship_ai->ClearRumor(); - break; - } - } - } - else { - rumor = 0; - ship_ai->ClearRumor(); - } - - ship_ai->SetRumor(rumor); - ship_ai->SetThreat(threat); - ship_ai->SetThreatMissile(threat_missile); -} - -// +--------------------------------------------------------------------+ - -void -TacticalAI::FindSupport() -{ - if (!ship_ai->GetThreat()) { - ship_ai->SetSupport(0); - return; - } - - // pick the biggest friendly contact in the sector: - Ship* support = 0; - double support_dist = 1e9; - - ListIter contact = ship->ContactList(); - - while (++contact) { - if (contact->GetShip() && contact->GetIFF(ship) == ship->GetIFF()) { - Ship* c_ship = contact->GetShip(); - - if (c_ship != ship && c_ship->Class() >= ship->Class() && !c_ship->InTransition()) { - if (!support || c_ship->Class() > support->Class()) - support = c_ship; - } - } - } - - ship_ai->SetSupport(support); -} - -// +--------------------------------------------------------------------+ - -void -TacticalAI::FindFormationSlot(int formation) -{ - // find the formation delta: - int s = element_index - 1; - Point delta(10*s, 0, 10*s); - - // diamond: - if (formation == Instruction::DIAMOND) { - switch (element_index) { - case 2: delta = Point( 10, 0, -12); break; - case 3: delta = Point(-10, 0, -12); break; - case 4: delta = Point( 0, 0, -24); break; - } - } - - // spread: - if (formation == Instruction::SPREAD) { - switch (element_index) { - case 2: delta = Point( 15, 0, 0); break; - case 3: delta = Point(-15, 0, 0); break; - case 4: delta = Point(-30, 0, 0); break; - } - } - - // box: - if (formation == Instruction::BOX) { - switch (element_index) { - case 2: delta = Point(15, 0, 0); break; - case 3: delta = Point( 0, -1, -15); break; - case 4: delta = Point(15, -1, -15); break; - } - } - - // trail: - if (formation == Instruction::TRAIL) { - delta = Point(0, 0, -15*s); - } - - ship_ai->SetFormationDelta(delta * ship->Radius() * 2); -} -- cgit v1.1