Event modding

From Stellaris Wiki
Jump to navigation Jump to search

Version

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

This article is for the PC version of Stellaris only.

Event types[edit]

There are seven types of events:

  • event - event for an entire game
  • country_event - event for an entire empire
  • planet_event - event for a planet
  • fleet_event - event for a fleet
  • ship_event - event for a ship
  • pop_faction_event - event for a faction
  • pop_event - event for a population unit
  • observer_event - event for the observer (development usage only; use console command "observe" to enter observer mode)

Basic Behavior[edit]

Namespace and Event ID[edit]

The top of every event file must contain a namespace line. The namespace is used as the basis for identifying all events in the file. If you open the on_action_events.txt file near the top of the file you will find namespace = action. Then every event in this file has an ID that starts with "action" followed by a period then a unique number as shown here for the actions.8 event:

country_event = {
	id = action.8
	hide_window = yes
	is_triggered_only = yes
	
	trigger = {
		is_country_type = default
		FROM = { is_country_type = default }
		NOT = { has_communications = from }
		is_hostile = from
	}
	
	immediate = {
		establish_communications = from
		fromfrom = {
			conquer = root
			set_controller = root
		}
 	}
}

Anywhere in the game that needs to call this event will use the ID property.

An event file can have any number of namespace.

Event id can't have letters, or it will be recognized as "namespace.0".

Execution[edit]

By default every event has all its triggers checked against every game object at least once per game day, possibly once per game tick. For the objects where the triggers are all met, the code of the event is executed with the scope of said object.

However, events should usually not be run in this fashion, as it is very expensive on performance. Instead, an event should either use is_triggered_only = yes, which will exempt the event from this persistent polling, and trigger the event from elsewhere; or it should use mean_time_to_happen, which increases the intervals at which the event is checked. See below for more details on both of these processes.

Similarly works fire_only_once = yes. However unlike is_triggered_only the code will still be polled, only to be removed after it was executed at least once. Also, as of 2.1.2, the conditions for a fire_only_once event should exclude the event firing a second time or error log entries are created.

Condition[edit]

Events can have conditions represented with trigger section of Condition statements . If the condition is evaluated false, this event doesn't trigger. Here is an example from the event "apoc.1".

trigger = {
	owner = {
		NOT = { has_country_flag = encountered_first_gateway }
	}
	FROM = {
		has_star_flag = abandoned_gateway
		any_system_megastructure = { is_megastructure_type = gateway_ruined }
	}
}

Planet events may also have pre_triggers section that is evaluated before the regular conditions and only a few Y/N statements can be used here. Use this to improve performance related to planet events. Here is an example given in "000_added_pre_triggers_to_planet_events.txt".

pre_triggers = {
	has_owner = yes
	is_homeworld = no
	original_owner = yes
	is_ai = no
	has_ground_combat = no
	is_capital = no
	is_occupied_flag = no
}

Visibility[edit]

By default every Event will display a window and thus needs at least one option and a number of textfields to display. In order to suppress that window (and the need to set all the necessary text fields) hide_window = yes can be used. This is mainly used to trigger events that are used for achievements or events that the player should not know are happening.

Code execution[edit]

The primary place for code is the immediate section of Effect statements. Most events have all their code contained inside this block. You can use if statement in immediate section, like the following, as seen in colony.events.txt.

immediate = {
	if = {
		limit = {
			event_target:subterranean_nation = {
				NOT = { has_country_flag = tech_request_approved }
			}
		}
		create_army = {
			name = "NAME_Invading_Horde"
			owner = event_target:subterranean_nation
			species = event_target:subterranean_species
			type = "industrial_army"
			leader = last_created_leader
		}
	}
}


However visible events need at least one option (the OK Button of a message box). Every option can carry its own code. See Options for details.

Scope is a very important consideration for any code executed in any event and it is heavily based on how the event was called.

Mean Time To Happen[edit]

A additional condition very often used on event that triggered by regular polling, is the mean_time_to_happen (MTTH). This will delay the execution of the event by a random amount of ingame days. On average the activation will be delayed as given, but the exact number can vary considerably. Counting starts the moment all other trigger conditions are met and it is not clear how exact the internal implementation of this delay works, however it seems likely the MTTH is regularly polled just like all other triggers.

  • mean_time_to_happen = { months = 5 } or mean_time_to_happen = { days = 15 }

The MTTH can be modified with any number of conditional modifiers and it seems such things can take effect after the MTTH counting has begun.

Testing has shown that a simple MTTH will be called with a 50% chance during the period of time specified for each item in that scope e.g. each country for a country event. It will then check the triggers, and if they are true, the event will be triggered. However, if modifiers to the MTTH are introduced, the game will check the conditions every day for each scope - meaning that these are considerably more expensive in terms of performance.

Calling events from other Events[edit]

It is possible to manually call a Event onto any game object from a running event. Doing so will put the event into the game objects scope. There might be a slight delay of at least one or more game ticks until the event is actually called. For example:

random_planet = {
    planet_event = { id = my_planet_event.1 }
}

While doing this the triggering can be delayed by any fixed or semi-fixed timeframe. For example country_event = { id = crisis.2000 days = 200 random = 100 } delays it by 200–300 days.

As the event attempts to execute, if its conditions are not met, it doesn't execute. If the event is delayed, the conditions are checked only before execute, but not before queuing up.

On Actions[edit]

Aside from the default polling another very common way to get an event triggered is on_action. The vanilla game itself has a number of events registered this way in Stellaris\common\on_actions\00_on_actions.txt

Mods can provide their own, uniquely named, on_action file whose events are called in addition the vanilla events. The file should be put into Stellaris\common\on_actions\, but the Modding Guidelines should be observed

The situations of triggering range from polling less aggressive (monthly or yearly) to numerous developments of the galaxy like ending of a planetary invasion, survey, entering of a system and so forth. This is later part is comparable to registering an Event in a GUI Environment of many higher programming languages.

Registered events should be marked with "triggered only" modifier. As easily dozens of events can be registered to any one development (often belonging to the same chain) the triggers decide which events are actually called.

A special case is random events which have a specific chance to trigger anytime that development happens. However, the triggers can make this a lot rarer than even chance might indicates.

Aside from on_actions, events can be triggered from any file with an effects field. Most notable are anomalies, but one can similarly trigger an event from e.g. within an edict (it will then trigger every time the edict is activated), policy, diplomatic action, etc.

Conditional Description[edit]

The description of an event may change based on conditions. It uses the following syntax as seen in colony_events.txt.

planet_event = {
 	id = colony.182
	title = "colony.182.name"
	desc = {
		trigger = {
			owner = { NOT = { has_authority = auth_machine_intelligence } }
		}
		text = colony.182.desc
	}
	desc = {
		trigger = {
			owner = { has_authority = auth_machine_intelligence }
		}
		text = colony.182.desc.mach
	}
       ...
}

If multiple triggers match, only one of them will be shown at random. If none of the triggers match, the system will show the first description anyway.

Conditional description and static description cannot be used together. For instance

planet_event = {
 	id = colony.182
	title = "colony.182.name"
	desc = {
		trigger = {
			owner = { NOT = { has_authority = auth_machine_intelligence } }
		}
		text = colony.182.desc
	}
	desc = colony.182.desc.mach
       ...
}

will always show colony.182.desc.mach.

Options[edit]

All visible events need at least one Option (comparable to the "Ok" Button of a Message Box). Options themself have numerous variables that can be set.

The after block has to be written as a peer of the options, it is executed after an option is chosen, regardless which option was chosen. That makes it comparable to a finally block in many error handling systems of a language like Java.

name defines the display name of the option. It is the only value somewhat mandatory. Often a localiseable string is used here. There are numerous default strings that can be used here wich are already localised.

trigger defines if the option is shown at all. If it's not shown, so is it unchoosable.

default_hide_option default_hide_option = yes will hide the option, unless it is the only option avalible.

allow defines if the option is choosable. A option can be both shown and not choosable, often to show wich options will become avalible with specific play. This subblock is comparable to the "Enabled" value or property in many GUI environmnets. The check is done only when the Event window is first shown and not updated with game progress.

Effect statements can be put into the body of the option. No special block must be put around it.

The game tries to generate a tooltip for this option based on all the effects. custom_tooltip = xxx can be used to show the player a tooltip in addition to the generated tooltips. Also, hidden_effect = { ... } can be used to contain other effects to prevent the game from generating tooltips based on them.

option = {
	name = "xxx"
	custom_tooltip = "yyy"
	hidden_effect = {
		(Effect statements to be executed when this event is chosen)
	}
}

ai_chance use this subblock to allow an AI to do a semi-random decision between all available options, to make some choices more likely to be picked by the AI. Here is an example from a War in Heaven event.

ai_chance = {
	factor = 100
	modifier = {
		factor = 0
		OR = {
			has_valid_civic = civic_hive_devouring_swarm
			has_valid_civic = civic_fanatic_purifiers
			has_valid_civic = civic_machine_terminator
		}
	}
}

Best Practices[edit]

Triggering[edit]

Hide Window and Triggered Only are the two most common settings for events, with the bulk of vanilla events having either or even both of them. The MTTH is also a very common setting on any event that is using polling to add some randomness to the game where it seems useful.

Due to a possible massive CPU load load regular polling should be avoided unless absolutely nessesary. The prefered way to get a series of events started initially is either via a gatekeeper event that does use regular polling with early failing triggers, or by having it triggered by a on_action development. A combination of on_action developments that lead to gatekeeper events may also be useful.

Chaining Events, Scope, Execution Order[edit]

The vanilla code often has long chains of events calling one another indicating that this chaining might be nessesary for proper execution and scope setting.

In particular a pattern where there is a hidden Event is calling a visible event is extremely common. The hidden event often doing actually (setup) work. This indicates that even "Immediate" code needs a least until the next event is called to take effect past any internal caching.

Example Events[edit]

Visible Events (Robot Rebellion, 1.6.2 version):

# Servant AI Perfected
country_event = {
	id = crisis.2192
	title = crisis.2192.name
	desc = crisis.2192.desc
	picture = GFX_evt_robot_assembly_plant
	show_sound = event_laboratory_sound
	location = root

	is_triggered_only = yes

	immediate = {
		set_country_flag = robots_pacified
	}

	option = {
		name = crisis.2192.a
		custom_tooltip = crisis.2192.a.tooltip
		hidden_effect = {
			set_country_flag = ai_perfect_servants
		}
	}

	option = {
		name = crisis.2192.b
		hidden_effect = {
			country_event = {
				id = crisis.2000
				days = 400
				random = 400
			}
		}
	}
}

Invisible Events (Prethoryn Crisis 1.8.3 version):

# WARNING: May cause galactic mass extinction and/or loss of appetite
country_event = {
	id = crisis.199 ''# Event ID''
	hide_window = yes ''# Makes the event run without the player knowing, essential because this is a trigger event.''

	trigger = { always = no } ''# Makes event only trigger when called on (I Think)

	immediate = {
		set_global_flag = prethoryn_invasion_happened ''# Global Flags are created to tell the game that the crisis is happening''
		set_global_flag = prethoryn_transmission
		begin_event_chain = {
			event_chain = "coming_storm_chain"
			target = ROOT
		}
		random_rim_system = {
			set_star_flag = swarm_invasion_target_1
			save_event_target_as = prethoryn_invasion_system
		}
		create_point_of_interest = {
			id = coming_storm_poi.1
			name = "coming_storm_poi_1_poi"
			desc = "coming_storm_poi_1_poi_desc"
			event_chain = "coming_storm_chain"
			location = event_target:prethoryn_invasion_system
		}
		country_event = { id = crisis.17 days = 10 }
	}
}

Example event with all parameters[edit]

# Specifies the type of the event. 
country_event = {
	#Unique ID for your event. Must match namespace parameter
	id = example.1
	
	#Specifies localization string for the title (Header) of the event
	title = example.1.name
	
	#Specifies localization string for event text that describes what's happening.
	#Multiple is allowed; random one will be shown.
	desc = example.1.desc
	
	#Descriptions can be conditonal
	desc = {
		text = example.1.desc.conditional #Localization string
		trigger {
			#Conditions for this to be avalable.
			#For example: has_authory = auth_machine_intelligence
		}
	}
	
	#a name of a picture to display. Pictures are defined at "interface/xxx.gfx".
	picture = GFX_evt_exploding_ship
	
	#A scope to the object that is relevant to the event that player can move to. For example, the planet where event is happening.
	location = from
	
	#Name of the sound clip to be played when event is shown. Sounds are defined in "sound/xxx.asset".
	show_sound = event_ship_explosion
	
	#If event is not meant to be seen or there's no person to see. Makes title and desc unnecessary.
	#Service event that runs some sort of routine or prepares grounds for other events should have this.
	hide_window = yes
	
	#Makes event look like diplomatic communications. First contact events use this.
	diplomatic = yes
	
	#An optional setting that changes the looking of this event window, if "diplomatic = yes".
	custom_gui = "enclave_curator_option"
	
	#Specifies picture for diplomatic event. Most options are optional here
	picture_event_data = {
		#Animated portrait. Accepts country, leader or species scope as an input
		portrait = event_target:contact_empire
		
		#Planet background, if your picture has a window
		planet_background = event_target:contact_empire
		
		#City graphic type on the planet in the window
		graphical_culture = event_target:contact_empire
		
		#The size of the city. Usable to make planet behind look like capital
		city_level = event_target:contact_empire
		
		#Static background. Can use static pictures or scopes as the input
		room = event_target:contact_empire.ruler
	}
	
	#This event will not fire itself. It must be called by another event or an on_action.
	#Most events will use this.
	is_triggered_only = yes
	
	#Makes the event happen only once per game
	fire_only_once = yes
	
	#The event be considered for starting with daily probability calculated so on average it would happen in time specified.
	mean_time_to_happen = {
		#To specify average time for the event to fire. "months" or "days" are also acceptable input.
		years = 100
		
		#Mean time to happen can be conditionally modified
		modifier = {
			#Multiply MTTH by number specify. Here it will make this event trigger in a mean time of 10 years.
			factor = 0.1
			
			#Condition statements
		}
	}
	
	#If neither is_triggered_only or MTTH is set, the event will trigger every day the conditions are met.
	#So don't forget them, lest you might affect the entire galaxy or see event trigger again and again forever.
	
	#Trigger block contains conditions. The event will not start if conditions inside aren't met.
	#Used for events on mean_time_to_happen or for events that called from situation where
	#you can't or want specify conditions for it to happen, before calling
	#(For example an event in on_action block or a delayed event that might be blocked by other events)
	trigger = {
		#Condition statements
	}
	
	#Effects that are applied the moment event fires. Can be effects that can't wait like
	#setting a flag to prevent other events, or the undesirable effects you don't want player to be able to delay
	#(For example, killing science ship's captain.) This is also the only block you'll need on hidden events
	immediate = {
		#Effect statements
	}
	
	#The button under the event, allowing player to pick a reaction.
	#Any event that is not hidden, needs at least one. Multiple can be specified.
	option = {
		#Reference to localization string with option text
		name = example.1.a
		
		#If not met, this option will be disabled and hidden.
		trigger = {
			#Condition statements
		}
		
		#If not met, this option will be disabled, but still shown.
		allow = {
			#Condition statements
		}
		
		#Effects of the event can be described here. They will generate tooltips shown when hovering over option.
		
		#This is a special Effect that it does nothing more than a customizable tooltip.
		#Static strings can be used here, but using localisation keys is recommended.
		custom_tooltip = example.1.a.tooltip
		
		#This is a special Effect that it makes the game to generate tooltips based on the Effects inside.
		#These statements have NO actual effects, just tooltips.
		tooltip = {
			#effect statements
		}
		
		#This is a special Effect that it prevents the game from generating tooltips based on the Effects inside.
		hidden_effect = {
			#effect statements
		}
	}
	
	after = {
		#Effects that are applied after option is chosen.
		#Will generate a tooltip in addition the tooptip of every option, unless hidden_effect is used.
	}
}

References[edit]


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 • Economy
Dynamic modding Dynamic moddingEffectsConditionsScopesModifiersVariablesAI
Media/localisation Maya exporterGraphicsPortraitsFlagsEvent picturesInterfaceIconsMusicLocalisation
Other Console commandsSave-game editingSteam Workshop