Galatia Academy

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:

  1. 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 world after last dot.
  2. 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".
  3. Go in you side panel, and Right click on world package> New > Package > create package starsystems
  4. 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

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:

Last updated 9/28/2026, 3:00:56 PM by Vexlia Artemiss, Cursed by Zeta · revision 16