Balance of power modding

本页面所适用的版本可能已经过时,最后更新于1.13
秋起.讨论 | 贡献2024年8月9日 (五) 15:56的版本

A balance of power (also internally referred to as bop or power balance) is typically used to represent a conflict between two sides, using a progress bar divided into distinct sections and decisions to push it towards some side for benefits. These are defined in /Hearts of Iron IV/common/bop/*.txt files.

Each balance of power consists of 2 or more sides. While a balance of power can contain more than 2 sides, at any time the player may only see 2 which are chosen in the effect to initialise the balance of power.
To track the current balance, a single value is used, which can range from -1 to 1 with up to 3 decimal points. The [math]\displaystyle{ [-1; 0) }[/math] range is used for the left side, [math]\displaystyle{ [0; 1] }[/math] is used for the right side. A side having power only has a cosmetic impact, changing the icon and the text used in scripted localisation. A side isn't inherently set as being left or right, instead it is assigned which side is on which half when the power balance is initialised.

Depending on the value, the country is placed in some range representing the exact degree of power on a certain side. This is usually used to grant certain modifiers or to execute an effect upon entering it. Most ranges are placed inside of sides, making it only possible to enter when that side is active. A range may also not be assigned to any side and be always visible, usually used for a single "balanced" range in the middle.
A range being assigned to a side only means that it'll be visible when the range is active, and a side may have ranges on both halves of the balance at the same time. Entering a range defined on a certain side does not necessitate that the balance is tipped in favour of that side. For example, if a side is set as a left side and yet it includes a range of [math]\displaystyle{ [0.1, 0.3) }[/math], then if the value is at 0.2, then the balance will be tipped towards the right side visually, but it will still be inside of that range.

A balance of power is tracked globally. If 2 countries are assigned the same BoP, then the value of the balance will always be exactly the same between both countries. If both of these countries have modifiers pushing the BoP to some side daily or weekly, the effects of these modifiers will stack.

Code structure

Balances of power are created in any /Hearts of Iron IV/common/bop/*.txt file. A BoP is defined as a root-level block, where the name of the block determines the unique ID of the BoP. In case of duplicates, the error.log entry Template ID duplicate: bop_name is generated and the game will prioritise the one that was created later, determined using the filename and order in files.
There are no strictly mandatory arguments, however it is preferred to have at least 2 sides as the UI is built on the assumption that there is always a left and a right side active. In particular, these arguments exist:

  • intial_value = -0.1 is the default value of the BoP. If unset, defaults to 0.
  • left_side = side_id is the default left side of the BoP.
  • right_side = side_id is the default right side of the BoP.

The default values are used when the BoP is first initialised with set_power_balance (unless overwritten by its arguments) or when that same effect includes set_default = yes to reset it.

  • decision_category = TAG_example_category will move that decision category to the balance of power view. In particular, this ensures that it's impossible to access the decision category in any way other than by the balance of power. Any trigger blocks in the decision category, such as allowed = { ... }, are still checked for the country with the BoP, however.

In addition, side = { ... } and range = { ... } are possible with more detail.

Side

Each side is defined using a side = { ... } block in the balance of power. In particular, these arguments exist:

  • id = side_id is the identifier used for the side. There should not be overlap in the same BoP, however overlap may exist between different BoPs. In case of overlap in the same BoP, only the first-created side will be used with a Side ID duplicate: side_id entry in error.log.
  • icon = GFX_idea_unknown is the spriteType used as the image to represent the side. If undefined or if the spriteType doesn't exist, the icon will not be created with no default to replace it.
  • range = { ... } is a range set to exist if and only if that side is active.

Range

A range is defined as a range = { ... } block which may exist either directly in the BoP or inside of a side = { ... }. This is used to determine when the range will be visible to the player: a range directly in a BoP is always visible, while a range in a side will only be visible if that side is visible. There are 3 mandatory attributes:

  • id = range_id is used to assign an ID to the specified range. There shouldn't be duplicates across the entire BoP (even in different sides), but there may be between different BoPs. A duplicate will be marked with a Range ID duplicate: range_id entry in error.log. In this case, the game will still display each range with the same ID that is not in a disabled side. However, this can cause issues with strict non-equal comparison when using is_power_balance_in_range: in particular, the game will only recognise the range that's placed furthest to the left. For example, if there are duplicate range_id on intervals of [math]\displaystyle{ [-0.3, -0.1) }[/math] and [math]\displaystyle{ [0.1, 0.3) }[/math], then a strict equilibrium will be recognised as being to the right of range_id, but not to the left of it.
  • min = -0.1 is the minimum value in the range's interval. If the value is exactly at this point, this range will be applied.
  • max = 0.1 is the maximum value in the range's interval. If the value is exactly at this point, this range will be applied if and only if max is set to 1. Otherwise, the exact point is excluded from the interval.

There are also blocks that decide which impact exactly a range has on the country when the value is inside that. In particular:

  • modifier = { ... } decides on the modifiers applying to the country when the value is in that range.
  • rule = { ... } decides on the game rules applying to the country when the value is in that range.
  • on_activate = { ... } executes effects the instant that the value enters this range.
  • on_deactivate = { ... } executes effects the instant that the value exits this range.

Example

TAG_example_balance = {
    initial_value = 0.25
    left_side = default_left_side
    right_side = default_right_side
    decision_category = TAG_example_category
    range = {
        id = mid_range
        min = -0.1
        max = 0.1
        modifier = {
            war_support_weekly = 0.5
        }
        rule = {
            can_create_faction = yes
        }
    }

    side = {
        id = default_left_side
        icon = GFX_idea_generic_agrarian_society
        range = {
            id = left_side_range
            min = -1
            max = -0.1
            modifier = {
                war_support_weekly = 0.1
            }
            on_activate = {
                random_owned_controlled_state = {
                    limit = {
                        NOT = { is_core_of = PREV }
                    }
                    add_core_of = PREV
                    set_state_flag = cored_by_bop
                }
            }
            on_deactivate = {
                random_owned_controlled_state = {
                    limit = {
                        has_state_flag = cored_by_bop
                    }
                    remove_core_of = PREV
                    clr_state_flag = cored_by_bop
                }
            }
        }
    }
    side = {
        id = default_right_side
        icon = GFX_idea_generic_degauss_ship_hulls
        range = {
            id = right_side_range
            min = 0.1
            max = 1
            modifier = {
                war_support_weekly = 0.9
            }
        }
    }
}

Implementation

As a balance of power isn't inherently assigned to any country, it must be manually initialised. This is done using the set_power_balance effect. For example, this is the most bare-bones way to initialise the BoP, which will set it to the default:

set_power_balance = {
    id = TAG_example_balance
}

The effect can also be used to change details about the currently-active balance of power, such as changing the sides or the value:

set_power_balance = {
    id = TAG_example_balance
    left_side = default_left_side
    right_side = default_right_side
    set_value = 0.1
}

If necessary to reset it completely to the default, set_default = yes will do so.

set_power_balance = {
    id = TAG_example_balance
    set_default = yes
}

As an effect, it can be used in any effect block. If it should be initialised at the game's start, this is usually done in country history, alternatively it can be initialised when needed, such as in a focus reward or an event option.

Balance of power modifiers

参见:Modifiers

Balance of power modifiers are static modifiers that are applied directly to a balance of power, from which they are cloned to each country that has the BoP assigned. As static modifiers, they're defined in any /Hearts of Iron IV/common/modifiers/*.txt file. They typically include within of themselves only the power_balance_daily and/or power_balance_weekly modifiers in order to gradually tip the balance towards one side. These modifiers will be shown in the tooltip when hovering over the current value.

An example of a BoP modifier is as such:

my_bop_modifier = {
    power_balance_weekly = -0.01 # Changes by 1% each week to the left
}

Balance of power modifiers are added via the add_power_balance_modifier effect as such:

add_power_balance_modifier = {
    id = my_bop    # The ID of the balance of power
    modifier = my_bop_modifier # The modifier to add
}

The remove_power_balance_modifier effect, with the same syntax, or remove_all_power_balance_modifiers can be used to remove these from the country.

Since these are duplicated to each country that has the BoP, the weekly or daily effect for each modifier will increase for each country that is assigned the BoP. For example, if the BoP is assigned to 3 countries, power_balance_daily = 0.01 in a BoP modifier will, in practice, make the balance get pushed by 3% to the right in total daily.

Effects

Parts of this section are transcluded from Effect § Balance of power.

Other than set_power_balance, these effects exist which can be used to modify a balance of power:


Balance of power-related country-scoped effects:
Name Parameters Examples Description Notes Version Added
remove_power_balance id = <BoP ID>
Balance of power to modify.
remove_power_balance = {
    id = my_bop
}
Removes the balance of power in entirety. 1.12
add_power_balance_value id = <BoP ID>
Balance of power to modify.

value = <decimal>
The value to add.
tooltip_side = <BoP side ID>
The side to show in the tooltip. Optional.

add_power_balance_value = {
    id = my_bop
    value = -0.1
    tooltip_side = my_bop_side
}
Pushes the balance of power towards one side. 1.12
add_power_balance_modifier id = <BoP ID>
Balance of power to modify.

modifier = <static modifier>
The static modifier to apply.

add_power_balance_modifier = {
    id = my_bop
    modifier = my_static_modifier
}
Applies a balance of power modifier. 1.12
remove_power_balance_modifier id = <BoP ID>
Balance of power to modify.

modifier = <static modifier>
The static modifier to apply.

remove_power_balance_modifier = {
    id = my_bop
    modifier = my_static_modifier
}
Cancels a balance of power modifier. 1.12
remove_all_power_balance_modifiers id = <BoP ID>
Balance of power to modify.
remove_all_power_balance_modifiers = {
    id = my_bop
}
Cancels all balance of power modifiers. 1.12
set_power_balance_gfx id = <BoP ID>
Balance of power to modify.

side = <BoP side ID>
The side whose GFX to change.
gfx = <sprite>
The sprite to change the GFX to.

set_power_balance_gfx = {
    id = my_bop
    side = my_bop_side
    gfx = GFX_my_bop_side_new
}
Changes the appearance of one of the sides within the balance of power. Sprites are defined within /Hearts of Iron IV/interface/*.gfx files. 1.12


Triggers

Parts of this section are transcluded from Triggers § Balance of power.

These triggers exist for checking the state of a balance of power. Some of these can only be used in country scope.

Balance of power-related country-scoped triggers:
Name Parameters Examples Description Notes Version Added
has_power_balance id = <bop ID>
The balance to check for.
has_power_balance = {
    id = TAG_my_bop
}
Checks if the current scope has the specified balance of power active. 1.12
has_any_power_balance <bool>
Boolean.
has_any_power_balance = yes
Checks if the current scope has any balance of power active. 1.12
power_balance_value id = <bop ID>
The balance to check in.

value = <float>
The value to check for.

power_balance_value = {
    id = TAG_my_bop
    value > 0.7
}
Checks if the current scope has the specified value within the balance of power. Either =, >, or < operators are allowed. 1.12
power_balance_daily_change id = <bop ID>
The balance to check in.

value = <float>
The value to check for.

power_balance_daily_change = {
    id = TAG_my_bop
    value < -0.01
}
Checks if the current scope's balance of power changes each day by the specified value. Either =, >, or < operators are allowed. 1.12
power_balance_weekly_change id = <bop ID>
The balance to check in.

value = <float>
The value to check for.

power_balance_weekly_change = {
    id = TAG_my_bop
    value < -0.01
}
Checks if the current scope's balance of power changes each week by the specified value. Either =, >, or < operators are allowed. 1.12
is_power_balance_in_range id = <bop ID>
The balance to check in.

range = <range ID>
The range to check for.

is_power_balance_in_range = {
    id = TAG_my_bop
    range > TAG_my_bop_right_range
}
Checks if the current scope's balance of power value lies within the specified range. Ranges are defined within the balance of power. Can use either =, >, and < operators. In case of > or <, the comparison is 'strict', i.e. excluding the range itself. 1.12
is_power_balance_side_active id = <bop ID>
The balance to check in.

side = <side ID>
The side to check.

is_power_balance_side_active = {
    id = TAG_my_bop
    side = TAG_my_bop_right_range
}
Checks if the specified balance of power has a side active. Sides are defined within the balance of power. "Active" means that the side is among those that are currently visible instead of relying on the current value. 1.12
has_power_balance_modifier id = <bop ID>
The balance to check in.

modifier = <modifier ID>
The static modifier.

has_power_balance_modifier = {
    id = TAG_my_bop
    modifier = TAG_my_bop_modifier
}
Checks if the current scope's balance of power value activates a modifier. BoP modifiers are defined within /Hearts of Iron IV/common/modifiers/*.txt files, while they're activated in the balance of power definition. 1.12