Event modding

本頁面適用於最新的版本(1.12)。

事件定義於/Hearts of Iron IV/events/文件夾中。事件分為以下5類:

country_eventnews_eventstate_eventunit_leader_eventoperative_leader_event

事件外觀和目標因類別而異。

不論何種事件,ROOT作用域總是國家。但是THIS作用域不是,在state_eventunit_leader_eventoperative_leader_event三類事件中,作用域還包含事件類型對應作用域,自動分類接受適用於對應作用域效果。為防部分效果作用域重合(例:add_manpower 同時對地區國家生效),建議編寫效果時指定國家。
出於同樣的原因,建議少寫state_event,因為其與country_event相像。(Event targets can be used within country events aimed to a specific state instead), however unit leader and operative leader events can be unavoidable due to their distinct appearance.事件雖然分類,其編寫時幾乎沒有區別, with the exception of appearance, default scopes, and the fact that news event can have their pop-up disabled (However, the country will still get them). If the player country gets an event and doesn't pick a choice, the first option will get assumed by default.

When fired using an effect, the game ports over the regular event targets existing in the previous effect block to the event, as well as the ROOT scope of the block, which becomes FROM within the event. The previous effect block's FROM scopes get shifted one level down, with the effect block's FROM becoming FROM.FROM, previous FROM.FROM becoming FROM.FROM.FROM, and et cetera. Due to this, FROM is commonly called the 'sender' of the event in jargon, making FROM.FROM the sender to the sender. Although this does not always apply, as FROM.FROM can be something else entirely, e.g. an additional scope within on actions or the target of a decision. Importantly, it's what the FROM block of the effect block that fired it is, which depends on where it's fired exactly.
This does not happen if the event gets fired using an on action's random_events block: the same scoping rules as in the regular on action apply.

請注意:因不擁有任何土地等原因而不存在的國家無法看到事件,即使是指令觸發的或指定了major = yes的也如此。這些國家出現後上述事件會一併觸發。

創建一個事件

每個事件都包含於一個對應事件類型的代碼塊,如 country_eventnews_event 。每個事件的id 是必填項,例如 id = my_event.123

ID編號規則

事件ID應符合以下編號規則: <命名空间>.<编号>。例如,事件 my_event.123的命名空間是「my_event」、ID是「123」。編號應為整數,原因見下文。

定義用到某命名空間的事件前應先定義此命名空間(放大一下, 以防有人沒看到)要定義命名空間,請在事件內容外使用 add_namespace 語句,例: add_namespace = my_event 。如不定義命名空間,遊戲將視此事件ID生成有誤、不生效,所謂malformed token。命名空間命名可包含字符、下劃線和點(是的 my_event.subtopic 也是合法的命名空間名,只要事件id寫成 id = my_event.subtopic.1這樣就行)。

在遊戲內部,事件ID還將被轉換為內部ID,轉換方法為:按命名空間所在文件的ASCII可顯示字符順序和命名空間在文件中的位置先後給各命名空間分配命名空間ID,第一個命名空間ID為10,第二個為11,以此類推,分配好後各命名空間ID乘以100000加上各事件ID中的編號。值得注意的是,遊戲視事件ID最後一個點後的字符串為其編號,並將其轉換為整數,如編號轉換失敗則默認記為0,這意味着同一命名空間下所有非整數編號事件會被遊戲視為同一事件,因此事件編號必須為整數。

本地化

titledesc 行用於指定事件的 本地化條目 的名稱、並按玩家的遊戲語言顯示其遊戲內標題和描述,例: title = my_event.123.tdesc = my_event_description。每個事件都應該有標題和描述,除非它是隱藏事件。

本地化條目定義於 /Hearts of Iron IV/localisation/<語言>/ 文件夾中。我們強烈建議開展本地化工作時創建一個全新的文件而非隨便複製粘貼一個遊戲本體文件過來清空內容重寫以防遊戲本體版本更新後兼容性不良。遊戲要求本地化文件使用帶BOM的UTF-8編碼。要使遊戲能正確加載本地化文件,本地化文件的文件名應以 _l_<语言>.yml 結尾(下劃線後面的字符是小寫L,不是大寫i)。文件第一行應先寫 l_<语言>: 。本地化內容格式如下所示:

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

寫好幾個標題讓遊戲按實際狀況選擇事件標題和描述是可行的,遊戲會選擇滿足要求的本地化條目,如有多個則選擇第一個。比如:

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

此例中如彈出事件的國家的tag為ENG,遊戲將選取本地化條目 my_event.123.t.a 指定的內容作為事件的標題,否則選取本地化條目 my_event.123.t.b 指定的內容作為事件的標題。 trigger 用於填寫觸發器 ,默認要求其中所有條件都為真。

描述和標題格式一樣, title 換成 desc就行。

事件配圖

要給事件配圖,請加 picture 代碼塊。參數填指向圖片的sprite名,例: picture = GFX_my_sprite.

sprite定義於 /Hearts of Iron IV/interface 文件夾中所有文件後綴為.gfx的文件,默認為eventpictures.gfx。這些文件用普通文件編輯器是打得開的。和本地化文件一樣,也建議用新文件寫這些sprite相關內容。要定義sprite,請使用 spriteTypes 代碼塊,例:

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

創建此sprite後 <你正在做的mod文件夾>/gfx/event_pictures/my_event_picture.dds 即可以 picture = GFX_my_sprite的身份出現在事件中。

觸發器

要控制何時觸發事件,請使用 trigger 代碼塊。滿足所有條件才能觸發事件。例:

trigger = {
    tag = GER
    has_political_power > 100
}

請注意:如不註明is_triggered_only = yes遊戲將每20天檢查一次是否符合觸發條件[1]。如用控制台指令強行調出事件( 此處使用指令應為event my_event.123 ),控制台會顯示事件強行觸發時已滿足和未滿足的條件。

mean_time_to_happen 可用於讓事件在指定的時間前後隨機觸發。用daysmonthsyears來表示事件從符合觸發條件到觸發所需的大概用時,其中months按30天計,years按365天計。此外還可以用 modifier來對上述時間做運算,其中add增加時間、factor 乘以係數、base 直接改變計算基數。例子如下:

mean_time_to_happen = {
    days = 10
    years = 1
    modifier = {
        factor = 0.2        #对tag为POL的国家,触发时间乘20%。
        tag = POL
    }
}

一旦事件符合通過上述20天檢查,遊戲就將每天判定是否要觸發此事件。

值得注意的是,使用此功能會使事件的觸發服從失敗率為[math]\displaystyle{ 2^{-\frac{1}{指定的触发天数}} }[/math]幾何分布。考慮到其數學期望為[math]\displaystyle{ \frac{2^{\frac{1}{指定的触发天数}}}{2^{\frac{1}{指定的触发天数}}-1} }[/math]而非指定天數,此功能設置的不是每天判定開始到觸發用時的算術平均——對每天概率加和就會發現其實是中位數。

由於每天判定時也同時判斷事件是否仍符合觸發條件,建議各位用戶在自定義事件時限制此類事件數量以防性能不良。

出於同樣的原因也建議:如無必要,可用is_triggered_only = yes關閉事件自動觸發。做此設置可保證事件不能自動觸發,而只能為國策效果或其他事件等指令觸發,尤以國家事件新聞等為甚,因事件類型而異。這對事件鏈編寫很重要。如不做或忽略此設置,即使不定義觸發條件事件也會自動觸發。
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.

如果希望待設計的事件整場遊戲只觸發一次,請使用fire_only_once = yes,這將防止事件以控制台以外的任何方式被再度觸發,不論是效果mean_time_to_happen還是別的什麼機制。 如上所述,如此設置後待設計的事件整場遊戲只觸發一次,如果一個國家觸發了此事件,其他國家就不能再次觸發了,除非上控制台。

如需像新聞一樣讓所有國家都能看到此事件,請用major = yes。請注意此代碼不能和fire_only_once = yes並用,後者指的是總觸發次數只有1次而非每個國家1次,並用將導致此事件只能在觸發此事件的國家觸發。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.

選項

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.

其他可指定參數

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.

minor_flavor = yes marks the event as being a minor flavour event. This does not change its appearance or change its effects, but allows turning off the pop-up within the game's decision menu.

事件效果

Any effect block can be used to fire an event, such as focus rewards, event options, or decision effects. This is usually paired with is_triggered_only = yes within the event as to disable automatic firing.

In its simplest way, this is done with the effect of country_event = my_event.1 (or news_event = my_event.1), which'll instantly fire the event for the scoped country. As country and news events are the exact same thing under the surface, both shortened effects can be used for either country and news events, with there being no difference between them whatsoever.
However, more options can be added primarily for setting up the delay. Additionally, expanded versions are mandatory for state and operative leader events.

A more complex effect to fire is country_event = { id = my_event.1 days = 100 random_days = 123 }. This'll fire the event in 100 to 223 ([math]\displaystyle{ 100 + 123 }[/math]) days. There are the following arguments that can go into the effect (All of them are optional with one exception):

  • id = my_event.1 — The ID of the event to fire. This is mandatory as to let the game know which event to fire.
  • hours = 1|days = 2|months = 3 — The lower bound on the needed time that the event will fire in. In this case, a month is treated as exactly 30 days. If multiple of these are used, the game will add them up together (e.g. the example with 1 hour, 2 days, and 3 months will fire in 92 days and 1 hour).
  • random_hours = 1|random_days = 2 — This sets the upper bound on the needed time that the event will fire in. The game selects a random amount (uniform distribution) of days and hours between 0 and the set amount, including both ends, and adds them to the delay to fire the event. Similarly to above, if both are specified, the game will add them together. random = 123 also serves as an equivalent for random_hours = 123.
  • tooltip = my_event.1.t — This decides what the name of the event that would fire would be displayed as in localisation. Defaults to the event's name, this may be useful if the title may change between the effect to fire it and its actual appearance.

Example effect using several of these parameters:

country_event = {
    id = my_event.1
    hours = 12
    random_hours = 6
    days = 2
    tooltip = another_event.1.t
}

The optional delay is incredibly useful, as hidden events can be used to create a delay between an effect block's execution and the actual application of effects without the player detecting anything. This can also be used to make the events appear more "natural": news events can have a delay of a few hours (typically around 6-12) in order to simulate the time it takes for the news agencies to report on the event. Similarly can be done with other event types to simulate waiting a few hours for a diplomatic response, rather than it being unnaturally instant.
trigger = { ... } of the event gets checked when it would fire, meaning that it's also possible to fire the event on startup of the game with a needed delay to get it on a specific day, then use the event's trigger to simulate additional requirements.

Additionally, there are these event type-specific arguments:

  • trigger_for = TAG (Unique to state events) — The country for which the event will fire for. This is mandatory as state events are to be fired in state scope. This can also be replaced with controller, owner, occupied, or a dual scope that can be used as a target, such as ROOT or FROM.
  • originator = TAG (Unique to operative leader events) — The country which serves as the originator of the event (i.e. FROM). Defaults to the operative's owner if unset.
  • recipient = TAG (Unique to operative leader events) — The country which would receive the event. Defaults to the operative's owner if unset.
  • set_root = TAG (Unique to operative leader events) — Changes the scope of ROOT within the dynamic localisation of the event, without actually changing it in code.
  • set_from = TAG (Unique to operative leader events) — Changes the scope of FROM within the dynamic localisation of the event, without actually changing it in code.
  • set_from_from = TAG (Unique to operative leader events) — Changes the scope of FROM.FROM within the dynamic localisation of the event, without actually changing it in code.

實踐易錯點

Some errors are quite common to make when beginning to make events, whether it's poor practice or if it would prevent the event from working in entirely. Some of them may be hard to notice when modding, with the event seemingly working fine, such as the error that prevents news events from being fired for more than one country.
This covers some of them, as well as the less intuitive errors in the log.

Unlogged errors

  • Leaving an event as triggered only when it's to be fired automatically (Event never fires) – is_triggered_only = yes disables the automatic firing of the event, instead enforcing using the effect to do so. Therefore, if it is left in within an event intended to fire automatically, the event will never do so.
Note that trigger = { ... } can still co-exist with is_triggered_only = yes, so an event with both is_triggered_only = yes and trigger = { ... } may still be correct. Valid usages of them at the same time include the event being triggered via an on action's random_events or the effect firing it has a delay (where the trigger would check if when the event is to be received), along others.

Template:VisibleCollapse

  • Not checking the country in the trigger for country-specific auto-triggered events (Event fires for the wrong country/never fires) – The events are not assigned to countries in any way (namespaces and filenames serve a purely organisational purpose), and each event trigger is checked for each country in order specified in the tag list.
In the provided example, the event requires ITA to have more than 123 political power, upon which the country receiving the event would annex AUS. However, once ITA has that much, this trigger would be true regardless of where it's checked. The first country in the taglist by default is GER, and so it'll be the first country where the triggers are checked. In practice, this event will result in GER annexing AUS rather than ITA.
In the correction, a change is made: first it checks that the country that would receive the event is ITA, and only then then it checks that it has more than enough political power. This makes sure that no other countries can receive this event. Specifying the tag is unnecessary if the trigger itself already implies a certain tag (e.g. has_completed_focus with a tag-specific focus tree), but is needed otherwise.

Template:VisibleCollapse

  • Unnecessarily using auto-triggered events instead of ones that are triggered only (Poor practice/optimisation) – This is more of a poor practice than an error. In general, if an event's condition can be triggered with an effect, it should be.
The example is the most obvious way of doing this: a has_completed_focus check instead of firing it directly in the focus. However, other such cases can occur, e.g. when a war starts between two countries, when a state gets occupied, or for firing one on a specific date. It's best practice to check on actions before creating an automatically-triggered event to see if they can be made to replicate.
Firing it via an effect has a purpose of being instant instead of having to wait up to 20 days. If so desired, a delay of a few hours can be added to make it appear more natural to the player. Additionally, it serves as a way to optimise the modification, as this reduces the amount of trigger checks repeatedly done. Events with a large mean-time-to-happen are particularly awful for performance and can be replaced with an effect block firing one with a large delay created with random_days within the effect in some cases.

Template:VisibleCollapse

  • Setting a news event to fire only once or not setting one as major (News event only fires for one country) – An event requires major = yes in order to appear for every country. News events are purely a reskin of country events and are not set to fire for every country by default, so this line is mandatory. Alongside that, events that fire only once don't appear more than once globally rather than per country. The event appearing for more than one country counts as it firing once again, so setting an event to fire only once will lead to only one country getting the news event instead of every one as intended.

Template:VisibleCollapse

Unintuitive logged errors

  • Event is triggered only, but does not have a 1 base-factor. – This occurs when there are contradictory arguments within an event about whether it's allowed to be fired automatically or if it can only be fired manually. In particular, mean_time_to_happen = { ... } only has an effect when the event can only be fired automatically. However, if the event is triggered only, it cannot be fired automatically, resulting in the error being created.

Template:VisibleCollapse

  • Event is set to trigger every day. – This occurs when all of the following is true for the event:
    • The event is possible to be fired automatically. In other words, is_triggered_only = yes is not present in the event.
    • The event has a mean time to happen of 1 or less days. If it is omitted, it counts as 1 day.
    • The event can fire more than once. In other words, fire_only_once = yes is not present in the event.
This error warns the player that the event may fire every day once the 20-day period passes. In order to remove the error, either of the three necessary clauses can be made to be not true for the event. For example, in a lot of cases, it is possible to make the event not be fired automatically and use an effect block to fire it instead, such as by using on actions.

Template:VisibleCollapse

  • Malformed token: event_id.123, near line on a line specifying the event ID – This occurs if the event namespace was not added as needed.
  • Failed to create id 12300000 50. Already exists in game. This might crash the game. Reverse id lookup: id 12300000 = my_namespace.0 – Note that this is the exact same error split into two instead of being two separate errors as it might seem on the first glance. This means that the internal event ID is used by 2 or more events. There are the following reasons for this error to appear:
    • Putting 2 events with the exact same ID by error – id = my_namespace.0 is included in two events at once. This is self-explanatory.
    • Using non-integers as the numeric ID after the namespace – One event has id = my_namespace.abc, the other has id = my_namespace.cba. Due to how the game generates internal IDs, a non-numeric ID is not supported, always becoming the number 0. As such, these are the exact same ID, even if they appear different.
    • Using a numeric ID not smaller than 100000 – Two events across different namespaces got assigned the same internal IDs. By the virtue of how the game generates internal IDs, each namespace is assigned 100000 numeric IDs, from 0 to 99999. Anything larger than that will start encroaching to other namespaces' IDs. The game fully allows numbers this large as the numeric IDs, so the event might work, but the duplicate internal ID means that there is a pair of events that are treated as the same event, meaning one of them will fire the other one instead.
Since the reverse id lookup is not always provided, the way to tell if this is event-related is the second number: 50 signifies that it's event-related, while a different number means a different database entry, e.g. 54 means country leader IDs and 55 means unit leader IDs, the numeric legacy IDs which are unneeded due to the 1.11-introduced character system

Template:VisibleCollapse

成品案例

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
    }
}

Integration with on actions

參見:On actions

These events are the primary types to trigger via on_actions:

add_namespace = on_action_events
news_event = {      # City capture news event
    id = on_action_events.1
    title = on_action_events.1.t    # Fall of Giza
    desc = on_action_events.1.desc
    
    is_triggered_only = yes
    major = yes
    
    option = {
        trigger = {
            OR = {
                tag = EGY
                is_in_faction_with = EGY 
                is_subject_of = EGY
            }
        }
        name = on_action_events.1.a
    }
    
    option = {
        trigger = {
            NOT = {
                tag = EGY
                is_in_faction_with = EGY 
                is_subject_of = EGY
            }
        }
        name = on_action_events.1.b
    }
}

country_event = {       # Fired on a specific day if circumstances are met
    id = on_action_events.2
    title = on_action_events.2.t
    desc = on_action_events.2.desc
    
    is_triggered_only = yes                     # Prevents from firing automatically.
    trigger = {
        has_completed_focus = BHR_focus_name    # If the focus isn't completed, will never fire.
    }
    
    option = {
        name = on_action_events.2
    }
}

country_event = {       # Other types of on_actions
    id = on_action_events.3 # In this case, a prompt on annexing a country with an option to release it.
    title = on_action_events.3.t
    desc = on_action_events.3.desc
    
    is_triggered_only = yes                     # Prevents from firing automatically.  
    
    trigger = {     # If all core states of FROM are cored or claimed by ROOT, should never appear.
        NOT = {     # Triggered within on_annex's random_events = { ... }, so has the same FROM as the on_action
            any_state = {
                NOT = {
                    is_core_of = ROOT
                    is_claimed_by = ROOT
                }
                is_core_of = FROM
            }
        }
        FROM = {
            NOT = {
                tag = GER       # Has separate event
            }
        }
    }
    
    option = {
        name = on_action_events.3.a   # "Release [FROM.GetName] as puppet"
        every_owned_state = {
            limit = {
                is_core_of = FROM 
                NOT = {
                    is_core_of = ROOT
                    is_claimed_by = ROOT
                }
            }
            transfer_state_to = FROM
        }
        if = {
            limit = {
                has_dlc = "Together for Victory"
            }
            set_autonomy = {
                target = FROM
                autonomy_state = autonomy_integrated_puppet
            }
        }
        else = {
            puppet = FROM
        }
    }
    
    option = {
        name = on_action_events.3.b # "Don't release [FROM.GetName]"
        add_stability = -0.1
        add_war_support = -0.1
    }
}

In order to fire this, code has to be created within any /Hearts of Iron IV/common/on_actions/*.txt file, sometimes paired with boolean flags to prevent them from being fired more than once if fire_only_once is impossible. In the above example, this would be used:

on_actions = {
    on_state_control_changed = {
        effect = {
            if = {
                limit = {
                    FROM.FROM = {
                        state = 999 # Custom state ID. Will crash the game if doesn't exist.
                    }
                    NOT = {
                        has_global_flag = giza_fall # To prevent from firing twice.
                    }                               # Due to 'major = yes', a fire_only_once will NOT work
                }
                news_event = { id = on_action_events.1 hours = 6 random_hours = 3 } # Fires in 6-9 hours to feel more natural.
            }
        }
    }
    on_startup = {
        effect = {
            BHR = {
                country_event = { 
                    id = on_action_events.2 
                    days = 357  
                    random_days = 7     # Fires in the last week of 1936, assuming default start date.
                }
            }
        }
    }
    on_annex = {
        random_events = {
            1 = on_action_events.3
        }
    }
}

參考資料

  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.