I'm generally pretty terrible at writing tutorials, so hopefully this doesn't cause more harm than good
.
This is meant to be a step by step guide to adding your own faction to the campaign. I'll assume you already know how to create a mod directory and create the necessary folder structure.
First a quick note: Capitalization matters! Make sure you use the same capitalization in everything. Ship variants, variables, types, spawn points - if you don't capitalize it the same across the board it won't work.
Step 1: Creating the faction file.In YourMod\data\world\factions you will need a faction file. I suggest copying an existing core file over to your mod directory and editing it. For this tutorial, I'll use the core Tri-Tachyon faction. Comments from me in
blue.{
id:"tritachyon", This is the ID of the faction that you'll refer to it by in scripts.
"color":[135,206,255,255], The color for the faction, using an RGBA color code
"displayName":"Tri-Tachyon", The name as it appears ingame
"description":"No description yet.",
"names":{ The character names the faction will use.
"modern":1,
},
"portraits":{ The portraits that the faction will use.
"standard_male":[
"graphics/portraits/portrait_corporate01.png",
"graphics/portraits/portrait_corporate03.png",
"graphics/portraits/portrait13.png",
],
"standard_female":[
"graphics/portraits/portrait_corporate02.png",
"graphics/portraits/portrait_mercenary02.png",
"graphics/portraits/portrait16.png",
],
},
"fleetCompositions":{ Here's the meat of the faction file for now.
"scout":{ The ID of your fleet.
"displayName":"Scout", The name displayed – will read Factionname Scout.
"maxFleetPoints":11, The max fleet points used for randomly generated fleets.
"daysWorthOfSupplies":[30, 50], The stuff the fleet spawns with – supplies, fuel, extra crew, marines.
"lyWorthOfFuel":[40, 60],
"extraCrewPercent":[20, 30],
"marinesPercent":[5, 10],
"ships":{ The make up of the fleet. You have “variant ID”:[min, max],.
"tempest_Attack":[1, 1], Here we have minimum 1 tempest attack, and below 1 wasp wing. These fleets will not be random.
"wasp_wing":[1, 1],
},
},
"raiders":{
"displayName":"Raiders",
"maxFleetPoints":25,
"daysWorthOfSupplies":[30, 50],
"lyWorthOfFuel":[40, 60],
"extraCrewPercent":[20, 30],
"marinesPercent":[5, 10],
"ships":{ This fleet IS random, thanks to min and max values. The game will generate the ships listed randomly within the min and max, but keep it all within the max fleet points given above.
"tempest_Attack":[0, 3],
"wolf_CS":[0, 2],
"wolf_Assault":[0, 2],
"wasp_wing":[0, 2],
},
},
"attackFleet":{
"displayName":"Attack Fleet",
"maxFleetPoints":50,
"daysWorthOfSupplies":[30, 50],
"lyWorthOfFuel":[40, 60],
"extraCrewPercent":[50, 70],
"marinesPercent":[10, 20],
"ships":{
"aurora_Balanced":[1, 1],
"medusa_Attack":[0, 2],
"medusa_CS":[0, 2],
"tempest_Attack":[0, 3],
"wolf_CS":[0, 2],
"xyphos_wing":[0, 2],
"wasp_wing":[0, 2],
},
},
"securityDetachment":{
"displayName":"Security Detachment",
"maxFleetPoints":100,
"daysWorthOfSupplies":[30, 50],
"lyWorthOfFuel":[40, 60],
"extraCrewPercent":[50, 70],
"marinesPercent":[20, 30],
"ships":{
"odyssey_Balanced":[0, 1],
"paragon_Elite":[0, 1],
"astral_Elite":[0, 1],
"aurora_Balanced":[0, 2],
"medusa_Attack":[0, 2],
"tempest_Attack":[0, 3],
"wolf_CS":[0, 2],
"dagger_wing":[0, 2],
"xyphos_wing":[0, 2],
"wasp_wing":[0, 2],
},
},
},
"traits":{
"admiral":{
},
"captain":{ Affects AI captain behavior in battle.
"cowardly":1,
"cautious":1,
"steady":1,
"aggressive":1,
"suicidal":1,
"fearless":1,
},
},
"dialogue":{ What the faction will tell players in the comm.
"greetingFriendly":"This Tri-Tachyon vessel is on a classified mission. Identity and friendly status confirmed.",
"greetingNeutral":"We of the Tri-tachyon corporation have scanned your vessel and analyzed your tactical capabilities. Maintain your distance.",
"greetingHostileAggressive":"Attention. Your vessel has scanned and identified as hostile to the Tri-Tachyon corporation. Prepare for annihilation.",
"greetingHostileTimid":"Attention. Your vessel has scanned and identified as hostile to the Tri-Tachyon corporation. Further attempts at aggression will be recorded.",
}
},
Simply edit the copied file to make your own faction, making sure to have a unique ID. Also, rename it to that ID for simplicity.
Step 2: Making the game read your faction file.In yourmod\data\world\factions you need to make a file factions.csv. Inside this file, all you need is faction followed by a reference to your faction file, like so:
faction
data/world/factions/yourfactionfile.faction
Once that's in there, the game will read the faction.
Step 3: Getting your faction into the game, part 1: Your spawn point.Now you need a spawn point file, which takes your fleets defined in the faction file and creates them in the game. Again, it's easy to copy an existing spawn point and edit it. I'll use the basic PirateSpawnPoint. To explain a few things. Again, my comments are in
blue.package data.scripts.world;
import com.fs.starfarer.api.campaign.CampaignFleetAPI;
import com.fs.starfarer.api.campaign.FleetAssignment;
import com.fs.starfarer.api.campaign.LocationAPI;
import com.fs.starfarer.api.campaign.SectorAPI;
import com.fs.starfarer.api.campaign.SectorEntityToken; These are imports, so you can utilize functions from the various API. If you try to use a function from another spawn point file and it crashed on compile with a message that it can't determine the type name, you have probably forgotten to add the required API.
public class PirateSpawnPoint extends BaseSpawnPoint { When editing the file, make sure you change the part here that says PirateSpawnPoint to whatever your spawn point is called.
public PirateSpawnPoint(SectorAPI sector, LocationAPI location, Change PirateSpawnPoint here too – again to whatever your spawn point is called.
float daysInterval, int maxFleets, SectorEntityToken anchor) {
super(sector, location, daysInterval, maxFleets, anchor);
}
@Override
protected CampaignFleetAPI spawnFleet() {
String type = null; Here it's setting the type randomly. This type corresponds to the ID of your fleets in the faction file – capitalization matters.
float r = (float) Math.random(); If you want one spawn creating a random assortment of fleets, this is a good way to do it. If you just want one fleet type, you could skip all this entirely and replace type in the createFleet thing with your fleet type in quotes.
if (r > .8f) {
type = "scout";
} else if (r > 0.45f) {
type = "raiders1";
} else if (r > 0.3f) {
type = "raiders2";
} else if (r > 0.15f) {
type = "attackFleet";
} else if (r > 0.05f) {
type = "carrierGroup";
} else {
type = "armada";
}
CampaignFleetAPI fleet = getSector().createFleet("pirates", type); Here we are setting up the fleet. The createFleet part requires a factionid and a type. The “pirates” is the faction, so change that to your faction id, type is the string we set above at random.
getLocation().spawnFleet(getAnchor(), 0, 0, fleet); Here we are spawning the fleet. This is spawnFleet(location, xoffset, yoffset, fleet). getAnchor() is finding the anchor that you will set when you create the spawn point in your gen file (I'll cover that next). The offset allows you to spawn the fleet away from your spawn point, but it isn't necessary.
if (type.equals("scout") || type.equals("raiders1") || type.equals("raiders2") || type.equals("attackFleet")) { Here it's checking for the type and adding conditional assignments based on that. These assignments will only apply to scout, raiders1, raiders2, and attackFleet.
fleet.addAssignment(FleetAssignment.RAID_SYSTEM, null, 10); The assignments work like this: addAssignment(assignment, location, maxdurationindays) Valid assignments can be found in the FleetAssignment.java file in the Starfarer API. RAID_SYSTEM simply roams the system attacking stuff and doesn't require a specified location.
fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, getAnchor(), 1000); Here it's telling the fleet to go to the anchor location and de-spawn after it's done raiding, with a 1000 day duration so it won't give up on it. This prevents fleets that get worn down to a single fighter wing or such from flying around forever – they eventually de-spawn so another fleet can spawn.
} else {
if ((float) Math.random() > 0.5f) {
fleet.addAssignment(FleetAssignment.RAID_SYSTEM, null, 30);
fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, getAnchor(), 1000);
} else {
fleet.addAssignment(FleetAssignment.DEFEND_LOCATION, getAnchor(), 20);
fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, getAnchor(), 1000);
}
}
return fleet;
}
}
Just edit that file to your liking, including your specific fleet types and faction id, then rename the file to the name chosen for your spawn point – like say MyModSpawnPoint.java.
Step 4: Getting your faction into the game, part 2: The generator file.Here is where we will add your spawn point to the game. Adding spawn points and even stations can also be done inside spawn points (you can look at my Tri-Tachyon station mod for an example), but the simplest way is to just spawn them in when the world is generated using a gen file.
First we need a gen file. I'll use the custom gen file I have in my mod as an example.
package data.scripts.world;
import java.awt.Color;
import java.util.List;
import com.fs.starfarer.api.campaign.CampaignFleetAPI;
import com.fs.starfarer.api.campaign.CargoAPI;
import com.fs.starfarer.api.campaign.FactionAPI;
import com.fs.starfarer.api.campaign.FleetAssignment;
import com.fs.starfarer.api.campaign.SectorAPI;
import com.fs.starfarer.api.campaign.SectorEntityToken;
import com.fs.starfarer.api.campaign.SectorGeneratorPlugin;
import com.fs.starfarer.api.campaign.StarSystemAPI;
import com.fs.starfarer.api.campaign.CargoAPI.CrewXPLevel;
import com.fs.starfarer.api.fleet.FleetMemberType; Again, imports. I just copied the import list from the core SectorGen. I don't actually use them all in this simple gen file, but I don't think it really hurts anything to import and not use them.
@SuppressWarnings("unchecked")
public class GRModGen extends SectorGen { Here we're labeling the class, so just make sure GRModGen is the name of your gen file. This is just telling it to extend the core SectorGen – “implements SectorGeneratorPlugin” also works.
public void generate(SectorAPI sector) {
StarSystemAPI system = sector.getStarSystem("Corvus"); This is finding the star system named Corvus and assigning it as system for later use.
SectorEntityToken token = system.createToken(15000, 0); Here I'm creating a token at a fixed point in space, in order to add a spawn to a specific spot on the map that doesn't move. 15000,0 corresponds to the center of the far right side of the map.
system.addSpawnPoint(new GRConvoySpawnPoint(sector, system, 30, 1, token)); Here is where I actually add my spawn point. This one is with a 30 day delay with 1 max fleet spawned at a time, at the location set above for the token. To make your own, be sure to change the GRConvoySpawnPoint to whatever your spawn point is called.
token = system.createToken(15000, -15000); Setting token to a new location for another spawn point.
system.addSpawnPoint(new TTConvoySpawnPoint(sector, system, 14, 1, token)); Adding a second spawn point, this time at the new location and with a 14 day interval.
FactionAPI gunrunners = sector.getFaction("gunrunners"); Here I'm getting the faction called gunrunners. To get your own faction, simply change “gunrunners” to your faction id. Also probably want to change the variable to your faction name for readability, but that's not necessary.
gunrunners.setRelationship("hegemony", -1); Here I'm taking the faction we got above and setting its relation to the hegemony faction. -1 means they don't like eachother, and will fight.
gunrunners.setRelationship("tritachyon", -1);
gunrunners.setRelationship("pirates", 0); Here I'm setting them to 0 for pirates, which means they're neutral and won't fight.
gunrunners.setRelationship("independent", 0);
gunrunners.setRelationship("player", 0); Also start off neutral to players, although the player can change that by attacking them.
}
}
Just edit your gen file to do what you want, and rename it appropriately.
Then you will also need a generators file that tells the game to look for this generator, just like we did with factions.csv. It needs to be in YourMod\data\world. Here is what my file looks like:
className
data.scripts.world.GRModGen
You just need className followed by that one entry – the location of your mod gen file (in this case, I'm telling the game to find a file called GRModGen in data\scripts\world.).
Then when a new game is created it will read your gen file, generate your spawn point, and begin spawning in your fleets. The fleets will do what you assigned them to do in the spawn file. They will attack any fleet you set them to be hostile to, and ignore the others.
A few other options.If you want to set your spawn points to be tied to particular planets or stations, you can do that too – you just have to get the planet or station and use it in place of a token. Here is an example:
SectorEntityToken CorvusIIIA = system.getEntityByName("Corvus IIIA");
system.addSpawnPoint(new GRPirateSpawnPoint(sector, system, 5, 5, CorvusIIIA));
I'm finding the entity named Corvus IIIA (which was named that in the core SectorGen when it was created). This happens to be the moon of Corvus III, and the spawn point of pirates. Then I'm adding a spawn point, using a custom pirate spawner. I'm giving it a 5 day delay, 5 max fleets, and setting it to be anchored at CorvusIIIA. Note that you have to have already defined system before using this, so you'd also need this somewhere above it (but only once):
StarSystemAPI system = sector.getStarSystem("Corvus");
You can also set up the spawn to spawn fleets immediately, so the game starts out with stuff flying around. Here is an example of doing that:
GRPirateSpawnPoint GRPirateSpawn = new GRPirateSpawnPoint(sector, system, 5, 5, CorvusIIIA);
system.addSpawnPoint(GRPirateSpawn);
for (int i = 0; i < 5; i++) GRPirateSpawn.spawnFleet();
In this case, I'm setting up the spawn point before I actually add it so that I have it assigned to a variable – that way I can reference it once it's added. I assigned it to GRPirateSpawn, but I could have called it anything. Then when I add it I just reference it by that. Then I have it running a for loop with i set to 0 adding one each time as long as i < 5 – just a basic loop that repeats 5 times. Each loop, it's referencing our spawn point and telling it to spawn a fleet. So it creates 5 fleets when the spawn point is placed.
You can also add stations in your gen file, which is also quite simple:
SectorEntityToken CorvusIIIA = system.getEntityByName("Corvus IIIA");
SectorEntityToken station = system.addOrbitalStation(CorvusIIIA, 45, 150, 30, "Pirate Stronghold", "pirates");
That would add a station orbiting CorvusIIIA at 45 degrees, 150 distance, with an orbital period of 30 days, named Pirate Stronghold, belonging to the pirates faction. Then you can use:
CargoAPI cargo = station.getCargo();
To get a reference for that station's cargo, and add whatever you want to it by going
cargo.addWeapons("flak", 3);
cargo.addCrew(CrewXPLevel.ELITE, 25);
cargo.addMothballedShip(FleetMemberType.SHIP, "hyperion_Strike", null);
And so on. You can get a good example of adding cargo to a station in the core SectorGen file.
DISCLAIMER: Currently, due to a bug, getEntityByName does not work in the gen files, which is why most mods are editing the default SectorGen. For now, you can do the same – but your mod won't be all that compatible with other mods if you do. You could do a workaround like I did with adding them in the convoy scripts. You can see an example of how I did that in my mod. This is FIXED in the next patch though, so as soon as we get the next patch making your own gen file and using getEntityByName will be the preferred way of adding things.