Anomalies modding

From Stellaris Wiki
Jump to navigation Jump to search


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

This article is for the PC version of Stellaris only.

Anomalies are special events that have a chance of triggering whenever your science ship surveys a stellar body. Anomalies consist of two parts:

  • Anomaly Categories
  • Anomaly Events

Which are stored in the following directories, respectively:

  • Stellaris\common\anomalies\
  • Stellaris\events\

An important distinction between the two is that anomaly categories define anomalies that are initially discovered by science ships after surveying a celestial body. These appear in the 'Anomalies' tab of the Normal sit log.png situation log. On the other hand, anomaly events are fired once a science ship finishes Fleet task survey system.png investigating an anomaly.

Anomaly spawn mechanics[edit | edit source]

The Stellaris anomaly spawning system is hardcoded into the game, and therefore there are no script files from which to reverse-engineer the system. Nonetheless, it is possible to gain insight into how it works at a higher level through experimentation in-game. The following is an attempt to characterize the anomaly spawning system based on what information is available in the game files, player experiences, and some speculation.

Every time a celestial body is surveyed for the first time, the game rolls to determine if an anomaly will be spawned. This can only happen the first time said body is surveyed by any empire, either player or AI. The base chance of this check passing is 5%, and each time it fails, the chance increases an additional 0.5%. This is set and can be modified in Stellaris\common\defines\defines00.txt:

ANOMALY_SPAWN_CHANCE				    = 0.050 # Percent chance that a planet gets a discovery when surveyed
ANOMALY_SPAWN_CHANCE_INCREMENT			= 0.005 # Percentage increase towards next chance when failing

If this check succeeds, then the game will randomly pick an anomaly category to spawn from the weighted list of anomaly categories. Only anomaly categories for which all of the following is true can be selected:

  1. Does not currently exist elsewhere (i.e. if it has been spawned before it has been investigated and it has triggered an anomaly event).
  2. Can be spawned for the empire type: player or AI. Determined by the should_ai_use and should_ai_and_humans_use fields.
  3. max_once = yes and this anomaly has never been spawned by the owner of the science ship.
  4. max_once_global = yes and this anomaly has never been spawned.
  5. spawn_chance = {} does not evaluate to 0.
  6. An anomaly event exists in on_success = {} that can be triggered. This depends on the anomaly event's max_once and max_once_global fields and whether or not it has been triggered before.

Finally, one last check is performed against the null_spawn_chance field. If this check succeeds, the anomaly will not spawn. It is unclear if the anomaly spawn system will then go and grab another anomaly category from the weighted list if this check succeeds. Nonetheless, if this last check fails, then the anomaly event will be spawned.

There are three important takeaways from this. First, due to point #1 above, if an empire spawns an anomaly and does not research it immediately, it cannot spawn again until they do. Therefore, if you have multiple anomaly events tied to that anomaly category, all of those events are effectively being held hostage until the empire investigates the anomaly. Hypothetically, suppose the empire is destroyed before it can investigate the anomaly. In that case, this could lead to a situation where all anomaly events associated with that anomaly category become permanently inaccessible for the remainder of that game.

Second, because anomalies can only spawn on celestial bodies that have never been surveyed, it isn't easy to thoroughly test custom anomaly categories using the survey console command. This command will survey all bodies in the galaxy, and as a consequence, will most likely spawn every possible anomaly, including your custom anomaly. However, because everything has now been surveyed, it will no longer be possible to investigate your custom anomaly and then try to get it to spawn again, should you have set it up such that it is able to.

Third, by default, most anomaly categories can only spawn for player empires to keep AI empires from stealing them all. This is controlled by the should_ai_use and should_ai_and_humans_use fields. AI empires are instead given access to much less narratively stimulating but functional AI events, found in Stellaris\common\anomalies\99_anomaly_categories_ai.txt.

Anomaly categories[edit | edit source]

An anomaly category defines the name and description of an anomaly as seen in the situation log, the likelihood of the anomaly appearing, any restrictions on when and where the anomaly can appear, and the events that can occur after investigating it. Anomaly categories used to be split up into two files, but this changed in 2.1.[1] Thorough documentation of proper anomaly category formatting and all possible configuration options was kindly provided in the game files at Stellaris\common\anomalies\readme.txt:

an_anomaly_category = {				   # Anomaly category ID key

    should_ai_use = yes/no			   # Allows AI empires to generate the category. Default: no
	should_ai_and_humans_use = yes/no  # If yes, both AI and human empires can use this anomaly (overrides should_ai_use)

	desc = "key"					   # Optional, if no desc is given "<category key>_desc" is assumed

	desc = {					 	   # Can also use triggered descs. First valid entry will be used.
		trigger = { ... }			   # Scope: planet, from = ship
		text = "key"				   # Localization key for description
	picture = GFX_picture			   # Picture displayed in category window
	level = int						   # Anomaly level, 1 to 10

	null_spawn_chance = 0.5			   # Default 0. 0.0 - 1.0 (0 to 100%) chance category will NOT spawn
									   # even if it is picked by the anomaly die roll. Used to make
									   # categories for unusual objects (e.g. black holes) actually rare.

	max_once = yes/no				   # default NO, if true will spawn category only once per empire
	max_once_global = yes/no		   # default NO, if true will spawn category only once per game

	spawn_chance = {				   # Chance for this anomaly category to spawn,
		base = <num>				   # relative to other valid categories. Default: base = 0
        modifier = {				   # Spawn chance modifier
			add/factor = <num>
			<triggers>				   # Scope: planet, from = ship

	on_spawn = { <effects> }		   # Executes immediately when anomaly category is spawned.
									   # Scopes are this/root: planet, from: ship
									   # NOTE: on_spawn effects will not run if category is spawned through console

	on_success = {					   # Picks anomaly event to fire; similar to random_list
		1 = {						   # Base chance
			max_once = yes			   # Individual outcomes default to max_once = yes,
			max_once_global = no	   # and max_once_global = no
            modifier = {			   # Optional modifiers
				add/factor = <num>
				<triggers>			   # Scope: ship, from: planet
			anomaly_event = <id>	   # New effect anomaly_event fires specified event ID. Scope: ship, from: planet
		}							   # Can also use ship_event, though it gets different scopes:
									   # ship, from: ship, fromfrom: planet

		1 = <event id>				   # shorthand for 1 = { anomaly_event = <event id> }

	on_success = <event id>			   # Shorthand for on_success = { 1 = { anomaly_event = <event id> } }
}									   # Only use if there is only one outcome in the category

Example[edit | edit source]

Consider, for instance, you wish to create an anomaly that only occurs on asteroids (i.e., it can only spawn when surveying an asteroid). Your first step will be making a new anomaly category file in your mod at common\anomalies\. You can name the file whatever you want, but let's call this one my_asteroid_anomaly_category.txt. Your initial skeleton would look like this:

an_anomaly_category = {
    should_ai_use = 

	desc = 
	picture = 
	level = 

	null_spawn_chance = 
	max_once = 
	max_once_global = 

	spawn_chance = {
		base = 1

	on_success = {

First you want to define the name of the anomaly category. This must be unique, and the best practice is to use a common prefix across all files and objects you create for any mods you make. In this case we'll go with my_asteroid, which makes the name of your anomaly category my_asteroid_category. Next, set the should_ai_use field to no, as we don't want the pesky AI stealing your narrative masterpiece from the player! The desc field is where you reference a localization key for the description that appears in the situation log; let's call it "my_asteroid_category_desc". Similarly, the picture field is where you reference a event image that will appear in the situation log; let's use GFX_evt_asteroid_field, since this is an asteroid event. Finally, pick an appropriate difficulty level for the anomaly and fill in the level field. This is just a simple asteroid anomaly, so let's go with 3.

The next set of fields relate to when your anomaly can spawn. The first one, null_spawn_chance, is used to create a chance the anomaly won't spawn even if the anomaly spawn system picks it. This is meant more for making anomalies rarer, so let's just set it to 0 to disable it. The next two fields, max_once and max_once_global, specify whether or not your anomaly can spawn more than once. Normally you would want to set max_once to yes for more generic anomalies with a single outcome, and max_once_global to yes for narratively unique anomalies that have ramifications extending beyond the empire discovering it. In this case, you want to have several outcomes of this same event, so let's set both to no. Finally, the spawn_chance block determines the weighted chance your anomaly will spawn. This is where we can ensure your anomaly only spawns on asteroids by changing the base chance to 0 and adding a modifer block to add 1 when the body is an asteroid.

Finally, you can use the on_success block to define the possible events you want to be able to fire when the anomaly is investigated. In this case, you want to define three events, with the first having twice the chance of happening compared to the other two. This is achieved by setting a base chance of 2 for the first event and 1 for the other two events. You also don't want the same player to see these events multiple times, so you set the max_once field of each event to yes. These events aren't so unique it wouldn't make sense for other empires to encounter them though, so you set max_once_global to no.

Your anomaly category code now looks like the following:

my_asteroid_category = {
    should_ai_use = no

	desc = "my_asteroid_category_desc"
	picture = GFX_evt_asteroid_field
	level = 3

	null_spawn_chance = 0
	max_once = no
	max_once_global = no

	spawn_chance = {
		base = 0
        modifier = {
            add = 1
            is_asteroid = yes

	on_success = {
        2 = {
			max_once = yes
			max_once_global = no
			anomaly_event = my_asteroid_event.1
        1 = {
			max_once = yes
			max_once_global = no
			anomaly_event = my_asteroid_event.2
        1 = {
			max_once = yes
			max_once_global = no
			anomaly_event = my_asteroid_event.3

my_asteroid_event.1, my_asteroid_event.2, and my_asteroid_event.3 are the event IDs of your three anomaly events, one of which will be shown in the continuiation of this example in the next section.

Anomaly events[edit | edit source]

Anomaly events are identical to standard events. The unique part about them is that anomaly categories trigger them, and they must be ship events. Please visit the events page for a more detailed look at events.

Example[edit | edit source]

Continuing the prior example, the following is an implementation of the anomaly event my_asteroid_event.1. This code will need to reside in your mod's events\ directory. As before, you can name the file whatever you want, but let's call this one my_asteroid_event.txt. We won't go into much detail this time, but there are some important things to note. First, anomaly events have the context root = ship, from = planet. Second, anomaly events exclusively set is_triggered_only to yes, as they never trigger themselves. Third, it is common to use the immediate block to clean up the celestial body the event is triggering on. In our example, we want to reward the player with either an engineering deposit or a mineral deposit, so we want first to remove any pre-existing deposits on the asteroid.

Your anomaly event code would therefore look like the following:

namespace = my_asteroid_event

ship_event = {
	id = my_asteroid_event.1
	title = ""
	desc = "my_asteroid_event.1.desc"
	picture = GFX_evt_mining_station
	show_sound = event_ship_bridge
	location = from

	is_triggered_only = yes

	immediate = {
		from = { clear_deposits = yes }

	option = {
		name = "my_asteroid_event.1.a"
		from = {
			set_deposit = d_engineering_2
    option = {
		name = "my_asteroid_event.1.b"
		from = {
			set_deposit = d_minerals_5

References[edit | edit source]

Empire EmpireEthicsGovernments • Civics • OriginsMandatesAgendasTraditions • Ascension perksEdictsPoliciesRelicsTechnologiesCustom empires
Pops JobsFactions
Leaders LeadersLeader traits
Species SpeciesSpecies traits
Planets PlanetsPlanetary feature • Orbital depositBuildings • DistrictsPlanetary decisions
Systems SystemsStarbasesMegastructuresBypassesMap
Fleets FleetsShips • Components
Land Warfare ArmiesBombardment stance
Diplomacy Diplomacy • Federations • Galactic communityOpinion modifiersCasus Belli • War goals
Events EventsAnomaliesSpecial projectsArchaeological sites
Gameplay GameplayDefinesResources • EconomyGame start
Dynamic modding Dynamic moddingEffectsConditionsScopesModifiersVariablesAI
Media/localisation Maya exporterGraphicsPortraitsFlagsEvent picturesInterfaceIconsMusicLocalisation
Other Console commandsSave-game editingSteam WorkshopModding tutorial