I've got a ship system that is meant to functionally turn a capitol carrier into a deployable mobile station, but i'm wondering if there is ANY existing AI that would make it move to a more populated area before deploying, instead of activating it immediately at the back of the map and sitting there. At the moment it is a pure toggle (Unlimited Duration) with a short but non-instant start-up and a considerable cooldown.
Just wondering if there's anything existing that would work or if i'm going to have to learn how to do a custom AI.
You're probably going to have to write a custom AI. I can get you started a bit though.
1) Create a script that extends ShipSystemAIScript and provide an implementation of the "advance" method. This method runs roughly every frame, so you will need to set up an IntervalUtil to track how often the script is run. This will save on performance.
2) Point your ship system AI to the new script.
3) In the script, determine what to analyze in order to determine whether to use the system. LazyLib's CombatExtensionsKit has a method to get all enemy ships within a range that you can determine. The higher the range the more iterations so I would be prudent for performance. I set up a rudimentary weight system to determine how threatening the ships are based upon their deployment cost. The system also weights by hullsize so that capitals are considered a greater threat than frigates. Here is an example: (Keep in mind that copy pasting this won't work out of the box. You will have to adjust things and create your own implementation. This is just for a general reference to start.)
Spoiler
private static boolean assessNearbyThreats(ShipAPI ship, ShipwideAIFlags flags, float noVentDuration) {
List<ShipAPI> shipList = CombatExtensionsKt.getNearbyEnemies(ship, nearbyShipAssessmentRange);
double weight = 0;
for(ShipAPI enemyShip : shipList) {
switch (enemyShip.getHullSize()) {
case FIGHTER:
break;
case FRIGATE:
weight += (frigateWeight * enemyShip.getMutableStats().getSuppliesToRecover().getBaseValue());
break;
case DESTROYER:
weight += (destroyerWeight * enemyShip.getMutableStats().getSuppliesToRecover().getBaseValue());
break;
case CRUISER:
weight += (cruiserWeight * enemyShip.getMutableStats().getSuppliesToRecover().getBaseValue());
break;
case CAPITAL_SHIP:
weight += (capitalWeight * enemyShip.getMutableStats().getSuppliesToRecover().getBaseValue());
break;
default:
break;
}
}
if (weight >= weightThreshold) {
flags.setFlag(ShipwideAIFlags.AIFlags.DO_NOT_VENT, noVentDuration);
flags.setFlag(ShipwideAIFlags.AIFlags.KEEP_SHIELDS_ON, noVentDuration);
if (LOG_INFO) {
DecimalFormat round = new DecimalFormat("#.##");
LOG.info("Threat assessment returned true. NearbyFleetStrength: " + Double.valueOf(round.format(weight))
+ " ---- Ship won't vent flux AI flag: " + flags.hasFlag(ShipwideAIFlags.AIFlags.DO_NOT_VENT)
+ " ---- Ship keeps shields up AI flag: " + flags.hasFlag(ShipwideAIFlags.AIFlags.KEEP_SHIELDS_ON));
}
return true;
} else {
if (weight >= (weightThreshold / weightThresholdNoVentDivisor)) {
flags.setFlag(ShipwideAIFlags.AIFlags.DO_NOT_VENT, noVentDuration);
}
if (weight >= (weightThreshold / weightThresholdKeepShieldsRaisedDivisor)) {
flags.setFlag(ShipwideAIFlags.AIFlags.KEEP_SHIELDS_ON, noVentDuration);
}
if (LOG_INFO) {
DecimalFormat round = new DecimalFormat("#.##");
LOG.info("Threat assessment returned false. NearbyFleetStrength: " + Double.valueOf(round.format(weight))
+ " ---- Ship won't vent flux AI flag: " + flags.hasFlag(ShipwideAIFlags.AIFlags.DO_NOT_VENT)
+ " ---- Ship keeps shields up AI flag: " + flags.hasFlag(ShipwideAIFlags.AIFlags.KEEP_SHIELDS_ON));
}
return false;
}
}
This AI isn't a toggle and I also track other things like hull damage, etc, so yours will definitely look different. My advice is to measure nearby enemies and perform a threat assessment like above, and then when the value falls below the assessment threshold you would toggle the system back to the carrier. That's just what I would do though. You might have more nuanced ideas and it's pretty fun to play around with use cases in my experience. Also you can ignore the AI flag part of the code since that is the AI making a determination about whether to vent or keep its shields up based upon a lower threshold than system activation.
4) When your above implementation of the script returns true, first check if the ship is in station mode (to prevent infinite toggling) and if it isn't - toggle the system using: ShipAPI.useSystem();
5) When your implementation of the script returns false (threats have left or been destroyed), check if the ship is in carrier mode and if not, toggle the system.
That's a very rough overview of how I would approach what you are trying to do. The extra details you will need will have to come from trial and error.
Hope that helps!
where can i take a look at the BURN_DRIVE aiType script? or other ship system aiTypes for that matter
i want to do a custom AI because a ship system i have just won't work with any of the existing ones i tried in the hands of the AI, so i wanted to take a look at how some of the existing AIs work as reference but i just can't find them
I think the only example that is available is the FastMissileRacksAI under shipsystems\scripts\ai but that will get you started on the basics of how to set up an AI in the first place, and then you can create your own.
It probably doesn't need to be said that AI is very complicated. My use case is essentially "Ship in trouble? Activate super system!" and the AI to perform that relatively simple analysis is like 500 lines of code with a fair amount of moving parts.
My advice to you and the above poster - Log log log! It will save you
a lot of time.