Imperator Development Diary - 15th of April

  • 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.

Trin Tragula

Design Lead - Crusader Kings 3
Paradox Staff
28 Badges
Aug 1, 2003
  • Victoria: Revolutions
  • IPO Investor
  • Paradox Order
  • Mount & Blade: Warband
  • Europa Universalis IV: Pre-order
  • Crusader Kings II: Holy Knight (pre-order)
  • 500k Club
  • 200k Club
  • Victoria 2: Heart of Darkness
  • Victoria 2: A House Divided
  • Victoria 2
  • Sengoku
  • Semper Fi
  • Rome Gold
  • Arsenal of Democracy
  • Europa Universalis III Complete
  • March of the Eagles
  • Magicka
  • Europa Universalis III Complete
  • Heir to the Throne
  • Hearts of Iron III
  • For the Motherland
  • For The Glory
  • Europa Universalis IV
  • Divine Wind
  • Europa Universalis III Complete
  • Deus Vult
  • Crusader Kings II
Hello and welcome to another Development Diary for Imperator: Rome!
Today will be a different type of Diary compared to some previous ones as we will be focusing entirely on some of the new technical things that Imperator brings to our scripting capabilities. In other words we will be looking at what new things we are adding that you will be able to use to mod the game.
As in any Paradox Game there will be a wide list of triggers, effects, scopes, etc, but this diary will focus on more high level new things and systems, such as the new script value system or the scriptable gui system.
These things are likely best described by those who created them so with that I will leave the word to @blackninja9939 who will talk about scopes, variables and the scriptable GUI and then @Meneth who will be introducing the wonderful world of Scriptvalues.


Hello all! I'm Matthew Clohessy and I work as a programmer at PDS. Up until six months ago I was a Content Designer before moving roles, some of you might've seen me around in various modding sections of the forums before as I used to mod Crusader Kings 2 a lot. Over the past year whilst working on <insert cool projects here> I've been doing a variety of improvements to the new Jomini script system to make it a lot more usable, versatile and consistent than the old versions.

Jomini is our Grand Strategy Library which is a midlayer between the game projects and the Clausewitz engine, it contains things that a GSG game can share such as the idea of the game state update, multiplayer, provinces and of course our script system.

Here I'll give you a brief overview of some of the brand new things in the Jomini script system as well as updated and improved versions of systems we had in older games.

With that preamble out of the way lets dive in! The idea of scope types and switching between them is in Jomini, the current Jomini scope types are: no scope, bool, value, color and flag. Yes numbers and bools etc. are a scope type, it has its pros and cons. We refer to these scope types as primitive scopes due to their basic nature and generally not having an object attached to it just the raw ID.
Every event or interaction has a “top scope” which stores root, saved scopes and local variables.

Event Targets are how we 1-1 switch between scope objects, they are comprised of one or more “links” separated by dots. Eg: root.mother.father
As they are separated by dots they can be used in one line so you can do
set_character_religion = root.father.mother.religion
A link can have multiple input types to lead to one output type, allowing polymorphic links that can do more than one thing! So “culture” can move from a province, character, country, pop etc. to their culture.

A scope object can be saved with an arbitrary name to reference later on in the top scope, in our older games these were called event targets. The name was changed as internally both were called event targets before and one is shorter to type for script. Eg:
father = { save_scope_as = cool_person }
scope:cool_person = { kill_painfully = yes }

Gone are the days of needing things like father = { character = root.mother.father } as a condition to see if two characters are the same.
Now we can just do father = root.mother.father, this works for any event target so you can compare things very simply.

This also allows the comparison of numeric values using >, <, >=, <=, = and !=

Numeric links can be used as the value in an effect such as mother = { add_loyalty = root.prominence }


Script lists are how we move from one scope to one or more from a list of similar objects. Eg: any_sibling

With the new system we internally only register the list builder such as sibling, the code then automatically generates the various versions for the script.
Currently we have four versions created: any_, every_, random_ and ordered_
The first three should be recognizable, but they’ve all been extended with new functionality
  • Any: Is a trigger that returns true if any of the list meet certain conditions, can have an optional count or percent parameter to indicate X many or Y percent of the list must meet the conditions
  • Every: Runs effects on all members of the list if they meet certain conditions. Can have multiple alternative_limits for backup conditions if the previous set was not met.
  • Random: Runs effects one one member of the list if they meet certain conditions. Can also have alternative_limits as well as a weight to influence which random object to run effects on.
  • Ordered: Runs effect on the entry in a list based on position or range of positions. The list can be ordered by any script value such as loyalty or gold. Can have a limit and alternative_limits on it to filter members of the list.

Any non-primitive scope type can be made to store variables in it, which scope types to have them is a game level decision so if you find a scope that does not make a request for it to be added, variables themselves can be any scope type. You can store a value, bool, flag or character etc. inside of them.
This allows for recording a saved scope on a specific object instead of just in a top scope.

Variables can be stored in three places: a scope object (character, country etc.), locally in a top scope (like a normal saved scope) or globally in the game state.

Variables themselves are treated as a scope object referring to whatever is stored in them allow you to scope to them.
One could have a best friend variable on a character which they save someone as the value then scope to that best friend variable to give the character a gift later on.

The event target link to scope to a variable depends on the storage type:

There are effects and triggers to manipulate and check numeric variables to change their value etc.


You can create a custom list of event scopes or variables which can be iterated over as a script list.
every_character = {
    limit = {
        has_variable = olympic_attendee
    add_to_list = olympic_competitor_list
random_in_list = {
    list = olympic_competitor_list
    die_very_painfully = yes

You can remove items from lists and check for their presence etc.


We have previously made attempts at automatic documentation with varied success, some bits could be outdated or incorrect, some core information was not printed at all etc.
Now the script documentation console command has been moved to Jomini and it outputs to separate files in your games log folder:
  • All effects, the scopes they can be used in and a brief description, if they are a script list the scope they lead to.
  • All triggers, the scopes they can be used in and a brief description, if they are a script list the scope they lead to.
  • All scope types, character, country, value etc.
  • All event target links, the scopes they can be used from, the scope they output to and a brief description.
  • All saved scopes created by the code.
  • All modifiers, the scope they can be applied to eg: levy_reinforcement_rate
  • All on actions, if they are from code or script and the expected scope they are called in


We have a new GUI system for the games which works with its own specif setup of scripting, it is also the same system used for the localization system. Collectively this is called the Data System
Everything you can run must be either registered in by the code or made as a scripted gui.

All things you can use in the data are split into four categories:
  1. Types, the type of an object which corresponds to its class/struct in the code
  2. Promotes, moving from an object of one type to an object of another
  3. Functions, calling a function on an object which returns something
  4. Callbacks, calling a function on an object which does not return anything

Another thing to keep in mind is that the data system obeys (for the most part) how const works in C++. Without getting too technical functions, promote and callbacks can be marked as const only, which means that object which are const cannot call non-const. This is unlikely to affect you if you use the scripted guis though.

Scripted GUI

The scripted gui lets you evaluate and execute arbitrary script via the UI in a manner that will keep the game synchronized in multiplayer. You define the script in common/scripted_guis and can then reference that in data entries.

For example a cheat button to give you gold and take it from another character:
# common/scripted_guis
cheat_gold_button = {
    scope = character
    saved_scopes = {
    is_shown = { # Can be omitted as always true
        always = yes
    is_valid = {
        gold < 5000
    effect = {
        add_gold = 500
        scope:second = {
            add_gold = -500

# in a gui entry
button = {
    name = "my_cheat_button"
    datacontext = "[GetScriptedGui('cheat_gold_button')"
    texture = "gfx/interface/icons/shared_icons/"
    visible = "[ScriptedGui.IsShown( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"
    enabled = "[ScriptedGui.IsValid( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"
    onclick = "[ScriptedGui.Execute( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"
    tooltip = "[ScriptedGui.BuildTooltip( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"

The AI will currently not use these buttons, you could however make hidden pulse events to have them evaluate the same actions.

And with that it is time for @Meneth to talk about what Script values are and why we love them.

Script Values

Good afternoon. I'm Magne "Meneth" Skjæran, and I'm a programmer at PDS. I used to work on Crusader Kings II, where I among other things made a variety of improvements to the script system.
For a while now, I've been working on <redacted>, and I've also recently had a 1 month stint on Imperator.
As part of my work, I added a script math system to Jomini. Jomini is the layer between Clausewitz and the game that handles things like script system basics that don't relate to any specific game.
This is not something we've had in any previous game; the closest one could get was heavy use of variables, which was highly limiting.
I'm here today to talk in some detail about this system, outlining what it can do, so let's start from the beginning.

Script values

The script math system builds on the script value system. Most of our games have some version of this: the ability to define named values in one file for use in multiple places:
some_value_name = 1000
Which can then be used wherever:
add_gold = some_value_name
In our older games, the support for this could at times be spotty. In games based on Jomini, this is supported almost anywhere numbers can be used.
In the Jomini games, these values can also be things that aren't just simple numbers. You can for instance do things like:
add_gold = # Adds as much gold as "some_country" has

Mathematical operations

With the script math system, you can now do simple math in script. Instead of "some_value_name = 1000", you can insert math:

some_value_name = {
    value =
    add = 50
    multiply = 100

Which would result in ( "some country"'s gold + 50 ) * 100.
We support the following operations:

  • value = ... # Sets the value to the right-hand-side (RHS)
  • add = ... # Adds the RHS
  • subtract = ... # Subtracts the RHS
  • multiply = ... # Multiplies with the RHS
  • divide = ... # Divides with the RHS
  • modulo = ... # Takes the remainder from dividing with the RHS
  • min = ... # Increases the value to the RHS if it is lower
  • max = ... # Decreases the value to the RHS if it is higher
  • floor = yes # Rounds down. 1.2 -> 1, -1.8 -> -2
  • ceiling = yes # Rounds up
  • round = yes # Rounds to the nearest integer
As you can see, this allows you do to complex math, letting you implement things like costs that depend on a lot of factors in a simple manner.


Taking the system further, anything that supports taking a script value by name (E.G., add_gold = some_value_name) also supports doing that math inline.
So instead of "add_gold = some_value_name", you can do this:

add_gold = {
    value =
    add = 50
    multiply = 100

Which will give the exact same result. This is very handy when a value is only used in a single place, since you can then easily see and tweak it where it is being used.
You can even do this inside the math itself. Imagine you want to do the math "gold * ( prestige + 50 )". While you could do this by reordering the math, that'd be pretty tedious. With inlining, you don't have to:

add_gold = {
    value = gold
    multiply = {
        value = prestige
        add = 50

There's no limit on how far you can nest the math.

Conditional logic
Beyond just simple math, you can also have conditional logic. For instance, perhaps you want a reward to be higher if a country has a specific innovation:
add_gold = {
    value = 100
    if = {
        limit = { has_innovation = some_innovation }
        multiply = 3
    else_if = {
        limit = { has_innovation = some_other_innovation }
        multiply = 2
This will result in 300 if the country has some_innovation, 200 if it only has some_other_innovation, and 100 if it has neither.


For effects, you can also randomize numbers.
You can do this in two ways.
First there's a very simple syntax:
add_gold = { 10 100 }
Which would result in a random amount between 10 and 100.

This would also work:
add_gold = { some_value some_other_value }

However, this syntax does not work with inlining of math. So for that, we have two statements; integer_range and fixed_range.
integer_range will give an integer number in the designated range (E.G., 1, 2, 3). fixed_range will give a fixed-point number (E.G., 0.1, 0.2, 0.345).
An example of this:
add_gold = {
    integer_range = {
        min = { value = gold multiply = 2 }
        max = { value = gold multiply = 10 }

This would give between 2 and 10 times the country's gold.


We also support list operations, allowing you to work with collections of items and base the math on each individual item in the collection.
Any list that works in normal script (E.G., every_country, every_subject, every_character) will also work in script math.
The script below for example would add the gold of all your subjects:
add_gold = {
    every_subject= {
        add = gold
You can also change scope. Perhaps you want to add all of your overlord's subjects' gold instead:
add_gold = {
    overlord = {
        every_subject = {
            add = gold
As you can see, this system makes it simple to do a lot of things that in our past games was either difficult or even impossible to do in script.
We've used the system a lot in Imperator, and we look forward to seeing what modders will do with it as well.


And with that this developer diary is at an end. Since script does not lend itself to pretty screenshots here is one from the various screens you can get when the game ends. It's appearance and the text will differ depending on how well you did, this was gotten by making the game end in our devclash save, where I'm in control of the proud nation of Bactria. The game seems to think we've achieved little of note sadly :)
I'm also not a modder, but I work in Jackson-based parser that finally parses almost all game data files from Hearts of Iron 4.
Even if the syntax is not pure JSON, I was able to implement custom parser that now allows me to browse the ships and modules (work in progress).

This summary is nice insight into the syntax.
Last edited:
You can mod the enddate :)
Oh nice
I hope someone extends the end date till just before the collapse of the Western Roman Empire. Mostly to bridge the gap between I:R and WtWSMS.
Did you think about including an animated map of your conquests, on the end screen like in total war ? That is very satisfying to watch. Also graphs and various stats can do.
Is this possible to create an event that give permanent 100 loyalty and 100 hapiness for all characters and pops of a country?I want to do it for rp reasons.
Thanks for any reply about this.
Interesting, so how would you scope to a random POP? By going or by something like any_pop and limit the search?
Yeah pretty much, there is a script list to move from a city to the pops in it and then you use the limit to restrict the search.
Another question, I reckon scripted GUIs have the same capabilities as they are implemented in HoI4, or can we expect new things like pie charts since Imperator uses them relatively often, or even sliders?

I also want to say how awesome you guys are for ever expanding the modding possibilities!
add_gold = {
integer_range = {
min = { value = gold multiply = 2 }
max = { value = gold multiply = 10 }
This would give between 2 and 10 times the country's gold.

Interesting read! Just wondering, in EU4 when you get a bad event that costs you more money than you have, it will leave you with a negative amount of gold until the monthly tick turns this deficit into loans. In Imperator, could it be possible that after such an event your negative amount of gold will be multiplied and you'll actually lose a shitload of money?