OSP Code QoL Bodyguards/Escorts in Town/Village Scenes (Open Source)

Currently Viewing (Users: 0, Guests: 1)

Caba`drin

Count
M&BWBWF&SNWVC
Best answers
0
Another response to something odd noted a number of different places across the forums: your warlord/noble/monarch character is fool enough to wander unsuspecting into towns and villages without an escort for protection and thus can get caught outnumbered by ruffians or unawares by an assassin at any turn.

The following trigger can be added to the appropriate mission_templates to allow a set number of your NPC companions join you as you visit towns and villages. As coded, the number of bodyguards available to a player is governed by their renown and leadership skill: 1 bodyguard per 3 levels of leadership or 400 points of renown up to a maximum of 4 bodyguards. The guards are taken from the non-wounded NPC/heroes/companions that player has in their party, in the order they appear in the party list...just as they would be taken for a bandit raid mission or some such.


Without further ado...
UPDATED!
Create a common trigger block, as follows, near the top of module_mission_templates...with the other common triggers. It must include the first line importing skill definitions (as coded, at least).
Code:
from header_skills import *
bodyguard_triggers = [
 (ti_after_mission_start, 0, ti_once, [(neq, "$g_mt_mode", tcm_disguised)], #condition for not sneaking in; to exclude prison-breaks, etc change to (eq, "$g_mt_mode", tcm_default")
   [
    #Get number of bodyguards
    (store_skill_level, ":leadership", skl_leadership, "trp_player"),
    (troop_get_slot, ":renown", "trp_player", slot_troop_renown),
    (val_div, ":leadership", 3),
    (val_div, ":renown", 400),
    (store_add, ":max_guards", ":renown", ":leadership"),
    (val_min, ":max_guards", 4),
   
    (ge, ":max_guards", 1),

    #Get player info
    (get_player_agent_no, ":player"),
    (agent_get_team, ":playerteam", ":player"),
    (agent_get_horse, ":use_horse", ":player"), #If the player spawns with a horse, the bodyguard will too.

    #Prepare Scene/Mission Template
    (assign, ":entry_point", 0),
    (assign, ":mission_tpl", 0),
    (try_begin),        
        (party_slot_eq, "$current_town", slot_party_type, spt_village),
        (assign, ":entry_point", 11), #Village Elder's Entry
        (assign, ":mission_tpl", "mt_village_center"),
    (else_try),
        (this_or_next|eq, "$talk_context", tc_prison_break),
        (this_or_next|eq, "$talk_context", tc_escape),
        (eq, "$talk_context", tc_town_talk),
        (assign, ":entry_point", 24), #Prison Guard's Entry
        (try_begin),
            (party_slot_eq, "$current_town", slot_party_type, spt_castle),
            (assign, ":mission_tpl", "mt_castle_visit"),
        (else_try),
            (assign, ":mission_tpl", "mt_town_center"),
        (try_end),
    (else_try),
        (eq, "$talk_context", tc_tavern_talk),
        (assign, ":entry_point", 17), #First NPC Tavern Entry
    (try_end),
    (try_begin),
        (neq, "$talk_context", tc_tavern_talk),
        (gt, ":use_horse", 0),
        (mission_tpl_entry_set_override_flags, ":mission_tpl", ":entry_point", 0),
    (try_end),
    (store_current_scene, ":cur_scene"),
    (modify_visitors_at_site, ":cur_scene"),  
   
    #Find and Spawn Bodyguards
    (assign, ":bodyguard_count", 0),   
    (party_get_num_companion_stacks, ":num_of_stacks", "p_main_party"),
    (try_for_range, ":i", 0, ":num_of_stacks"),
        (party_stack_get_troop_id, ":troop_id", "p_main_party", ":i"),
        (neq, ":troop_id", "trp_player"),
        (troop_is_hero, ":troop_id"),
        (neg|troop_is_wounded, ":troop_id"),
        (val_add, ":bodyguard_count", 1),
                
        (try_begin), #For prison-breaks
            (this_or_next|eq, "$talk_context", tc_escape),
            (eq, "$talk_context", tc_prison_break),      
            (troop_set_slot, ":troop_id", slot_troop_will_join_prison_break, 1),
        (try_end),

        (add_visitors_to_current_scene, ":entry_point", ":troop_id", 1),

        (eq, ":bodyguard_count", ":max_guards"),
        (assign, ":num_of_stacks", 0), #Break Loop       
    (try_end), #Stack Loop
    (gt, ":bodyguard_count", 0), #If bodyguards spawned...
    (set_show_messages, 0),   
    (team_give_order, ":playerteam", 8, mordr_follow), #Division 8 to avoid potential conflicts
    (set_show_messages, 1),   
   ]),   

 (ti_on_agent_spawn, 0, 0, [], 
   [
    (store_trigger_param_1, ":agent"),
    (agent_get_troop_id, ":troop", ":agent"),
    (neq, ":troop", "trp_player"),
    (troop_is_hero, ":troop"),
    (main_party_has_troop, ":troop"),
    
    (get_player_agent_no, ":player"),
    (agent_get_team, ":playerteam", ":player"),
    (agent_get_position,pos1,":player"),        
    
    (agent_set_team, ":agent", ":playerteam"),
    (agent_set_division, ":agent", 8),
    (agent_add_relation_with_agent, ":agent", ":player", 1),
    (agent_set_is_alarmed, ":agent", 1),
    (store_random_in_range, ":shift", 1, 3),
    (val_mul, ":shift", 100),
    (position_move_y, pos1, ":shift"),
    (store_random_in_range, ":shift", 1, 3),
    (store_random_in_range, ":shift_2", 0, 2),
    (val_mul, ":shift_2", -1),
    (try_begin),
        (neq, ":shift_2", 0),
        (val_mul, ":shift", ":shift_2"),
    (try_end),
    (position_move_x, pos1, ":shift"),
    (agent_set_position, ":agent", pos1),
   ]),
  
 (ti_on_agent_killed_or_wounded, 0, 0, [],
    [
     (store_trigger_param_1, ":dead_agent"),
        
     (agent_get_troop_id, ":troop", ":dead_agent"),
     (neq, ":troop", "trp_player"),
     (troop_is_hero, ":troop"),
     (main_party_has_troop, ":troop"),
     (party_wound_members, "p_main_party", ":troop", 1),
    ]),
 ]
Add the trigger at the end of the consequences block of the following mission templates:
"bandits_at_night", "castle_visit", "village_center", "town_center", "town_default"

by finding the closing
  ]
),
at the very end of the template, right before the next one begins.

and adding "+ bodyguard_triggers," to the bracket so it looks like:
  ] + bodyguard_triggers,
),

Also, to avoid odd dialog if the player chooses to talk with their guard/escort, one line is needed in module_dialogs
Find the following chunk of dialog code (around line 2895) and add the marked line
Code:
  [anyone, "start", [(is_between, "$g_talk_troop", companions_begin, companions_end),
                     (this_or_next|eq, "$talk_context", tc_tavern_talk),
                     (this_or_next|eq, "$talk_context", tc_town_talk), #ADD THIS LINE FOR BODYGUARD CODE
                     (eq, "$talk_context", tc_court_talk),
                     (main_party_has_troop, "$g_talk_troop")],
   "Let's leave whenever you are ready.", "close_window", []],



The only caveat/problem with coding this as I have here is that sometimes the bodyguards will spawn with errors to their facial appearance (wrong hair/color/beard...maybe even a change to the face). Having your companions wear helmets clearly reduces the visibility of any such errors. (The solution to this problem would be to relocate the code to the game_menus and set the troops as scene visitors, but this presents its own sets of problems and complications.)

Create a common trigger, as follows, near the top of module_mission_templates...with the other common triggers. It must include the first line importing skill definitions (as coded, at least).
Code:
from header_skills import *
bodyguard_trigger = (
  ti_after_mission_start, 0, ti_once, [(neq, "$g_mt_mode", tcm_disguised)], #condition for not sneaking in; to exclude prison-breaks, etc change to (eq, "$g_mt_mode", tcm_default")
   [
    #Get number of bodyguards
    (store_skill_level, ":leadership", skl_leadership, "trp_player"),
    (troop_get_slot, ":renown", "trp_player", slot_troop_renown),
    (val_div, ":leadership", 3), 
    (val_div, ":renown", 400),
    (store_add, ":max_guards", ":renown", ":leadership"),
    (val_min, ":max_guards", 4),
    
    (ge, ":max_guards", 1),

    #Get player info
    (get_player_agent_no, ":player"),
    (agent_get_team, ":playerteam", ":player"),
    (agent_get_position,pos1,":player"),
    (agent_get_horse, ":use_horse", ":player"), #If the player spawns with a horse, the bodyguard will too.
    
    (assign, ":bodyguard_count", 0),
    (assign, ":shift", -200),        
    (party_get_num_companion_stacks, ":num_of_stacks", "p_main_party"),
    (try_for_range, ":i", 0, ":num_of_stacks"), 
        (party_stack_get_troop_id, ":troop_id", "p_main_party", ":i"),
        (neq, ":troop_id", "trp_player"),
        (troop_is_hero, ":troop_id"),
        (neg|troop_is_wounded, ":troop_id"),
        (val_add, ":bodyguard_count", 1),
        
        (try_begin),
            (le, ":use_horse", 0), #The player didn't spawn with a horse, so prevent the bodyguard
            (troop_get_inventory_slot, ":horse", ":troop_id", ek_horse), #by temp. removing their horse
            (gt, ":horse", 0),
            (troop_get_inventory_slot_modifier, ":horse_mod", ":troop_id", ek_horse),
            (troop_remove_item, ":troop_id", ":horse"),
        (try_end),

        (position_move_x, pos1, ":shift"),
        (set_spawn_position, pos1),
        (spawn_agent, ":troop_id"),
        (assign, ":guard", reg0),        
        (agent_set_team, ":guard", ":playerteam"),
        (agent_add_relation_with_agent, ":guard", ":player", 1),
        (agent_set_is_alarmed, ":guard", 1),
        
        (try_begin),
            (eq, ":bodyguard_count", 2),
            (position_move_y, pos1, 200),        
        (try_end),        
        (val_mul, ":shift", -2),
        
        (try_begin),
            (le, ":use_horse", 0), #No horses in scene
            (gt, ":horse", 0), #But bodyguard had a horse
            (troop_add_item, ":troop_id", ":horse", ":horse_mod"), #Give their horse back
            (troop_equip_items, ":troop_id"), #make sure they keep the horse
        (try_end),

        (eq, ":bodyguard_count", ":max_guards"),
        (assign, ":num_of_stacks", 0), #Break Loop        
    (try_end), #Stack Loop
    (gt, ":bodyguard_count", 0), #If bodyguards spawned...
    (set_show_messages, 0),    
    (team_give_order, ":playerteam", grc_everyone, mordr_follow),
    (set_show_messages, 1),    
   ])
Add the trigger in the operations block of the following mission templates:
"bandits_at_night", "castle_visit", "village_center", "town_center", "town_default"

do this by adding the line:
bodyguard_trigger,
 

Lumos

Archduke
WBWF&SNWM&BVC
Best answers
0
Nice. Combined with my customised town battles code... it will be awesome.
 

Caba`drin

Count
M&BWBWF&SNWVC
Best answers
0
theAthenian said:
Will this work with Mount and Blade(not Warband)?
No guarantees, as I don't have original M&B, but it doesn't use any commands that have been added since the 1.113 version of Warband (when I started modding Warband) so I doubt that it uses any Warband-specific commands. But I'm not sure.

It wouldn't be painful to just toss it in and test, though. ...and to report back as inquiring minds will want to know.
 

lovebeatles

Recruit
Best answers
0
when i enter a city or town do not appear the bodyguards, I should have 2 or 3
what is the problem?
does anyone have the translation into spanish of the mod? (you know, i dont have a good english :mrgreen:)
 

iggorbb

Master Knight
Best answers
0
As much as I understood, you must have leadership minimum 3 and some renown. Do you have that?
 

ithilienranger

Knight at Arms
Best answers
0
Why can't I use the add_visitors_to_sight instead of the spawn agent? I have another mission trigger that uses that with a ti_on_agent_killed_or_wounded interval.
 

Caba`drin

Count
M&BWBWF&SNWVC
Best answers
0
lovebeatles said:
when i enter a city or town do not appear the bodyguards, I should have 2 or 3
what is the problem?
does anyone have the translation into spanish of the mod? (you know, i dont have a good english :mrgreen:)
You get 1 bodyguard for every 3 points leadership or 400 points of renown. If you meet these criteria, then you've made some mistake in putting the trigger into module_mission_templates.py
As for spanish translation...I don't believe they translate the Module System...though I may be mistaken. And there is no dialog/text that gets edited.

ithilienranger said:
Why can't I use the add_visitors_to_sight instead of the spawn agent? I have another mission trigger that uses that with a ti_on_agent_killed_or_wounded interval.
If you can get it to work, I'd love to see the code...I've been fighting with doing that on and off since about a week before I made this thread.
My problems have been partly chronicled in the Q&A thread, but what it boils down to, mainly, is finding a generic entry point that will work in all town/village/tavern scenes. I've been unable to add visitors to the first 7 entries that the player uses, for instance, I believe because those are marked scene_source rather than visitor_source. Additionally, moving entry points from within the mission seems impossible, so they'll spawn wherever. Beyond that, I haven't found a common entry point in the necessary mission templates and I didn't want to move this to a game_menu code.

Secondary issues would then be that with add_visitors_to_current_scene is that you'd need extra triggers--an on_agent_spawn trigger to assign the new agent to the player's team, make them alert...move them from the entry point to the correct position by the player, etc, and the on_agent_killed trigger to apply casualties to the player party if the companion gets wounded.

But the main problem is getting the entry point BS figured out.
 

dunde

Count
WB
Best answers
0
Copying the entry that player use, to new one, and then change the flag of the new entry to be visitor_source may help. By using mission entry, we don't need to set the team or set the spawned agent to alarm, as they 're all set by the mission entry's  flags. Overriding the horse and equipment can be done by the flags too.
The only problem is the position. spawn_agent has more freedom to set the spawning position.
 

GetAssista

Baron
M&BWF&SWB
Best answers
1
Caba`drin said:
but what it boils down to, mainly, is finding a generic entry point that will work in all town/village/tavern scenes.
Why not spawn bodyguards at whatever spawn points that are currently free, depending on the scene, and then teleport them in the vicinity of player at mission start?
 

Caba`drin

Count
M&BWBWF&SNWVC
Best answers
0
ithilienranger said:
Wouldn't add_visitors_to_current_scene fix the problem with the face codes getting messed up?
Yes, it would, as I indicate in the OP. It also has all of the complications that I haven't been able to work through in a simple fashion, as I indicate in the OP and in the response earlier today.

GetAssista said:
Why not spawn bodyguards at whatever spawn points that are currently free, depending on the scene, and then teleport them in the vicinity of player at mission start?
That is what I was trying to do last week or so, when I made my most recent inquiries in the Q&A thread, but I haven't had any luck identifying open spawn points from within the mission. Again, I'm trying to keep this confined to the mission template, without any recourse to game_menus and the set-up of the scene. If I'm missing an obvious way to do this, I'd be much obliged to learn it.
 

Lumos

Archduke
WBWF&SNWM&BVC
Best answers
0
What would happen if you spawn them at the player's entry point? (Never tried this)
 

Caba`drin

Count
M&BWBWF&SNWVC
Best answers
0
Lumos said:
What would happen if you spawn them at the player's entry point? (Never tried this)
It doesn't work, because the player's entry point is scene_source, not visitor_source. (See above.)
 

GetAssista

Baron
M&BWF&SWB
Best answers
1
Caba`drin said:
That is what I was trying to do last week or so, when I made my most recent inquiries in the Q&A thread, but I haven't had any luck identifying open spawn points from within the mission. Again, I'm trying to keep this confined to the mission template, without any recourse to game_menus and the set-up of the scene. If I'm missing an obvious way to do this, I'd be much obliged to learn it.
Well, you probably won't be able to get it done in a mission template trigger common for all the templates, cause entry points are a mess in vanilla. But you can easily done it if you specify a specific entry for each MT, right? Moreover, much of your matching code would not be needed as you would pick entry point similar in setup to player entry. Why then not leave the hard work of inserting entry #s into a full text trigger for the users? :smile: Hand them a table of entry numbers they need to fill in for each MT they want bodyguards in and presto

Besides, have you tried tinkering with add_reinforcements_to_entry? Like creating a large dummy enemy for game to get stats from, and then add bodyguards as tiny reinforcement. I never tried, don't even know if it gets overided by scene_source, but I'm curious.
 

Caba`drin

Count
M&BWBWF&SNWVC
Best answers
0
Alright...new version in the OP. It takes 3 triggers, but I haven't seen a single problem yet. Your companions look like your companions finally. :smile: I've not been able to see if there are any issues if bandits attack at night. After 30 min of trying to get mugged I gave up and decided to post this anyway. EDIT: And it works as intended, even when attacked in taverns or by bandits at night. You may want to order your bodyguard to hold position, rather than follow you, but other than that, all is good.

As you'll see in the OP, it uses add_visitors_to_current scene, by choosing spawn points known to exist. It differentiates the mission template using the talk context global variable. Horse use/disuse is achieved by editing the override...but apparently the entry point still can't be moved, so positioning the bodyguard is taken care of in a second trigger. The third and final trigger is used to ensure injuries to bodyguards appear in the party.

Thanks all for the ideas here and in the Q&A thread...helped me know where to guess, try, fail, and try something else.
 

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
I actually wrote a bodyguard script myself (along the same lines), but used a lot of workarounds so they follow the player and get injured properly in the various encounters (they're spawned agents that are stored in slot_center_mercenary_troop/amount for p_main_party, gets kicked back to the actual party when wounded, etc). Not going to bother digging everything up for now.

I wrote a bunch of debugging scripts to test their functionality. You might find them helpful.
Code:
  (
    "town_cheats",0,
    "Do whatever you want",
    "none",[],
    [

      ("summon_drunk",
      [(party_slot_eq, "$current_town", slot_party_type, spt_town),
       # (troop_get_slot, ":town", "trp_belligerent_drunk", slot_troop_cur_center),
       (try_begin),
         # (is_between, ":town", towns_begin, towns_end),
         (troop_slot_eq, "trp_belligerent_drunk", slot_troop_cur_center, "$current_town"),
         (assign, reg10, 1),
       (else_try),
         (assign, reg10, 0),
       (try_end),
       ],
      "{reg10?Dismiss:Get} a drunkard.",
      [
        (try_begin),
          (eq, reg10, 1), 
          (troop_set_slot, "trp_belligerent_drunk", slot_troop_cur_center, -1),
        (else_try),
          (troop_set_slot, "trp_belligerent_drunk", slot_troop_cur_center, "$current_town"),
        (try_end),
      ]),
      

      ("summon_ass",
      [(party_slot_eq, "$current_town", slot_party_type, spt_town),
       (try_begin),
         # (is_between, ":town", towns_begin, towns_end),
         (troop_slot_eq, "trp_hired_assassin", slot_troop_cur_center, "$current_town"),
         (assign, reg11, 1),
       (else_try),
         (assign, reg11, 0),
       (try_end),
      ],
      "{reg11?Scare away:Hire} an assassin.",
      [
        (try_begin),
          (eq, reg11, 1), 
          (troop_set_slot, "trp_hired_assassin", slot_troop_cur_center, -1),
        (else_try),
          (troop_set_slot, "trp_hired_assassin", slot_troop_cur_center, "$current_town"),
        (try_end),
      ]),

      ("summon_bandit",
      [
       (neg|party_slot_eq, "$current_town", slot_party_type, spt_castle),
       (party_get_slot, reg12, "$current_town", slot_center_has_bandits),
       # (try_begin),
         # (party_slot_ge, "$current_town", slot_center_has_bandits, 1),
         # (assign, reg12, 1),
       # (else_try),
         # (assign, reg12, 0),
       # (try_end).
      ],
      "{reg12?Kick out:Get ambushed by} some bandits.",
      [
       (try_begin), #cleanse
         (ge, reg12, 1),
         (party_set_slot, "$current_town", slot_center_has_bandits, 0),
       (else_try), #ambush
         (store_random_in_range, ":bandit", bandits_begin, bandits_end),
         (party_set_slot, "$current_town", slot_center_has_bandits, ":bandit"),
         (assign, "$town_nighttime", 1),
         (assign, "$sneaked_into_town", 0),
         (assign, "$g_defending_against_siege", 0),
         (call_script, "script_cf_enter_center_location_bandit_check"),
         # (assign, "$town_nighttime", 1),
       (try_end),
      ]),