Event modding

From Hearts of Iron 4 Wiki
Jump to navigation Jump to search

Events are defined in the /Hearts of Iron IV/events/ folder. There are several types of events, changing the appearance or the target of the events.

The list of event types includes country_event, news_event, state_event, unit_leader_event, and operative_leader_event.

In each event type, the ROOT scope refers to the country getting the event, however, the default assumed scope, also known as THIS, is not always the country: In state events, the default scope is the state, while in operative events, the default scope is the operative. It is to be noted that in unit leader events, the default scope is still the country that received the event. Other than the appearance and the default scope, there is nearly no difference between event types. Although it's also notable that it's possible to hide news events, making it so that they cannot be fired, so putting actual effects in them should be avoided.

Additionally, the FROM scope will refer to the "sender" of the event, which exists if the event was fired via an effect rather than firing automatically by the trigger being met. The scope previous to the scope where the effect was executed will be considered FROM. For example, if an effect block contains POL = { GER = { country_event = event.123 } }, in the event that GER gets, POL will be considered FROM. In case of an event being a part of a chain or being fired by an on action where FROM has its own meaning, it is also possible to stack FROM indefinitely separating each one with a dot, as FROM.FROM.FROM.

It is notable that a country that does not exist (i.e. does not own any states) cannot get an event in any way, even if fired via an effect.

Event creation[edit | edit source]

Each event is contained within a code block corresponding to the event type, such as country_event or news_event. Within the event, a mandatory line is id, corresponding to the event's ID, such as id = my_event.123.

ID rules[edit | edit source]

Within an event file, all events have to have an ID in the format of <namespace>.<integer ID>. For example, in an event my_event.123, the namespace will be "my_event", while the ID is "123".

A namespace must be first created before defining any events that use it. This is done with the line add_namespace = my_event which must be outside of any event. If a namespace is not defined, then the event ID will be considered a malformed token, leading it to not working in-game.

Internal IDs are defined for events by assigning an ID to each namespace (IDs are assigned in the order in the code, files being loaded by filename in the character order), with the first-defined namespace being assigned an ID of 10, incrementing it by 1 for each next created namespace, multiplying it by 100000, and adding the integer ID. If the ID after the namespace fails conversion to an integer, then it'll be considered 0. For this reason, every event with a non-integer ID will be considered the same event, so the ID of the event after the namespace must be an integer.

Localisation[edit | edit source]

The lines title and desc are used to assign a localisation key to the event, creating its title and description depending on the current language of the game. An example line is title = my_event.123.t or desc = my_event_description. An event is required to have a title or a description unless it is hidden.

Localisation is defined in the /Hearts of Iron IV/localisation/english/ folder for the English language. It is preferable to use a new file in the folder instead of overwriting any base game files. The newly-created file will have to end with _l_english.yml in its filename (Note that it is a lowercase L, not an uppercase i) for it to be loaded properly. Additionally, it has to use the UTF-8-BOM text encoding. Exact details on how to change the encoding depend on the text editor used. Within the file, assuming that l_english: is already added as the first line, localisation can be added as such:

 my_event.123.t: "My event title"
 my_event_description: "My event description"

It is also possible to add multiple titles and descriptions to an event, making it choose one depending on conditions. This is done as the following:

title = {
    text = my_event.123.t.a
    trigger = {
        tag = ENG
    }
}
title = {
    text = my_event.123.t.b
}

The game will choose the first localisation key where the conditions are met. In this case, the event title will use the my_event.123.t.a localisation key if the country receiving the event has the tag of ENG, and every other country will have the event title use the my_event.123.t.b localisation key. trigger is a trigger block, requiring all of the triggers inside to be true by default. The formatting for event descriptions is the same, with title changed for desc.

Picture[edit | edit source]

In order to add a picture to be shown for the event, the picture argument is used with the name of the sprite leading to the file of the picture, such as picture = GFX_my_sprite.

Sprites are defined in any /Hearts of Iron IV/interface/*.gfx file, by default using eventpictures.gfx, opened within any text editor. It is recommended to create a new file in the folder instead of using a base game file in the mod for update compatibility reasons.
Within the /Hearts of Iron IV/interface/*.gfx file of your choice, the following lines can be added within the spriteTypes = { ... } block to define a sprite:

spriteType = {
    name = "GFX_my_sprite"
    texturefile = "gfx/event_pictures/my_event_picture.dds"
}

After creating this sprite, the file <yourmod>/gfx/event_pictures/my_event_picture.dds can be used within an event as picture = GFX_my_sprite

Triggering[edit | edit source]

In order to control when the event should fire, the trigger trigger block is used, requiring each condition to be fulfilled for the event to fire. This will look like the following:

trigger = {
    tag = GER
    has_political_power > 100
}

Note that the trigger is evaluated for each country individually every 20 days[1] for the automatic firing done without is_triggered_only = yes. If the event is fired in console (using the event my_event.123 console command), the console's outputs will show which triggers were met and which weren't at the time of it being fired.

mean_time_to_happen can be used to change the time for the event to fire. days as a whole number is the base amount of days used in the calculation. months and years are also used as the base, both being whole numbers, getting multiplied by 30 and 365 respectively. Additionally, modifier can be used as a block with arguments of either add to add the amount of days, factor to multiply the amount of days, or base to change the base amount of days if the conditions are met. This looks like the following:

mean_time_to_happen = {
    days = 10
    years = 1
    modifier = {
        factor = 0.2        # Amount of days is multiplied by 20% for POL.
        tag = POL
    }
}

Once the event triggers are evaluated as true in a 20-day check, the mean time to happen is checked daily. Despite its name, it is not an arithmetic average, but a median: an event has a 50% chance to fire within the given day range and a 50% chance to fire later. Daily, the chance for an event to fire is the following, if the mean-time-to-happen's value is taken as M: Since this check is evaluated daily for each country where the triggers are met, it is preferable to limit the amount of events with a mean time to happen to prevent slowdown.

It can be preferable to disable the event firing automatically by using is_triggered_only = yes - it being set to true will ensure that the event will not fire automatically and can only be triggered via an effect, such as a focus reward or a different event's option: particularly, the effects of the type country_event, news_event, and so on depending on the event's type. This can be useful for event chains. If omitted or set to false, the event will fire automatically even if the trigger isn't defined.
It is to be noted that trigger is not entirely useless in this case: it will be evaluated when the event is intended to fire, preventing it from firing if not met, which can be useful if the event is set to fire with a delay or for the random_events section in on actions. For optimisation reasons, it is preferable to make the event be triggered only if possible: on actions can commonly be used to fire the event, such as on_startup serving as a way to fire an event on a specific day, by defining a delay in days from the start date of the game.

If a event is to only fire once total, the fire_only_once = yes can be added. This will prevent the event from firing more than once in any way, be that via an effect or automatically with the mean time to happen. This means once total: if it gets fired for any country in any way, no other country will be able to get the event again. It will, however, still be possible to fire it in console again.

In order to make an event fire for every country, such as is done in news events, major = yes can be used. Note that this cannot be used in conjunction with fire_only_once = yes, as that makes it fire only once total rather than per country, meaning that it'll only fire for the country where the event is originally triggered. This will bypass the trigger = { ... } block for the countries that did not have this event fired originally, instead relying on the show_major = { ... } trigger block, if one is present. Additionally, fire_for_sender = no, if added, will prevent the major event from appearing for the country that it originally got fired for, via an effect or by the trigger being met.

Options[edit | edit source]

An event option is added with an option = { ... } block. An event option is an effect block, with a few extra options:

name decides the localisation key used for the event, such as name = my_option_name. It is not possible to make the option name depend on the triggers in the same way it's possible for event titles, instead, completely different event options can be used, disabling each one with a name that shouldn't be used.

trigger = { ... } is a trigger block, deciding when the option is visible to be picked. If the trigger is false at the time of being fired, it will not appear until the event is fired again. Additionally, for major events, original_recipient_only = yes can be used to ensure that only the country that fired the event has this option available, with others not having it.

ai_chance = { ... } is a block deciding the AI chance for event options: deciding in a proportional way which option to pick. The AI chance in event options do not have to add up to 100, as it is proportional. It is structured in a near-identical way to the mean time to happen. If unset, assumed to be 1. The probability of each option is its weight divided by the sum of all option weights. If all options have a weight of zero, the first one is chosen. The randomized choice is made by rolling a d100, so options can't have an effective non-zero probability below 1%. The choice remains consistent across reloads, based on unique game seed, in-game time, country, and unit leader.

Additional arguments[edit | edit source]

immediate = { ... } is an effect block, executed as soon as the event is fired, before an option is chosen by the player. This can also be used for AI: AI only picks an option after the event triggers are evaluated for every other country, while immediate is executed immediately, before evaluating other events. This can be used in mean-time-to-happen type major events: by making the immediate set a global flag, which is required to be unset in the event trigger, this will prevent it from being fired more than once for each country, but it is preferable to avoid mean-time-to-happen events in entirety. Note that the effect will appear in the tooltip after the event's description, so the hidden_effect flow tool can be helpful.

timeout_days = 20 sets the amount of days that the player has to pick an option before the first option is automatically selected. This can be used to make the event be more or less urgent than default. If unset, assumes to be 13 days[2].

hidden = yes will make an event hidden. A hidden event does not need a title or a description. The first defined option, if one is present, will be automatically picked upon being fired. Hidden events can be useful instead of scripted effects to delay the execution of an effect block by a period of time or to utilise the FROM scope.


Event file example[edit | edit source]

add_namespace = my_event
add_namespace = my_hidden_event

country_event = {
    id = my_event.1
    title = my_event.1.t
    desc = my_event.1.desc
    
    is_triggered_only = yes
    
    option = {
        name = my_event.1.a
        add_political_power = 100
    }
}

add_namespace = my_news_event
news_event = {
    id = my_news_event.1
    title = {
        text = my_news_event.1.t.a
        trigger = {
            tag = POL
        }
    }
    title = {
        text = my_event.1.t
    }
    desc = {
        text = my_news_event.1.desc.a
        trigger = {
            tag = POL
        }
    }
    desc = {
        text = my_event.1.desc
    }
    
    picture = GFX_my_news_event_picture
    
    is_triggered_only = yes
    major = yes
    
    option = {
        name = my_news_event.1.a
        trigger = {
            tag = POL
        }
    }
    
    option = {
        name = my_news_event.1.b
        trigger = {
            NOT = { tag = POL }
        }
    }
    
    option = {
        name = my_news_event.1.c
        original_recipient_only = yes
    }
}

country_event = {
    id = my_hidden_event.1
    
    trigger = {
        has_country_flag = event_happened
        country_exists = BHR
    }
    
    mean_time_to_happen = {
        days = 10
        months = 2
        years = 1
        modifier = {
            base = 300
            country_exists = QAT
        }
        modifier = {
            add = 10
            country_exists = OMA
        }
    }
    
    fire_only_once = yes
    hidden = yes
    
    immediate = {
        random_country = {
            limit = {
                is_neighbor_of = BHR
            }
            annex_country = {
                target = BHR
                transfer_troops = yes
            }
        }
    }
}

state_event = {
    id = my_event.2
    title = my_event.2.t
    desc = my_event.2.desc
    picture = GFX_my_event_picture
    
    trigger = {
        ROOT = {
            has_country_flag = fire_this_event
        }
    }
    is_triggered_only = yes
    
    option = {
        name = my_event.2.a
        transfer_state_to = ROOT
    }
    
    option = {
        name = my_event.2.b
        ai_chance = {
            base = 0        # Never pick this option.
        }
        transfer_state_to = FROM
    }
}

References[edit | edit source]

  1. NDefines.NCountry.EVENT_PROCESS_OFFSET = 20 in Defines
  2. NDefines.NGame.EVENT_TIMEOUT_DEFAULT = 13 in Defines

Note that when editing defines, it is far preferable to use an override file than copying over the entire file, as defines are edited commonly even in 'minor' updates, which can cause crashes when the game updates.

Documentation EffectsTriggersDefinesModifiersList of modifiersScopesLocalisationOn actionsData structures (Flags, Event targets, Country tag aliases, Variables, Arrays, Scorers)
Scripting AIAI focusesAutonomous statesBookmarks (Scenarios)BuildingsCharactersCosmetic tagsCountriesDivisionsDecisionsEquipmentEventsIdeasIdeologiesNational focusesResourcesScripted GUITechnologies and doctrinesUnits
Map MapStatesSupply areasStrategic regions
Graphical InterfaceGraphical assetsEntitiesPosteffectsParticlesFonts
Cosmetic PortraitsNamelistsMusicSound
Other Console commandsTroubleshootingMod structureMods