Event modding

From CK3 Wiki
Jump to navigation Jump to search

Events are the meat of every well-rounded mod; smaller and larger bits of story that can happen to a player during the campaign.

Scripting Tools[edit | edit source]

There are various tools capable of helping modders script events with greater ease.

Visual Studio Code[edit | edit source]

Visual Studio Code is considered to be the superior choice for modders due to the fact that it features various extensions that allow it to syntax highlight ParadoxScript.

Recommended Extensions:

Sublime Text[edit | edit source]

Sublime Text is a popular choice amongst many because it excels at handling localization files. This is a free software.

Notepad++[edit | edit source]

Notepad++ is a direct update over using regular notepad for scripting, if the two options above seem too daunting, you can start here.

Location[edit | edit source]

Events belong in a .txt file inside the events directory directly below your root mod folder. Each file can hold as many events as one would like. The events directory may also have sub-folders containing their own event files, if one prefers.

Structure[edit | edit source]

The overall structure is similar to that of a CK2 event, with some tweaks to the syntax and a whole lot of extra features, many of them optional. The barest possible event is laid out here, and each element is described individually in a later section.

namespace = example
example.1 = {
	desc = example.1.desc
	option = {
		name = example.1.a

There you go! Add this to your mod, trigger it from the in-game console using "event example.1", and you have got yourself a working event! Everything else is optional, but necessary to really flesh out the events. This is as bare-bones as it gets. Here is an example of a more fleshed out event, containing only the basics:

## This a basic event, use it as a base for other events. Though you probably will want to remove the annotation spam first.
superexample.1337 = { # Use comments (like this one!) to put the event name here, this way other scripters can find the event you are working on without knowing the ID.
	type = character_event
	title = "A Modding Example Worthy of Kings" # Protip: you can use strings and later replace it with loc refs later
	desc = birth.1003.b # For Sublime users: there is a "find in files" feature that is excellent for digging through loc

	theme = mental_break
	left_portrait = root

	option = { # Use comments to state what the option says or does (eg "No, I denounce you heretic!" or "Engage in duel against child"), much like with event titles, it's good practice.
		name = stewardship_domain_special.1424.a

ID and namespace[edit | edit source]

Namespaces can be any alphanumeric string (without the '.' character), and are used as prefix in the form <namespace>.<id>. The ID uniquely identifies your event.

If an event file uses a namespace, it has to be declared at the beginning of the file with namespace = <namespace>. This has to be done for every file the namespace is used in.

Flags[edit | edit source]

These are top-level variables that determine your event's kind and appearance. They have a limited set of values.

Flag Meaning Possible values
type The kind of event. It determines what sort of scope the root is.
  • character_event
  • letter_event
  • duel_event
  • none (when an event doesn't use the root scope at all)
  • empty (necessary for characterless events to trigger. NOTE: this means typing type = empty )
hidden Set this to true, and the event will not be shown at all; it will happen in the background. Useful for doing maintenance events that are not immediately relevant to the player. true, false

Portraits[edit | edit source]

In Crusader Kings III, portraits are now in 3D, and can now be animated as well! What follows is a list of the different portrait positions, as well as a list of animations for them.

Portrait Positions[edit | edit source]

Portrait Positions
Portrait Position Description
left_portrait Shown on the left side of the event scene.
right_portrait Shown on the right side of the event scene.
lower_left_portrait Shown on the lower left part of the event scene.
lower_center_portrait Shown on the lower center part of the event scene.
lower_right_portrait Shown on the lower right part of the event scene.

Here is an example of all of the portrait positions in use at the same time, along with a screenshot:

 1example_event.1001 = {
 2	left_portrait = {
 3		character = ROOT # Whoever this is scoped to will show up in this event window position, exhibiting the chosen animation. 
 4		animation = fear # Take note that characters with SOME genetic traits (for example, gigantism, dwarfism) that change their character models have different animations, and if you assign one of THOSE animations to a character that does not have that model, crashes may occur.
 5	}
 6	right_portrait = {
 7		character = ROOT
 8		animation = scheme
 9	}
10	lower_left_portrait = {
11		character = ROOT
12	}
13	lower_center_portrait = {
14		character = ROOT
15	}
16	lower_right_portrait = {
17		character = ROOT
18	}

Portraits can take the following parameters:

Parameter Description Example
character The character whose portrait is shown. character = scope:event_target
animation The animation that will play animation = anger
triggered_animation Plays a certain animation if the triggers are met. If not, will default to animation set with animation =
triggered_animation = {
	trigger = {}
	animation = fear
triggered_outfit Set an outfit for use in this event. (Additional Information on outfit_tags)
triggered_outfit = {
	trigger = {}
	outfit_tags = no_clothes (also accepts multiple tags, in the format outfit_tags = { tag1 tag2 }
	remove_default_outfit = yes/no
hide_info Prevents the game from showing any info on the character (tooltip, COA, clicks, etc). Only the portrait will be shown. hide_info = yes/no

Animations[edit | edit source]

Event-Compatible Animation IDs
idle chancellor steward marshal spymaster chaplain
anger rage disapproval disbelief disgust fear
sadness shame shock worry boredom grief
paranoia dismissal flirtation flirtation_left love schadenfreude
stress happiness ecstasy admiration lunatic scheme
beg pain poison aggressive_axe aggressive_mace aggressive_sword
aggressive_dagger aggressive_spear aggressive_hammer celebrate_axe celebrate_mace celebrate_sword
celebrate_dagger celebrate_spear celebrate_hammer loss_1 chess_certain_win chess_cocky
laugh lantern eyeroll eavesdrop assassin toast
toast_goblet drink drink_goblet newborn sick severelywounded
prisonhouse prisondungeon war_attacker war_defender war_over_tie war_over_win
war_over_loss pregnant personality_honorable personality_dishonorable personality_bold personality_coward
personality_greedy personality_content personality_vengeful personality_forgiving personality_rational personality_irrational
personality_compassionate personality_callous personality_zealous personality_cynical frontend_center_idle frontend_left_idle
frontend_right_idle throne_room_chancellor throne_room_kneel_1 throne_room_kneel_2 throne_room_curtsey_1 throne_room_messenger_1
throne_room_messenger_2 throne_room_messenger_3 throne_room_conversation_1 throne_room_conversation_2 throne_room_conversation_3 throne_room_conversation_4
throne_room_cheer_1 throne_room_cheer_2 throne_room_applaud_1 throne_room_bow_1 throne_room_bow_2 throne_room_bow_3
throne_room_one_handed_passive_1 throne_room_one_handed_passive_2 throne_room_two_handed_passive_1 throne_room_writer test_case_1 throne_room_ruler

Themes[edit | edit source]

A Theme is a collection of background, lighting environment for character portraits, and sound effects. They are declared in common/event_themes/.

abduct_scheme alliance bastardy battle
befriend_scheme claim_throne_scheme corruption crown
culture_change death default diplomacy
diplomacy_family_focus diplomacy_foreign_affairs_focus diplomacy_majesty_focus dread
dungeon dynasty education fabricate_hook_scheme
faith family feast_activity friend_relation
friendly generic_intrigue_scheme healthcare hunt_activity
hunting intrigue intrigue_intimidation_focus intrigue_skulduggery_focus
intrigue_temptation_focus learning learning_medicine_focus learning_scholarship_focus
learning_theology_focus love lover_relation marriage
martial martial_authority_focus martial_chivalry_focus martial_strategy_focus
medicine mental_break mental_health murder_scheme
party pet physical_health pilgrimage_activity
pregnancy prison realm recovery
rival_relation romance_scheme secret seduce_scheme
seduction skull stewardship stewardship_domain_focus
stewardship_duty_focus stewardship_wealth_focus sway_scheme unfriendly
vassal war witchcraft

Individidual elements of the theme can be overridden using override_background, override_icon, override_sound, and override_environment.

Backgrounds[edit | edit source]

alley_day alley_night armory army_camp
battlefield bedchamber burning_building corridor_day
corridor_night council_chamber courtyard docks
dungeon farmland feast gallows
garden market market_east market_india
market_tribal market_west physicians_study sitting_room
study tavern temple temple_church
temple_generic temple_mosque temple_scope terrain
terrain_activity terrain_scope throne_room throne_room_east
throne_room_india throne_room_mediterranean throne_room_scope throne_room_tribal
throne_room_west wilderness wilderness_desert wilderness_forest
wilderness_forest_pine wilderness_mountains wilderness_scope wilderness_steppe

Environments[edit | edit source]

When you've selected a background, the appropriate environment is automatically selected. Only overwrite it when necessary.

environment_body environment_council environment_cw_east_main
environment_cw_east_spouse environment_cw_east_throneroom_main environment_cw_east_throneroom_spouse
environment_cw_india_main environment_cw_india_spouse environment_cw_india_throneroom_main
environment_cw_india_throneroom_spouse environment_cw_mediterranean_main environment_cw_mediterranean_spouse
environment_cw_mediterranean_throneroom_main environment_cw_mediterranean_throneroom_spouse environment_cw_tavern
environment_cw_tavern_spouse environment_cw_tribal_main environment_cw_tribal_spouse
environment_cw_west environment_cw_west_spouse environment_event_alley
environment_event_alley_day environment_event_armory environment_event_battlefield
environment_event_bedchamber environment_event_church environment_event_corridor_day
environment_event_courtyard environment_event_desert environment_event_docks
environment_event_dungeon environment_event_farms environment_event_feast
environment_event_forest environment_event_forest_pine environment_event_gallows
environment_event_garden environment_event_genericcamp environment_event_market_east
environment_event_market_tribal environment_event_market_west environment_event_mosque
environment_event_mountains environment_event_sittingroom environment_event_standard
environment_event_steppe environment_event_study environment_event_study_physician
environment_event_tavern environment_event_temple environment_event_throne_room_west
environment_frontend_east_heir environment_frontend_east_main environment_frontend_east_secondary
environment_frontend_india_heir environment_frontend_india_main environment_frontend_india_secondary
environment_frontend_mediterranean_heir environment_frontend_mediterranean_main environment_frontend_mediterranean_secondary
environment_frontend_tribal_heir environment_frontend_tribal_main environment_frontend_tribal_secondary
environment_frontend_west_heir environment_frontend_west_main environment_frontend_west_secondary
environment_head environment_hud environment_portrait_editor
environment_shoulders environment_standard environment_torso

Trigger[edit | edit source]

This is an additional requirement for an event to work.

trigger = { # This is the set of requirements necessary for this event to enable (a gigant IF statement for the event itself)
	culture = {
		has_innovation = innovation_guilds # Checks if you have unlocked guilds on your cultural research

You can also lock certain requirements in a trigger behind a trigger of their own, using trigger_if.
The requirements inside of the trigger_if will only be checked if the contents of the limit block are true. Optionally, you can add a trigger_else afterwards to check alternative requirements if the trigger_if fails.

trigger = {
	any_held_county = { # We check that we have a blacksmith
		any_county_province = {
			has_building_or_higher = blacksmiths_01

	trigger_if = { # If our character is greedy, then we add the requirement to have 500 gold
		limit = { has_trait = greedy }
		gold > 500
	trigger_else = { # Otherwise, you must have at least 50 piety and 10 gold
		piety > 50
        gold > 10

on_trigger_fail[edit | edit source]

Runs when the trigger fails.

Description[edit | edit source]

Explain how a description can be composed of multiple strings; with stuff like first_valid, and all the alternatives.

Immediate[edit | edit source]

This is a block of effect script: it will be ran immediately as your event is triggered, before the title, description, portraits, are even evaluated let alone rendered. This block is useful for setting variables and saving scopes to use in your text or for portraits; or for functional effects that you want to happen without the player having any control over it.

"has happened" tooltip.

immediate = { # Stuff that happens when the event appears on screen, works regardless of what option you select.
	add_gold = 50 # adds 50 gold to the player 

Options[edit | edit source]

Options within events are able to be pressed by the user. Each event may have any number of options, including none at all (a common example includes hidden events). Each option in defined in the main event block, like so:

example.1 = {

	# [...]

	option = {
		# option info

	# [...]


A more complex example:

option = { # Option title
	name = stewardship_domain_special.1424.a
	trigger_event = { # Makes another event happen
		id = yearly.1012 # The event ID is the thing at the top (so stewardship_domain.6017 is valid, as is any other event, so long as it exists).
		days = { 7 14 } # Get random number between two values (unknown wether it is inclusive or exclusive), anything that takes = {X Y} can also just work as = X

	hidden_effect = { # Hides stuff from showing up on the tooltip of the option
		scope:county = { # Gets the location stored in the scope "county"
			add_county_modifier = { # To add modifiers (bonuses or penalites)
				modifier = governance_land_cleared_for_settlement_modifier # https://ck3.paradoxwikis.com/Modifier_list be sure to use one that belongs to the right type (in this case, country).
				days = 3650 # How long it lasts, you can use days = {X Y} too

	ai_chance = {
		base = 50 # What are the chances of selecting this option over others? (Does not need to be 0 to 100, it can be anything)
		modifier = {  # You can change the value based on a variety of things, in this case it is the traits of the AI character
			add = 15
			has_trait = sadistic # List of traits can be found at ..\game\common\traits\00_traits.txt
		modifier = {
			add = -40 # To remove something you just add a negative number (5 + -10 = -5)
			has_trait = compassionate

The table below describes available keys within the option block:

Key Required Description Example
name Yes Points to a localization key for the event option button text. name=example.1.a
(effects) No Any effects that the option may have can be written directly in the option block. play_music_cue = mx_cue_banquet
trigger No Defines a trigger that has to be fulfilled for the option to be valid and thus available to the user. Not to be confused with the main event trigger.
trigger = {
	has_trait = shy
show_as_unavailable No If the option is invalid, but this trigger is, the option will be shown, but disabled. This behavior is also influenced by the EVENT_OPTIONS_SHOWN_HIDE_UNAVAILABLE define.
show_as_unavailable = {
	short_term_gold < medium_gold_value
trait No If the player has the given trait, show it on the left side of the option. Hovering over it will say the option is available because of the trait. This is only providing flavor, and does not actually affect the functionality of the option.

trait = honest

skill No Show the chosen skill on the left side of the option. Hovering over it will say the option is available because of your high skill. This is only providing flavor, and does not actually affect the functionality of the option.

skill = prowess

add_internal_flag No Can take the values "special" or "dangerous". The key "special" highlights the option as yellow, "dangerous" highlights the option as red. This is only providing flavor, and does not actually affect the functionality of the option.

add_internal_flag = special

highlight_portrait No Highlights the event portrait of this character while this option is hovered. This is in addition to the automatic highlighting when hovering an event option that has an effect that affects portrait characters. highlight_portrait = scope:custom
fallback No If this is yes: if no other options meet their triggers, then this option will be shown even if its trigger is not met either. You can use this together with trigger = { always = no } to create an option that is only ever shown as a last resort. fallback = yes
exclusive No exclusive = yes
flavor No Flavor text that is shown in the tooltip of the option. The flavor can either be a loc key or a dynamic desc with first_valid etc. flavor = my_events.1001.a.flavor

After[edit | edit source]

This is a block of effect script that runs after the event has ran its course and an option has been chosen. Works the exact same as the immediate block. Won't do anything if the event has no options (for hidden events, for example).
It is most commonly used for clean-up duty, removing variables, characters, and other kinds of data that are likely to persist when not intended to.

As an example, in the event fp2_struggle.2009, "Catching Thieves of Myth", the after block is used to check if we have a saved scope (used as a boolean) to decide if we should delete the event-generated character once the event is over.

after = {
	if = {
		limit = { NOT = { exists = scope:fp2_2009_thief_permanence_scope } } # Acts as a boolean, if this exists, then it is true
		scope:fp2_2009_garduna_young_thief = { silent_disappearance_effect = yes } # We kill (delete) the young thief, as it is no longer of use for future events

Widgets[edit | edit source]

What types of widgets are there, with screenshots for each of what they look like.

On Actions (on_action)[edit | edit source]

On Actions are scripts that execute each and every time a specific action (such as a child being born, a character inheriting land or using a hook) is called by the game code.
This allows modders to intercept and run their own scripts whenever said On Actions are called.

On Actions are divided into two main types:

  1. Those that are called from code.
  2. Those that are called from script.

These on_action scripts are weird in a sense that it does not allow appending effects and triggers, making it impossible to create a mod with good compatibility. However, you can achieve a similar effect via this:

on_birth_child = { 
	on_actions = { # "On_actions" inside on_action are appended, thus can safely use the existing on_action from the table below. 
on_action_my_on_action = { 
	trigger = { ... } 
	effect = {... } 

If the script you write is like the one below, triggers and effects will not append, so conflict might happen between mods modifying the same on_actions.

on_birth_child = { # An existing on_action from the table below. 
	trigger = { ... } 
	effect = {... } # Trigger and effect are overwritten, not appended. 

Variables in on_actions[edit | edit source]

This table uses contents from /common/on_action/on_actions.info file.

Name Description Expected type Example
trigger On_actions can have triggers. If an on_action fires and its trigger returns false, nothing happens boolean
trigger = {
	trigger_conditions = yes
weight_multiplier Used to manipulate the weight of this on_action if it is a candidate in a random_on_actions list (see below) integer
weight_multiplier = {
	base = 1
	modifier = {
		add = 1
		trigger_conditions = yes
events Events listed in "events" brackets will always fire as long as their trigger evaluates to true
events = {
	delay = { days = 365 }		# A delay will mean that all events listed after it will only be fired after the delay has passed. NOTE: For performance reasons, an event will only successfully fire if it is valid both when the on_action is executed AND once the delay is complete. All firing entries support delays, whether for events or on_actions.
	delay = { months = { 6 12 } }	# Setting a new delay overrides a previous delay. Delays support random ranges
random_events A single event will be picked to fire
random_events = {	# A single event will be picked to fire
	chance_to_happen = 25	# A percentage chance determining whether the events involved will be evaluated at all

	chance_of_no_event = { 	# An entry that can be formatted as a script value (and therefore have conditional entries). Separated from "chance_to_happen" for performance reasons. Will only be evaluated if chance_to_happen is true.
		value = 0
		if = {
			limit = { trigger_conditions = yes }
			add = 10

	100 = event_id_1 	# The number is the weight for picking a specific event. The weight is factored by the event's weight_multiplier entry. (If no weight_multiplier is defined for the event, it is 1)
	200 = event_id_2
	100 = 0		# Having a "0" entry means that there is a chance no event fires, even if there are other valid events. Good for making sure that rare events don't always fire just because every other possible event is invalid.

first_valid Pick the first event for which the trigger returns true List<event>
first_valid = {		# Pick the first event for which the trigger returns true
on_actions An on_action can fire other on_actions, following the same rules as with events List<on_action>
on_actions = {	# An on_action can fire other on_actions, following the same rules as with events
random_on_actions Same as with events. On_actions are also factored by their weight_multipliers, which defaults to 1
random_on_actions = {
	100 = on_action_1
	200 = on_action_2
	100 = 0
first_valid_on_action List<on_action>
first_valid_on_action = {
effect An on_action can run effects. It can access the same default or saved scopes as the script chain/code functionality it was fired from. Note that it happens concurrently to events triggered by the on_action, NOT before. Effects run here create a separate chain than events the on_action fires, so you can for example not manipulate values in the effect, and then reliably access those in an event that was fired at the same time. Scopes or local variables set in the effect here will not carry over to any event fired by the on_action.
effect = {
	effects = yes
fallback on_actions can define a fallback on_action. If no events/on_actions are run by the on_action, the fallback gets called instead. Avoid creating infinite fallback loops, or the game may be prevented from advancing time! on_action
fallback = another_on_action

On_actions from code[edit | edit source]

Name Description From code Expected scope Other
on_prestige_level_loss Yes None
on_rank_down Yes None
on_weight_changed Yes Character
on_faith_monthly Yes Faith
on_knight_combat_pulse Yes Character
on_war_invalidated Yes None
on_war_transferred Yes Character
on_divorce Yes None
on_leave_court Yes Character
on_guest_ready_to_move_to_pool Yes Character
on_guest_arrived_from_pool Yes Character
on_siege_completion Yes Character
on_war_won_attacker Yes Casus belli
on_alliance_added Yes None
on_pregnancy_mother Yes Character
on_raid_action_start Yes None
on_county_faith_change Yes Landed Title
on_title_gain_usurpation Yes None
on_release_from_prison Yes Character
random_yearly_playable_pulse Yes Character
on_raid_action_completion Yes Army
on_death Yes Character
on_birth_father Yes None
on_betrothal_broken Yes None
on_war_white_peace Yes None
three_year_playable_pulse Yes Character
on_defeat_raid_army Yes Army
on_army_enter_province Yes Character
on_join_court Yes Character
on_fired_from_council Yes Character
on_raid_loot_delivered Yes Army
on_pregnancy_ended_mother Yes None
on_title_lost Yes None
on_title_gain Yes Character
on_character_culture_change Yes Character
on_birth_child Yes Character
on_holy_order_hired Yes None
on_great_holy_war_invalidation Yes Great Holy War
on_combat_end_loser Yes Combat Side
on_concubinage Yes None
on_commander_combat_pulse Yes Character
random_yearly_everyone_pulse Yes Character
five_year_everyone_pulse Yes Character
on_perks_refunded Yes None
quarterly_playable_pulse Yes None
on_prestige_level_gain Yes None
on_faith_created Yes Character
on_holy_order_new_lease Yes None
on_title_gain_inheritance Yes None
on_game_start Yes None
on_character_faith_change Yes Character
on_combat_end_winner Yes Combat Side
on_courtier_decided_to_move_to_pool Yes Character
on_culture_era_changed Yes None
on_birthday Yes Character
on_faith_conversion Yes Character
on_raid_action_weekly Yes None
on_explicit_claim_gain Yes Character
on_courtier_ready_to_move_to_pool Yes Character
on_potential_great_holy_war_invalidation Yes Great Holy War
on_holy_order_destroyed Yes None
on_war_won_defender Yes Casus belli
yearly_global_pulse Yes None
on_great_holy_war_countdown_end Yes GreaT Holy War
yearly_playable_pulse Yes Character
three_year_pool_pulse Yes Character
on_pregnancy_father Yes None
on_piety_level_loss Yes None
on_piety_level_gain Yes None
on_siege_looting Yes None
on_title_destroyed Yes None
on_army_monthly Yes None
on_game_start_after_lobby Yes None
on_imprison Yes Character
on_birth_mother Yes Character
on_dynasty_created Yes None
on_alliance_removed Yes None
on_county_occupied Yes None
on_rank_up Yes None
on_vassal_become_powerful Yes None
on_join_war_as_secondary Yes Character
on_explicit_claim_lost Yes Character
on_alliance_broken Yes None
on_natural_death_second_chance Yes None
on_leave_council Yes Character
on_county_culture_change Yes None
on_war_started Yes None
on_marriage Yes Character
on_great_holy_war_participant_replaced Yes Character
five_year_playable_pulse Yes Character
on_birth_real_father Yes None
on_game_start_with_tutorial Yes None

On_actions from script[edit | edit source]

Scripted on_actions can be found in /common/on_actions folder.

Strategy[edit | edit source]

Triggering the event[edit | edit source]

Should probably become its own page.

Techniques and design patterns[edit | edit source]

Pinging events, message events.

Other fancy ideas.