• We have updated our Community Code of Conduct. Please read through the new rules for the forum that are an integral part of Paradox Interactive’s User Agreement.

HOI4 Dev Diary - Christmas & Modding

Hi everyone, its that time again. The one normal people spend on finding Christmas presents, drinking eggnog and/or glühwein and game devs scramble to finish stuff in time for the new year :D We figured this would be an excellent time to try to break the record on longest dev diary! Also, I'm not passing up a chance to paint a horrible Christmas picture!

xmastiger.png

We are now going on a Christmas break (well on Friday) and will be back in January with diaries and working on Waking the Tiger. However I arranged a cool special diary for next week for you so there would be something :) @Jamor (the project lead for Stellaris), who is also a huge WW2 buff, is going to write a big piece on division names in the German armed forces. He has been helping us out and filling out our new naming system for Germany, so expect it to be massive and chock-full of historical details!

Handing over to Havebeard now for tons of details on modding. If you know nothing about modding... this is gonna get technical! The simple translation to the below is that modding teams will now be able to do really cool stuff when 1.5 Cornflakes drops!

Hi all, Havebeard here.
This diary will be about the new script features for modders so I am gonna warn you again - If you are not a modder, we are now about to dive into the juicy/scary technical stuff!

Still with us? Great!
We come bearing gifts for the modding community and those who enjoy their work.

Decisions
One of the new shiny tools Content Designers on HOI4 have received is the Decision system. We have already covered what it is in a previous diary, but I figured there might be a modder or 2 who would like to use it, so here is a somewhat detailed rundown of how to use this new feature.


The simplest decision you can script is this:
Code:
christmas_presents = {


    more_guns = {


    }

}

This is put in a .txt file in this folder: common\decisions
All decisions need to be wrapped in a category, in the above the category is christmas_presents.

christmas_presents need to be defined like this:
Code:
christmas_presents = {

 

}

In a .txt file in this folder: common\decisions\categories

This will produce the following:
Decisions1.png


This requires nothing and does nothing, which is a pretty big disappointment when someone expects guns. Not the most exciting thing so far, but we have to start somewhere.


Effects
Adding an effect when the decision is selected is done like this:
Code:
complete_effect = {

    add_equipment_to_stockpile = {

        type = infantry_equipment_1

        amount = 1337

    }

}


Prerequisites
You can add an allowed trigger to either decision or category.

Any country matching this trigger on startup will have the decision in its pool of possible decisions for the duration of the game. If you script allowed on a category, all decisions wrapped in this category will check the trigger. This is similar to how political advisor are scripted, where they are limited by country tag.


Blocking a decision based on a dynamic trigger is done very similarly to focus, by scripting an available trigger.
Code:
available = {

    has_country_flag = santa_podcat

}


We will add another decision called invite_santa where we set that flag. In this decision we script the cost to take the decision. This is the cost in political power.
Code:
cost = 42


Once you’ve made a couple hundred decisions the interface gets a little cluttered and a lot of them probably might be blocked by ideology, war or other things that means it won’t be relevant for the player. Enter visible trigger. When this is true the decision is shown in the interface.


available and visible can also be scripted on the category, so if you have some complex trigger you want to apply to a bunch of decisions, you just have to add it in one place. This does not help with readability though, so use with care (or just use a scripted trigger).


Localisation
Both categories and decisions can have name and description.
Code:
christmas_presents:0 "Christmas Presents"

christmas_presents_desc:0 "To fight off the cold and the dark we need to gather christmas present."

invite_santa:0 "Invite Santa"

invite_santa_desc:0 "In the darkest of times there is only one place to turn. Ask Santa Podcat for gifts."

You can also use the generated localisation like [Root.GetNameDef], [Root.GetLeader] and so forth.


Our decision now looks like this:
Decisions2.png


This is the basics of decisions, but there are a few more things you can do with them.


Stay, leave, come back, stay away
Some decisions you want to keep in the interface for a little while after selecting them. Some you will want to re-enable after a period of time and some you only want to happen once.

Code:
days_remove = 5 #Stays for 5 days before being removed

days_re_enable = 5 #Will show up in the interface and can be selected again after 5 days

fire_only_once = yes #Will not re enable after being removed


We also added an effect that is run when a decision is removed, by scripting:
Code:
days_remove = 5

remove_effect = {

    add_political_power = 50

}
This removes the decision after 5 days and add 50 political power to the country.


Modifiers
Scripting a modifier on a decision will apply it to the country for a long as the decision is active (in the interface)
Code:
modifier = { political_power_gain = 1 }


Here is the script and a little example of how all of this might look:
Code:
christmas_presents = {

 


    invite_santa = {


        allowed = { tag = SWE }


        available = {

            date > 1936.11.30

            date < 1936.12.25

        }


        visible = {

            NOT = { has_country_flag = sadness_and_darkness }

        }


        fire_only_once = yes


        cost = 42


        complete_effect = {

            custom_effect_tooltip = invite_santa_tt

            set_country_flag = santa_podcat

        }

    }


    more_guns = {


        allowed = { tag = SWE }


        available = {

            has_country_flag = santa_podcat

        }


        visible = {

            NOT = { has_country_flag = sadness_and_darkness }

        }


        modifier = {

            political_power_gain = 1

        }


        days_re_enable = 5

        days_remove = 5


        remove_effect = {

            add_political_power = 50

        }


        complete_effect = {

            add_equipment_to_stockpile = {

                type = infantry_equipment_1

                amount = 1337

            }

        }

    }

}

Decisions3.gif



Targeted decisions
Let’s say you want to make a decision that involves two countries, but you want it to work with more than just one single specific country. Scripting one for each country in the world and all the potential other countries they can affect gets messy quick.

Enter, targeted decisions. Still a little messy, but in a much better way.

Once per day the system looks for countries that match the target_trigger and if a match is found a new decision with that particular country as target is added.

Triggers and effects for targeted decisions can access the other country using FROM. (We are using FROM because of reasons. Trust us, they are good reasons. No, we can’t tell you.)
Code:
ruin_christmas = {


    allowed = { tag = FIN }


    target_trigger = {

        FROM = {

OR = {

                tag = SWE

                tag = DEN

                tag = NOR

            }

}

    }


    available = {

        FROM = { has_country_flag = santa_podcat }

    }


    complete_effect = {

        declare_war_on = {

            target = FROM

            type = ruin_christmas

        }

        FROM = {

            set_country_flag = sadness_and_darkness

        }

    }

}

Decisions4.png


In the example above Finland has the opportunity to ruin christmas and declare war on Sweden, Denmark or Norway. Way to go Finland…

Notice the little flag denoting the target country and FROM being used in place of the target country. This makes it a powerful way of generating interactions between countries. FROM can be used in target_trigger, available, visible, remove_effect, complete_effect and ai_will_do.


The target can also be used in localisation like this.
Code:
ruin_christmas:0 "Ruin Christmas for [From.GetNameDef]"


Notes
Since target_trigger is checked for every country it can have quite a heavy effect on performance. If you simply want to add a targeted decision against a specific country from a focus you can do it by using an effect.

Code:
activate_targeted_decision = { target = TAG decision = decision_to_activate }

You can also use one targeted decision to active a new one against the same country.

Code:
activate_targeted_decision = { target = FROM decision = decision_to_activate }

In this case you do not need to script the allowed or target_trigger, but can just let the decision sit in the database until you need it.


Missions
Scripted in the same place and almost the same way as decision. These require you to either fulfill or avoid certain conditions in a given time frame.


Code:
more_cake = {


    allowed = { tag = SWE }


    available = {

        controls_state = 64 #THIS IS THE ACTUAL OBJECTIVE

    }


    activation = {

        has_stability < 0.3 #This starts the mission

    }


    Is_good = no


    days_mission_timeout = 3


    fire_only_once = yes


    timeout_effect = {

        DEN = { annex_country = { target = ROOT } }

    }


    complete_effect = {

        add_political_power = 1337

    }

}

Decisions5.png


Here the Swedes have reached low stability, causing the mission to activate (activation trigger becomes true). The available trigger is used for the objective and if it is met, the complete_effect will be run. (There are good reasons why available is the objective. Very good reasons. The best reasons.)

Is_good = yes/no only defines the tooltips and color of the progress bar. It is always the timeout_effect that is run when days_mission_timeout goes below 0.


Note that modifiers do not work with missions, but we are probably going to add that in the future.


AI
I would not recommend straight up war from a decision, but here are a couple of ways to tell the AI to watch out when certain decisions are in play.
Code:
war_with_on_remove = TAG

war_with_on_complete = TAG

war_with_on_timeout = TAG

war_with_target_on_remove = yes

war_with_target_on_complete = yes

war_with_target_on_timeout = yes

ai_will_do is scripted same way as for any national focus. It will weigh decisions against each other and pick the highest scoring one. If ai_will_do is not scripted for a decision it will not be selected by the AI.


Custom Costs
For some decisions you might not want the cost to be political power. We have added a custom cost trigger that lets you define your own cost. Here is a simple one as an example.

Code:
custom_cost_trigger = {

    has_army_experience > 10

}

custom_cost_text = army_xp_more_than_10
Code:
army_xp_more_than_10:0 "§H10§!£army_experience"

army_xp_more_than_10_blocked:0 "§R10§!£army_experience"

army_xp_more_than_10_tooltip:0 "It costs 10£army_experience to select this."

Decisions6.png


When you script a custom_cost_trigger the normal cost is ignored, and instead this trigger is checked.

The localisation key we point to in custom_cost_text is what is used to generate the text in the interface and on the tooltip. In our example when we fulfill the cost trigger army_xp_more_than_10 is shown, but when we don’t have enough XP, army_xp_more_than_10_blocked is shown. Army_xp_more_than_10_tooltip is optional and will replace the usual text on the bottom of the tooltip if the key is added.

EDIT:
The custom cost trigger is mainly there for presentation. It is only a trigger that checks if you are allowed to take the decision. It does not in itself modify anything. In the case above you will have to deduct the army xp in the effect (and then probably hide it to not have double tooltip).

The same thing can be achieved with putting the trigger in available and then reducing whatever you cost is in the effect, but this looks nicer.

For cost over time (political power drain fx) this is a way of showing the player that cost instead of it looking like a freebie at first glance and this is not something you can as easily communicate in the trigger.


Highlight States
Highlight a random state that matches the trigger when hovering over the decision. Right clicking moves the map to the state.

Example:

Code:
highlight_states = { state = 64 }


Category Sorting
Categories can have priority scripted to order them in the interface. This number is scriptable, so you can change priority based on a trigger.

Code:
christmas_presents = {

    priority = {

        base = 0

        modifier = {

            add = 10

            has_country_flag = christmas_time

        }

    }

}


Icons and pictures
picture = picture_name
Corresponds to GFX_decision_cat_picture_name

icon = icon_name
Corresponds to GFX_decision_icon_name


Targeted Missions
Activated in the same way as normal targeted decisions, but if days_mission_timeout is scripted, they will be a targeted mission instead.



Turns out there are quite a few things you can do with this system and it takes a while to explain in writing. I think I covered most parts, but don’t hesitate to ask in the comments or on the modding forum once you get your hands on the script.



New script features
For those of us on the team who use the scripting language it is always a great pleasure when scripting features from other projects get ported to Hearts of Iron.

@shultays has been a true champion of script during development and has blessed us with variables. Make sure to name your firstborns after him.


Code:
set_variable = { a = 20 }

set_variable = { b = ROOT.political_power }

multiply_variable = { var = b value = 2 }

check_variable = { a > b }


This sets variable a to 20, b to the ROOT country’s political power, multiplies it and then checks if a is greater than b.

Variables can be named dynamically in the same way as flags using @. That has already been covered earlier, so I won’t spend more time on it.

Checking PP like that is of course nothing fancy, but you get the idea and with the following available you start being able to do a lot more interesting things with variables.


Code:
set_variable, add_to_variable, subtract_from_variable, multiply_variable, divide_variable, clamp_variable, has_variable, check_variable (compare = less_than, less_than_or_equals, greater_than, greater_than_or_equals, equals, not_equals)

You can also “chain scopes” when getting and setting variables. It would probably be better to save it as an event target instead, but you may find yourself in situations where that is not possible.
Code:
set_variable = { b = FROM.FROM.FROM.political_power }


There are also temporary variables, which only exist inside the current trigger or effect. They work like normal variables, but are set with set_temp_variable. The advantage of these is that they can be set and used in triggers to do some calculation relevant for it.


Variables can also be used in localisation using ?.
Code:
Some_loc_key:0 "Value of a: [?Root.a]"

This will print: Value of a: 20 (since we set our variable a to 20 further up)


You can also get party support of a specific ideology as a variable like this:
party_popularity@democratic.


Variables like party_popularity and political_power are special variables that exist from the start and are read only. They are meant to get data from the game. There are a bunch of them and more will probably be implemented as we go.


Country: political_power, command_power, manpower, max_available_manpower, max_manpower, opinion, stability, modifier, party_popularity.


State: resistance, arms_factory_level, industrial_complex_level, infrastructure_level


Opinion is another special one where you can go

Code:
set_variable = { var = eng_opinion_on_turkey value = ENG.opinion@TUR }


Variables are stored on the scope you are currently in unless you use global.

The first example below set the variable a to 1 and stores it on state 64.

The second example sets variable b to 1 and saves it as a global variable.

Code:
64 = { set_variable = { a = 1 } }

set_variable = { global.b = 1 }


If you name your own variables political_power or other reserved variable names, all manners of crazy events can happen, ranging from the universe imploding, fire raining from the sky to Sweden defeating Italy in football and the dead rising from their graves to feast upon the living.


So don’t do it.


At some point you may find that you have been scripting for a couple of hours and absolutely nothing works (this has absolutely never happened to anyone at paradox, so this is merely hypothetical). Using list_vars in the console will print variables for the country or state you have currently selected. You can also give the state or country as an argument in case you don’t want to select it.

In the console:

list_vars ENG (prints variables set on ENG)

list_vars 37 (prints vars on Sjælland)


Variables can be used in place of numbers in a lot of places.

Code:
add_political_power = 141.infrastructure_level

This would add political power equal the Infrastructure level in Svealand, so not a whole lot...

Code:
set_temp_variable = { a = 141.infrastructure_level }

multiply_variable = { var = a value = 20 }

add_political_power = a

This would add political power equal 20 time the Infrastructure level in Svealand, so still not a whole lot…


Most simple effects and triggers that just take a number will work with variables, but the ones where you pass multiple arguments are not guaranteed to work.



“So”, you may say, “this is kind of like flags. Should we just throw away all flags now? Is that what you want from us, Paradox? A flagless society?”

No. Flags are faster to check performance wise, so keep using them if all you need to do is mark something.

@shultays also added list_flags to the console, so even flags are easier to work with now.


This dev diary has gotten way too long and run way way over time, but I promise variables will be covered in more detail on the modding forum next year (when you ask me how it works...).


Scripted Localisation
Quite well documented in the file and available on other projects, so for a lot of people this will not be news.

Allows you to create your own dynamic keys to be called in localization.

defined_text -> this is it, we're defining the text
text -> a discrete entry that can be picked to display in loc.
trigger -> determines if a text entry will be picked or not.
(The triggers need to be valid for the scope the key is called in (eg Root or From.From)
localization_key -> points to the localization key that'll be used if trigger passes

Code:
defined_text = {

    name = GetIsThisDiaryLong

    text = {

        trigger = {

            always = yes

        }

        localization_key = YES_YES_YES

    }

    text = {

        localization_key = NO

    }

}

In localisation: [Root.GetIsThisDiaryLong] will be replaced with YES_YES_YES.

YES_YES_YES:0 “Maybe a little.”


Sidenote: Focuses can take scripted localisation as a name if you set them to dynamic = yes


Namelists
This is how you script namelists.
Code:
GER_Inf_01 = {

    name = "Infantry Divisions"


    for_countries = { GER }


    can_use = { always = yes }


    division_types = { "infantry" }


    # Number reservation system will tie to another group.

    link_numbering_with = { GER_dog_divisions }


    fallback_name = "%d. Infanterie-Division"


    # Names with numbers (only one number per entry).

    # It's okay to have gaps in numbering.

    ordered =

    {

        1 = { "%d. Infanterie-Division" }

        2 = { "%d. Infanterie-Division" }

        3 = { "%d. Infanterie-Division" }

        4 = { "%d. Infanterie-Division" }
Pretty self explanatory.

link_numbering_with means that divisions from this naming template will share numbering with this one.
can_use is a way to limit when the namelist is active.
Example:
Code:
    name = "SS Divisions"

    can_use = {

        has_government = fascism

    }
When the scripted names run out the fallback_name starts getting used.


Stability and War Support
Since national unity is no longer a thing, modders can already now start replacing set_national_unity and add_national_unity with the new set_stability and add_stability and war_support. Things like national_unity_factor work the same way, just replace national_unity with stability or war_support as appropriate. Triggers work as you’d expect (has_stability and has_war_support). Setting and trigger off of stability/war_support is on a scale of 0-1 (so set_stability = 0.5 and has_war_support > 0.3)


Locking Division Templates
You can script templates as is_locked = yes. This disables modifications and training of that template. You can delete these special templates later with
Code:
delete_unit_template_and_units = { division_template = "Snowmen" }.



Unit Leader Scope
We also added a unit leader scope, which lets you run effects on random or all unit leaders (Generals, Field Marshals, Admirals) based on a trigger.

Here is how you get them: random_unit_leader, every_unit_leader, random_army_leader, every_army_leader, random_navy_leader, every_navy_leader.

Use trigger_docs to find effects you can use on them.


Example:
Code:
random_army_leader = {

    limit = {

        average_stats < 2

        is_field_marshal = no

    }

    set_unit_leader_flag = some_flag

    add_trait = useless_trait

}


Focus Tree
If you define a focus by writing shared_focus instead of focus you can add the focus to another tree using shared_focus = focus_id_here. The focus and all its children (and their children etc.) will be added to the country.

To avoid shared focus overlapping with normally scripted ones you can script dynamic positioning.

Code:
offset = {

    x = -6

    y = 0

    trigger = {

        tag = PRC

    }

}

load_focus_tree = id_of_focus_tree is a new effect that will change the current country’s focus tree to whatever tree is specified.


Targeted Modifiers
National spirits can have targeted modifiers, meaning the modifier applies only in relation to a specified other country.

Example giving a country attack and defense bonus against Sweden :
Code:
targeted_modifier = {

    tag = SWE

    attack_bonus_against = 0.1

    defense_bonus_against = 0.05

}


State Events
Defining an event as a state_event and triggering it using the effect with the same name lets you fire an event specific to that state. The receiver is defined in trigger_for.

Code:
state_event = { id = some_event.1 trigger_for = controller }


Off-Map Buildings
As part of the Chinese focus tree, we added support for off-map factories. This supports any kind of building that does not need to be on the map to work (civilian and military factories, shipyards, synthetic refineries, nuclear reactors)
Code:
add_offsite_building = { type = arms_factory level = 2 }


The buildings act like any on-map building with the exception that they, obviously, can’t be bombed or conquered. To remove off-map buildings, you add negative ones:
Code:
add_offsite_building = { type = arms_factory level = -2 }
Removing off-map buildings that aren’t there leads to a hard reset of the entire universe so we’d all appreciate it if you didn’t do that.


Hopefully this will get you started using the new systems and be hungry for modding again. Remember that trigger_docs in the console is your friend and ask on the modding forum when you don’t find your answer there.

Good luck out there! Script good and crash well!
 
Last edited by a moderator:
And I am still wondering, why Podcat always said the release of Waking The Tiger is after Christmas instead telling the people "It will be early 2018". I mean, for what? So confusing. Well .. have some nice Christmas, refresh your batteries and give us a good start into the new year with new overwhelming news for Waking The Tiger ;)
well both are about similarly specific ;) I think my mind is just very used to mark time with holidays
 
Happy Christmas Paradox!
 
well both are about similarly specific ;) I think my mind is just very used to mark time with holidays

Hm, telling someone something will happen after Christmas indicates it could happen in the same year. Telling someone it will be 2018 clarifies that it will not be in 2017. But, after I watched Star Wars 8 I am probably that depressed, that I don't care anymore about the details of the release :(
 
well both are about similarly specific ;) I think my mind is just very used to mark time with holidays
that and those that have been around know that "after XMas" means it'll be in 2018 as the office shuts down for the most part over the holidays

Thanks for the great write up and...

I hope team Awesome enjoys a Merry Christmas and a happy New Year!

upload_2017-12-20_9-56-58.png
 
Looks like great work concerning modding. I love how the tiger is sleeping peacefully. Hopefully he awakens from his slumber soon!
 
I will absolutely update it. Remind me again when I forget..
21ew6z.jpg

Not sure which sublime plugins you mean. I use a few different ones that suit my workflow, but mainly just Sublime editing features.
Would you mind sharing what plugins exactly? I am always curious about tools people use.
EDIT: We do have a plugin that helps with localisation by showing that the text that corresponds to the key and letting you jump to the file. When it is working properly we might release it.
That would be great!
 
21ew6z.jpg


Would you mind sharing what plugins exactly? I am always curious about tools people use.

That would be great!
Replace space with underscore in selection and vice versa.
Converting name_of_thing to name_of_thing:0 "Name of Thing"
Text Pastry for number stuff. Has some limitations, so been thinking of doing my own version for my specific needs.
Find selected path in game folder and open location. (used very rarely)
Snippets for creating whatever stuff I need.. it's mainly for when I need to do a big batch of things though.

But as I said.. mainly just use vanilla Sublime
 
Hope someone makes a mod that prevents certain national focus units like Maori volunteers from been recruited 50 times
 
Custom Costs
For some decisions you might not want the cost to be political power. We have added a custom cost trigger that lets you define your own cost. Here is a simple one as an example.

Code:
custom_cost_trigger = {

has_army_experience > 10

}

custom_cost_text = army_xp_more_than_10
Just wondering, why is it "has_army_experience", and not simply "army_experience"?
 
Just wondering, why is it "has_army_experience", and not simply "army_experience"?
Two reasons.
1) To me, words like is, has, can, etc. means you ask the game about something, so you know it's a trigger from the name.
2) army_experience is used for the effect and it messes up search, highlighting and readability having triggers and effects named the same thing.