Hello and welcome to another Development Diary for
Imperator Rome!
Today we will be talking about how you can assign your units Objectives that they will perform for you, and how to mod these. We will also give a summary of the starting situation in the Balkans and Dacia.
Unit Objectives
As you may have noticed in our streams automation is now an integral part of playing the game, should you want to. Unit objectives are a way to outsource the management of some armies to the AI, essentially using the same things that the AI itself uses to control its armies and navies. You can at any time select an objective for each army or navy under player control (or several units at once, in which case the objective applies separately to each one currently), which enables AI for said unit, making it automatically take actions almost as if it was controlled by an AI country.
We don’t see this as something you use for your main armies but it can be useful for any secondary armies or forces left behind to control or defend your country. Split/merging/consolidation/abilities are not used by objective units except specifically where it's a necessity.
The design goal with objectives to simplify the codebase on our end, expose unit behavior to modders in a modular fashion, and ultimately to provide the player with a greater amount of variation in NPC behavior.
While you may not want to sign away control of all of your armies to the AI the AI objectives makes it possible for you to let the AI handle some of your armies, while you focus on others.
Available objectives
- Naval Landing: Functionally similar to EU4 auto transport. If there is at least one available navy which has had Naval Landing selected, clicking to an inaccessible city with an army will make this navy pick up that army and ship it to that city.
- Independent Operations: Will turn on the regular army AI for this unit. Making it operate freely, attacking enemy armies, occupying enemy cities, etc.
- Defend Border: Regular AI as above but stays within country's borders, effectively just doing defensive tasks.
- Reconnaissance: Patrols across the country's borders reporting enemy movements.
- Fight Rebels: Army will focus on destroying any armies owned by the Rebel tag (Slave Revolt stacks).
- Keep in Reserve: Avoid contact with enemies while staying within country borders. Mostly because a common experience is MP is forgetting troops you're currently not using and having them destroyed when looking elsewhere.
- Hunt Fleets: Search and destroy enemy fleets.
Special Unit Objectives
There are also many Unit Objectives that you cannot pick but that you may still encounter.
Disloyal Generals will make use of a specific unit objective where they essentially do what they themselves want to. Making you unable to order them around or do things like send them to a certain death. Likewise
Mercenaries that you do not pay will stop taking your orders and operate on their own.
There is also special logic for Slave Revolts, where rebel stacks will seek out the closest rich city to attempt to loot and set free more of their kin.
Another good thing about the Unit Objective AI system is how moddable it is. @Chaingun who created this system will now talk a bit about that before I then continue to summarize the last part of the map
Modding
Internally, many behaviors such as rebels, disloyal generals and units trying to join up with the target unit of the Recruit to Unit action are also represented as objectives and thus moddable to a degree.
Some objectives started out as hardcoded early in development and still are, but they are still present in the files so they can be tweaked by modders. We may in addition port non-objectives such as piracy, barbarian behavior to the objective system in the future, as well as e.g. subject stances from EU4. In fact, you could probably implement your own variation of subject stances in a mod already.
For those familiar with finite state machines, you may draw upon the comparison of a unit corresponding to a FSM and an objective to a state. Don’t worry if this is unfamiliar, it’s a very simple concept dressed in fancy words. What it means simply is, objectives are mutually exclusive and a unit will have precisely one at a time. Transitions from one objective to another can happen in these ways:
- The player select a new objective from the GUI (requires interface_allow trigger to evaluate to true to be listed).
- The objective is automatically switched to due to having a priority above zero (requires allow trigger to evaluate to true).
- Code is hardcoded to switch to certain objectives (e.g. naval landings).
Since I’ve started to mention various script features, let’s take a look at an example script:
Code said:
#Disloyal unit objective. Automatically switched to when unit commander goes disloyal.
objective_disloyal = {
priority = 100 #If above 0, check allow and automatically switch to this objective if there are no objectives with higher priority allowed.
allow = {
exists = commander
OR = {
commander = {
loyalty <= 33
}
AND = {
unit_owner = {
treasury < 0
}
commander = { is_mercenary = yes }
}
}
}
interface_allow = {
always = no
}
#Custom evaluation function.
mode = add #none/add/replace
#Custom evaluation function. Unit scope. Use scope:province to access the province being evaluated.
#Negative => better, positive => worse. If total sum exceeds 1 million unit will not go there. Beware fixed point overflow at 2 million...
pure_weight = yes
weight = {
modifier = {
trigger = {
scope:province = {
NOT = { owner = root.unit_owner } #Prefer our homeland (since we want to revolt there).
}
}
add = {
value = 2000
}
}
modifier = {
trigger = {
scope:province = {
AND = {
owner = root.unit_owner
this = root.unit_owner.capital_scope
}
}
}
add = {
value = 1050000
}
}
}
}
interface_allow and allow have already been mentioned. If any objective should be possible, allow should evaluate to true. However, if it needs to be invisible and not selectable by the player, the interface_allow should evaluate to false. An objective with greater than zero priority will also be automatically switched to whenever allow evaluates to true and there is no higher priority objective that takes precedence.
There is one very central concept that hasn’t been explained yet: Weights/scores/values. These basically represent how much a unit wants to go to a certain city, or more accurately given the scale where higher scores are worse, how much it doesn’t want to. For the AI inclined, this is basically a utility function.
So how do you mod the utility function? You can set the mode field to add your own value to the existing function (recommended) or replace for hardcore modding which completely ditches the existing function. Then, you can specify the weight per city with the weight field, where the not so aptly named scope: province represents the city being evaluated (the default scope is the unit).
It should further be mentioned that there’s a lot of hardcoded behavior behind the scenes by default, including hardcoded prioritization outside simple greedy value maximization. Quite often, to get sufficient control, it is necessary to set pure_weight = yes to disable this hardcoded prioritization if it doesn’t fit with whatever behavior you intend to accomplish. Example of this prioritization is whether to decide to siege or initiate battle depending on certain circumstances that are orthogonal to the utility function’s output.
What does the utility function score mean? Currently, anything greater than or equal to one million means the unit will not go to the location. Peaceful movements typically lie in the range of one hundred thousand, whereas sieges are usually about fifty thousand, and battles around twenty thousand.
Be aware that the weight scale might be changed in a future version as the one in use heralds from EU4 and has some issues in relation to fixed point overflow in modding.
You can visualize the weight scale by typing “mapmode ai” into the console and selecting a unit of yours (please make sure you let game tick a day first or it may crash). It will result in a color coded map with a tooltip like this:
While at this, I should explain that the regular utility function’s output aside from some other nonlocal factors will depend on what objective is currently is selected, whether the AI is enabled for the country (ai in console), and what role the army has been assigned by AI. A brief mention on unit roles: Armies are casually assigned as either siege armies or hunter killers where the latter will prioritize killing enemy armies more highly than besieging in the default configuration.
There is another concept you will probably come across while modding unit AI: Threat, which can be checked with the unit_threat trigger. The threat value is an influence map (sorry, another AI term) calculation. Currently it can only be seen in the Release D version in Steam by typing “ai.map threat” in console (will sort this out, we have a problem with engine hiding most debug commands in release). A threat value of 1.0 is an enemy the same size as the unit is fairly close, a value of 10 that enemies nearby are 10 times as strong, etc. A screenshot I had lying around from an old version:
So what objectives does the AI currently use? Only objective_null (the default, represents having no objective at all and means the unit is player controlled or controlled by regular AI) and objective_naval_landing which is used for AI naval transport and player autotransport. Objectives linked to code like naval landing has a lot of hardcoded magic inside it and will probably be quite difficult to modify in a sensible manner other than minor tweaks to the utility function.
Finally, a note on performance: When you script unit AI all bets are off whether you will be able to run it without causing performance degradation of the game. Tweaking the following defines can be useful:
MAX_UNITS_PER_TURN = 200
TACTICAL_DEPTH = 60
Use the "timing show" and "ai.pawns" console commands to find out what is causing slowness and how responsive the unit AI is. The AI has an internal execution time allocation system to process important units more frequently than non-important ones (as by size, vicinity to human players, time since last processing, etc.)
Thank you for listening. This is a merely a part of the AI and I haven’t touched into all details, but hopefully one that you will be able to create great mods despite the opaqueness.
Dacia & The Balkans
Lastly we turn to one of the last regions so far not covered by our Development Diaries. The Balkans!
Much like many of the regions we have covered in these diaries the Balkans and the greater Danubian region in the time of Imperator was primarily dominated by tribal entities. This is a region with strong influences from both the Hellenic world, and subject to a great influx of Celts. More directly this is a region that would come under pressure from the expanding Roman Republic and which was the frequent scene for Macedonian excursions, first under Alexander and the Argeads, but also from the successor kings such as Lysimachos in Thrace. It was also through this region that the Celtic peoples that settled in central Anatolia, the Galatians, came migrating, clashing with both locals and greeks alike.
The native inhabitants of the region, Illyrians in the west and Dacians in the east are divided into many smaller tribal states. As in other regions we have covered, a successful tribe may aspire to form a tribal federation, Dacia or Illyria.
Starting Countries:
- Getia: The game’s largest Tribal state of Getians. Comparatively strong in its region but less densely populated that some of the neighbors to its south.
- Tyrgetia: Small Getian tribe on the river Tyras (right next to a Hellenic City on the estuary state mentioned in an earlier diary).
- Moesia: Settled tribe in Upper Thrace, medium power in the region and a potential danger to the kingdom of Thrace to its south and east. Not much is known about the Moesians before the end of this era when they encounter the Romans.
- Triballia: Settled tribe along the Danube. In frequent conflict with the Getians and the Macedonians, both before and after our era.
- Dardania: One of the stronger kingdoms of this region, Dardania has a history of meddling in the politics of all surrounding, including Macedon, which they have at times been a de facto had quite strong influence over. More recently however it has been a subject under Macedon. With Alexander’s death however Dardania is free and, like Taulantia to its south, quite likely to once again end up at odds with Macedon.
Celto-Pannonians:
By this time a number of Celtic tribes had already settled in the Pannonian basin and along the Danube. Not long from our start date Celtic peoples would also come to invade Macedon and Anatolia, eventually settling down to form a Galatian kingdom (something a Celtic Tribe that conquers land and relocates to Anatolia can also do in the game).
Nonetheless the Celto-Pannonian Tribes are not as numerous as either the Dacians and Getians to the east or the Illyrians to the west:
- Dindaria
- Tilataeia
- Scordiscia
- Cotinia
Illyrians:
The western Balkans and the Dalmatian coast is dominated by a number of Illyrian Tribes. Many of these have regular contact, both trade and diplomacy, with Italic and Greek states but are also home to a number of raiders and pirates which are not as well seen by their neighbors. Should the Illyrian tribes unite and form a greater Illyria they could become a more credible threat to their surroundings.
- Armistia
- Daorsia
- Pieria
- Delmatia
- Libernia
- Ditionia
- Oxuaioia
- Scordiscia
- Breucia
- Deuria
- Ditionia
- Libernia
- Cataria
- Cotinia
- Iapodia
- Istria
- Abria
Dacian Tribes:
Not much is known of the origin of the Dacians, but like the Illyrians in the west this people was likely present here long before the start of our era. Dacian culture is broken up into a number of small tribal states that are all part of a greater Dacian identity. As mentioned they may eventually form a stronger united Dacia that could pose a bigger threat to their neighbors.
As divided as Dacia is however it also harbors a number of commodities such as large amounts of Iron, Precious and Base Metals.
- Potulatensia
- Bastia
- Ratacensia
- Buridavensia
- Caucoensia
- Ansamensia
- Appulia
- Albocensia
- Saldensia
That was all for today! Next week we will be back with a slightly unusual diary. Diving down even deeper into the modding capabilities of Imperator and all the new things that modders will now be able to do to alter the game.