Fighter AI improvements: Battery powered fighters and fighter role enhancements
Introduction
Tired of pesky energy generation outfits on fighters? Wish you could deploy fighters only carrying a battery? Wish your battery powered fighters would return to the carrier for a recharge?
Look no further...
AI Overhaul (Fighter AI changes this PR provides)
See feature details for low powered fighters (click to expand)
- Player ship, escorts, fighters, and drones can be powered only by batteries. (No power gen)
- Carrier Tanker Refueling AI
- When you have a carrier with lots of fuel and fighters or drones with fuel pods they can be used to refuel your escort fleet.
- Your fleet can focus on combat while the carrier tanker can refuel them.
- Smart refueling behavior: All ships get 1 jump first, then all escorts get 100% refueled, then all mission NPC escorts get 100% refueled. Finally, when all escorts are refueled the fighters or drones help to refuel the parent carrier ship back to full for the next refueling run.
- Ship recovery AI updates
- Ships out of battery will become disabled and can call for help for a recharge. This includes escorts and the player flagship.
- Battery powered fighters and drones will automatically return to carriers to recharge. This includes low powered fighters which have battery and small amounts of energy generation.
- Battery powered fighters sharing energy with other ships during recovery operations reserve enough energy to be able to return to parent carriers. This enables battery powered fighters and drones to be effective when aiding in disabled ship recovery.
- Fighters and drones can recover other ships and drones (including battery powered fighters and drones). This is useful when mining in the ember wastes with battery powered fighters and drones.
- Carriers will recover their own carried ships which were disabled during battle.
- All ships (including carriers) will recover disabled fighters and drones which are not carried by them. This enables fighters associated with destroyed carriers to find a new home in a new carrier with empty bays.
- "No suicide pact" for defenseless fighters AI updates
- Fighters will refuse to launch if they have no weapons and there are enemies in the system.
- Fighters will retreat and re-dock with carrier if they have no weapons and enemies enter the system.
- Minimum 10 second flight time. Fighters will refuse to deploy if they do not have sufficient energy for 10 seconds of flight time overall. This includes worst case scenario of using weapons the entire time.
- Vulnerable fighters are less vulnerable in battle (like boxwings) because they stay docked.
- Fighters will only deploy if their outfits allow them sufficient flight time in battle.
- Anti-Missile Defense AI updates
- Fighters equipped with anti-missile and no weapons will still deploy since they're not considered completely defenseless. Their purpose is to defend the parent carrier.
- Fighters equipped with anti-missile and no weapons should not move when player issues orders for fleet to attack enemy. They should keep station by their parent carrier to defend the carrier from missile attacks.
Videos
- Fighters moving position and returning to carrier for recharge when low battery.
- Carriers assisting the fighters and drones of your fleet.
-
Fighters repairing drones. Drones have
"cannot repair other" 1attribute enabled so they do not repair. Carriers have stop orders to highlight fighter/drone behavior. -
Fighters and drones assisting each other unrestricted. Carriers have stop orders and drones are able to assist fighters in this video because they do not have the
"cannot repair other" 1. Fighters are still battery powered so they transfer charge and also return to carriers for recharge before giving out more assistance. - Early game fighter highlights how a new player can obtain their first low powered miner with 6 million credits (Dagger + Mule).
Related issues
- Closes #6724 because this change implements it.
- Closes #4241 because in this PR carriers pick up fighters.
- Closes #2336 because this PR provides fighter battery recharge behavior.
- Partially addresses #4034 because AI enhancements do a good job at being useful. Refueling ships and defenseless fighters automatically boarding in combat. Fighters also being repaired by other fighters and carriers makes them less annoying to use.
Checklists
See all completed checklists (click to expand)
- [x] Fighters return to carrier for recharge when low battery (even if they have power gen)
- [x] Remove all TODO items
- [x] DRY up code calculations and move some to
Ship.cpp.
The following check lists are changes since the PR was opened. This is complete so I am hiding it.
Ember Wastes Fixes
- [x] Escort carriers now collect disabled escort drones.
- [x] Fighters and Drones repair each other and other ships.
- [x] New data file attribute
cannot repair otherwhich disables a ship's ability to repair other ships. e.g. the Star queen can be set to not repair other ships if that is desirable. This could be activated on drones to re-enable drones not repairing other ships. The attribute provides flexibility. - [x] Low powered fighters will become disabled if they are out of power. This means they can be boarded/repaired (for power transfer).
- [x] Low powered fighters out of power now have drag since they're disabled. This means no more floating off into infinity when out of battery power.
Bugs discovered in play testing
Bugs need to be fixed before merging this:
- [x] Fighters/drones with no ramscoop should not rejoin the parent to "refuel parent".
- [x] Fighters/drones with fuel but no ramscoop should always deploy with max fuel regardless of parent
- [x] Ship no power warning should go away unless a fighter truly does not have enough power.
- [x] Ship no power warning should show up if a fighter/drone does not have enough power to launch from carrier (fighters/drones will actively refuse to launch if they do not have enough power for flight and fighting).
- [x] Fighters mixed with battery powered should not attempt to recover/recharge unless battery ship is completely disabled. Battery powered ships should be returning to the parent for recharge.
- [x] Fighters/drones with anti-missile should still launch in battle even though they can't attack. The purpose is defense for the carrier.
- [x] Fighters/drones with no parent cannot be recovered by non-carrier flagship/player ship.
- [x] Any battery powered ship out of energy cannot call for help if it is not a fighter.
- [x] A battery powered fighter will recover another battery powered fighter to its own detriment. This can create an infinite loop of two fighters recovering and disabling via energy transfer. It should transfer up to its minimum power requirement to return to the carrier.
- [x] Segfault when deploying fighters.
- [x] Out of fuel escorts should only be considered if they're in the current system.
- [x] Segfault on flagship nullptr when capturing a ship and player dies.
- [x]
IsEnergyLow()prevents parent carriers from recovering hull-disabled fighters/drones. - [x] Refueling shuttle fighters do not return to parent to get fuel.
- [x] Carrier refueling issue: With mission NPC escorts the
IsEscortsFullOfFuel()report included them. This causes issues with carrier refueling after the fleet is full of fuel (excluding mission NPCs). - [x] Fighters "rubber band" to refuel instead of waiting for at least 25 fuel to be generated when they have ramscoop.
- [x] Fighters or drones with no fuel pods and no ramscoop are included in parent refueling behavior which is distracting.
- [x] Refueling is prioritized over orders. Orders should be prioritized first.
Items with the "checkbox" filled are fixed bugs.
Review status
:heavy_check_mark: ready for code review
Performance impact
Initially there were performance issues but they're now resolved.
This makes a lot of sense even as a thing to do for fighters by default, to distinguish them from interceptors. If all (human) fighters had only batteries and no generators, they'll have more room for weapons and could therefore be more heavily armed. Needing to periodically return to the mothership for a recharge is no problem, since there is a mothership. Heck you could even remove their shield generators under the same logic; then an interceptor would be more meaningfully differentiated form a fighter in that it would be fully self-sufficient, with its own hyperdrive, power generation, and shield recharge capabilities. Carried fighters would have to rely on their motherships for all of those things, in exchange for the benefit of being more heavily armed.
I think this is a needed behavior regardless of changes to stock load outs.
Fighters should always at least try to return to mothership for all problems they can't solve on their own (or even if their own solutions aren't up to task against say, ion damage) instead of letting themselves end up drifting lifeless.
I updated the status. This is ready for code review
I have been thoroughly testing this standalone and combined with #6669 .
Gameplay is smooth. 10 seconds of charge left to go recharge feels reasonable in gameplay.
Do we really need to exactly calculate the exact amount of energy remaining? Would a heuristic like 25% not work as well?
I've been play testing this and it feels reasonable. 25% is pretty aggressive if you have extremely large batteries installed. Let's say someone goes nuts and installs 5000+ battery. 25% time to recharge would be well over 2 minutes of operating time remaining for the fighters I was testing.
We can make it 30 seconds to empty if you're concerned about the fighters running out of power however in practice fighters have been able to return to carriers in under 5 seconds. But I think a percent-based calculation is not really appropriate or accurate enough for the purpose of a low powered fighter. It would make fighters return to the flagship far too often. It could even make the fighters actually run out of power before they're able to return.
Here's some scenarios to think about which this change already solves with the current calculation.
- Battery with 0 idle power.
- Battery with negative idle power (possible with outfits that require power). e.g. kahet
- Battery with 6 idle power (small amounts of power gen).
- Ships with mostly sufficient energy but a small supercapacitor (fast recharge and quick to empty). i.e. regen is faster than 10 seconds
The current calculation ensures that the players ship returns at the appropriate time (with sufficient battery power left in order to return successfully) regardless of power gen/power required outfits configurations. It also ensures the ship does not return inappropriately when it doesn't need to rely on the carrier for recharge (supercapacitor situation example). I feel "time to empty" battery calculation along side "time to recharge to full" is best for this.
Fighters under heavy battery load
Video (side note the video combines the mining pull request #6669 for demonstration purposes of using battery based fighters and drones).
https://discord.com/channels/251118043411775489/285540320823869441/968280941564932096
Fighter/drone outfits


After some discussion in discord the ember wastes could pose an issue. I'm going to look into play testing it as well as try different scenarios in the ember wastes.
Loving the behavior in that video. :)
Ember Wastes Fixes
- Escort carriers now collect disabled escort drones.
- Fighters and Drones repair each other and other ships.
- New data file attribute
cannot repair otherwhich disables a ship's ability to repair other ships. e.g. the Star queen can be set to not repair other ships if that is desirable. This could be activated on drones to re-enable drones not repairing other ships. The attribute provides flexibility. - Fighters will become disabled if they are out of power. This means they can be boarded (for power transfer).
- Fighters out of power now have drag since they're disabled. This means no more floating off into infinity when out of battery power.
Videos
The following video is after an ion storm. Battery only drones were disabled by the ion storm. The drones have cannot repair other attribute so when they are repaired they do not attempt to repair other drones.
https://discord.com/channels/251118043411775489/947227869036286033/968750322954678323
The following shows carriers aiding their fighters (collecting and repairing fighters they carry).
https://discord.com/channels/251118043411775489/947227869036286033/968757597710737418
The follow shows fighters and drones repairing each other. This is different from the first clip because the drones do not have the cannot repair other attribute. So you can see the drones participate in repairing other drones and fighters.
https://discord.com/channels/251118043411775489/947227869036286033/968759740958113822
For play testing here's a full list of carriers which have fighter/drone bays along with their capacities.
36 bays on ship "Heron"
16 bays on ship "Tek Far 109"
12 bays on ship "Class C Freighter"
10 bays on ship "Tek Far 71 - Lek"
10 bays on ship "Carrier"
9 bays on ship "Tek Far 78 - Osk"
8 bays on ship "Pond Strider"
6 bays on ship "Solifuge"
6 bays on ship "Skein"
6 bays on ship "Ibis"
6 bays on ship "Heliarch Judicator"
6 bays on ship "Emerald Sword"
4 bays on ship "Roost"
4 bays on ship "Korath Dredger"
4 bays on ship "Cruiser"
4 bays on ship "Auxiliary" "Auxiliary (Transport)"
4 bays on ship "Auxiliary" "Auxiliary (Cargo)"
4 bays on ship "Auxiliary"
3 bays on ship "Bactrian"
2 bays on ship "Nest"
2 bays on ship "Korath Raider"
2 bays on ship "Kestrel"
2 bays on ship "Fetri'sei" "Fetri'sei (Disable-able)"
2 bays on ship "Aerie"
1 bays on ship "Mule"
Command used
find data -type f -name '*.txt' -print0 | xargs -0 -- awk '$0 ~ /^ship/ {previous=ship; ship=$0 }; $1 == "bay" && ( $2 == "\"Fighter\"" || $2 == "\"Drone\"" ) { x[ship]++ }; END {for(key in x) { if(length(x[key]) == 1) { bays=" bays on"; } else { bays="bays on"; }; if(length(key)) {print x[key], bays, key;}}}' | sort -nr
Carrier Tanker Refuels Fleet with Fighters
Video: https://youtu.be/uYqHNLw5bOQ
A Carrier Tanker follows a fleet and stops to refuel it.
Showing off recent bug fixes around refueling escorts.
Carrier Tanker Refuels Fleet with Fighters
Video: https://youtu.be/uYqHNLw5bOQ
A Carrier Tanker follows a fleet and stops to refuel it.
Carrier tanker uses fighters to refuel fleet. The fighters go back and forth between escorts and tanker until fleet has enough jump fuel to make the next jump.
Fuel Tanker Carrier Update
Fuel tanker carriers are specialized configurations with lots of fuel and ramscoop. Where fighters and drones refuel the fleet. So your fleet can specialize in damage and the fuel tanker takes care of refueling them. Once the fleet is full all the fighters and drones with ramscoop replenish the carrier.
No suicide pact for defenseless fighters
- Fighters will refuse to launch if they have no weapons and there are enemies in the system.
- Fighters will retreat and re-dock with carrier if they have no weapons and enemies enter the system.
- Minimum 10 second flight time. Fighters will refuse to deploy if they do not have sufficient energy for 10 seconds of flight time overall. This includes worst case scenario of using weapons the entire time.
This poses a couple of interesting scenarios.
- Vulnerable fighters are less vulnerable in battle (like boxwings).
- Fighters will only deploy if their outfits allow them sufficient flight time in battle.
Tankers will fully refuel the fleet
- If there's no hostiles in the system then a Carrier fuel tanker can use its fighters to replenish fuel within the fleet up to maximum capacity.
Useful scenario: You're in the Ember Wastes and there's a super nova. Really easy to refuel your fleet when solar winds are plentiful for ramscoop.
Fighters refuel tanker when fleet is full
Fighters equipped with ramscoop:
- will refuel the fleet until fleet is full.
- will collect fuel and refill the tanker in parallel until the tanker is full.
This way your entire fleet gets refueled.
This sounds awesome, particularly the refuelling behavior. A small part of me wishes we could set up in-combat re-arming runs where a ship running some kind of fuel-based artillery is constantly being refueled by a stream of fighters from a tanker. But we don't have any such weapon, so it's a moot point. Anyway, I really appreciate all this work. Solid improvement to the AI behaviors.
A small part of me wishes we could set up in-combat re-arming runs where a ship running some kind of fuel-based artillery is constantly being refueled by a stream of fighters from a tanker.
In-combat fueling runs are probably not possible without game-breaking changes. For example, how refueling works is:
- Refueler drones/fighters are on standby and do nothing
- The escort which needs to be refueled calls out for "help I need fuel"
- Current escort logic is to prioritize combat over everything else. Escort logic (not tanker logic) will need to be updated so that they temporarily leave combat and then call for help to get refueled.
Another thing this PR does is drones and fighters with no weapons stay inside tanker.
Question
Should drones/fighters which are not armed but do have antimissile deploy for players who use fighters for antimissile?
OK, that's alright then. It was a wish, but probably overcomplicating things.
As for the fighters-with-AM, I would say that ships with AM should count as "armed." We even have the Wanderers that explicitly use piles of drones with AM, so chances are good some players will want to do the same.
Bugs need to be fixed before merging this:
- Fighters/drones with no ramscoop should not rejoin the parent to "refuel parent".
- Fighters/drones with fuel but no ramscoop should always deploy with max fuel regardless of parent
- Ship no power warning should go away unless a fighter truly does not have enough power.
- Ship no power warning should show up if a fighter/drone does not have enough power to launch from carrier (fighters/drones will actively refuse to launch if they do not have enough power for flight and fighting).
- Fighters mixed with battery powered should not attempt to recover/recharge unless battery ship is completely disabled. Battery powered ships should be returning to the parent for recharge.
- Fighters/drones with anti-missile should still launch in battle even though they can't attack. The purpose is defense for the carrier.
I updated the PR description to include a checklist of TODOs.
All bugs found have been fixed. There's no outstanding issues with this PR.
Warnings Update
Battery-only powered fighter no longer shows warning if there's enough power for sustained flight.

However, if the battery power is so low that the fighter would refuse to deploy in space the player gets an outfitter warning.

Additionally, non-fighter ships which run only on battery power get the usual warning displayed.

Anti-missile fighter update
Fixes for bugs introduced by this PR.
- Fighters with fuel should not target other fighters for refueling. Since no fighters jump across systems they should always return 0 jump fuel required.
- Fighters equipped with anti-missile and no weapons should still deploy.
Fixes bugs which exist in master branch.
- Fighters equipped with anti-missile and no weapons should not move when player issues orders for fleet to attack enemy. They should keep station by the player ship or whatever order they had. Even more strange is the no-weapon anti-missile fighters would actually fly away from the player ship and scatter. This update fixes all of these issues with anti-missile fighters.
While play testing #6783 I found a few play-ability issues I want fixed.
- Fighters/drones with no parent cannot be recovered by non-carrier flagship/player ship.
- Any battery powered ship out of energy cannot call for help if it is not a fighter.
I think I want to fix both of these issues in this pull request before merge so I updated the Bugs list to include them.
While play testing #6783 I found a battery powered fighter issue.
- A battery powered fighter will recover another battery powered fighter even if itself becomes disabled. This can create an infinite loop of two fighters recovering and disabling via energy transfer.
Further enhancements and bug fixes
e85c89b59b03d2257e9fbdcc4e43ac64290538ef includes the following fixes.
- Non-carrier parents can assist fighters
- Battery based ships will disable and request help/recharge. This includes all ships and not just fighters.
- Battery based ships and fighters will not transfer all energy. Only spare energy will be transferred. Spare energy is defined as enough energy storage to allow 10 seconds of sustained flight (worst case usage)
- Battery based ships and fighters will not transfer all energy. Only spare energy will be transferred. Spare energy is defined as enough energy storage to allow 10 seconds of sustained flight (worst case usage). Spare energy is any capacity over that amount. This allows battery based fighters to recover other battery based fighters enough so that both are able to return to their parent carrier ship.
While play testing I encountered a segmentation fault with CanBeCarried. The way I handled weak_ptr without checking for objects caused the exception. I pushed a fix for the crash and also found other areas of the code where I made a similar mistake.
https://github.com/endless-sky/endless-sky/pull/6726/commits/3787fe80163fffdd07dd577119099f9dbbb23c28 contains the full fix
While play testing I discovered another playability bug. Low fuel escorts not in the system (for example halted in another system) prevent tanker refueling behavior. Only escorts in the current system should affect tanker refueling behavior.
Added bug to list: Out of fuel escorts should only be considered if they're in the current system.
IsEnergyLow() prevents parent carriers from recovering hull-disabled fighters. A disabled ship should should be repaired by the parent before it boards the parent.
Bug added to list.
Bug fixes
- Only escorts within the current system are considered when calculating out of fuel escorts.
- Fix segfault causing crash when player dies during ship capturing.
-
IsEnergyLow()no longer interferes with carriers recovering hull-disabled fighters.
Additional testing done
- Carriers now successfully board disabled fighters, repair them, and then carry them.
-
IsEnergyLow()still works to appropriately recover fighters which are otherwise healthy just out of energy. - Refueling: Jumping with escorts and leaving them behind in a separate system. The carrier will refuel escorts and itself.
- Refueling: When fleet in current system is full, if escorts jump into the system (which are now low or out of fuel) the carrier appropriately refuels the escorts which are newly arrived and need refueling.
More playtest bugs
- Refueling shuttle fighters do not return to parent to get fuel.
- Carrier refueling issue: With mission NPC escorts the
IsEscortsFullOfFuel()report included them. This causes issues with carrier refueling after the fleet is full of fuel (excluding mission NPCs).
Mission NPC Escort Update
- Fuel shuttle fighters/drones return to the carrier to get more fuel again.
- Mission NPC escorts are now included with escort refueling behavior.
New escort refueling behavior is the following.
- Refuel all escorts (including mission NPCs) with at least 1 jump worth of fuel.
- Prioritize refueling all player escorts before mission NPC escorts.
- If player-owned fleet is full of fuel, then refuel mission NPC escorts.
- Once fleet is full of fuel (player-owned and mission NPCs) fighters will assist refueling the parent carrier if fighters or drones are equipped with fuel pods and ramscoop.
- Finally, fighters will stay deployed to refuel themselves with ramscoop.
That's some very nice sounding improvements to ship behavior. Way to go, making our fleets smarter!
Video showing new refueling behavior https://www.youtube.com/watch?v=5w9JJCT08Z4