﻿# 2.6.0 — Relationship proscription / persistent bounty-office hunters.
# Separate from political faction proscriptions so neither system can corrupt the other's target state.

ddm_issue_love_proscription_effect = {
    # root = proscribing ruler; saved scope ddm_love_proscription_target = unfaithful lover.
    # Defied-the-Writ immunity blocks the relationship writ itself so an immune target never accumulates
    # a misleading "active" contract that no hunter is willing to answer.
    save_scope_as = ddm_love_proscriber
    if = {
        limit = { scope:ddm_love_proscription_target = { NOT = { has_character_flag = ddm_bounty_hunter_immunity } } }
        if = { limit = { NOT = { has_variable = ddm_love_bounty_attempts_year } } set_variable = { name = ddm_love_bounty_attempts_year value = 0 } }
        scope:ddm_love_proscription_target = {
            set_variable = { name = ddm_love_proscribed_by value = scope:ddm_love_proscriber }
            set_variable = { name = ddm_love_proscription_attempts_year value = 0 }
            add_character_flag = ddm_love_proscription_active
        }
        if = {
            limit = { NOT = { has_variable_list = ddm_love_proscriptions } }
            add_to_variable_list = { name = ddm_love_proscriptions target = scope:ddm_love_proscription_target }
        }
        else_if = {
            limit = { NOT = { any_in_list = { variable = ddm_love_proscriptions this = scope:ddm_love_proscription_target } } }
            add_to_variable_list = { name = ddm_love_proscriptions target = scope:ddm_love_proscription_target }
        }
    }
}

ddm_clear_love_proscription_effect = {
    # Current scope is the proscribing ruler. Do not depend on ROOT from a character interaction.
    save_scope_as = ddm_love_proscriber
    if = {
        limit = {
            exists = scope:ddm_love_proscription_target
            scope:ddm_love_proscription_target = {
                has_variable = ddm_love_proscribed_by
                var:ddm_love_proscribed_by = scope:ddm_love_proscriber
            }
        }
        if = {
            limit = { has_variable_list = ddm_love_proscriptions }
            remove_list_variable = { name = ddm_love_proscriptions target = scope:ddm_love_proscription_target }
        }
        scope:ddm_love_proscription_target = {
            if = {
                limit = { has_variable = ddm_love_bounty_hunter var:ddm_love_bounty_hunter = { is_alive = yes } }
                var:ddm_love_bounty_hunter = { remove_character_flag = ddm_hunter_on_assignment }
            }
            remove_character_flag = ddm_love_proscription_active
            remove_variable = ddm_love_proscribed_by
            remove_variable = ddm_love_proscription_attempts_year
            remove_variable = ddm_love_bounty_hunter
            remove_variable = ddm_love_bounty_proscriber
        }
    }
}

# Creates one intentionally variable-quality hunter. The broad template spread makes some bounty
# hunters genuinely dangerous and others obvious bad hires without manufacturing super-characters.
ddm_update_hunter_level_effect = {
    if = { limit = { NOT = { has_variable = ddm_hunter_xp } } set_variable = { name = ddm_hunter_xp value = 0 } }
    if = { limit = { NOT = { has_variable = ddm_hunter_level } } set_variable = { name = ddm_hunter_level value = 1 } }
    if = { limit = { NOT = { has_variable = ddm_hunter_attempts } } set_variable = { name = ddm_hunter_attempts value = 0 } }
    if = { limit = { NOT = { has_variable = ddm_hunter_wins } } set_variable = { name = ddm_hunter_wins value = 0 } }
    if = { limit = { NOT = { has_variable = ddm_hunter_losses } } set_variable = { name = ddm_hunter_losses value = 0 } }
    if = { limit = { NOT = { has_variable = ddm_hunter_live_captures } } set_variable = { name = ddm_hunter_live_captures value = 0 } }
    if = { limit = { NOT = { has_variable = ddm_hunter_kills } } set_variable = { name = ddm_hunter_kills value = 0 } }
    if = { limit = { var:ddm_hunter_xp >= 25 var:ddm_hunter_level < 2 } set_variable = { name = ddm_hunter_level value = 2 } add_prowess_skill = 1 }
    if = { limit = { var:ddm_hunter_xp >= 75 var:ddm_hunter_level < 3 } set_variable = { name = ddm_hunter_level value = 3 } add_prowess_skill = 1 add_intrigue_skill = 1 }
    if = { limit = { var:ddm_hunter_xp >= 150 var:ddm_hunter_level < 4 } set_variable = { name = ddm_hunter_level value = 4 } add_prowess_skill = 2 add_martial_skill = 1 }
    if = { limit = { var:ddm_hunter_xp >= 300 var:ddm_hunter_level < 5 } set_variable = { name = ddm_hunter_level value = 5 } add_prowess_skill = 2 add_intrigue_skill = 1 add_martial_skill = 1 }
}

# 2.6.2 — Any hunter who answers either a political or relationship writ becomes a persistent
# Bounty Office professional for that ruler. Political hunters may be vassals or courtiers of vassals,
# so ownership is tracked by patron rather than by current employer.
ddm_register_bounty_office_hunter_effect = {
    # hunter scope; scope:ddm_proscriber (or scope:ddm_love_proscriber) must identify the patron.
    save_scope_as = ddm_registering_hunter
    if = {
        limit = { exists = scope:ddm_proscriber }
        set_variable = { name = ddm_bounty_hunter_patron value = scope:ddm_proscriber }
        scope:ddm_proscriber = { save_scope_as = ddm_hunter_registration_patron }
    }
    else_if = {
        limit = { exists = scope:ddm_love_proscriber }
        set_variable = { name = ddm_bounty_hunter_patron value = scope:ddm_love_proscriber }
        scope:ddm_love_proscriber = { save_scope_as = ddm_hunter_registration_patron }
    }
    if = {
        limit = { exists = scope:ddm_hunter_registration_patron }
        # Registration is deliberately idempotent. UI/roster repair may discover the same hunter
        # through multiple paths; do not repeatedly re-apply the persistent office marker/modifier.
        if = {
            limit = { NOT = { has_character_flag = ddm_registered_bounty_hunter } }
            add_character_flag = ddm_registered_bounty_hunter
        }
        if = {
            limit = { NOT = { has_character_modifier = ddm_bounty_office_hunter } }
            add_character_modifier = { modifier = ddm_bounty_office_hunter }
        }
        ddm_update_hunter_level_effect = yes
        scope:ddm_hunter_registration_patron = {
            # The patron pointer is authoritative. Remove/re-add makes registration idempotent and
            # repairs a save where hunter state survived but the patron's list reference did not.
            if = {
                limit = { has_variable_list = ddm_bounty_hunter_roster }
                remove_list_variable = { name = ddm_bounty_hunter_roster target = scope:ddm_registering_hunter }
            }
            add_to_variable_list = { name = ddm_bounty_hunter_roster target = scope:ddm_registering_hunter }
        }
    }
}

# Reuses the ruler's preferred/experienced hunter when possible. Only creates a new
# hunter when the office has nobody available, so the roster becomes a career system
# rather than an endless stream of disposable characters.
ddm_spawn_love_bounty_hunter_effect = {
    save_scope_as = ddm_love_proscriber

    # Explicit player preference wins when the hunter is still alive, employed here and available.
    if = {
        limit = {
            has_variable = ddm_selected_bounty_hunter
            var:ddm_selected_bounty_hunter = {
                is_alive = yes
                has_character_flag = ddm_registered_bounty_hunter
                NOT = { has_character_flag = ddm_hunter_on_assignment }
                has_variable = ddm_bounty_hunter_patron
                var:ddm_bounty_hunter_patron = scope:ddm_love_proscriber
            }
        }
        var:ddm_selected_bounty_hunter = { save_scope_as = ddm_love_bounty_hunter }
    }

    # Otherwise choose an available veteran from the office roster.
    if = {
        limit = { NOT = { exists = scope:ddm_love_bounty_hunter } has_variable_list = ddm_bounty_hunter_roster }
        random_in_list = {
            variable = ddm_bounty_hunter_roster
            limit = {
                is_alive = yes
                has_character_flag = ddm_registered_bounty_hunter
                NOT = { has_character_flag = ddm_hunter_on_assignment }
                has_variable = ddm_bounty_hunter_patron
                var:ddm_bounty_hunter_patron = scope:ddm_love_proscriber
            }
            save_scope_as = ddm_love_bounty_hunter
        }
    }

    # No veteran available: recruit a new hunter.
    if = {
        limit = { NOT = { exists = scope:ddm_love_bounty_hunter } }
        create_character = {
            gender_female_chance = 50
            age = { 20 50 }
            culture = root.culture
            faith = root.faith
            employer = root
            random_traits = yes
            martial = { min_template_low_skill max_template_very_high_skill }
            prowess = { min_template_low_skill max_template_very_high_skill }
            save_scope_as = ddm_love_bounty_hunter
        }
        random_list = {
            20 = { scope:ddm_love_bounty_hunter = { add_prowess_skill = -4 } }
            30 = { scope:ddm_love_bounty_hunter = { add_prowess_skill = -1 } }
            30 = { scope:ddm_love_bounty_hunter = { add_prowess_skill = 2 } }
            15 = { scope:ddm_love_bounty_hunter = { add_prowess_skill = 5 } }
            5 = { scope:ddm_love_bounty_hunter = { add_prowess_skill = 8 add_trait = brave } }
        }
        scope:ddm_love_bounty_hunter = {
            add_character_flag = ddm_registered_bounty_hunter
            add_character_modifier = { modifier = ddm_bounty_office_hunter }
            set_variable = { name = ddm_hunter_xp value = 0 }
            set_variable = { name = ddm_hunter_level value = 1 }
            set_variable = { name = ddm_hunter_attempts value = 0 }
            set_variable = { name = ddm_hunter_wins value = 0 }
            set_variable = { name = ddm_hunter_losses value = 0 }
            set_variable = { name = ddm_hunter_live_captures value = 0 }
            set_variable = { name = ddm_hunter_kills value = 0 }
        }
        add_to_variable_list = { name = ddm_bounty_hunter_roster target = scope:ddm_love_bounty_hunter }
    }

    scope:ddm_love_bounty_hunter = {
        ddm_register_bounty_office_hunter_effect = yes
        ddm_update_hunter_level_effect = yes
        add_character_flag = { flag = ddm_hunter_on_assignment months = 2 }
    }
    ddm_rebuild_hunter_roster_ui_effect = yes
}

ddm_send_love_bounty_hunter_effect = {
    # root = proscribing ruler; target is saved as ddm_love_proscription_target.
    save_scope_as = ddm_love_proscriber
    if = {
        limit = {
            OR = { NOT = { has_variable = ddm_love_bounty_attempts_year } var:ddm_love_bounty_attempts_year < 12 }
            scope:ddm_love_proscription_target = {
                has_character_flag = ddm_love_proscription_active
                has_variable = ddm_love_proscribed_by
                var:ddm_love_proscribed_by = scope:ddm_love_proscriber
                ddm_can_receive_love_bounty_hunter_trigger = yes
            }
        }
        if = { limit = { NOT = { has_variable = ddm_love_bounty_attempts_year } } set_variable = { name = ddm_love_bounty_attempts_year value = 0 } }
        change_variable = { name = ddm_love_bounty_attempts_year add = 1 }
        scope:ddm_love_proscription_target = {
            if = { limit = { NOT = { has_variable = ddm_love_proscription_attempts_year } } set_variable = { name = ddm_love_proscription_attempts_year value = 0 } }
            change_variable = { name = ddm_love_proscription_attempts_year add = 1 }
        }
        ddm_spawn_love_bounty_hunter_effect = yes
        if = {
            limit = { is_ai = no exists = scope:ddm_love_bounty_hunter }
            send_interface_toast = { type = event_toast_effect_neutral title = DDM_HUNTER_DISPATCH_TOAST left_icon = scope:ddm_love_bounty_hunter right_icon = scope:ddm_love_proscription_target }
        }
        ddm_rebuild_hunter_roster_ui_effect = yes
        scope:ddm_love_proscription_target = {
            set_variable = { name = ddm_love_bounty_hunter value = scope:ddm_love_bounty_hunter }
            set_variable = { name = ddm_love_bounty_proscriber value = scope:ddm_love_proscriber }
            trigger_event = { id = ddm_manager.5300 days = 1 }
        }
    }
}

ddm_love_proscription_monthly_effect = {
    save_scope_as = ddm_love_proscriber

    # First prune dead, captured or externally-cleared writs. Never remove entries from the persistent
    # list while CK3 is iterating it: snapshot valid contracts, then replace the registry after traversal.
    if = {
        limit = { has_variable_list = ddm_love_proscriptions }
        every_in_list = {
            variable = ddm_love_proscriptions
            limit = {
                is_alive = yes
                is_imprisoned = no
                has_character_flag = ddm_love_proscription_active
                has_variable = ddm_love_proscribed_by
                var:ddm_love_proscribed_by = scope:ddm_love_proscriber
                NOT = { is_in_list = ddm_love_proscriptions_rebuild }
            }
            add_to_temporary_list = ddm_love_proscriptions_rebuild
        }
        clear_variable_list = ddm_love_proscriptions
        every_in_list = {
            list = ddm_love_proscriptions_rebuild
            save_temporary_scope_as = ddm_love_proscription_survivor
            scope:ddm_love_proscriber = {
                add_to_variable_list = { name = ddm_love_proscriptions target = scope:ddm_love_proscription_survivor }
            }
        }
    }

    # Exactly one target is chosen per monthly opportunity. The ruler-wide office ceiling is twelve
    # dispatches per year even when several relationship writs are active.
    if = {
        limit = {
            has_variable_list = ddm_love_proscriptions
            OR = { NOT = { has_variable = ddm_love_bounty_attempts_year } var:ddm_love_bounty_attempts_year < 12 }
        }
        random_in_list = {
            variable = ddm_love_proscriptions
            limit = {
                has_character_flag = ddm_love_proscription_active
                has_variable = ddm_love_proscribed_by
                var:ddm_love_proscribed_by = scope:ddm_love_proscriber
                ddm_can_receive_love_bounty_hunter_trigger = yes
            }
            save_scope_as = ddm_love_proscription_target
        }
        if = { limit = { exists = scope:ddm_love_proscription_target } ddm_send_love_bounty_hunter_effect = yes }
    }
}
ddm_love_proscription_yearly_reset_effect = {
    save_scope_as = ddm_love_proscriber
    set_variable = { name = ddm_love_bounty_attempts_year value = 0 }
    if = {
        limit = { has_variable_list = ddm_love_proscriptions }
        every_in_list = {
            variable = ddm_love_proscriptions
            limit = { is_alive = yes has_variable = ddm_love_proscribed_by var:ddm_love_proscribed_by = scope:ddm_love_proscriber }
            set_variable = { name = ddm_love_proscription_attempts_year value = 0 }
        }
    }
}

# Target scope. Clear every active writ type DDM can currently represent against this character.
# Relationship and political proscriptions keep separate owner variables/lists, so clean each through its
# normal revoke path rather than merely deleting the visible modifier.
ddm_clear_all_writs_against_target_effect = {
    # Protection closes target-side writ state immediately without mutating either owner's persistent
    # registry from inside a hunter/event callback. Those registries prune themselves on their next safe pass.
    save_scope_as = ddm_bounty_survivor_target

    if = {
        limit = { has_variable = ddm_proscribed_by var:ddm_proscribed_by = { is_alive = yes } }
        var:ddm_proscribed_by = { save_scope_as = ddm_bounty_political_proscriber }
        every_character_war = {
            limit = {
                primary_attacker = scope:ddm_bounty_survivor_target
                primary_defender = scope:ddm_bounty_political_proscriber
                has_variable = ddm_rebellion_proscription_owner
                var:ddm_rebellion_proscription_owner = scope:ddm_bounty_political_proscriber
            }
            remove_variable = ddm_rebellion_proscription_owner
        }
        ddm_clear_target_proscription_effect = yes
    }

    # Cancel a pending relationship-writ callback as part of the same atomic close.
    if = {
        limit = { has_variable = ddm_love_bounty_hunter var:ddm_love_bounty_hunter = { is_alive = yes } }
        var:ddm_love_bounty_hunter = { remove_character_flag = ddm_hunter_on_assignment }
    }
    remove_character_flag = ddm_love_proscription_active
    remove_variable = ddm_love_proscribed_by
    remove_variable = ddm_love_proscription_attempts_year
    remove_variable = ddm_love_bounty_hunter
    remove_variable = ddm_love_bounty_proscriber
}

# Target scope. One survivor tier replaces the previous tier; it never stacks. Political and relationship
# hunter defenses share this one-year window. A hidden delayed event clears unfinished I-VIII progress.
ddm_refresh_bounty_survivor_modifier_effect = {
    remove_character_modifier = ddm_bounty_survivor_1
    remove_character_modifier = ddm_bounty_survivor_2
    remove_character_modifier = ddm_bounty_survivor_3
    remove_character_modifier = ddm_bounty_survivor_4
    remove_character_modifier = ddm_bounty_survivor_5
    remove_character_modifier = ddm_bounty_survivor_6
    remove_character_modifier = ddm_bounty_survivor_7
    remove_character_modifier = ddm_bounty_survivor_8
    if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 8 } add_character_modifier = { modifier = ddm_bounty_survivor_8 } }
    else_if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 7 } add_character_modifier = { modifier = ddm_bounty_survivor_7 } }
    else_if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 6 } add_character_modifier = { modifier = ddm_bounty_survivor_6 } }
    else_if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 5 } add_character_modifier = { modifier = ddm_bounty_survivor_5 } }
    else_if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 4 } add_character_modifier = { modifier = ddm_bounty_survivor_4 } }
    else_if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 3 } add_character_modifier = { modifier = ddm_bounty_survivor_3 } }
    else_if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 2 } add_character_modifier = { modifier = ddm_bounty_survivor_2 } }
    else_if = { limit = { has_variable = ddm_bounty_survivals_window var:ddm_bounty_survivals_window >= 1 } add_character_modifier = { modifier = ddm_bounty_survivor_1 } }
}

ddm_reset_bounty_survivor_window_effect = {
    remove_character_modifier = ddm_bounty_survivor_1
    remove_character_modifier = ddm_bounty_survivor_2
    remove_character_modifier = ddm_bounty_survivor_3
    remove_character_modifier = ddm_bounty_survivor_4
    remove_character_modifier = ddm_bounty_survivor_5
    remove_character_modifier = ddm_bounty_survivor_6
    remove_character_modifier = ddm_bounty_survivor_7
    remove_character_modifier = ddm_bounty_survivor_8
    remove_variable = ddm_bounty_survivals_window
    remove_character_flag = ddm_bounty_survivor_window_active
}

ddm_record_bounty_survival_effect = {
    # Current scope = quarry who survived a DDM hunter, whether the writ was political or relationship-based.
    if = {
        limit = { NOT = { has_character_flag = ddm_bounty_survivor_window_active } }
        ddm_reset_bounty_survivor_window_effect = yes
        set_variable = { name = ddm_bounty_survivals_window value = 0 }
        add_character_flag = ddm_bounty_survivor_window_active
        trigger_event = { id = ddm_manager.5304 days = 365 }
    }
    if = { limit = { NOT = { has_variable = ddm_bounty_survivals_window } } set_variable = { name = ddm_bounty_survivals_window value = 0 } }
    change_variable = { name = ddm_bounty_survivals_window add = 1 }
    if = { limit = { var:ddm_bounty_survivals_window > 8 } set_variable = { name = ddm_bounty_survivals_window value = 8 } }

    if = { limit = { var:ddm_bounty_survivals_window = 1 } add_prestige = 25 }
    else_if = { limit = { var:ddm_bounty_survivals_window = 2 } add_prestige = 50 }
    else_if = { limit = { var:ddm_bounty_survivals_window = 3 } add_prestige = 75 }
    else_if = { limit = { var:ddm_bounty_survivals_window = 4 } add_prestige = 100 }
    else_if = { limit = { var:ddm_bounty_survivals_window = 5 } add_prestige = 125 }
    else_if = { limit = { var:ddm_bounty_survivals_window = 6 } add_prestige = 150 }
    else_if = { limit = { var:ddm_bounty_survivals_window = 7 } add_prestige = 175 }
    else_if = { limit = { var:ddm_bounty_survivals_window >= 8 } add_prestige = 250 }

    ddm_refresh_bounty_survivor_modifier_effect = yes

    if = {
        limit = {
            var:ddm_bounty_survivals_window >= 8
            OR = { NOT = { has_variable = ddm_bounty_pressure } NOT = { var:ddm_bounty_pressure = 4 } }
            OR = { NOT = { has_variable = ddm_defied_writ_protection } var:ddm_defied_writ_protection = 1 }
        }
        # Existing saves without settings default to protection ON; initialize before the final check.
        if = { limit = { NOT = { has_variable = ddm_defied_writ_protection } } set_variable = { name = ddm_defied_writ_protection value = 1 } }
        if = {
            limit = { var:ddm_defied_writ_protection = 1 }
            remove_character_modifier = ddm_bounty_survivor_8
            add_character_modifier = { modifier = ddm_defied_the_writ years = 3 }
            add_character_flag = { flag = ddm_bounty_hunter_immunity years = 3 }
            ddm_clear_all_writs_against_target_effect = yes
            if = { limit = { is_ai = no } trigger_event = ddm_manager.5303 }
        }
    }
    else_if = {
        limit = { is_ai = no }
        send_interface_toast = { type = event_toast_effect_good title = DDM_BOUNTY_SURVIVED_TOAST left_icon = root }
    }
}

# AI rulers may independently notice actual infidelity, even when the relation is not a publicly exposed
# secret. One annual pass is deliberately conservative: each unfaithful partner has a 20% base chance
# of receiving a writ, rising for personalities that are more likely to retaliate.
ddm_ai_consider_unfaithful_lover_proscription_effect = {
    # root = candidate lover/soulmate; scope:ddm_love_proscriber is the AI ruler considering retaliation.
    save_temporary_scope_as = ddm_ai_unfaithful_lover
    set_variable = { name = ddm_ai_love_proscription_chance value = 20 }
    if = { limit = { scope:ddm_love_proscriber = { has_trait = vengeful } } change_variable = { name = ddm_ai_love_proscription_chance add = 30 } }
    if = { limit = { scope:ddm_love_proscriber = { has_trait = wrathful } } change_variable = { name = ddm_ai_love_proscription_chance add = 20 } }
    if = { limit = { scope:ddm_love_proscriber = { has_trait = sadistic } } change_variable = { name = ddm_ai_love_proscription_chance add = 15 } }
    if = { limit = { scope:ddm_love_proscriber = { has_trait = forgiving } } change_variable = { name = ddm_ai_love_proscription_chance subtract = 15 } }
    random = {
        chance = var:ddm_ai_love_proscription_chance
        save_scope_as = ddm_love_proscription_target
        scope:ddm_love_proscriber = { ddm_issue_love_proscription_effect = yes }
    }
    remove_variable = ddm_ai_love_proscription_chance
}

ddm_ai_try_unfaithful_lover_proscription_effect = {
    save_scope_as = ddm_love_proscriber
    # CK3's every_relation accepts one relation type per iterator. Run lover and soulmate separately;
    # the active-proscription guard prevents a soulmate who is also a lover being selected twice.
    every_relation = {
        type = lover
        limit = {
            is_alive = yes
            age >= 18
            NOT = { has_variable = ddm_love_proscribed_by }
            ddm_is_unfaithful_lover_of_proscriber_trigger = yes
        }
        ddm_ai_consider_unfaithful_lover_proscription_effect = yes
    }
    every_relation = {
        type = soulmate
        limit = {
            is_alive = yes
            age >= 18
            NOT = { has_variable = ddm_love_proscribed_by }
            ddm_is_unfaithful_lover_of_proscriber_trigger = yes
        }
        ddm_ai_consider_unfaithful_lover_proscription_effect = yes
    }
}

# 2.6.1 — Build a UI-safe, sortable view of the persistent Bounty Office registry.
ddm_rebuild_hunter_roster_ui_effect = {
    save_scope_as = ddm_hunter_roster_owner
    save_scope_as = ddm_proscriber

    # Recover every surviving professional tied to this patron, wherever they currently serve.
    every_courtier_or_guest = {
        limit = { has_character_flag = ddm_registered_bounty_hunter has_variable = ddm_bounty_hunter_patron var:ddm_bounty_hunter_patron = scope:ddm_hunter_roster_owner }
        ddm_register_bounty_office_hunter_effect = yes
    }
    every_vassal_or_below = {
        if = { limit = { has_character_flag = ddm_registered_bounty_hunter has_variable = ddm_bounty_hunter_patron var:ddm_bounty_hunter_patron = scope:ddm_hunter_roster_owner } ddm_register_bounty_office_hunter_effect = yes }
        every_courtier_or_guest = {
            limit = { has_character_flag = ddm_registered_bounty_hunter has_variable = ddm_bounty_hunter_patron var:ddm_bounty_hunter_patron = scope:ddm_hunter_roster_owner }
            ddm_register_bounty_office_hunter_effect = yes
        }
    }

    # Recover hunters from active political writs only after they have actually taken the field.
    if = {
        limit = { has_variable_list = ddm_active_proscriptions }
        every_in_list = {
            variable = ddm_active_proscriptions
            limit = { is_alive = yes has_variable = ddm_proscribed_by var:ddm_proscribed_by = scope:ddm_proscriber has_variable_list = ddm_proscription_hunters }
            every_in_list = {
                variable = ddm_proscription_hunters
                limit = { is_alive = yes OR = { has_character_flag = ddm_hunter_on_assignment AND = { has_variable = ddm_hunter_attempts var:ddm_hunter_attempts > 0 } AND = { has_variable = ddm_bountyhunter_completed var:ddm_bountyhunter_completed > 0 } } }
                ddm_register_bounty_office_hunter_effect = yes
            }
        }
    }

    if = { limit = { NOT = { has_variable = ddm_hunter_sort_mode } } set_variable = { name = ddm_hunter_sort_mode value = 1 } }
    if = { limit = { NOT = { has_variable = ddm_hunter_page } } set_variable = { name = ddm_hunter_page value = 0 } }

    # Repair the brief 2.6.2/2.6.3 volunteer-registration regression without deleting real professionals.
    # Snapshot survivors first; removing from ddm_bounty_hunter_roster while iterating it can invalidate
    # CK3's list cursor and is exactly the kind of engine-level failure this office must avoid.
    if = {
        limit = { has_variable_list = ddm_bounty_hunter_roster }
        every_in_list = {
            variable = ddm_bounty_hunter_roster
            if = {
                limit = {
                    is_alive = yes
                    has_character_flag = ddm_registered_bounty_hunter
                    has_variable = ddm_bounty_hunter_patron
                    var:ddm_bounty_hunter_patron = scope:ddm_hunter_roster_owner
                    OR = { NOT = { has_variable = ddm_hunter_attempts } var:ddm_hunter_attempts <= 0 }
                    NOT = { has_character_flag = ddm_hunter_on_assignment }
                    NOT = { has_character_flag = ddm_preferred_bounty_hunter }
                }
                remove_character_flag = ddm_registered_bounty_hunter
                remove_character_modifier = ddm_bounty_office_hunter
                remove_variable = ddm_bounty_hunter_patron
            }
            else_if = {
                limit = { NOT = { is_in_list = ddm_bounty_hunter_roster_rebuild } }
                add_to_temporary_list = ddm_bounty_hunter_roster_rebuild
            }
        }
        clear_variable_list = ddm_bounty_hunter_roster
        every_in_list = {
            list = ddm_bounty_hunter_roster_rebuild
            save_temporary_scope_as = ddm_hunter_roster_survivor
            scope:ddm_hunter_roster_owner = {
                add_to_variable_list = { name = ddm_bounty_hunter_roster target = scope:ddm_hunter_roster_survivor }
            }
        }
    }

    # Synchronize Preferred marker to the authoritative pointer.
    if = {
        limit = { has_variable_list = ddm_bounty_hunter_roster }
        every_in_list = {
            variable = ddm_bounty_hunter_roster
            limit = { is_alive = yes has_character_flag = ddm_registered_bounty_hunter has_variable = ddm_bounty_hunter_patron var:ddm_bounty_hunter_patron = scope:ddm_hunter_roster_owner }
            remove_character_flag = ddm_preferred_bounty_hunter
        }
    }
    if = {
        limit = { has_variable = ddm_selected_bounty_hunter var:ddm_selected_bounty_hunter = { is_alive = yes has_character_flag = ddm_registered_bounty_hunter has_variable = ddm_bounty_hunter_patron var:ddm_bounty_hunter_patron = scope:ddm_hunter_roster_owner } }
        var:ddm_selected_bounty_hunter = { add_character_flag = ddm_preferred_bounty_hunter }
    }

    clear_variable_list = ddm_ui_hunter_sorted_roster
    set_variable = { name = ddm_ui_hunter_count value = 0 }
    if = {
        limit = { has_variable_list = ddm_bounty_hunter_roster }
        ordered_in_list = {
            variable = ddm_bounty_hunter_roster
            limit = { is_alive = yes has_character_flag = ddm_registered_bounty_hunter has_variable = ddm_bounty_hunter_patron var:ddm_bounty_hunter_patron = scope:ddm_hunter_roster_owner }
            order_by = ddm_hunter_ui_sort_value
            max = 1000
            check_range_bounds = no
            save_temporary_scope_as = ddm_hunter_sorted_row
            scope:ddm_hunter_roster_owner = { add_to_variable_list = { name = ddm_ui_hunter_sorted_roster target = scope:ddm_hunter_sorted_row } change_variable = { name = ddm_ui_hunter_count add = 1 } }
        }
    }
    ddm_build_hunter_roster_page_effect = yes
}

# Compact four-row page built from the already-sorted authoritative display list.
ddm_build_hunter_roster_page_effect = {
    save_scope_as = ddm_hunter_roster_owner
    if = { limit = { NOT = { has_variable = ddm_hunter_page } } set_variable = { name = ddm_hunter_page value = 0 } }
    set_variable = { name = ddm_hunter_page_size value = 4 }
    set_variable = { name = ddm_hunter_last_page value = 0 }
    set_variable = { name = ddm_hunter_page_fill value = 0 }
    set_variable = { name = ddm_ui_hunter_count value = 0 }
    if = {
        limit = { has_variable_list = ddm_ui_hunter_sorted_roster }
        every_in_list = {
            variable = ddm_ui_hunter_sorted_roster
            scope:ddm_hunter_roster_owner = {
                if = { limit = { var:ddm_hunter_page_fill >= var:ddm_hunter_page_size } change_variable = { name = ddm_hunter_last_page add = 1 } set_variable = { name = ddm_hunter_page_fill value = 0 } }
                change_variable = { name = ddm_hunter_page_fill add = 1 }
                change_variable = { name = ddm_ui_hunter_count add = 1 }
            }
        }
    }
    if = { limit = { var:ddm_hunter_page < 0 } set_variable = { name = ddm_hunter_page value = 0 } }
    if = { limit = { var:ddm_hunter_page > var:ddm_hunter_last_page } set_variable = { name = ddm_hunter_page value = var:ddm_hunter_last_page } }

    set_variable = { name = ddm_hunter_page_first value = var:ddm_hunter_page }
    change_variable = { name = ddm_hunter_page_first multiply = var:ddm_hunter_page_size }
    set_variable = { name = ddm_hunter_page_stop value = var:ddm_hunter_page_first }
    change_variable = { name = ddm_hunter_page_stop add = var:ddm_hunter_page_size }
    set_variable = { name = ddm_hunter_page_index value = 0 }
    clear_variable_list = ddm_ui_hunter_roster
    if = {
        limit = { has_variable_list = ddm_ui_hunter_sorted_roster }
        every_in_list = {
            variable = ddm_ui_hunter_sorted_roster
            save_temporary_scope_as = ddm_hunter_page_row
            scope:ddm_hunter_roster_owner = {
                if = { limit = { var:ddm_hunter_page_index >= var:ddm_hunter_page_first var:ddm_hunter_page_index < var:ddm_hunter_page_stop } add_to_variable_list = { name = ddm_ui_hunter_roster target = scope:ddm_hunter_page_row } }
                change_variable = { name = ddm_hunter_page_index add = 1 }
            }
        }
    }
    set_variable = { name = ddm_hunter_page_display value = var:ddm_hunter_page }
    change_variable = { name = ddm_hunter_page_display add = 1 }
    set_variable = { name = ddm_hunter_pages_display value = var:ddm_hunter_last_page }
    change_variable = { name = ddm_hunter_pages_display add = 1 }
}

