OSP Code Combat Lancers: Use the right weapon! Battle edition

Users who are viewing this thread

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.

Code:
slot_agent_lance    = 26
slot_agent_spear    = 27
slot_agent_horsebow = 28
Set values to first 3 unused agent slots (26, 27, 28 in Native)

Code:
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,
    ]
Install as instructed above, though with "  ] + weapon_use_triggers,  "  in place of lance_spear_triggers

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.
 
StinkyMcGirk said:
Caba`drin said:
See final spoiler at bottom for combined code for Lancers, Horse Archers and Spearmen
...snip...

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.

I appreciate the feedback.

Currently, the horse-bow portion of the script is tied into the troop's "guaranteed ranged weapon" flag and if the bot spawns with a horse. Since companions do not have the guarantee ranged weapon flag, they won't ever be marked as horse archers. Perhaps changing this to work as lancers do (seeing if they have the appropriate weapon equipped) would be better than working with the guaranteed ranged flag. It would include companions then...and may ease the bandit conflict, though I can't say I understand why that would be happening. Agent/bots shouldn't be marked as more than one (lancer OR horse archer OR spearman...never a combination) so I don't think the conflict would be there. I'll do some more checking though....

Edit: Looks like there was potential for error. I've added a line in the original post: (le, ":horse", 0), that falls after (else_try), #Agent not mounted  in the classify_agent script

Does the idea of forcing horse archers to use all their ammo before changing to melee work well, or is that too stringent a restriction?
 
Caba`drin said:
I appreciate the feedback.

Currently, the horse-bow portion of the script is tied into the troop's "guaranteed ranged weapon" flag and if the bot spawns with a horse. Since companions do not have the guarantee ranged weapon flag, they won't ever be marked as horse archers. Perhaps changing this to work as lancers do (seeing if they have the appropriate weapon equipped) would be better than working with the guaranteed ranged flag. It would include companions then...and may ease the bandit conflict, though I can't say I understand why that would be happening. Agent/bots shouldn't be marked as more than one (lancer OR horse archer OR spearman...never a combination) so I don't think the conflict would be there. I'll do some more checking though....

Edit: Looks like there was potential for error. I've added a line in the original post: (le, ":horse", 0), that falls after (else_try), #Agent not mounted  in the classify_agent script

Does the idea of forcing horse archers to use all their ammo before changing to melee work well, or is that too stringent a restriction?

I think it's important to keep them shooting for most of the battle as the units that are horse archers never have points in power strike.  Companions could be the exception, but it seems to me it would be a nightmare sorting out how to get a companion armed with, say, a bow / arrows / 2h sword / lance to use the right weapon at a given time and switch at others.  If ordering them to 'hold their fire' can halt the A.I., more control never hurts - but I haven't tested what effect that actually has.  I'll try that and get back to you.

What is the intended behavior for units with lances and bows or spears + ranged weapons?  Could the script be alternating the unit between the two groups resulting in the odd behavior I witnessed?

Further tests showed me that mounted units with spears or lances + ranged weaponry also had the same conflicting behavior that ground based ones had.  This is a bit problematic, but by editing all troops' equipment list to remove spears and lances it just went away.  The issue is that the lances are key to some units' playstyle - especially Khergit Horsemen.  What might be really good, would be to prioritize lances / spears during the initial charge, then have the forced ranged weaponry kick in, but I don't know what kind of triggers you have to work with to perfect that. 

Something along those lines could also alleviate the issue of A.I. horse archers that use the script on the enemy side.  Currently A.I. horse archers like desert bandits, steppe bandits, and enemy khergit faction troops will correctly start with their bows, but as they charge in shooting, they plow headlong into the enemy formation.  From there they get (rather humorously) caught up in the initial blob of armies meeting, and often get butchered since they have no way to defend themselves outside of shooting at point blank.  Overall, however, I think this behavior of shooting point blank is superior to the default A.I.'s preference of attacking with ineffective melee weapons like maces and spears with no power strike or momentum behind it.  I can tell that the dilemma is that using the triggers available to you doesn't really provide you with a way to force horse archers to make decisions related to the positioning of armies on the battlefield...  so this is probably best left alone.

As the player, it's easy to just order your HA's off to the side until the armies break up a little, or lead them close before telling them to 'charge' so that their 'keep away' behavior kicks in...  the enemy armies don't have that luxury.  Perhaps the easiest solution is to simply change their equipment lists again so that they have steppe horses and steppe chargers instead of coursers - that way they'd naturally fall in behind the faster courser-mounted lancers.  I just think that the speed of the courser is almost integral to making the horse archers do what they do best:  Distract.
 
Caba`drin said:
Edit: Looks like there was potential for error. I've added a line in the original post: (le, ":horse", 0), that falls after (else_try), #Agent not mounted  in the classify_agent script

Okay, added that in, and this is what now happens:

Sea Raiders with spears + bows will never use the bows.  They will try to switch to the bows, then immediately back to the spears.
Sea Raiders with spears + throwing weapons will have the same behavior with throwing weapons.
Companion with Great Long Bardiche and Warbow would never use the warbow.

Swadian skirmishers/Xbowmen/etc will never use their ranged weapons

Steppe Bandits and Desert Bandits are fixed, though, they seems to be making the correct selections.

---

So... the ground based spear script is taking priority over ranged weapons, even on archers.  This makes ground based bandits with polearms unable to utilize bows or thrown weapons at all.

Also, I noticed a sea raider with a spear fighting a stationary swadian knight swap weapons to an axe, then immediately back to the spear even though he's at point blank range.  Perhaps consider canceling the spear script after the A.I. switches to a closer weapon?

Oh yeah, and when ordered to 'hold your fire,' horse archers briefly weapon swap, then swap back to the bows.
 
@Stinky McGirk, et al.
Hrm. Thanks again for your thorough testing and feedback. Clearly a number of oversights on my part, which is what I get for only testing the spear/pike portion with Rhodoks and then setting it aside to work on other things. I'd recommend deleting the spear portion from the classify_agent script...the easiest way to remove the mishaps with ground units until I reconsider how to address that.

None of the behavior you describe seems abhorrent, though...in so far as it all seems to be flowing in predictable ways from the code. It's just that the code itself is not half nuanced enough to deal with the varieties of situations you are throwing at it. It began as a very blunt instrument...which I tried to expand but neglected to make more complex, so I just made a bigger, blunter instrument. Not necessarily a step in the right direction. And so, we come back to the beginning of this post: too many oversights on my part. I'll "complexify" this as I integrate it into a more comprehensive kit with my order codes.

EDIT: For the time being, my code post has been stripped to just the lancer code with the proximity check for lance/sidearm switching. The full spear, horsearcher bit is still there in the final spoiler and I'm working on improving it, but I wanted to get the finished, working part separate.
 
Something sort of related, but Outlawed was talking about how he disabled the thrust attack for all lances, and what happened was pretty cool. The AI did a couche lance charge, then once they were in close combat they all took out their swords :eek:
 
Well, work well when limited (i have myself packed together all the cavalry acting as lancers for the behaviour not to contamine horse archer), but the thing is, at least for me, is there a way to force cavalry to have both lances and swords at the begining of a battle?

I have been working on this for a few week but still success.

by the way, i found ek_item_0 in one code given here. where does it comes from? can't find a reference anywhere. First i though this was the direct reference to equiped item by the agent in question (ie those 4 hand slots of the character) But a quick test gave nothing.

code of my last try, assuming these were as i though (obviously wrong)

Code:
 ("lance_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"),
   # is a lancer somehow.
	(agent_get_troop_id, ":troop",":agent"), # shorter, adn do not include horse archer lads (except the mongol-like one, but he has his own tweaks in other codes)
	(is_between, ":troop", heavy_cav_begin, cavalry_end),
	# Get wielded item.
        (agent_get_wielded_item, ":wielded", ":agent", 0),
	(assign, ":lance", 0), # just to be sure
        (assign, ":end", ek_item_0),
        (try_for_range_backwards, ":i", ek_head, ":end"),
			(troop_get_inventory_slot,":item",":troop",":i"),
			(try_begin), # well, a bit of a repetition, but needed for clarity
				(is_between, ":item","itm_light_lance","itm_phalanx_pike"),
				(assign, ":lance", 1),
                                (agent_set_slot, ":agent", slot_agent_lance, ":wielded"), # used the wielded thing just for debug purpose, don't mind this
			(try_end),
			(eq, ":i", ek_item_0),
			(lt, ":lance", 1),
			(try_begin), # no need to replace a shield, so take the precedent slot and force loop break
				(is_between, ":item", shields_begin, shields_end),
				(val_add, ":i", 1),
				(assign, ":end", ek_head),
			(try_end),
			(try_begin), #giving a lance by cavalry class
				(is_between, ":troop", heavy_cav_begin, medium_cav_begin),
				(troop_set_inventory_slot,":troop",":i","itm_great_lance"),
				(agent_set_wielded_item, ":agent", "itm_great_lance"),
				(agent_set_slot, ":agent", slot_agent_lance, "itm_great_lance"), 
			(else_try),
				(is_between, ":troop", medium_cav_begin, light_cav_begin),
				(troop_set_inventory_slot,":troop",":i","itm_heavy_lance"),
				(agent_set_wielded_item, ":agent", "itm_heavy_lance"),
				(agent_set_slot, ":agent", slot_agent_lance, "itm_heavy_lance"),
			(else_try),
				(is_between, ":troop", light_cav_begin, cavalry_end),
				(troop_set_inventory_slot,":troop",":i","itm_light_lance"),
				(agent_set_wielded_item, ":agent", "itm_light_lance"),
				(agent_set_slot, ":agent", slot_agent_lance, "itm_light_lance"),
			(try_end),
         (try_end),
   (try_end), #Agent Loop
    ]),
 
Yes - as long as there are empty weapon slots for the agent, you are able to use agent_equip_item on any valid agent. ek_item_slots refer to the 10 slots available in the middle of the inventory screen as opposed to the inventory for any troop (which starts at 10) - see header_items. Note that the comments for try_for_range_backwards are wrong - the last two parameters are still lower and upper bounds, so your loop will not run properly.
 
Right, i've been studying it for some time now, and i can't make it work at all (some of the lads are stubborn enough to not weild lances... heretics!)

does someone have an idea?

Code:
                        ("lance_use_classify_agent", [ #cavalry troops are standarised because they are in fact too similar to bother giving theim useless clones, at least for weapons (armors and horses are not)
                            (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"),
                              # is a lancer somehow.
                              (agent_get_troop_id, ":troop",":agent"),
                              (assign, "$reg_item", -1),
                              (is_between, ":troop", heavy_cav_begin, cavalry_end),
							  (try_for_range_backwards, ":i", ek_item_0, ek_head),
								(try_begin),
									(eq, ":i", ek_item_0),
									(try_begin),
										(is_between, ":troop", heavy_cav_begin, medium_cav_begin),
										(troop_set_inventory_slot,":troop",":i","itm_great_lance"),
										(agent_set_wielded_item, ":agent", "itm_great_lance"),
										(assign, ":lance", "itm_great_lance"),
									(else_try),
										(is_between, ":troop", medium_cav_begin, light_cav_begin),
										(troop_set_inventory_slot,":troop",":i","itm_heavy_lance"),
										(agent_set_wielded_item, ":agent", "itm_heavy_lance"),
										(assign, ":lance", "itm_heavy_lance"),
									(else_try),
										(is_between, ":troop", light_cav_begin, cavalry_end),
										(troop_set_inventory_slot,":troop",":i","itm_light_lance"),
										(agent_set_wielded_item, ":agent", "itm_light_lance"),
										(assign, ":lance", "itm_light_lance"),
									(try_end),
								(else_try),						
									(eq, ":i", ek_item_1),
									(call_script, "script_look_for_item",":agent", itp_type_one_handed_wpn),
									(troop_set_inventory_slot,":troop",":i","$reg_item"),
								(else_try),
									(eq, ":i", ek_item_2),
									(try_begin),
										(neq, ":troop", "trp_dese_wido"),
										(neq, ":troop", "trp_merc_cond"),
										(call_script, "script_look_for_item",":agent", itp_type_shield),
										(troop_set_inventory_slot,":troop",":i","$reg_item"),
									(else_try),
										(eq, ":troop", "trp_merc_cond"),
										(troop_set_inventory_slot,":troop",":i", -1),
									(else_try),
										(eq, ":troop", "trp_dese_wido"),
										(call_script, "script_look_for_item",":agent", itp_type_pistol),
										(troop_set_inventory_slot,":troop",":i","$reg_item"),
									(try_end),
								(else_try),
									(eq, ":i", ek_item_3),
									(try_begin),
										(neq, ":troop", "trp_dese_wido"),
										(neq, ":troop", "trp_mong_cav"),
										(troop_set_inventory_slot,":troop",":i", -1),
									(else_try),
										(eq, ":troop", "trp_dese_wido"),
										(troop_set_inventory_slot,":troop",":i","itm_cartridges"),
									(else_try),
										(eq, ":troop", "trp_mong_cav"),
										(troop_set_inventory_slot,":troop",":i","itm_khergit_lancer_bow"),
									(try_end),
								(try_end),
							 (try_end),
							 (agent_set_slot, ":agent", slot_agent_lance, ":lance"),
						   (try_end),
                      ]),
                      
                        ("look_for_item", [ #nothing optimized yet, have to make it work before O(2n) good enough for now
							(store_script_param_1, ":agent"),
							(store_script_param_2, ":target"),
							(agent_get_troop_id, ":troop", ":agent"),
							(troop_get_inventory_capacity,":cap",":troop"),
							(try_begin), # counting the items of the wanted kind. Used to randomize the returnerd weapon
								(eq, ":target", itp_type_one_handed_wpn),
								(assign, ":count", -1),
								(try_for_range, ":a", 0, ":cap"),
									(troop_get_inventory_slot,":item",":troop",":a"),
									(gt, ":item", 0),
									(item_get_type, ":type", ":item"),
									(gt, ":type", 0),
									(this_or_next|eq, ":type", itp_type_one_handed_wpn),# to prevent odd removing of bastard swords
									(eq, ":type", itp_type_two_handed_wpn),
									(val_add, ":count", 1),
								(try_end),
								(try_begin),
									(gt, ":count", 0),
									(store_random_in_range, ":var", 0 , ":count"),#gives a random number between 0 and the items amount of targeted type
								(else_try),
									(assign, ":var", 0),
								(try_end),
								(try_for_range, ":b", 0, ":cap"),
									(troop_get_inventory_slot,":item",":troop",":b"),
									(try_begin),
										(gt, ":var", 0),
										(gt, ":item", 0),
										(item_get_type, ":type", ":item"),
										(gt, ":type", 0),
										(eq, ":type", ":target"),
										(val_sub, ":var", 1),
									(else_try),
										(eq, ":var", 0),
										(gt, ":item", 0),
										(item_get_type, ":type", ":item"),
										(gt, ":type", 0),
										(this_or_next|eq, ":type", itp_type_one_handed_wpn),# to prevent odd removing of bastard swords
										(eq, ":type", itp_type_two_handed_wpn),
										(assign, "$reg_item", ":item"),
										(assign, ":cap", 0),# once the item defined, break tje loop
									(else_try),
										(lt, ":var", 0),
										(display_message, "@ Erreur sortie de parametre"),
									(try_end),
								(try_end),
							(else_try),# counting the items of the wanted kind. Used to randomize the returnerd weapon. Both block will be mixed together once the code is working (however, it will be O(2n) too)
								(assign, ":count", -1),
								(try_for_range, ":c", 0, ":cap"),
									(troop_get_inventory_slot,":item",":troop",":c"),
									(gt, ":item", 0),
									(item_get_type, ":type", ":item"),
									(gt, ":type", 0),
									(eq, ":type", ":target"),
									(val_add, ":count", 1),
								(try_end),
								(try_begin),
									(gt, ":count", 0),
									(store_random_in_range, ":var", 0 , ":count"),#gives a random number between 0 and the items amount of targeted type
								(else_try),
									(assign, ":var", 0),
								(try_end),
								(try_for_range, ":d", 0, ":cap"),
									(troop_get_inventory_slot,":item",":troop",":d"),
									(try_begin),
										(gt, ":var", 0),
										(gt, ":item", 0),
										(item_get_type, ":type", ":item"),
										(gt, ":type", 0),
										(eq, ":type", ":target"),
										(val_sub, ":var", 1),
									(else_try),
										(eq, ":var", 0),
										(gt, ":item", 0),
										(item_get_type, ":type", ":item"),
										(gt, ":type", 0),
										(eq, ":type", ":target"),
										(assign, "$reg_item", ":item"),
										(assign, ":cap", 0),# once the item defined, break tje loop
									(else_try),
										(lt, ":var", 0),
										(display_message, "@ Erreur sortie de parametre"),
									(try_end),
								(try_end),
							(try_end),
					]),
 
@Vjeff, a few comments...

First, within your try_for_agents loop in classify agent, the try_for_range_backwards loop serves no purpose. You aren't looking through each inventory slot, only the specific ones you identify in the try block that follows, so drop the loop and replace ":i" with the appropriate inventory slot. As is, since you are using a backwards loop, you never reach the primary weapon slot where you set the lance. A backwards loop begins at the upper bound (ek_head) and moves to one greater than the lower bound (ek_item_1)...the reverse of a forwards loop that moves from the lower bound (ek_item_0) to one less than the upper bound (whatever ek_head - 1 is). And, since the lance is never set, the slot for the lance is set to 0, so the switching portion of the code (if you are using that) won't do anything either.

Second, it isn't clear if non-hero troops use the ek_ inventory slot system as well (in other words, if their equipped items are confined to the first 10 inventory slots) which is why "agent_has_item_equipped" is a key command for non-hero troops when dealing with their items and code dealing with them typically loops through the full inventory. Unless, of course, you've done some testing to show that they do use the ek_ system? In that case, I think a number of us would be happy to see this! We could simplify much code!

Third, with point two in mind, it may be prudent to use agent_equip_item rather than setting the inventory slot. Or, in your testing, after you set the slot, use "agent_has_item_equipped" to double check that you've actually given the item to the agent.

Further, consider the broader implication of what you are doing...you are cycling through agents (varied duplicates of the troop template) and for each agent, you are trying to change the troop template that agent is based on. Troop_set_inventory_slot affects the entire troop template, for all troops and agents based off of them, not just the agent you are dealing with in your agent loop. This may not be the best, or even a workable way to accomplish what you are trying...or it may be, if you are attempting to have every agent based off that troop type be exactly the same. In this case there is just a considerable amount of redundancy in setting the troop's inventory slots over and over.

Finally a few cosmetic things. There is no reason to create your own global variable to pass the item from your search script to the classify script...that is precisely what the actual registers are for. Just use reg0 or reg1 or what-have-you.  Next, for non-nested loops...the series of try_for_ranges you have in the search script, you can use the same iterator variable. Use ":i" or whatever, for each one. It is freed up again once the loop is closed. I didn't look much more at the search script, but, as you indicate, it could use a significant amount of optimization.

 
Ok, thanks for you answer, Caba`drin.

for the first point, i just forgot to remove it.

for the second point, it's about to be clarified for good. Once i have tested this long enough I'll tell the community about that.
but i think troop also have those ek_***. well, no proof for now.

about agent_equip_item... i don't have it in header_operations, neither it is used in module_scripts. the thing is no slot can be focused on with this.  For now, i'm still using the crossing between troop_ and agent_ to see more about the second point (although i'll follow your advice and set a few debugs to see how it's going)

I was not aware that could "standarise" lancers equipement. if ek_ doesn't work, i'll find another way.

about the registers. I had a few glitches with theses recently. I'm working with an already made mod, and it seems that more registers (and, by the way, also strings one) than usual are used. I came with some unsuspected values working with registers, and a knight trying to makes my male caracter mary one of his lords, adn few duplicates of myself came in some halls. Until i haven't see through all register usage of the module, i'll stick to global variable. Cause you know, i have nothing against gay mariage, but i'm not sure it fit in this fantasy world...

whatever, i give a try and check back with news soon
 
Ok, tests have been done, and i can tell for sure  about ek_ slots

they do have those, but do not used theim. As expected,  the equipement was standarised in ek_ slots. But in the field the troops had various equipement, not only the standarised one.

well i do have to change all of this to agent_equipe and weild.
 
Vjeff said:
about agent_equip_item... i don't have it in header_operations, neither it is used in module_scripts. the thing is no slot can be focused on with this.
Agent_equip_item was added to the Module System with Warband version 1.130. Perhaps I missed before if you were editing original M&B.

Vjeff said:
about the registers. I had a few glitches with theses recently. I'm working with an already made mod, and it seems that more registers (and, by the way, also strings one) than usual are used. I came with some unsuspected values working with registers, and a knight trying to makes my male caracter mary one of his lords, adn few duplicates of myself came in some halls.
Hrm...well registers are useful for passing data between scripts, and then assigning that data to a local variable. Trying to use the data in a register for longer than that near-instantaneous transfer can definitely lead to problems, as many other scripts will rewrite and work with those same registers. But, I'm not sure if that's the problem you're explaining or not.

Concerning regs, agent-vs-troop, etc, I'm not sure if you've taken a gander at my brief thread on Mod System Syntax (linked in my sig), but it may be somewhat helpful. Specifically the agent/troop messiness is something I struggled with when I began coding Warband...look back at my hilariously misguided attempts to work with the weapon breaking script for more evidence than I'd like to admit is there...
 
Hrm...well registers are useful for passing data between scripts, and then assigning that data to a local variable. Trying to use the data in a register for longer than that near-instantaneous transfer can definitely lead to problems, as many other scripts will rewrite and work with those same registers. But, I'm not sure if that's the problem you're explaining or not.

actually that exactly the problem i am talking about. this mod include many things that use registers, and some of theim for long time use. But whatever, until i can find time to check all registers in all files one by one, i will not try to mess with theim.

Concerning regs, agent-vs-troop, etc, I'm not sure if you've taken a gander at my brief thread on Mod System Syntax (linked in my sig), but it may be somewhat helpful. Specifically the agent/troop messiness is something I struggled with when I began coding Warband...look back at my hilariously misguided attempts to work with the weapon breaking script for more evidence than I'd like to admit is there...

Yep, i did see that. actually i'm following the community for a long time now, even if i only registered for a short time.
 
So with this script, my pal Lezalit will actually actually use his great sword during the late night mountain bandit lair raid, instead of attempting point blank thrusts with his great lance like a moron?

This should be in-game by default.
 
I've tried back and forth with this for a week now, and I just won't seem to get a hang of it. No matter how many times i put it in, I get a C++ runtime error after the module.INI has been loaded on screen. Although I don't have any coding experience whatsoever, so I might overlook something. However reverting back to an unchanged mission_template works... although I do want this to work. Vanilla horse battles just suck ***.

Also, I understand there's a similar code for horse archers? Or is that some wild fantasy on the top of my head :grin: anyway... my main reason for writing is:

Can't someone be very kind and mail THEIR edited mission_template to me from the native WB with this code? I'd be eternally grateful. If someone reads this, just throw me a PM for my e-mail. Many and eternal thanks in advance.

:oops:
 
Brody said:
Also, I understand there's a similar code for horse archers? Or is that some wild fantasy on the top of my head :grin:
Not a wild fantasy. A version of it can be found in the spoiler at the bottom of this post a few pages back in this thread. rubik also has a version in the open source of his Custom Commander mod.

An improved version integrated with some other tweaks (and with an on/off switch) is included in my Pre-Battle Orders & Deployment mod.

Brody said:
Can't someone be very kind and mail THEIR edited mission_template to me from the native WB with this code? I'd be eternally grateful. If someone reads this, just throw me a PM for my e-mail. Many and eternal thanks in advance.
I'd (of course) recommend just downloading my PBOD mod as these codes are built in, along with a number of other battle tactic-related tweaks that you can choose to use or not use. Native save games are completely compatible.

Or, if you want to get the hang of adding these little code tweaks yourself, feel free to upload your module_mission_templates.py somewhere and send me a PM with the link. I'd happily take a look at it to figure out what went wrong.
 
Thanks for the reply, but the thing is that I've customized my own mod folder with several mods, and I'm perfectly happy in keeping it that way. The only thing I'm missing is that very code. Hmm...

I haven't edited the mission_template file yet, so it should be overwritable. :smile:

But I'll take a look at the things you said.

But if anyone feels up to it, plz mail me an edited mission_template :smile:

Thanks anyway.
 
Back
Top Bottom