Advanced Guide to Modules: Syncing Modules
This guide cover some points and implementation questions missing from great guides:
- [Basic Guide to Modules]
- [A Technical Guide To Armor Modules]
Readers should read through A Basic Guide to Modules first and have a general understanding of how modules behave and work. Also readers should have grasp of SS/Java coding and use IDE.
This guide, to some extent retelling of guide: https://fractalsoftworks.com/forum/index.php?topic=17097.0, which is also referenced by A Basic Guide to Modules. But in more organized and modular format with pointing out things, the referenced guide skips over.
How can you sync modules?
The first thing you could notice after creating your own modular ship is the fact modules by themselves don't actually interact with Mothership. They don't activate engines at all or in sync with mothership, fighters fly how module wants and ignore your commands and shipsystem only affects mothership. What the problem, you may ask? The answer is simple, as you should know after reading A Basic Guide to Modules, modules are in fact are just AI controlled ships with limited mobility. And through some AI behavior can be controlled using tags in ship_data.csv, most complex stuff falls in your own codding hands.
In this chapter we go over most basic things you potentially would want to sync. But before we dive in coding and concrete application lets talk about what you actually need to implement it.
There are several ways to interact with modules, but all of them have one common quality: they all advance in combat. In other words, they run every frame while the ship is in combat. Here are few of the most common examples:
- Hullmods (with advanceInCombat() method)
- Weapon's Every frame scripts (weapon with script defined in "everyFrameEffect" in .weapon)
- Shipsystems (apply runs everyframe)
For module syncing (and some movement) the simplest way is hullmods. It is easy to insure they are always active using build-in hullmods and there is no intersection with other effects of which we may worry about.
Weapon scripts are mostly used for custom weapons (punching, super missiles or launching ships) and require custom coding, so they mostly out of scope of this part of a guide, even if guide should give you some basis to get started in Chapter 2.
And when it comes to shipsystems, effects only limited by imagination and coding ability, but most of them could include moving and rotating modules.
Most of this part will work with hullmod's code, and their advanceInCombat() method, outside some specific cases which I will point out. In most cases hullmod goes on core ship, but you could access core ship through module as well if you need it.
advanceInCombat()
Before going into individual code, lets see base structure of our hullmod's advanceInCombat() method
public void advanceInCombat(ShipAPI ship, float amount) {
super.advanceInCombat(ship, amount);
java.util.List<ShipAPI> children_list = ship.getChildModulesCopy();
ModuleEnginesAffectThrust(ship, children_list);
for (ShipAPI child : children_list){
SyncEngines(child, ship);
SyncWings(child, ship);
SyncShields(child, ship);
SyncSystem(child, ship);
}
}
Jumping somewhat forward I will note a few things. Firstly I design code to be approachable/readable, you don't need to make everything same way. You may have no need to wing syncing, or your maybe don't need system syncing. Feel free to skip to stuff you more interested in. Secondly, there two ways to sync system, one done using hullmod if you can't modify shipsystem directly. Second is adding sync in ship system directly. Both discussed in their dedicated chapters.
Syncing engines of modules.
Lets start with engines. Always great visual element and sometimes absolutely needed if your mothership has dedicated engine module/modules. It also great example of how coordinating mothership and modules work.
To sync engines we simply see in what state mothership's engines are and assign the same state to module, here how it looks in code:
// Mirrors Core ship engine states
void SyncEngines(ShipAPI child, ShipAPI parent) {
ShipEngineControllerAPI parent_ec = parent.getEngineController();
if (parent_ec != null) {
if (parent.isAlive()) {
if (parent_ec.isAccelerating()) {
child.giveCommand(ShipCommand.ACCELERATE, null, 0);
}
if (parent_ec.isAcceleratingBackwards()) {
child.giveCommand(ShipCommand.ACCELERATE_BACKWARDS, null, 0);
}
if (parent_ec.isDecelerating()) {
child.giveCommand(ShipCommand.DECELERATE, null, 0);
}
if (parent_ec.isStrafingLeft()) {
child.giveCommand(ShipCommand.STRAFE_LEFT, null, 0);
}
if (parent_ec.isStrafingRight()) {
child.giveCommand(ShipCommand.STRAFE_RIGHT, null, 0);
}
if (parent_ec.isTurningLeft()) {
child.giveCommand(ShipCommand.TURN_LEFT, null, 0);
}
if (parent_ec.isTurningRight()) {
child.giveCommand(ShipCommand.TURN_RIGHT, null, 0);
}
}
ShipEngineControllerAPI cec = child.getEngineController();
if (cec != null) {
if ((parent_ec.isFlamingOut() || parent_ec.isFlamedOut()) && !cec.isFlamingOut() && !cec.isFlamedOut()) {
child.getEngineController().forceFlameout(true);
}
}
}
}
And let's now check how ship looking:
// PICTURE
Nice, now all engines work together. But that just visuals. engines on modules actually not affect ship speed or manoeuvrability when disabled, or module itself destroyed. So lets do some additional coding to add that.
Engine power-sharing feature
If you're designing a modular ship, where bulk of its engines are on the modules, you should consider making them affect overall ship available engine power. Then targeting engines on modules or even destroying modules become valuable strategy to limit modular ship mobility.
To implement it, you need to calculate how modules engines should affect ship power. Lucky for you, the modding community figured it out already.
// Disabled Engines on modules have effect on Core ship thrust
void ModuleEnginesAffectThrust(ShipAPI parent, List<ShipAPI> children) {
ShipEngineControllerAPI ec = parent.getEngineController();
if (ec != null) {
// Full mass of the ship with the modules
float originalMass = 2500;
// Amount of engines on core ship and all modules combined
int originalEngines = 18;
float thrustPerEngine = originalMass / originalEngines;
// Doesn't count parent's engines for this stuff - game already affects stats
float workingEngines = ec.getShipEngines().size();
for (ShipAPI child : children) {
if ((child.getParentStation() == parent) && (child.getStationSlot() != null) && child.isAlive()) {
ShipEngineControllerAPI cec = child.getEngineController();
if (cec != null) {
float contribution = 0f;
for (ShipEngineControllerAPI.ShipEngineAPI ce : cec.getShipEngines()) {
if (ce.isActive() && !ce.isDisabled() && !ce.isPermanentlyDisabled() && !ce.isSystemActivated()) {
contribution += ce.getContribution();
}
}
workingEngines += cec.getShipEngines().size() * contribution;
}
}
}
float thrust = workingEngines * thrustPerEngine;
float enginePerformance = thrust / Math.max(1f, parent.getMassWithModules());
parent.getMutableStats().getAcceleration().modifyMult("hullmodID", enginePerformance);
parent.getMutableStats().getDeceleration().modifyMult("hullmodID", enginePerformance);
parent.getMutableStats().getTurnAcceleration().modifyMult("hullmodID", enginePerformance);
parent.getMutableStats().getMaxTurnRate().modifyMult("hullmodID", enginePerformance);
parent.getMutableStats().getMaxSpeed().modifyMult("hullmodID", enginePerformance);
parent.getMutableStats().getZeroFluxSpeedBoost().modifyMult("hullmodID", enginePerformance);
}
}
Syncing wings of Modules
Let's talk about wings. By default module's AI will act independent of the Mothership, which makes coordinating attacks on supercarriers impossible.To prevent this we in similar way as with engines can access and manipulate modules AI to command its wings:
// Mirror parent's fighter commands
void SyncWings(ShipAPI child, ShipAPI parent){
if (child.hasLaunchBays()) {
if (parent.getAllWings().isEmpty() &&
(Global.getCombatEngine().getPlayerShip() != parent ||
!Global.getCombatEngine().isUIAutopilotOn())) {
// otherwise module fighters will only defend if AI parent has no bays
parent.setPullBackFighters(false);
}
if (child.isPullBackFighters() ^ parent.isPullBackFighters()) {
child.giveCommand(ShipCommand.PULL_BACK_FIGHTERS, null, 0);
}
if (child.getAIFlags() != null) {
if (((Global.getCombatEngine().getPlayerShip() == parent) ||
(parent.getAIFlags() == null))
&& (parent.getShipTarget() != null)) {
child.getAIFlags().setFlag(ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET,
1f, parent.getShipTarget());
} else if ((parent.getAIFlags() != null)
&& parent.getAIFlags().hasFlag(ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET)
&& (parent.getAIFlags().getCustom(ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET) != null)) {
child.getAIFlags().setFlag(
ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET,
1f, parent.getAIFlags().getCustom(
ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET));
} else if (parent.getShipTarget() != null){
child.getAIFlags().setFlag(ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET, 1f, parent.getShipTarget());
}
}
}
}
Syncing shields of modules
Quite rarely seen feature, as AI quite good at using shields, but why not cover it too while we at it?
// Mirror parent's shield state
void SyncShields(ShipAPI child, ShipAPI parent){
//Code here
}
Syncing effects of system for modules - Hullmod approach.
Syncing effects of system for modules - Ship system approach.
// Mirror parent's shield state
void apply(ShipAPI child, ShipAPI parent){
...
...
... other system code
syncModules();
}