A modder tool to assist in the process of linearising the leader traits contained in
common/traits.txt
.
Not a modder? Check out our releases.
In Victoria II generals and admirals (collectively leaders) are each granted a personality and a background (collectively traits). Each trait grants stats to its leader (some beneficial and some not), such that the player has to take into account both personality and background at once to figure out how valuable a leader is. The consequence is that the leader selection interface, where the player picks the right leader for an army or fleet, is notoriously unhelpful:
Picking a general from among the 1836 Austrian officer corps to lead the Fifth Army. Unless the player has memorised what Wrathful and Natural Born Leader do (out of the 105 base game personalities and 57 backgrounds) there is simply no telling at a glance that Julius von Haynau is a good choice when on the offensive thanks to his +3 attack score.
This shortcoming is somewhat mitigated by the tooltip which does list aggregate stats for a leader, though that only works for one at a time. Another mitigation strategy is reliance on the visible prestige bar: a competent leader may see a lot of battles, and eventually through victories will amass leader prestige setting him apart from the rest. (This does not work as well to identify e.g. fast leaders, as they are also valuable for non-combat tasks and may not see as much action.)
The Trait Lineariser is a tool to help the modder turn each possible combination of separate traits into one combined composite trait. Now Julius von Haynau is a Wrathful, Natural Born Leader all at once, which is one possibility out of 105×57=5985. This preserves what stats could be gained from traits, the flavour that the personalities & backgrounds add to the game, and crucially this approach does all of this while preserving how unlikely it is to roll any kind of leader. (More explanation in the Implementation details section.)
By including the stat totals with the name of the composite trait, the leader selection interface can be modified to finally present the player with complete, accurate information:
The same officer corps. Julius von Haynau stands out, though now it is more obvious he is matched by Josef Radetzky despite each having completely different respective personalities and backgrounds.
(Side-by-side interface comparison.)
The country military screen suffers the most from the switch to composite traits, as reflected in this side-by-side of the left side panels including the list of military leaders:
Flaws include:
- the list of leaders feels even more cramped, and the long descriptions of composite traits may get cut-off (not displayed above)
- the checkbox that controls leader use has been moved from the name column to the army column to make room for the long trait descriptions, and may obscure the leader’s army location
- the list “leaks” the tooltips of its leaders down below beyond its normal boundaries which can impede but does not outright prevent using the interface elements there (i.e. the leader creation buttons and the automatic setting checkboxes)—this may be a base game limitation exacerbated by the long descriptions
- the resulting traits contained in
common/traits.txt
are much harder to understand & modify: if one personality needs to be adjusted (e.g. it needs to grant more attack score), all corresponding composite traits have to be adjusted in a consistent manner
The game allows the player to sort the list of leaders in the leader selection screen, by clicking the list headers. While this works fine to sort by leader prestige or by name, this appear to be of little use for traits as the game seemingly relies on an internal order (possibly sorting by leader creation date, with older leaders first). This is base game behaviour that affects the regular trait system with separate personality & background as well.
The composite traits always include their stat description wherever they are named, which includes the effect tooltips of decisions or events that define leaders:
This tends to look clumsy.
A GitHub Workflow allows you to run the tool on the data of your choice without needing to install anything. You must know how to fork a repo and make a commit with new files. Proceed as follows:
- fork this repo
- from your fork settings enable GitHub Actions
- once in your fork you can work on any branch
- place your mod data root inside an
input
directory in the project root, the name doesn’t matter (read about the mod data requirements: in the end trait data should be found atinput/<mod-data-root>/common/traits.txt
) - there must be exactly one directory of mod data inside
input
- add this data to be committed, e.g.
git add --force input
- commit the data & push it to the GitHub fork
- on the GitHub fork visit the Actions tab & wait for the job to appear
- if no job appear, you can also try your hand at running it yourself from within the Actions tab of your fork
- if you are a first-time contributor the job might require approval from this repository to run, in which case please open a Request for workflow run issue and allow the maintainers some time to answer it
- you can now watch and wait for progress to be made… and wait… and wait… and maybe wait just a little bit more
- once the job has successfully completed, an archive of the generated output will appear in the job summary (away from the logs)
Read Using the tool to learn how to use this generated data.
Nix users can follow a haskell-nix workflow through the provided Nix files. It requires cabal2nix to be installed. This allows you to prepare, and then drop into a Nix-provided environment. From within the project directory:
$ # compute packages
$ mkdir -p nix && pushd nix && { cabal2nix .. >trait-lineariser.nix; popd }
$ # enter development environment
$ nix-shell
This is otherwise a regular cabalised project so all users should be able to proceed as usual. For instance when using the new style of distribution, from within the project directory:
$ # not strictly necessary, but it's nice to get installation of the
$ # dependencies out the way
$ cabal v2-build --only-dependencies
$ # hack on the source, if you feel like it
$ #...
$ # not necessary, but can be nicer e.g. when looking at build errors
$ cabal v2-build
$ # run the build
$ cabal v2-run trait-lineariser -- --help
The tool operates on a directory that respects mod structure. (This includes the unmodded game directory.) It expects the following:
- the traits file i.e.
common/traits.txt
(copy over the unmodded traits if your mod does not already have one) - the
localisation
directory (it can be empty)
You will likely also need to consider order-of-battle data, which is normally provided in the
history/units
directory (including the subdirectories therein e.g. history/units/1861
for other
bookmarks, usually). Whenever an OOB file defines a leader, you’ll want it to be updated to use
linearised traits. The tool is designed to handle this tedious process, but will only consider OOB
data from your mod. This may require you copying over the unmodded data from the game into your
mod directory if you haven’t already. In addition to or instead of this, you may want to add
appropriate replace_path
instructions to you .mod
file to disallow the game from using unmodded
OOB data with your mod.
The tool is non-destructive. It places all its results in a fresh output
directory, aborting
the process if it already exists, and will not modify input files in-place. The results within
output
include:
- a
common/traits.txt
file containing linearised traits - a
localisation/linearised-traits.csv
file containing localisation entries - a
history/units
directory containing order-of-battle data that needed to be updated to using the linearised traits
This result is not complete mod data, in particular it does not include OOB data that didn’t need to be linearised. Consequently you as the modder have the responsibility to merge in the results into your mod as needed.
Check out the output of --help
to learn about extra options. These mostly have to do with control
of the localisation information that is used to generated the resulting entries.
You will also likely want to modify the interface to display the linearised traits appropriately.
For this purpose it is suggested you take a look at the provided reference implementation in
resources
(also provided as a mini-mod release).
The following lists the tweaks contained in the reference implementation (against the unmodded game).
In order to put the stat summary on a grid, more predictable widths are required for some glyphs:
- percent sign (%) must be thinner with
xadvance=6
instead of the original oversized 9 - en-dash (–) is repurposed in the translation strings as a numeric minus due to its resemblance—its characteristics should match that of the plus sign (+)
The reference implementation contains a modified version of the original Arial 12pt font that the game uses featuring the above tweaks:
gfx/fonts/Arial12_numeric.fnt
gfx/fonts/Arial12_numeric.tga
In addition extra text colours are also used in the translation strings, to improve the readability of leader stat summaries. These colours as well as any extra font should be referenced in:
interface/core.gfx
: the version we provide adds all required colours not just to support colour highlights withArial12_numeric
, but for all fonts
Changing the appearance of the columns that make up this grid involve the following supporting files:
gfx/interface/sortbutton_1.dds
: a dummy 1-width column header background texture, actually invisiblegfx/interface/sortbutton_240.dds
: a column header background texture that is twice as wide, to subsume both the Personality and Background column backgrounds into oneinterface/general_gfx.gfx
: should reference the two above textures
As well as the following tweaks in interface/unitpanel.gui
:
- for the elements corresponding to the selected leader:
- hide
unitleader_personality
by collapsing it to 0 width & height - nudge & expand
unitleader_background
to take over the former Personality column, and switch to the tweaked font
- hide
- for the column headers of the list of candidates:
- hide the
sort_personality
background by substituting in thesortbutton_1.dds
texture - nudge & expand the
sort_background
background to take over the former background, and substitute in thesortbutton_240.dds
texture
- hide the
- for the elements corresponding to each leader entry in the list of candidates:
- nudge & expand the
unitleader_background
text box to take over the personality box, and switch to the tweaked font
- nudge & expand the
Tweaks in interface/country_military.gui
:
- nudge the
background
text box, switch to the tweaked font, and turn it into flowing text - collapse the
personality
text box - nudge the
use_leader
checkbox
Further tweaks performed with the help of localisation/linearised-traits.csv
:
- some of the interface elements mentioned previously are effectively hidden by setting an
associated localisation entry to the special value
*
, in particular those referring to leader personalities - interface elements that previously referred to leader backgrounds are tweaked to talk about personality & background as a whole (sometimes as “personality & background”, sometimes as “traits”)
These entries are located at the top of the localisation file. The rest of the entries are the translations for the composite traits. Note that the tool includes both kinds of entries when it generates its output.
Linearised traits are achieved by storing the formerly separate personalities & backgrounds of
leaders as a single composite background under the hood, which acts as a combination of the two.
Only one personality is left: unit_personality
with no stats, which should not be confused with
the special no_personality
entry that the game expects and uses e.g. for leaderless armies.
The “unit” and “pair” terminology comes from the mathematical Cartesian product operation, which is what is taking place here when turning M personalities and N backgrounds into M×N composite traits. The “linearised” terminology comes from turning two separate lists into a longer, unified one.
A stat summary consists of the following, with appropriate colour coding:
attack score, defence score, speed modifier, extras
Extras, if present, denote one or two remarkable modifier to organisation and/or morale—think of it as a tiebreaker for generals with otherwise similar stats. Leader prestige, indicated by the column to the left of the portrait, plays a similar role. Extras are displayed as one of the following from best to worst: ‘!!!’, ‘?’, blank when unremarkable, ‘¿’, ‘¡¡¡’.
A stat is displayed as blank when neutral (e.g. no bonus to attack, no speed modifier) or unremarkable. Great care has been taken that stat summaries have pixel-perfect alignment with one another at all times in the leader selection panel, and you can think of all aggregate stats as being displayed in columns (e.g. for attack) as well as in leader rows.
In unmodded Heart of Darkness the highest defence score a leader can reach is +6 through the School of Defense background together with one of the Cautious, Implacable, or Defiant personalities. Hitting the right background has probability 1 in 105, one of the right personalities is 3 in 57, and together that will happen with probability 3 in 5985.
By linearising each distinct combination into 5985 cases, we can notice that hitting one of the 3 pairs of interest has exactly that same probability. All that this requires is that the game is good enough about its random trait rolls that the personality and background picks can be considered independent, and that a large amount of traits does not compromise this process of random selection. The latter was (very) informally tested:
Running the game with 16384 possible leader personalities (only 1 possible background). Frequency histogram of the personalities from many generated leaders.