Creating custom Starsystem
Guide by Vexlia Artemiss
This guide is work in progress, expect information to be changed and not always be correct.
To be done:
- All the code stuff
- Getting code loaded in plugin
- A lot of explanation of stuff
- I soooo much like tomatopaste example starsysten so it going be based on it.
This guide will go in depth on how to create starsystem, make it generate in sector and also help you with setting up markets.
If you been refered to this page from somewhere online and don't know anything about modding starsector you should visit Welcome page to help you with getting started. When you finished with setting up mod folder and IDE you're welcome to continue with this guide.
Initial setup
For the start, we should go over file standard structure for adding start system for your mod.
Usually generation will be structured like this:
Your mod Plugin
^^^
Generator Class Java File - Includes calls to starsystem and other things mod want to generate (faction relations,
^^^
StarSystem Class Java File - Defines star system and almost everything within it
If you followed Getting Started you should have mod plugin already, that leaves us with need to create Generator Class and Starsystem Class. You don't strictly need to use generator class, but having function separeta helps to keeps mod plugin tidy and clearer and what each part does. Besides java files itself it would be useful to have dedicated Packages (folders) for new files. So lets start:
- Go in you side panel, find place you like your main generator directory to be and Right click it > New > Package > create new package by adding
worldafter last dot. - Go in you side panel, click place you like your starsystem code to be and Right click > New > Java Class. Name your file (for this guide example, I will use "MyMod_Gen".
- Go in you side panel, and Right click on
worldpackage> New > Package > create packagestarsystems - Go in you side panel, find place you like your starsystem code to be and Right click > New > Java Class. Name your file (for this guide example, I will use "MyNewStarsystem".
Remember to keep your names useful, it will save your time in the future!
You should end up with something like (add image later):
- /other/packages depends on where you created `world` package
- world
- starsystems
- MyNewStarsystem
- MyModId_Gen
- starsystems
- world
You should now have two blank class files looking something like:
What we want now is to link all new files togeter with mod plugin. To do that we first need to have methods to actually ecxecute by call of our classes.
For now follow me and add inside the class
public void generate(SectorAPI sector) {
}
to both MyMod_Gen and MyNewStarsystem:
You will end up with something like:
Creating Star System and adding stars
After setting up file structure we can return to out actual star system code. You have your generate method and we can start working with first of all telling gane we want to create system by adding:
//create a new variable called system. this is assigned an instance of the new star system added to the Sector at the same time. "New Starsystem" will be system name
StarSystemAPI system = sector.createStarSystem("New Starsystem");
//sets location of system in hyperspace. map size is in the order of 100000x100000, and 0, 0 is the center of the map, this will set the location to the east and slightly south of the center
//0, 0 of the vanilla sector located just under Hybrasil
system.getLocation().set(15000, -2000);
system.setBackgroundTextureFilename("graphics/backgrounds/background1.jpg"); //sets the background image for when in the system. this is a filepath to an image in the core game files
Beside creating system it also needs init (main) star.
Code to adding one will look something like:
// create the star
PlanetAPI star = system.initStar("modID_MyNewStarsystem_star", // unique id for this star
"star_yellow", // id in planets.json
900f, // radius (in pixels at default zoom)
400); // corona radius, from star edge
system.setLightColor(new Color(169, 177, 65)); // light color in entire system, affects all entities
star.setDiscoverable(false);
//set up star
PlanetAPI star = system.initStar( //stars and planets are technically the same category of object, so stars use PlanetAPI
"example_star_id", //set star id, this should be unique
"star_orange", //set star type, the type IDs come from starsector-core/data/campaign/procgen/star_gen_data.csv
900, //set radius, 900 is a typical radius size
15000, //sets the location of the star's one-way jump point in hyperspace, since it is the center of the star system, we want it to be in the center of the star system jump points in hyperspace
-2000,
900 //radius of corona terrain around star
);
All star systems need init (main) star. It not always an actual star (for example in nebulas there no central star) but an entity serving as center of the system.
Adding planets
PlanetAPI planet1 = StarSystem.addPlanet("MyModId_planet1", //planet id - should be unique
Star, //orbit focus
"First planet",
Planets.PLANET_TERRAN, //Planet type
20, //angle
130f, //radius
4500f, //distance from star
600f); //how many days to orbit
planet1.setCustomDescriptionId("MyModId_planet1"); //for custom descriptions
planet1.setDiscoverable(false);
You maybe noted what planet type was defined by Planets.PLANET_TERRAN. Planets is ID class inside Starsector API, it used to easily access and search value defined somewhere else. Planets class corresponds to planets inside planet_gen_data.csv. In this case Planets.PLANET_TERRAN equivalent to inputing "terran" in same place.
There are multiple ID classes in API, used for different things, you can find most of them in com.fs.starfarer.api.impl.campaign.ids.
Now what we added first actually orbiting entity (to note: planets are also entities in game engine), we can talk about orbit focus. In simple terms Orbit Focus is what (in this case) planet will orbit around. Our focus references Star, value of which we set when creating init star. Naturaly focus can be any entity (specifically SectorEntityToken which PlanetAPI extends of). Setting focus on different things can help create moons, multistar solar systems and so on.
Adding Custom Entities (Stations, stable points and so much more)
Custom entities cover most things in system what are not planets: station, gates, stable points, structures for stable points and so many more. Full list of available custom entities located in starsector-core/data/config/custom_entities.json. In this section I cover most used ones but adding any one of them should work similar.
Besides what available in vanilla you can create your own custom entity by creating custom_entities.json inside your mod data/config and following vanilla file json structure.
Now lets go over some most common entities you may want to add:
Adding stable points and stable point structures
SectorEntityToken loc1 = StarSystem.addCustomEntity(null,
null,
"comm_relay", //id in custom_entities.json
"faction_id"; //which faction own the entity
loc1.getMemoryWithoutUpdate().set("$isotd_FIMBRelay", true);
loc1.setCircularOrbitPointingDown(Star, 65, 9000, 170);
Adding other entities
Adding asteroid belts and rings
Asteroid belt is belt or ring of actual asteroids. These asteroids are separate entities inside these, it will randomly distribute a defined number of them around the ring.
//adds an asteroid belt. asteroids are separate entities inside these, it will randomly distribute a defined number of them around the ring
system.addAsteroidBelt(
star, //orbit focus
80, //number of asteroid entities
innerOrbitDistance + 500, //orbit radius is 500 gap for outer randomly generated entity above
255, //width of band
190, //minimum and maximum visual orbit speeds of asteroids
220,
Terrain.ASTEROID_BELT, //ID of the terrain type that appears in the section above the abilities bar
"Example Asteroid Belt" //display name
);
For ring band:
//adds a ring texture. it will go under the asteroid entities generated above
system.addRingBand(star,
"misc", //used to access band texture, this is the name of a category in settings.json
"rings_asteroids0", //specific texture id in category misc in settings.json
256f, //texture width, can be used for scaling shenanigans
2,
Color.white, //colour tint
256f, //band width in game
innerOrbitDistance + 500, //same as above
200f,
null,
null
);
Asteroid belt is belt or ring of actual asteroids. They in fact are real entities, and what you get collision with when flying throught.
system.addAsteroidBelt(planet3, 50, 850, 120, 90, 110, Terrain.ASTEROID_BELT, "Asteroid belt");
Creating markets
Adding Markets are bit more complex than just adding entities and planets to star system. Main complication is that there just so much market can do and be. While hard to cover everything tomatopaste example star system established quite popular and comfortable template for basic market setup by using separate addMarketplace class
A market is separate to a Planet, and contains data about population, industries and conditions. This is a method from the other script in this mod, that will assign all marketplace conditions to the planet in one go, making it simple and easy
//uses helper method from other script to easily configure the market
MarketAPI planetOneMarketplace = ESS_AddMarketplace.addMarketplace(
Factions.INDEPENDENT, //Factions.INDEPENDENT references the id String of the Independent faction, so it is the same as writing "independent", but neater. This determines the Faction associated with this market
planetOne, //the PlanetAPI variable that this market will be assigned to
null, //some mods and vanilla will have additional floating space stations or other entities, that when accessed, will open this marketplace. We don't have any associated entities for this method to add, so we leave null
"Example Planet One", //Display name of market
4, //population size
new ArrayList<>(Arrays.asList( //List of conditions for this method to iterate through and add to the market
Conditions.EXTREME_WEATHER,
Conditions.POPULATION_4,
Conditions.RARE_ORE_ABUNDANT,
Conditions.HABITABLE,
Conditions.HOT
)),
new ArrayList<>(Arrays.asList( //list of submarkets for this method to iterate through and add to the market. if a military base industry was added to this market, it would be consistent to add a military submarket too
Submarkets.SUBMARKET_OPEN, //add a default open market
Submarkets.SUBMARKET_STORAGE, //add a player storage market
Submarkets.SUBMARKET_BLACK //add a black market
)),
new ArrayList<>(Arrays.asList( //list of industries for this method to iterate through and add to the market
Industries.POPULATION, //population industry is required for weirdness to not happen
Industries.SPACEPORT, //same with spaceport
Industries.MINING,
Industries.FUELPROD,
Industries.GROUNDDEFENSES
)),
true, //if true, the planet will have visual junk orbiting and will play an ambient chatter audio track when the player is nearby
false //used by the method to make a market hidden like a pirate base, not recommended for generating markets in a core world
);
This big code block covers 90% of creating a market and pretty much all what you need for basic one, more custom bits covered bellow.
Adding colony items to industries
To add items to colony structures you need to grab the industry from a market and use setSpecialItem()
planet1_Marketplace.getIndustry(Industries.ORBITALWORKS).setSpecialItem(new SpecialItemData(Items.CORRUPTED_NANOFORGE, null));
Finishing touches
Jump points
Simplest way is to allow game to autogenerate jump points
//autogenerate jump points that will appear in hyperspace and in system
system.autogenerateHyperspaceJumpPoints(true, true);
TBD: add info on manualy creating jump points
Clean up the hyperspace
The following is hyperspace cleanup code that will remove hyperstorm clouds around this system's location in hyperspace. It's more or less copied from vanilla
//set up hyperspace editor plugin
HyperspaceTerrainPlugin hyperspaceTerrainPlugin = (HyperspaceTerrainPlugin) Misc.getHyperspaceTerrain().getPlugin(); //get instance of hyperspace terrain
NebulaEditor nebulaEditor = new NebulaEditor(hyperspaceTerrainPlugin); //object used to make changes to hyperspace nebula
//set up radiuses in hyperspace of system
float minHyperspaceRadius = hyperspaceTerrainPlugin.getTileSize() * 2f; //minimum radius is two 'tiles'
float maxHyperspaceRadius = system.getMaxRadiusInHyperspace();
//hyperstorm-b-gone (around system in hyperspace)
nebulaEditor.clearArc(system.getLocation().x, system.getLocation().y, 0, minHyperspaceRadius + maxHyperspaceRadius, 0f, 360f, 0.25f);
Bag of tricks
This section is for more miche short tips for stuff you can run into when bringing you ideas to life
Making Free market start fully active
You can get access to FreeMarket variable and just set days it was active
FreeMarket.get(Revachol_Marketplace).setDaysActive(366);
If IDE complains about line above make sure you imported the class:
import com.fs.starfarer.api.impl.campaign.econ.FreeMarket;
Making system map available from start
System map is only available if system was visited by player, but it is in fact public variable, so you can just change it:
system.setEnteredByPlayer(true);
"My markets have no people in comms"
If your markets get created after game does its economyLoad on initial new game creation, they will not get automatically populated. Luckly Alex added way to fix it.
CoreLifecyclePluginImpl.createInitialPeople(planet1_Marketplace, new Random());
Example Code
If you want to copy example code, bellow is code for star system using every bit of code demonstated in the guide:
Don't forget to fix package to one for your mod. Intelij can help with it (it will show you error if package is wrong and offer to automatically redo it to file current package placement: