void AI::DoCloak(Ship &ship, Command &command, const list<shared_ptr<Ship>> &ships) { if(ship.Attributes().Get("cloak")) { // Never cloak if it will cause you to be stranded. if(ship.Attributes().Get("cloaking fuel") && !ship.Attributes().Get("ramscoop")) { double fuel = ship.Fuel() * ship.Attributes().Get("fuel capacity"); fuel -= ship.Attributes().Get("cloaking fuel"); if(fuel < ship.JumpFuel()) return; } // Otherwise, always cloak if you are in imminent danger. static const double MAX_RANGE = 10000.; double nearestEnemy = MAX_RANGE; for(const auto &other : ships) if(other->GetSystem() == ship.GetSystem() && other->IsTargetable() && other->GetGovernment()->IsEnemy(ship.GetGovernment())) nearestEnemy = min(nearestEnemy, ship.Position().Distance(other->Position())); if(ship.Hull() + ship.Shields() < 1. && nearestEnemy < 2000.) command |= Command::CLOAK; // Also cloak if there are no enemies nearby and cloaking does // not cost you fuel. if(nearestEnemy == MAX_RANGE && !ship.Attributes().Get("cloaking fuel")) command |= Command::CLOAK; } }
EscortDisplay::Icon::Icon(const Ship &ship, bool isHere) : sprite(ship.GetSprite().GetSprite()), isHere(isHere && !ship.IsDisabled()), stackSize(1), cost(ship.Cost()), system((!isHere && ship.GetSystem()) ? ship.GetSystem()->Name() : ""), low{ship.Shields(), ship.Hull(), ship.Energy(), ship.Heat(), ship.Fuel()}, high(low) { }
// Pick a new target for the given ship. shared_ptr<Ship> AI::FindTarget(const Ship &ship, const list<shared_ptr<Ship>> &ships) const { // If this ship has no government, it has no enemies. shared_ptr<Ship> target; const Government *gov = ship.GetGovernment(); if(!gov) return target; bool isPlayerEscort = ship.IsYours(); if(isPlayerEscort) { shared_ptr<Ship> locked = sharedTarget.lock(); if(locked && locked->GetSystem() == ship.GetSystem() && !locked->IsDisabled()) return locked; } // If this ship is not armed, do not make it fight. double minRange = numeric_limits<double>::infinity(); double maxRange = 0.; for(const Armament::Weapon &weapon : ship.Weapons()) if(weapon.GetOutfit() && !weapon.IsAntiMissile()) { minRange = min(minRange, weapon.GetOutfit()->Range()); maxRange = max(maxRange, weapon.GetOutfit()->Range()); } if(!maxRange) return target; shared_ptr<Ship> oldTarget = ship.GetTargetShip(); if(oldTarget && !oldTarget->IsTargetable()) oldTarget.reset(); shared_ptr<Ship> parentTarget; if(ship.GetParent()) parentTarget = ship.GetParent()->GetTargetShip(); if(parentTarget && !parentTarget->IsTargetable()) parentTarget.reset(); // Find the closest enemy ship (if there is one). If this ship is "heroic," // it will attack any ship in system. Otherwise, if all its weapons have a // range higher than 2000, it will engage ships up to 50% beyond its range. // If a ship has short range weapons and is not heroic, it will engage any // ship that is within 3000 of it. const Personality &person = ship.GetPersonality(); double closest = person.IsHeroic() ? numeric_limits<double>::infinity() : (minRange > 1000.) ? maxRange * 1.5 : 4000.; const System *system = ship.GetSystem(); bool isDisabled = false; for(const auto &it : ships) if(it->GetSystem() == system && it->IsTargetable() && gov->IsEnemy(it->GetGovernment())) { if(person.IsNemesis() && !it->GetGovernment()->IsPlayer()) continue; double range = it->Position().Distance(ship.Position()); // Preferentially focus on your previous target or your parent ship's // target if they are nearby. if(it == oldTarget || it == parentTarget) range -= 500.; // If your personality it to disable ships rather than destroy them, // never target disabled ships. if(it->IsDisabled() && !person.Plunders() && (person.Disables() || (!person.IsNemesis() && it != oldTarget))) continue; if(!person.Plunders()) range += 5000. * it->IsDisabled(); else { bool hasBoarded = Has(ship, it, ShipEvent::BOARD); // Don't plunder unless there are no "live" enemies nearby. range += 2000. * (2 * it->IsDisabled() - !hasBoarded); } // Focus on nearly dead ships. range += 500. * (it->Shields() + it->Hull()); if(range < closest) { closest = range; target = it; isDisabled = it->IsDisabled(); } } bool cargoScan = ship.Attributes().Get("cargo scan"); bool outfitScan = ship.Attributes().Get("outfit scan"); if(!target && (cargoScan || outfitScan) && !isPlayerEscort) { closest = numeric_limits<double>::infinity(); for(const auto &it : ships) if(it->GetSystem() == system && it->GetGovernment() != gov && it->IsTargetable()) { if((cargoScan && !Has(ship, it, ShipEvent::SCAN_CARGO)) || (outfitScan && !Has(ship, it, ShipEvent::SCAN_OUTFITS))) { double range = it->Position().Distance(ship.Position()); if(range < closest) { closest = range; target = it; } } } } // Run away if your target is not disabled and you are badly damaged. if(!isDisabled && (ship.GetPersonality().IsFleeing() || (ship.Shields() + ship.Hull() < 1. && !ship.GetPersonality().IsHeroic()))) target.reset(); return target; }