changing agent's inventory/equipment

Users who are viewing this thread

I'm fairly new to scenes and all that stuff, especially battles, so here's what I know:
- agent is not a troop, agent is based on a troop
- agent can only equip a weapon that is in his inventory, and agent's inventory is a sub-set of items in troop's inventory
- I have a rough idea of how triggers work (once, on spawn, on hit, on dismount etc.)

Here's what I'm trying to do and have no idea how:
I have a special mounted troop that's supposed to use a bow when enemies are far, and lance (the nice-looking, tournament shaped lance) when enemies are near. However, when dismounted (due to horse dying, or because it's a siege or because player orders army to dismount), the agent is supposed to switch from bow+lance combo to bow+sword combo. So far I noticed that there is no tf_guarantee_everything, so I can't give both sword and lance to my troop - if I do, the agents get randomly either sword or lance. I solved this by not giving a sword to the troop. Using tf_guarantee_melee with tf_guarantee_ranged my agents get bow+lance 100% of the time. Giving only 1 melee weapon to my troop also solves the problem of forcing the agents not to use swords on horseback, so I'd prefer to stick to this setup.

Now, what I want to do is, when agent isn't mounted, I want to remove lance from him and give him the sword instead.
Detecting that he isn't mounted is easy, using on spawn, then on dismount and if horse death doesn't count as dismount, I can just follow with on agent death, and if it's a horse, then get rider agent and replace lance with sword.

Or at least that's the idea.
The problem is, I don't know how to edit agent's inventory during battle (it's not a troop).
I also can't use commands that force the agent to switch between weapons, because one of the weapons doesn't exist in agent's inventory.

Any help would be greatly appreciated.
 
Last edited:
Solution
So far I've been absolutely amazed by how counterintuitive the triggers are.
Anyway, thanks to hints, tips and actual code from @Cokjan and @Veledentella I managed to put together the code I wanted and it works for me.

I wanted to avoid at all costs using triggers that keep triggering all the time. It's a matter of performance.
What I came up with is limited to these triggers:
- ti_on_agent_spawn
- ti_agent_dismount
- ti_on_agent_mount

The biggest obstacle was detecting if the soldier has a horse in ti_on_agent_spawn.
Tricks like checking the tf_guarantee_horse flag or the tf_mounted flag on agent is useless, because in sieges they return true, and horses are not given to soldiers. I tried using agent_get_horse and got...
How many inventory slots are used by the troop? Arrows + a bow + a lance + a sword = 4?
No, I forgot to mention that they have shield as well... So that's 5.
I've been looking through this website: https://mbcommands.fandom.com/wiki/Operators#Agent_equipment
and it says that agent_equip_item *adds* an item to the agent and agent_unequip_item *removes* item from agent.
I'm not sure if it's just broken English, I kinda remember reading that agents can only equip what's already in their inventory.
But maybe that would work?
 
Last edited:
Upvote 0
agent_equip_item will add the item. The other operations in which requires the item to actually exist is agent_set_wielded_item

Personally, I'd give them bow, arrows, and shield in the module_troops. So I'd always know that the last empty weapon slot is 3 and can replace the slot with a sword/lance
 
Upvote 1
Ideally, you should not overcomplicate it and just stick to 4 items per 4 weapon item slots. If you want, you may do what Cokjan has suggested above and fiddle with commands that will do something with the empty slot.

And, instead of using triggers for mount/ dismount, you may write one detecting if agents are actively mounted. You may use try_for_agents and checking every 0.25 or 0.5 seconds if conditions are met (when you think that every frame is too resource-intensive).
Python:
(agent_get_horse, ":horse", ":your_agent"),
(...)
      (try_begin), #on foot
         (le, ":horse", -1),
(...)
      (else_try), #on horseback
(...)
      (try_end),
 
Upvote 1
So far I've been absolutely amazed by how counterintuitive the triggers are.
Anyway, thanks to hints, tips and actual code from @Cokjan and @Veledentella I managed to put together the code I wanted and it works for me.

I wanted to avoid at all costs using triggers that keep triggering all the time. It's a matter of performance.
What I came up with is limited to these triggers:
- ti_on_agent_spawn
- ti_agent_dismount
- ti_on_agent_mount

The biggest obstacle was detecting if the soldier has a horse in ti_on_agent_spawn.
Tricks like checking the tf_guarantee_horse flag or the tf_mounted flag on agent is useless, because in sieges they return true, and horses are not given to soldiers. I tried using agent_get_horse and got really weird results - all my riders returned -1 (meaning no horse) when checked with agent_get_horse, despite me seeing all of them on horses. I was absolutely stumped.
Turns out horses ALWAYS spawn AFTER riders, so when I check on spawn, they aren't there yet.
So I can't just go to ti_on_agent_spawn and do:
(store_trigger_param_1,":agent_no"),
(agent_get_horse,":horse",":agent_no"),
(le,":horse",-1),
#do weapon swapping stuff here, to riding gear or from riding hear

What must be done is:
1. In module_troops.py I have to set up my troop with the equipment meant for fighting on foot.
2. Then go to ti_on_agent_spawn and do:
(store_trigger_param_1,":agent_no"),
(neg|agent_is_human,":agent_no"),
(agent_get_rider,":rider",":agent_no"),
#do weapon swapping to riding gear, but not the other way around


Luckily the rest of it was cake, because contrary to my worries and fears, ti_on_agent_dismount fires even when the troop isn't dismounting but rather falling off a horse (due to the horse getting unfortunately un-alived).

So, the code. This goes at the beginning of module_mission_templates.py, somewhere before mission_templates = [
Python:
#MOD BEGIN - swap riding gear for my units
rndl_gear_swap_on_spawn = (ti_on_agent_spawn,0,0,
    [],
    [
        (store_trigger_param_1,":agent_no"),
        (neg|agent_is_human,":agent_no"),
        (agent_get_rider,":rider",":agent_no"),
        (gt,":rider",-1), #horses normally spawn with riders, but just in case
        (call_script,"script_cf_rndl_agent_swap_weapons",":rider",1), #1 means swapping from on-foot gear to riding gear
    ]
)
rndl_gear_swap_on_dismount = (ti_on_agent_dismount,0,0,
    [],
    [
        (store_trigger_param_1,":agent_no"),
        #not checking if agent is human, because horse can't dismount a horse
        (call_script,"script_cf_rndl_agent_swap_weapons",":agent_no",0), #0 means swapping from riding gear to on-foot gear
    ]
)
rndl_gear_swap_on_mount = (ti_on_agent_mount,0,0,
    [],
    [
        (store_trigger_param_1,":agent_no"),
        #not checking if agent is human, because horse can't mount a horse
        (call_script,"script_cf_rndl_agent_swap_weapons",":agent_no",1), #1 means swapping from on-foot gear to riding gear
    ]
)
#MOD END - swap riding gear for my units

Then we gotta add those trigger definitions to every mission template that has horses and non-player troops in them. Those would be:
- lead_charge
- village_attack_bandits
- village_raid
- ai_training (I recommend to test on this one, because one of the many ways to enter is through camp cheat that lets you walk around the camp)
- multiplayer_ccoop
We add them like so (example for lead_charge):
Python:
("lead_charge",mtf_battle_mode|mtf_synch_inventory,charge,"You lead your men to battle.",
    [
        (1,mtef_defenders|mtef_team_0,0,aif_start_alarmed,12,[]),
        (0,mtef_defenders|mtef_team_0,0,aif_start_alarmed,0,[]),
        (4,mtef_attackers|mtef_team_1,0,aif_start_alarmed,12,[]),
        (4,mtef_attackers|mtef_team_1,0,aif_start_alarmed,0,[]),
    ],
    [
        #MOD BEGIN - swap riding gear for my units
        rndl_gear_swap_on_spawn,
        rndl_gear_swap_on_dismount,
        rndl_gear_swap_on_mount,
        #MOD END - swap riding gear for my units
        (ti_on_agent_spawn,0,0,[],
            [
                #... code of this trigger
            ]
        ),
        #... other triggers
    ],
),

All 3 triggers shown above use the helper script I wrote and put at the end of module_scripts.py:
Python:
#MOD BEGIN - swap gear for my units
#script_cf_rndl_agent_swap_weapons
#helper script to swap agent's gear in battle
#Input: param1 = agent id, optional param2 = mode
#mode = 0 means swapping to on-foot gear from riding gear
#mode = 1 means swapping from on-foot gear to riding gear
("cf_rndl_agent_swap_weapons",
    [
        (store_script_param,":agent",1),
        (store_script_param,":mode",2),
        #check if this is the agent we want to work with (for me it's any agent spawned from my unique troops
        (agent_get_troop_id,":troop",":agent"), #find out which troop was used as basis for this agent
        #only work on agents spawned from these troops:
        (this_or_next|eq,":troop","trp_rndl_trainee"),
        (             eq,":troop","trp_rndl_elite"),
        #swapping to on-foot gear (and initializing variables)
        (assign,":item_to_remove","itm_rndl_lance"),
        (assign,":item_to_add","itm_rndl_bastard_sword_b"),
        (try_begin),
            (eq,":mode",1), #swapping to riding gear
            (assign,":item_to_remove","itm_rndl_bastard_sword_b"),
            (assign,":item_to_add","itm_rndl_lance"),
        (try_end),
        (agent_get_wielded_item,":wielded",":agent",0), #0 = mainhand item, 1 = offhand item (bow is also mainhand, for 2-handed weapons returns -1 for offhand)
        (agent_unequip_item,":agent",":item_to_remove"), #removing item from agent's inventory
        (agent_equip_item,":agent",":item_to_add"),
        (try_begin),
            (eq,":wielded",":item_to_remove"), #agent was wielding what we removed
            (agent_set_wielded_item,":agent",":item_to_add"), #make him wield item we just added, so he won't use fists
        (try_end),
        (agent_force_rethink,":agent"), #just in case - new weapon may need a change of strategy, this doesn't work on player, only NPCs
    ]
),
#MOD END - swap gear for my units

Turns out I don't have to worry about figuring out which item slot holds the item I'm removing. If agent has the item, then it will be removed, creating an empty slot. If any empty slot exists, a new item can be added.
 
Last edited:
Upvote 0
Solution
So far I've been absolutely amazed by how counterintuitive the triggers are.
Anyway, thanks to hints, tips and actual code from @Cokjan and @Veledentella I managed to put together the code I wanted and it works for me.

I wanted to avoid at all costs using triggers that keep triggering all the time. It's a matter of performance.
What I came up with is limited to these triggers:
- ti_on_agent_spawn (I actually just slip my code into the existing trigger, right after param storing, surrounded with try block just in case)
- ti_on_agent_dismount and ti_on_agent_mount

The biggest obstacle was detecting if the soldier has a horse in ti_on_agent_spawn.
Tricks like checking the tf_guarantee_horse flag or the tf_mounted flag on agent is useless, because in sieges they return true, and horses are not given to soldiers. I tried using agent_get_horse and got really weird results - all my riders returned -1 (meaning no horse) when checked with agent_get_horse, despite me seeing all of them on horses. I was absolutely stumped.
Turns out horses ALWAYS spawn AFTER riders, so when I check on spawn, they aren't there yet.
So I can't just go to ti_on_agent_spawn and do:
(store_trigger_param_1,":agent_no"),
(agent_get_horse,":horse",":agent_no"),
(le,":horse",-1),
#do weapon swapping stuff here, to riding gear or from riding hear

What must be done is:
1. In module_troops.py I have to set up my troop with the equipment meant for fighting on foot.
2. Then go to ti_on_agent_spawn and do:
(store_trigger_param_1,":agent_no"),
(neg|agent_is_human,":agent_no"),
(agent_get_rider,":rider",":agent_no"),
#do weapon swapping to riding gear, but not the other way around


Full code that I put into ti_on_agent_spawn trigger:
Python:
(store_trigger_param_1,":agent_no"), #this is vanilla line, just to show where my code goes
#MOD BEGIN - swap gear for my units
(try_begin),
    (neg|agent_is_human,":agent_no"),
    (agent_get_rider,":rider",":agent_no"),
    (gt,":rider",-1), #horses normally spawn with riders, but just in case
    (call_script,"script_cf_rndl_agent_swap_weapons",":rider",1),#1 means swapping to riding gear, 0 or nothing means to on-foot gear
(try_end),
#MOD END - swap gear for my units

The rest of it was cake, because contrary to my worries and fears, ti_on_agent_dismount fires even when the troop isn't dismounting but rather falling off a horse (due to the horse getting unfortunately un-alived).
So the code for those 2 triggers is as follows:
Python:
#MOD BEGIN - swap gear for my units
(ti_on_agent_dismount,0,0,
    [],
    [
        (store_trigger_param_1,":agent_no"),
        #not checking if agent is human, because horse can't dismount a horse
        (call_script,"script_cf_rndl_agent_swap_weapons",":agent_no"), #no parameter means passing a 0 - switching to on-foot gear
    ]
),
(ti_on_agent_mount,0,0,
    [],
    [
        (store_trigger_param_1,":agent_no"),
        #not checking if agent is human, because horse can't dismount a horse
        (call_script,"script_cf_rndl_agent_swap_weapons",":agent_no",1), #1 means switching to riding gear
    ]
),
#MOD END - swap gear for my units

For now I've done this only in "lead_charge" mission template, but same should be repeated for all mission templates that allow non-player troops and horses.
These would be:
- lead_charge
- village_attack_bandits
- village_raid
- multiplayer_ccoop

If there is a smart way to write those triggers once somewhere and insert them into mission templates, that would be cool, but I have yet to figure out how.

All of the above use the helper script I wrote and put at the end of module_scripts.py:
Python:
#MOD BEGIN - swap gear for my units
#helper script to swap agent's gear in battle
#Input: param1 = agent id, optional param2 = mode
#mode = 0 (default) means swapping to on-foot gear from riding gear
#mode = 1 means swapping from on-foot gear to riding gear
("cf_rndl_agent_swap_weapons",
    [
        (store_script_param,":agent",1),
        (store_script_param,":mode",2),
        #check if this is the agent we want to work with (for me it's any agent spawned from my unique troops
        (agent_get_troop_id,":troop",":agent"), #find out which troop was used as basis for this agent
        #only work on agents spawned from these troops:
        (this_or_next|eq,":troop","trp_rndl_trainee"),
        (             eq,":troop","trp_rndl_elite"),
        #swapping to on-foot gear (and initializing variables)
        (assign,":item_to_remove","itm_rndl_lance"),
        (assign,":item_to_add","itm_rndl_bastard_sword_b"),
        (try_begin),
            (eq,":mode",1), #swapping to riding gear
            (assign,":item_to_remove","itm_rndl_bastard_sword_b"),
            (assign,":item_to_add","itm_rndl_lance"),
        (try_end),
        (agent_get_wielded_item,":wielded",":agent",0), #0 = mainhand item, 1 = offhand item (bow is also mainhand, for 2-handed weapons returns -1 for offhand)
        (agent_unequip_item,":agent",":item_to_remove"), #removing item from agent's inventory
        (agent_equip_item,":agent",":item_to_add"),
        (try_begin),
            (eq,":wielded",":item_to_remove"), #agent was wielding what we removed
            (agent_set_wielded_item,":agent",":item_to_add"), #make him wield item we just added, so he won't use fists
        (try_end),
        (agent_force_rethink,":agent"), #just in case - new weapon may need a change of strategy, this doesn't work on player, only NPCs
    ]
),
#MOD END - swap gear for my units

Turns out I don't have to worry about figuring out which item slot holds the item I'm removing. If agent has item, then it will be removed, creating an empty slot. If any empty slot exists, a new item can be added.
I'm fairly confident that passing nothing just leaves the previous call_script's values inside the script parameters. If you use store_trigger_params you can retrieve junk values left over from previous trigger calls. I think it's just a global register that gets called/stored in a unique way.
 
Upvote 0
Back
Top Bottom