System modding

Version

Please help with verifying or updating older sections of this article. At least some were last verified for version 3.4.

This article is for the PC version of Stellaris only.

This guide is for adding and modifying Solar Systems to the game.

## Game Map and System Creation

Stellaris appears to create the game map first using the \map files to create coordinates for the stars on the galaxy map, and then use the \star\_classes file to pick what star class is at each coordinate, and then finalize the system with additional contents (planets, moons, asteroid belts) from the \system initializer files and \planet\_classes files.

## Star classes

Star classes can be found in *common/star\_classes*. If you want to create one from scratch, create a new file and place it in that folder.

Note that "stars" and "star classes" are two different things: individual stars are considered the same as planets in the game code, and are found in *common/planet\_classes*, while star classes refer to the classification of a solar system as a whole and will determine lighting, the system's icon in the galaxy map, and number of individual stars in a solar system. Information about modding individual stars can be found here.

### Adding new star classes

Making a new star class is easy. Here's an example of a binary system that can be found in *00\_star\_classes*:

```
sc_binary_1 = { # X-ray Binary Star System: Blue star around a Pulsar
    class             = a_star
    icon              = e_binary_star
    planet            = {
        key = pc_a_star
        class = a_star
    }
    planet            = {
        key = pc_pulsar
        class = pulsar
    }
    spawn_odds         = 5
    num_planets     = { min = 4 max = 10 }

    pc_desert       = { spawn_odds = 0 }
    pc_tropical     = { spawn_odds = 0 }
    pc_arid         = { spawn_odds = 0 }
    pc_continental  = { spawn_odds = 0 }
    pc_ocean        = { spawn_odds = 0 }
    pc_tundra       = { spawn_odds = 0 }
    pc_arctic       = { spawn_odds = 0 }
    pc_gaia         = { spawn_odds = 0 }
    pc_alpine       = { spawn_odds = 0 }
    pc_savannah     = { spawn_odds = 0 }

    is_environmental_hazard = yes
    modifier = { ship_shield_reduction = 1.0 }
}

```

* "sc\_binary\_1" is the name of the star class used by the code. To assign the name the player will see, see localisation modding.
* "class" is used to determine the system's lighting. You can use an existing one or create your own via gfx modding.
* "icon" is the icon used by this system type in the galaxy view. By default it'll search for one that matches the star name ("binary\_1\_star" in this case) in the *gfx/map/star\_classes* folder, but using "icon" lets you overwrite this with whatever icon you want.
* While not included in the example above, you can also use "icon\_scale = " to change the size of the icon on the galaxy map. 1 is default, 0.5 is half the size, etc.
* "planet" is the type of star used by this star class by default. If it's a unary system with just one star, you don't need to write "class" again.
* "spawn\_odds" is exactly what it sounds like: the likelihood that this star class will spawn if it's used in a random list such as *rl\_standard\_stars*. If you don't plan to put it in a list, you can use any number or leave it out entirely.
* "num\_planets" is the number of planets that can be found in these systems. The number of planets in the solar system initializer will overwrite this by default, but if a random range is used there it'll use the number from here.
* The planets' spawn odds are for if you want to alter the likelihood that certain planet classes will spawn in these systems. Neutron Stars, for example, have the values set to 0 by default since these systems won't be hospitable for life forms.
* "is\_environmental\_hazard" marks the system as a hazard, obviously. The "modifier" applies a buff or debuff to ships and stations in the system.

### Vanilla star classes

For reference, the existing star classes are the following:

## Solar system initializers

Solar systems are found in *common/solar\_system\_initializers*. If you want to create one from scratch, create a new file and place it in that folder.

For additional information, see *example.txt* inside the folder.

### Unary Systems

A unary system consists of just one star and a handful of planets and moons. These are the most common types of systems in Stellaris, and we'll start our tutorial by making one.

```
@base_moon_distance = 10

my_system_initializer = {
    name = "My System"
    class = "rl_standard_stars"

    asteroid_belt = {
        type = rocky_asteroid_belt
        radius = 70
    }

    planet = {
        class = star
        size = { min = 25 max = 30 }
        orbit_distance = 0
        orbit_angle = 1
    }

    change_orbit = 10

    planet = {
        class = pc_arid
        orbit_distance = 30
        orbit_angle = { min = 90 max = 270 }

        init_effect = {
            prevent_anomaly = yes
            generate_primitives_on_planet = yes
        }

        change_orbit = @base_moon_distance

        moon = {
            count = { min = 0 max = 1 }
            class = pc_molten
            orbit_distance = 5
            orbit_angle = { min = 90 max = 270 }
        }
    }

    change_orbit = 30

    planet = {
        count = { min = 1 max = 4 }
        class = pc_asteroid
        orbit_distance = 0
        orbit_angle = { min = 90 max = 270 }
    }

    planet = {
        count = { min = 3 max = 6 }
        orbit_distance = 20
        orbit_angle = { min = 90 max = 270 }

        change_orbit = @base_moon_distance

        moon = {
            count = { min = 0 max = 1 }
            orbit_distance = 5
            orbit_angle = { min = 90 max = 270 }
        }
    }

    neighbor_system = {
        trigger = { num_guaranteed_colonies >= 1 }
        distance = { min = 10 max = @distance }
        initializer = "sol_neighbor_t1_first_colony"
    }
}

```

As before, let's go over each element one by one.

```
@base_moon_distance = 10

my_system_initializer = {
    name = "My System"
    class = "rl_standard_stars"

    asteroid_belt = {
        type = rocky_asteroid_belt
        radius = 70
    }

```

* "@base\_moon\_distance" is a variable that can be set in each solar system initializer file. It's not strictly needed, but can be useful if you want to play around with moon orbit distances en masse rather than one at a time. All vanilla solar systems use 10 by default.
* "my\_system\_initializer" is, again, the name the game code uses to identify this system. No need for localisation this time; the name the player will see is right there below it (in this case, "My System").
* "class" is, of course, the star class. You can use a specific class such as *sc\_b*, or a random list such as *rl\_standard\_stars*, which chooses a random one based on the spawn\_odds in each star's code.
* "asteroid\_belt" adds an asteroid belt at a certain distance from the center of the system -- ***not*** necessarily from the sun! We'll get to that later. The valid types can be found in *Stellaris/common/00\_asteroid\_belts*; usually you'll use either *rocky* or *icy*. You can also mod new ones in if desired.

Now for the system's sun. Note that the star is, as I mentioned above, considered a planet by the game engine.

```
    planet = {
        class = star
        size = { min = 25 max = 30 }
        orbit_distance = 0
        orbit_angle = 1
    }

```

* "class", in the context of a planet refers to the planet class. Using "class = star" will make it the star class that matches the star type, so if we'd used *sc\_b* above, the star would be *pc\_b\_star* -- a Class B Star. You can use *pc\_b\_star* anyway if you want, but that can cause complications with star lighting and, in a binary/trinary system, planet naming. More on that below. Anyway, just using "class = star" for the stars is recommended unless you have a good reason not to.
* "size" is the size of the planet. This used to refer to the number of planet tiles, but since tiles are obsolete in general and useless on stars anyway, it just makes the star appear bigger or smaller. A size range of 20-35 is recommended for stars, since smaller than that looks silly and larger can swallow up ships flying overhead.
* "orbit\_distance" is the distance from the previous element, or if no such element exists, the system's center. For the lone star in a system, you naturally want this to be 0.
* "orbit\_angle" is the orbital angle from the previous planet, so if the previous had an angle of 30, using "orbit\_angle = 20" would give it an overall angle of 50. Again, for the central star, this doesn't matter so you can use any old number. Most vanilla stars use 1 for some reason, so that's what I did here.

Now that the star is done, let's move on to the first planet.

```
    change_orbit = 10

    planet = {
        class = pc_arid
        orbit_distance = 30
        orbit_angle = { min = 90 max = 270 }

        init_effect = {
            prevent_anomaly = yes
            generate_primitives_on_planet = yes
        }

```

* "change\_orbit" increases the spacing between planets (in the system scope), or between a planet and its first moon (in the planet scope).
* "class = pc\_arid", so it will be an  Arid World.
* "orbit\_distance = 30" adds 30 units of space between the planet and the last element (in this case, "change\_orbit = 10", so its total distance from the sun is 40 units).
* "orbit\_angle = { min = 90 max = 270 )" is pretty straightforward: a random angle, within ninety degrees of the previous element.
* "init\_effect" allows you to apply effects and scripted effects to the planet upon system generation. It can also be used in the system scope if you want to apply an effect to the system as a whole instead of just one planet.
	+ "prevent\_anomaly" is an effect that disables anomalies from appearing on this planet.
	+ "generate\_primitives\_on\_planet" is a scripted effect that creates a pre-FTL species on this planet.

Now for a moon.

```
        change_orbit = @base_moon_distance

        moon = {
            count = { min = 0 max = 1 }
            class = pc_molten
            orbit_distance = 5
            orbit_angle = { min = 90 max = 270 }
        }
    }

```

* "change\_orbit = @base\_moon\_distance" here adds 10 units of distance between the planet and its moon as indicated in the @ variable.
* "count = { min = 0 max = 1 }" to randomize the number of moons between 0 and 1. You can go as high or as low as you want with this, though usually in vanilla you won't find more than 1 moon unless the planet is a gas giant.
* "class = pc\_molten" (for a  Molten World).
* "orbit\_distance = 5" (for a total of 15, including the "change\_orbit" above).
* "orbit\_angle = { min = 90 max = 270 }", for the same randomized orbit angles as used in the planet itself.

```
    change_orbit = 30

    planet = {
        count = { min = 1 max = 4 }
        class = pc_asteroid
        orbit_distance = 0
        orbit_angle = { min = 90 max = 270 }
    }

```

Time to fill out the asteroid belt. We use "@change\_orbit" again, this time keeping a close eye on the number used so that the total number of space used matches the 70 defined in the asteroid belt at the top of the file. Now we add the asteroids, making sure that "orbit\_distance = 0" for all of them. We want them inside the asteroid belt, after all! "orbit\_angle" is a little tricky here: since the asteroids are all in the same orbit, it's possible that they might clip together. You can get away from that by directly choosing their specific orbit angles.

If your asteroid belt is icy rather than rocky, use "pc\_ice\_asteroid" instead of "pc\_asteroid". If it's crystalline, use "pc\_crystal\_asteroid" or "pc\_rare\_crystal\_asteroid".

```
    planet = {
        count = { min = 3 max = 6 }
        orbit_distance = 20
        orbit_angle = { min = 90 max = 270 }

        change_orbit = @base_moon_distance

        moon = {
            count = { min = 0 max = 1 }
            orbit_distance = 5
            orbit_angle = { min = 90 max = 270 }
        }
    }

```

More planets! We'll leave "class = " out for these ones, allowing them to spawn as whatever the game engine wants them to be (based on the criteria spelled out in *common/star\_classes*). The number of planets is also randomized between 3 and 6, each of which has 0 to 1 moons.

```
    neighbor_system = {
        trigger = {
            num_guaranteed_colonies >= 1
        }
        distance = { min = 10 max = @distance }
        initializer = "sol_neighbor_t1_first_colony"
    }
}

```

* "neighbor\_system" tells the game to spawn another, specific system within a certain distance of this system, and under what criteria. Pretty self-explanatory.

### Binary and Trinary Systems

Some star systems have two or three suns, respectively. Such systems can be pretty complex in real life (just ask Mintaka!) but generally there are two types of binary/trinary systems, classified as "P-Type" and "S-Type".

#### P-Type

P-Type (or "circumbinary") systems have planets that orbit the barycenter of both (or all three) stars simultaneously. Sirius is an example of this kind of system, as is Tatooine in the Star Wars universe.

P-Types are the easiest to make in Stellaris. Instead of a single star with "orbit\_distance = 0", do the following:

```
    planet = {
        class = star
        size = { min = 30 max = 35 }
        orbit_distance = 15
        orbit_angle = { min = 90 max = 270 }
    }
    planet = {
        class = star
        size = { min = 25 max = 30 }
        orbit_distance = 10
        orbit_angle = 180
    }

```

Then just add your planets as normal, change the star class to *rl\_binary\_stars* or *rl\_trinary\_stars* (or a specific class of either), and you're good to go!

* Using "class = star" for both/all three stars will assign their class in accordance with the type of binary/trinary system: with "sc\_binary\_1", for instance, the first star will be A-class, and the second will be a Pulsar.
* If it's a trinary instead of a binary:
	+ Add a third star in the same manner.
	+ Change the second star's orbit angle to 145.
	+ Give the third star an orbit angle of 70, an orbit distance of 0, and a maximum size of 25.
* The orbit distances, orbit angles, and star sizes are entirely up to you; the values given above just look the nicest in my opinion. Feel free to experiment!

#### S-Type

In S-Type systems, each star has its own system of planets. Think of them as two or three unary systems that are close enough together that you can travel between them without needing a hyperdrive. 40 Eridani is an example of such a system, as is Vulcan in the Star Trek universe.

To recap, a unary system with one sun has basically this structure:

```
    planet = {
        class = star
        size = { min = 25 max = 30 }
        orbit_distance = 0
        orbit_angle = 1
    }

    planet = {
        count = { min = 3 max = 6 }
        orbit_distance = 20
        orbit_angle = { min = 90 max = 270 }

        change_orbit = @base_moon_distance

        moon = {
            count = { min = 0 max = 1 }
            orbit_distance = 5
            orbit_angle = { min = 90 max = 270 }
        }
    }

```

The structure of an S-type star and its planets looks more like this:

```
    planet = {
        class = star
        size = { min = 25 max = 30 }
        orbit_distance = 0
        orbit_angle = 1
        satellite_naming_policy = upper_case_roman_numerals

        planet = {
            count = { min = 3 max = 6 }
            orbit_distance = 20
            orbit_angle = { min = 90 max = 270 }
            satellite_naming_policy = lower_case_latin_letters

            change_orbit = @base_moon_distance

            moon = {
                count = { min = 0 max = 1 }
                orbit_distance = 5
                orbit_angle = { min = 90 max = 270 }
            }
        }
    }

```

See the differences?

* In a unary system, the planets are all defined in the system scope. In an S-type binary or trinary system, the planets are defined ***in their specific star's scope***. This ensures that they will have the correct orbit lines and names.
* "satellite\_naming\_policy" indicates how this star's planets (or this planet's moons) will be named. A star should use "upper\_case\_roman\_numerals" for its planets; planets should use "lower\_case\_latin\_letters" for their moons.

A full S-type trinary system would look like this:

```
    planet = {
        class = star
        size = { min = 30 max = 35 }
        orbit_distance = 0
        orbit_angle = 1
        satellite_naming_policy = upper_case_roman_numerals

        planet = {
            count = { min = 3 max = 6 }
            orbit_distance = 20
            orbit_angle = { min = 90 max = 270 }
            satellite_naming_policy = lower_case_latin_letters

            change_orbit = @base_moon_distance

            moon = {
                count = { min = 0 max = 1 }
                orbit_distance = 5
                orbit_angle = { min = 90 max = 270 }
            }
        }
    }

    planet = {
        class = star
        size = { min = 25 max = 30 }
        orbit_distance = 230
        orbit_angle = { min = 90 max = 270 }
        satellite_naming_policy = upper_case_roman_numerals

        planet = {
            count = { min = 3 max = 5 }
            orbit_distance = 20
            orbit_angle = { min = 90 max = 270 }
            satellite_naming_policy = lower_case_latin_letters

            change_orbit = @base_moon_distance

            moon = {
                count = { min = 0 max = 1 }
                orbit_distance = 5
                orbit_angle = { min = 90 max = 270 }
            }
        }
    }

    planet = {
        class = star
        size = { min = 20 max = 25 }
        orbit_distance = 0
        orbit_angle = { min = 90 max = 270 }
        satellite_naming_policy = upper_case_roman_numerals

        planet = {
            count = { min = 2 max = 4 }
            orbit_distance = 20
            orbit_angle = { min = 90 max = 270 }
            satellite_naming_policy = lower_case_latin_letters

            change_orbit = @base_moon_distance

            moon = {
                count = { min = 0 max = 1 }
                orbit_distance = 5
                orbit_angle = { min = 90 max = 270 }
            }
        }
    }

```

* The second star, Star B, orbits 230 units away from the first, Star A. This is because we don't want the two systems to overlap. Star A has up to six planets, each orbiting at a distance of 20, which gives us a maximum system width of 120. Star B has up to five, giving it a maximum width of 100. 120 + 100 = 220, so we use 230 to give them a little extra space.
* The third star, Star C, orbits at the same distance from A as B but at a different angle. This isn't a hard requirement, you can space it out further if you want, but all the vanilla systems do it that way and it just looks nicer in my opinion. We don't want systems to be *too* big, or it'll take forever to cross them at sublight speeds.

### Usage

One thing I haven't gone over is usage -- how the game knows which system to generate. There are a few ways to do this, applied in the system scope.

* "usage = misc\_system\_init" tells the game to randomly generate this system somewhere in the galaxy, based on the value entered for "usage\_odds". You can also use modifiers for these if you want the system to spawn (or not) in a certain area of the galaxy. There's also "max\_instances = " to ensure that you don't get more than X instances of these systems. For example:

```
usage = misc_system_init
usage_odds = {
    base = 2000000
    modifier = {
        factor = 0
        is_bottleneck_system = yes
    }
}
max_instances = 1

```

* "usage = empire\_init" tells the game that this system is an empire homeworld, which either the player or the AI can end up with if they choose "Random" in the empire creation screen. If you use this, make sure the system has a planet with the following:

```
planet = {
    home_planet = yes
    init_effect = {
        generate_empire_home_planet = yes
        prevent_anomaly = yes
    }
}

```

* "usage = custom\_empire" is mostly the same as above, except it tells the game that this system is a *prescripted* empire homeworld which can be chosen in the empire creation screen, like Sol and Deneb. Make sure to give the system a name and description via localisation.

* If no usage value is used, or if "usage\_odds = 0", the system will never spawn in the galaxy unless specifically called to by an event or another external means.

### General Advice

* Remember when I said that using "class = pc\_b\_star" is just as good as "class = star" most of the time? That's true in unary systems, but it can lead to problems with planet and star names in binary or trinary systems. If you want to hard-code a specific star class regardless of the system class, use "class = star" and then add "init\_effect = { change\_pc = pc\_b\_star }" underneath.
* The same as above goes for giving planets specific names. If you name the third planet in the system "Joe", the fourth planet will use "StarName III" in its place. You can get around this by using "init\_effect = { set\_name = "Joe" }" instead.
* All planets, ships, etc. in a system will always be lit from the system's center regardless of the location of the star(s). The devs are working on a fix for this but until then we just have to live with it.
* Asteroids and Ringworld sections don't have visible orbital lines by default, but if they're placed in the star's scope (such as in an S-type binary system) they will have lines, which looks kind of messy. Make sure you place them in the system scope whenever possible.
* And speaking of Ringworlds, their individual segments always face the center of the system regardless of which star they orbit.
* Please note that "orbit\_distance" does ***not*** default to 0! If you want it to orbit at the same distance as the previous planet or change\_orbit, be sure to include "orbit\_distance = 0"! Otherwise the orbit distance will be randomized, the range of which is not yet known.

**Modding**