Try   HackMD

How to write a fast targeted decision by Yard1

So, you want to get on that sweet sweet targeted action, don't you? Have little flags next to the icons, code that's easier to write and maintain, dynamic targets The whole jazz. But, just after you write one, you get that dreaded feeling, that pit in the stomach Oh no. I can feel the game slowing down!. Well, no more! Just follow these simple practices to have your targeted decisions run as fast as possible!

Please note that this short guide focuses only on the performance aspect.

Limit your targets

This part is the most important. You know how target_trigger works - it checks countries/states, and those that match the trigger become decision targets. However, unless you limit the possible targets, every country or state in the game will be checked, every day! If you put in expensive triggers in there, it can really slow everything down. How can we limit targets, then, so that only a certain subset of entities is checked?

targets

First way is to use targets = {}. You simply put in the country tags, state IDs (depending on the type of your decision) or variables inside, and target_trigger will run only on those. Common use cases:

# we know beforehand that only TAG will be checked, so why check every country if we only want one?
target_trigger = {
    FROM = { tag = TAG }
}

targets = { TAG }
# more than one tag is possible
target_trigger = {
    FROM = {
        OR = {
            tag = TAG
            tag = BAG
        }
    }
}

targets = { TAG BAG }
# works with states, too
state_target = yes

target_trigger = {
    FROM = {
        OR = {
            state = 123
            state = 987
        }
    }
}

targets = { 123 987 }
# and with variables, both build in and the ones you set yourself. You can also chain them.
target_trigger = {
    FROM = {
        owns_state = 123
    }
}

targets = { 123.owner }
target_trigger = {
    FROM = {
        is_faction_leader = yes
        is_in_faction_with = ROOT
    }
}

targets = { faction_leader }

You can also have it target dynamic countries with targets_dynamic = yes in addition.

# we want to also get the eventual civil war countries (D## tags)
target_trigger = {
    FROM = {
        OR = {
            original_tag = TAG
            original_tag = BAG
        }
    }
}

targets = { TAG BAG }
targets_dynamic = yes

target_array

Very similar to targets, but instead of predefining the possible values in the file, you can just use an array - again, built-in, or one you've set up yourself. If you use multiple target_arrays, they will be merged into one. Common use cases:

target_trigger = {
    FROM = {
        is_in_faction_with = ROOT
    }
}

target_array = allies
target_trigger = {
    FROM = {
        is_subject_of = ROOT
    }
}

target_array = subjects
target_trigger = {
    FROM = {
        OR = {
            is_in_faction_with = ROOT
            has_war_with = ROOT
        }
    }
}

target_array = enemies
target_array = allies
state_target = yes

target_trigger = {
    FROM = {
        is_controlled_by = ROOT
    }
}

target_array = controlled_states
state_target = yes

target_trigger = {
    FROM = {
        is_core_of = TAG
    }
}

target_array = TAG.core_states

Limit your ROOTs

Make sure to use allowed and target_root_trigger to ensure that the target checking stage is only reached when really necessary. allowed is only checked on game start and when a country spawns, therefore you should only put in tag, original_tag and has_dlc triggers in it. target_root_trigger is checked daily, and is similar to visible - however, visible is checked after targets have been selected 9and thus allows the use of FROM), but target_root_trigger is checked before that (and thus only has ROOT). If target_root_trigger fails, target_trigger won't be ran at all. Compare the following:

# BAD
my_decision = {
    # decision is only visible for one country, but is being checked for every country...
    visible = {
        tag = TAG
        has_completed_focus = my_focus
    }
    
    # ...and every country checks every country for targets
    target_trigger = {
        FROM = {
            is_in_faction_with = ROOT
        }
    }
    
    complete_effect = {
        add_political_power = 10
    }
    
    # total checks - n*n, where n is the number of existing countries
}
# GOOD
my_decision = {
    # first we make sure that the decision will only be checked for TAG
    allowed = {
        tag = TAG
    }

    # since we don't care about the targets if the focus isn't complete, we avoid having to check the targets and discard all of them with target_root_trigger
    target_root_trigger = {
        has_completed_focus = my_focus
    }
    
    # we only check for targets if target_root_trigger is true
    target_trigger = {
        FROM = {
            is_in_faction_with = ROOT
        }
    }

    # and instead of checking all countries on the map and discarding those that don't meet the trigger, we make use of a built in array
    target_array = allies
    
    complete_effect = {
        add_political_power = 10
    }
    
    # total checks - 1*m, where m is the number of countries in faction (which is always going to be smaller than n)
}

Remember that targets are only added, and never removed

Once a country/state meets the triggers in target_trigger, it is added to possible targets. That means target_trigger will not be ran for that particular country/state again. Make sure that you put in all checks (such as exists = yes) in visible instead.

# BAD: if a country stop existing later, the decision will still be pickable
target_trigger = {
    FROM = {
        tag = TAG
        exists = yes
    }
}
# GOOD: we make sure to check if the country exists after it becomes a target
target_trigger = {
    FROM = {
        tag = TAG
    }
}

visible = {
    FROM = {
        exists = yes
    }
}

Use cheaper scopes

This section applies to not only targeted decisions, but anything. Below are some common mistakes and how to fix them.

# BAD: is ran for every state on the map
any_state = {
    is_controlled_by = PREV
    has_state_flag = my_flag
}
# GOOD: is only ran for the states we actually care about - the ones controlled
any_controlled_state = {
    has_state_flag = my_flag
}
# BAD: O(n) complexity, when we only care about the country that owns that state - and only one country can own a state at a time
any_country = {
    owns_state = 123
    has_country_flag = my_flag
}
# GOOD: scope directly to state's owner (or controller)
123 = {
    owner = {
        has_country_flag = my_flag
    }
}
# BAD: is ran for every state on the map
any_state = {
    is_core_of = TAG
    has_state_flag = my_flag
}
# GOOD: while there isn't a scope for core states, we can use an array
any_of_scopes = {
    array = TAG.core_states
    tooltip = MY_TOOLTIP # optional, but by default, array operations don't have tooltips. If you define one here, it will also show the triggers inside automatically in addition to whatever is in the localisation.
    has_state_flag = my_flag
}

Questions? DM on Discord.
Yard