StinkyMcGirk
Sergeant
Caba`drin said:See final spoiler at bottom for combined code for Lancers, Horse Archers and Spearmen
I've monkeyed with the scripts as provided for some time and tried to make the lance/nonlance decision making a bit more dynamic. Using similar logic, I've expanded it to include spearmen/pikemen (in the current version, anyone wielding a polearm at spawn) on foot. I've only begun testing for its effectiveness, but I didn't see harm in tossing this out here--particularly in that others are much better equipped to find ways to improve upon what I've started with, or to tell me I'm all wet and should throw in the towel on this code-dabbling.
While there does not seem to be a clean way to get/set an agent's target enemy agent (and thus its distance to that target, etc) to force wielding the weapon of appropriate reach, the code approximates this by doing two things: 1) it gets the average distance of the 3 closest enemies to the agent (using a native script) and 2) checks the agent's combat state. "Close combat" is here defined as an average distance to three enemies of less than 5 meters and a combat state of hitting, following through, being hit or blocking. If both are met, the agents switch to a non-lance/non-polearm weapon; if not, they switch to/maintain their lance or spear. Hopefully this means that the switch would only occur AFTER the contact of an initial charge, and then will continue to be dynamic throughout the battle.
I've broken the code into 3 parts: 1) a near-spawn process that tags lancers and spearmen using agent_slots and instructs lancers to wield their lances at the battle's start; 2) the main usage bit that checks if the lancer has been dehorsed OR if a lancer on horseback--or a spearman--is in close-range combat and then calls; 3) a script that equips a non-lance, non-polearm weapon.
Two slots are needed as constants:
slot_agent_lance
slot_agent_spear
Code:common_lance_spear_spawn = ( # Just after spawn, mark lancers and spears using a slot. # Force lancers to equip lances. 1, 0, ti_once, [], [(call_script, "script_lance_spear_classify_agent")]) common_lance_spear_usage = ( # Check to make sure there are no lance users on foot, if so force them to # switch to their sword. This should also affect troops that were NEVER mounted, # but are still equipped with lances, such as Taiga Bandits. # For mounted lancers and foot spears, affect their Decision on weapon use, # based on if closest 3 enemies are within 5 meters and if currently attacking/defending. 2, 0, 0, [], [# Run through all active NPCs on the battle field. (try_for_agents, ":agent"), # Hasn't been defeated. (agent_is_alive, ":agent"), (try_begin), (agent_get_slot, ":lance", ":agent", slot_agent_lance), (gt, ":lance", 0), # Lancer? # Get wielded item. (agent_get_wielded_item, ":wielded", ":agent", 0), # They riding a horse? (agent_get_horse, ":horse", ":agent"), (try_begin), (le, ":horse", 0), # Isn't riding a horse. (agent_set_slot, ":agent", slot_agent_lance, 0), # No longer a lancer (eq, ":wielded", ":lance"), # Still using lance? (call_script, "script_lance_spear_backup_weapon", ":agent"), # Then equip a close weapon (else_try), # Still mounted (agent_get_position, pos1, ":agent"), (agent_get_team, ":team_no", ":agent"), # Find distance of nearest 3 enemies (call_script, "script_get_closest3_distance_of_enemies_at_pos1", ":team_no", pos1), (assign, ":avg_dist", reg0), (try_begin), (lt, ":avg_dist", 500), # Are the enemies within 5 meters? (agent_get_combat_state, ":combat", ":agent"), (gt, ":combat", 3), # Agent currently in combat? ...avoids switching before contact (eq, ":wielded", ":lance"), # Still using lance? (call_script, "script_lance_spear_backup_weapon", ":agent"), # Then equip a close weapon (else_try), (neq, ":wielded", ":lance"), # Enemies farther than 5 meters and/or not fighting, and not using lance? (agent_set_wielded_item, ":agent", ":lance"), # Then equip it! (try_end), (try_end), (else_try), (agent_get_slot, ":spear", ":agent", slot_agent_spear), (gt, ":spear", 0), # Spear-Unit? (agent_get_wielded_item, ":wielded", ":agent", 0), # Get wielded (agent_get_position, pos1, ":agent"), (agent_get_team, ":team_no", ":agent"), # Find distance of nearest 3 enemies (call_script, "script_get_closest3_distance_of_enemies_at_pos1", ":team_no", pos1), (assign, ":avg_dist", reg0), (try_begin), (lt, ":avg_dist", 500), # Are the enemies within 5 meters? (agent_get_combat_state, ":combat", ":agent"), (gt, ":combat", 3), # Agent currently in combat? ...avoids switching before contact (eq, ":wielded", ":spear"), # Still using spear? (call_script, "script_lance_spear_backup_weapon", ":agent"), # Then equip a close weapon (else_try), (neq, ":wielded", ":spear"), # Enemies farther than 5 meters and/or not fighting, and not using spear? (agent_set_wielded_item, ":agent", ":spear"), # Then equip it! (try_end), (try_end), (try_end), ]) lance_spear_triggers = [ common_lance_spear_spawn, common_lance_spear_usage, ]
Copy and paste the code directly into the actual mission templates you want this to apply deleting the lance_spear_triggers = [ ] bit, or stick all of the above toward the top of the file (I put them after the multiplayer_* and before the common_* around line 630) and then just include the "lance_spear_triggers" title in the templates as demonstrated below:
Code://...a number of lines further down...// ( "lead_charge",mtf_battle_mode,charge, "You lead your men to battle.", [ //...more lines down...// (call_script, "script_battle_tactic_apply"), ], []), #applying battle tactic common_battle_order_panel, common_battle_order_panel_tick, ] + lance_spear_triggers, ), ( "village_attack_bandits",mtf_battle_mode,charge, "You lead your men to battle.", //...the next mission...//
Code:# script_lance_spear_backup_weapon # Input: arg1: agent # Output: none ("lance_spear_backup_weapon", [ # Find non-lance/spear item in inventory (store_script_param_1, ":agent"), (agent_get_troop_id, ":troop",":agent"), (assign,":has_choice",0), (try_begin), (troop_is_hero, ":troop"), (assign, ":end", ek_head), (try_for_range, ":i", ek_item_0, ":end"), (troop_get_inventory_slot,":item",":troop",":i"), (gt, ":item", 0), (item_get_type, ":weapontype", ":item"), (is_between, ":weapontype", itp_type_one_handed_wpn, itp_type_polearm),#one or two handed (assign,":has_choice",1), (assign, ":end", ek_item_0),#loop breaker (try_end), (else_try),#regular troops (troop_get_inventory_capacity,":cap",":troop"), (try_for_range, ":i", 0, ":cap"),#not sure if reg. troops have equipped items (slots < 10), but w/e (troop_get_inventory_slot,":item",":troop",":i"), (gt, ":item", 0), (item_get_type, ":weapontype", ":item"), (is_between, ":weapontype", itp_type_one_handed_wpn, itp_type_polearm),#one or two handed (agent_has_item_equipped, ":agent", ":item"),#this step is essential, (assign,":has_choice",1),#but I'm not sure if this would work or not (assign, ":cap", 0), (try_end), (try_end), (try_begin),# Equip their backup weapon. (eq, ":has_choice",1), (agent_set_wielded_item, ":agent", ":item"), (try_end), ]), # script_lance_spear_classify_agent # Input: None # Output: None ("lance_spear_classify_agent", [ (try_for_agents, ":agent"), (agent_is_alive, ":agent"), # Isn't a player. (agent_is_non_player, ":agent"), # Isn't a horse. (agent_is_human, ":agent"), # Get wielded item. (agent_get_wielded_item, ":wielded", ":agent", 0), # They riding a horse? (agent_get_horse, ":horse", ":agent"), (try_begin), (gt, ":horse", 0), # Is riding a horse. (try_begin), (this_or_next|is_between, ":wielded", "itm_jousting_lance","itm_glaive"), # Is it a lance? (is_between, ":wielded", "itm_light_lance","itm_pike"), # Is it a lance? (agent_set_slot, ":agent", slot_agent_lance, ":wielded"), (else_try), # Force the NPC to wield a lance, but this will only happen if they # actually have a lance in their inventory. Otherwise this does # nothing. (assign, ":end", "itm_glaive"), # adjust as needed (try_for_range, ":item", "itm_jousting_lance",":end"), (agent_set_wielded_item, ":agent", ":item"), (try_begin), # Get wielded item. Again. (agent_get_wielded_item, ":wielded", ":agent", 0), (eq, ":wielded", ":item"), (agent_set_slot, ":agent", slot_agent_lance, ":item"), #Mark lancers for later use (assign, ":end", "itm_jousting_lance"),#loop breaker (it's only 4 items, but w/e) (try_end), (try_end), (assign, ":end", "itm_pike"), # adjust as needed (try_for_range, ":item", "itm_light_lance",":end"), (agent_set_wielded_item, ":agent", ":item"), (try_begin), # Get wielded item. Again. (agent_get_wielded_item, ":wielded", ":agent", 0), (eq, ":wielded", ":item"), (agent_set_slot, ":agent", slot_agent_lance, ":item"), #Mark lancers for later use (assign, ":end", "itm_light_lance"),#loop breaker (it's only 4 items, but w/e) (try_end), (try_end), (try_end), (else_try), #Agent not mounted (try_begin), (gt, ":wielded", 0), #Make sure it's not fists (item_get_type, ":weapontype", ":wielded"), (eq, ":weapontype", itp_type_polearm), # Is it a spear? (agent_set_slot, ":agent", slot_agent_spear, ":item"), #Mark spearmen (else_try), #Check if there's a spear equipped, if not wielded (agent_get_troop_id, ":troop",":agent"), (troop_get_inventory_capacity,":cap",":troop"), (try_for_range, ":i", 0, ":cap"),#not sure if reg. troops have equipped items (slots < 10), but w/e (troop_get_inventory_slot,":item",":troop",":i"), (gt, ":item", 0), (item_get_type, ":weapontype", ":item"), (eq, ":weapontype", itp_type_polearm), # Is it a spear? (agent_has_item_equipped, ":agent", ":item"),#this step is essential, (agent_set_slot, ":agent", slot_agent_spear, ":item"), #Mark spearmen (assign, ":cap", 0), #loop Break (try_end), (try_end), (try_end), #Mounted or not (try_end), #Agent Loop ]),
EDIT: Changed the spawn script, as ti_on_agent_spawn cannot find horses--they haven't spawned yet--that trigger now fires 1 second into the mission, and only fires once to mark agents accordingly.
EDIT 2: Updated and error free, functioning as desired. Distance-to-enemy switch to short range weapon may need tweaking and fine-tuning, however.
EDIT 3: Below. Added horse archers and forced bow usage at spawn. Updated with extra checks to ensure agents have items equipped before trying to wield them.
Set values to first 3 unused agent slots (26, 27, 28 in Native)Code:slot_agent_lance = 26 slot_agent_spear = 27 slot_agent_horsebow = 28
Install as instructed above, though with " ] + weapon_use_triggers, " in place of lance_spear_triggersCode:common_weapon_use_spawn = ( # Just after spawn, mark lancers, spears, horse archers using a slot. # Force lancers to equip lances, horse archers to equip bows 1, 0, ti_once, [], [(call_script, "script_weapon_use_classify_agent")]) common_weapon_use = ( # Check to make sure there are no lance users on foot, if so force them to # switch to their sword. This should also affect troops that were NEVER mounted, # but are still equipped with lances, such as Taiga Bandits. # Check horse archers ammo, and if none left, switch to sword. # For mounted lancers and foot spears, affect their Decision on weapon use, # based on if closest 3 enemies are within 5 meters and if currently attacking/defending. 2, 0, 0, [], [# Run through all active NPCs on the battle field. (try_for_agents, ":agent"), # Hasn't been defeated. (agent_is_alive, ":agent"), (try_begin), (agent_get_slot, ":lance", ":agent", slot_agent_lance), (gt, ":lance", 0), # Lancer? # Get wielded item. (agent_get_wielded_item, ":wielded", ":agent", 0), # They riding a horse? (agent_get_horse, ":horse", ":agent"), (try_begin), (le, ":horse", 0), # Isn't riding a horse. (agent_set_slot, ":agent", slot_agent_lance, 0), # No longer a lancer (eq, ":wielded", ":lance"), # Still using lance? (call_script, "script_weapon_use_backup_weapon", ":agent"), # Then equip a close weapon (else_try), # Still mounted (agent_get_position, pos1, ":agent"), (agent_get_team, ":team_no", ":agent"), # Find distance of nearest 3 enemies (call_script, "script_get_closest3_distance_of_enemies_at_pos1", ":team_no", pos1), (assign, ":avg_dist", reg0), (try_begin), (lt, ":avg_dist", 500), # Are the enemies within 5 meters? (agent_get_combat_state, ":combat", ":agent"), (gt, ":combat", 3), # Agent currently in combat? ...avoids switching before contact (eq, ":wielded", ":lance"), # Still using lance? (call_script, "script_weapon_use_backup_weapon", ":agent"), # Then equip a close weapon (else_try), (neq, ":wielded", ":lance"), # Enemies farther than 5 meters and/or not fighting, and not using lance? (agent_set_wielded_item, ":agent", ":lance"), # Then equip it! (try_end), (try_end), (else_try), (agent_get_slot, ":bow", ":agent", slot_agent_horsebow), (gt, ":bow", 0), # Horse archer? # Get wielded item. (agent_get_wielded_item, ":wielded", ":agent", 0), # They have ammo left? (agent_get_ammo, ":ammo", ":agent"), (try_begin), (le, ":ammo", 0), # No ammo left (agent_set_slot, ":agent", slot_agent_horsebow, 0), # No longer a horse archer (eq, ":wielded", ":bow"), # Still using bow? (call_script, "script_weapon_use_backup_weapon", ":agent"), # Then equip a close weapon (else_try), (neq, ":wielded", ":bow"), # Still have ammo, and not using bow? (agent_set_wielded_item, ":agent", ":bow"), # Then equip it! (try_end), (else_try), (agent_get_slot, ":spear", ":agent", slot_agent_spear), (gt, ":spear", 0), # Spear-Unit? (agent_get_wielded_item, ":wielded", ":agent", 0), # Get wielded (agent_get_position, pos1, ":agent"), (agent_get_team, ":team_no", ":agent"), # Find distance of nearest 3 enemies (call_script, "script_get_closest3_distance_of_enemies_at_pos1", ":team_no", pos1), (assign, ":avg_dist", reg0), (try_begin), (lt, ":avg_dist", 500), # Are the enemies within 5 meters? (agent_get_combat_state, ":combat", ":agent"), (gt, ":combat", 3), # Agent currently in combat? ...avoids switching before contact (eq, ":wielded", ":spear"), # Still using spear? (call_script, "script_weapon_use_backup_weapon", ":agent"), # Then equip a close weapon (else_try), (neq, ":wielded", ":spear"), # Enemies farther than 5 meters and/or not fighting, and not using spear? (agent_set_wielded_item, ":agent", ":spear"), # Then equip it! (try_end), (try_end), (try_end), ]) weapon_use_triggers = [ common_weapon_use_spawn, common_weapon_use, ]
Code:# script_weapon_use_backup_weapon # Input: arg1: agent # Output: none ("weapon_use_backup_weapon", [ # Find non-lance/spear/bow item in inventory (store_script_param_1, ":agent"), (agent_get_troop_id, ":troop",":agent"), (assign,":has_choice",0), (try_begin), (troop_is_hero, ":troop"), (assign, ":end", ek_head), (try_for_range, ":i", ek_item_0, ":end"), (troop_get_inventory_slot,":item",":troop",":i"), (gt, ":item", 0), (item_get_type, ":weapontype", ":item"), (is_between, ":weapontype", itp_type_one_handed_wpn, itp_type_polearm),#one or two handed (assign,":has_choice",1), (assign, ":end", ek_item_0),#loop breaker (try_end), (else_try),#regular troops (troop_get_inventory_capacity,":cap",":troop"), (try_for_range, ":i", 0, ":cap"),#not sure if reg. troops have equipped items (slots < 10), but w/e (troop_get_inventory_slot,":item",":troop",":i"), (gt, ":item", 0), (item_get_type, ":weapontype", ":item"), (is_between, ":weapontype", itp_type_one_handed_wpn, itp_type_polearm),#one or two handed (agent_has_item_equipped, ":agent", ":item"),#this step is essential, (assign,":has_choice",1),#but I'm not sure if this would work or not (assign, ":cap", 0), (try_end), (try_end), (try_begin),# Equip their backup weapon. (eq, ":has_choice",1), (agent_set_wielded_item, ":agent", ":item"), (try_end), ]), # script_weapon_use_classify_agent # Input: None # Output: None ("weapon_use_classify_agent", [ (try_for_agents, ":agent"), (agent_is_alive, ":agent"), # Isn't a player. (agent_is_non_player, ":agent"), # Isn't a horse. (agent_is_human, ":agent"), (agent_get_troop_id, ":troop",":agent"), # Get wielded item. (agent_get_wielded_item, ":wielded", ":agent", 0), # They riding a horse? (agent_get_horse, ":horse", ":agent"), (try_begin), (gt, ":horse", 0), # Is riding a horse. (neg|troop_is_guarantee_ranged, ":troop"), # Not a horsearcher (try_begin), (this_or_next|is_between, ":wielded", "itm_jousting_lance","itm_glaive"), # Is it a lance? (is_between, ":wielded", "itm_light_lance","itm_pike"), # Is it a lance? (agent_set_slot, ":agent", slot_agent_lance, ":wielded"), (else_try), # Force the NPC to wield a lance, but this will only happen if they # actually have a lance equipped. Otherwise this does nothing. (assign, ":end", "itm_glaive"), # adjust as needed (try_for_range, ":item", "itm_jousting_lance",":end"), (agent_has_item_equipped, ":agent", ":item"), (agent_set_wielded_item, ":agent", ":item"), (agent_set_slot, ":agent", slot_agent_lance, ":item"), #Mark lancers for later use (assign, ":end", "itm_jousting_lance"),#loop breaker (try_end), (assign, ":end", "itm_pike"), # adjust as needed (try_for_range, ":item", "itm_light_lance",":end"), (agent_has_item_equipped, ":agent", ":item"), (agent_set_wielded_item, ":agent", ":item"), (agent_set_slot, ":agent", slot_agent_lance, ":item"), #Mark lancers for later use (assign, ":end", "itm_light_lance"),#loop breaker (try_end), (try_end), (else_try), (gt, ":horse", 0), # Is riding a horse. (troop_is_guarantee_ranged, ":troop"), # Is a horsearcher...redundant, but making sure (try_begin), (is_between, ":wielded", "itm_hunting_bow", "itm_crossbow"), # Is it a bow or a horse-useful crossbow? (agent_set_slot, ":agent", slot_agent_horsebow, ":wielded"), (else_try), # Force the NPC to wield their bow, but this will only happen if they # actually have a bow equipped. Otherwise this does nothing. (assign, ":end", "itm_crossbow"), # adjust as needed (try_for_range, ":item", "itm_hunting_bow",":end"), (agent_has_item_equipped, ":agent", ":item"), (agent_set_wielded_item, ":agent", ":item"), (agent_set_slot, ":agent", slot_agent_horsebow, ":item"), #Mark horse archers for later use (assign, ":end", "itm_hunting_bow"),#loop breaker (try_end), (try_end), (else_try), #Agent not mounted (try_begin), (gt, ":wielded", 0), #Make sure it's not fists (item_get_type, ":weapontype", ":wielded"), (eq, ":weapontype", itp_type_polearm), # Is it a spear? (agent_set_slot, ":agent", slot_agent_spear, ":item"), #Mark spearmen (else_try), #Check if there's a spear equipped, if not wielded (troop_get_inventory_capacity,":cap",":troop"), (try_for_range, ":i", 0, ":cap"),#not sure if reg. troops have equipped items (slots < 10), but w/e (troop_get_inventory_slot,":item",":troop",":i"), (gt, ":item", 0), (item_get_type, ":weapontype", ":item"), (eq, ":weapontype", itp_type_polearm), # Is it a spear? (agent_has_item_equipped, ":agent", ":item"),#this step is essential, (agent_set_slot, ":agent", slot_agent_spear, ":item"), #Mark spearmen (assign, ":cap", 0), #loop Break (try_end), (try_end), (try_end), #Mounted or not (try_end), #Agent Loop ]),
Okay, here's a little feedback on this:
Seems to work great with the Khergit troops. I tried a companion with a 1/2h bastard sword 2x arrows + a bow on a horse, though, and he didn't seem to start with his bow equipped ever. I tried moving the bow to different slots as well. He did stay shooting until he was out of ammo, though.
Also, there seems to be some odd behavior with bandits equipped with a spear, throwing or ranged weapons, shields, and 1h weapons. They try to switch to ranged weapons when you go away from them, then the script seems to force them back into using spears. They also stand still from time to time and look far upwards doing nothing. I suspect there's some kind of conflict with other A.I. related to switching between throwing weapons and melee weapons. Troops that would be effected by this are mercenary xbowmen, swadian militia, swadian crossbowmen, and most bandit types.