Lancers: Use the right weapon! Battle edition (script)

CryptoCactus

Sergeant Knight
M&BWB
Best answers
0
This is merely hatonastick's code adapted for battles and such. With it, cavalry units (any troop on a horse regardless of actual division) will use lances if they have them while on horseback, but will almost immediately switch to their sidearm (any non-lance weapon) if dehorsed or ordered to dismount and they are carrying such an item. No more dismounted Swadian knights stupidly getting killed by half-dead looters because they won't switch from their heavy lance on foot, etc.

Posted with hatonastick's permission, and the credit really goes to him as it's 98% his code. Also note that you may need to tweak the item checks or move stuff around in module_items to ensure all the weapons you want counted as "lances" are affected.

This goes into module_mission_templates.py. For battles, put it under lead_charge, for sieges, search for the words attack_castle and besiege (should be 4 places total for sieges, IIRC).

Code:
# LANCE USAGE BEGIN
   # Force mounted NPCs to switch to their lance.  This is called once at the
   # start of the battle. If you want lancers to ALWAYS use lances on horseback,
   # replace ti_once with 1. Otherwise they may switch to sword if bogged down
   (0, 1, ti_once, [],
   [
      # Run through all active NPCs on the battle field.
      (try_for_agents, ":agent"),
        # Isn't a player.
        (agent_is_non_player, ":agent"),
        # Isn't a horse.
        (agent_is_human, ":agent"),
        # Hasn't been defeated.
        (agent_is_alive, ":agent"),
        # They riding a horse?
        (agent_get_horse, ":horse", ":agent"),
        # Is riding a horse.
        (gt, ":horse", 0),
        # Get wielded item.
        (agent_get_wielded_item, ":wielded", ":agent", 0),
        # Is it a lance?
        (neg|is_between, ":wielded", "itm_light_lance","itm_pike"), # adjust as needed
        # Force the NPC to wield the lance, but this will only happen if they
        # actually have a lance in their inventory.  Otherwise this does
        # nothing.
		(try_for_range,":item","itm_light_lance","itm_pike"), # adjust as needed
			(agent_set_wielded_item, ":agent", ":item"),
		(try_end),
      (try_end),   
   ]),
   
   # 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.
   (0, 0, 1, [],
   [
      # Run through all active NPCs on the battle field.
      (try_for_agents, ":agent"),
        # Isn't a player.
        (agent_is_non_player, ":agent"),
        # Isn't a horse.
        (agent_is_human, ":agent"),
        # Hasn't been defeated.
        (agent_is_alive, ":agent"),
        # They riding a horse?
        (agent_get_horse, ":horse", ":agent"),
        # Isn't riding a horse.
        (le, ":horse", 0),
        # Get wielded item.
        (agent_get_wielded_item, ":wielded", ":agent", 0),
        # Is it a lance?
        (is_between, ":wielded", "itm_light_lance","itm_pike"),
		# Find non-lance item in inventory
		(agent_get_troop_id, ":troop",":agent"),
		(troop_get_inventory_capacity,":cap",":troop"),
        (assign,":has_choice",0),
		(try_for_range, ":i", 0, ":cap"),
			(troop_get_inventory_slot,":item",":troop",":i"),
			(neg|is_between, ":item", "itm_light_lance","itm_pike"), # adjust as needed
			(neg|is_between, ":item", "itm_wooden_shield","itm_darts"), # not a shield - adjust as needed
			(neg|is_between, ":item", "itm_hunting_bow","itm_torch"), # not ranged - adjust as needed
			(assign,":has_choice",1),
            (assign,":cap",0),
		(try_end),
        # Equip their backup weapon.
		(try_begin),
			(eq, ":has_choice",1),
			(agent_set_wielded_item, ":agent", ":item"),
		(try_end),
      (try_end),
   ]),
# LANCE USAGE END

Please report it here if you find any glaring flaws, and of course suggestions for improvements are always welcome.

 

gwanjun

Recruit
Best answers
0
That's really nice. I was thinking? Is there one for the spearmen on foot? Like the Rhodoks? Like it's hard since there's no mounted or dismounted trigger. I don't know anything about modding but, is it possible?

To like trigger the spearmen to use their spears/polearms as first priority. Without like taking away a secondary weapon from them which AI tend to use instead. Like if calvary are coming, they pull out polearms. Sort of like how if you hold fire, the enemy don't use shields. But when you do fire, they hold them up.
 

Panzercracker

Sergeant at Arms
Best answers
0
Which module I should put this code in? In module_scripts, it is just error.

Initializing...
Compiling all global variables...
Error in script:
(0, 1, 100000000.0, [], [(12, ':agent'), (1707, ':agent'), (1704, ':agent'), (17
02, ':agent'), (1714, ':horse', ':agent'), (32, ':horse', 0), (1726, ':wielded',
':agent', 0), (2147483681L, ':wielded', 'itm_light_lance', 'itm_great_lance'),
(6, ':item', 'itm_light_lance', 'itm_great_lance'), (1747, ':agent', ':item'), 3
, 3])
Error in script:
(0, 0, 1, [], [(12, ':agent'), (1707, ':agent'), (1704, ':agent'), (1702, ':agen
t'), (1714, ':horse', ':agent'), (2147483680L, ':horse', 0), (1726, ':wielded',
':agent', 0), (33, ':wielded', 'itm_light_lance', 'itm_great_lance'), (1718, ':t
roop', ':agent'), (1540, ':cap', ':troop'), (2133, ':has_choice', 0), (6, ':i',
0, ':cap'), (1541, ':item', ':troop', ':i'), (2147483681L, ':item', 'itm_light_l
ance', 'itm_great_lance'), (2147483681L, ':item', 'itm_wooden_shield', 'itm_dart
s'), (2133, ':has_choice', 1), (2133, ':cap', 0), 3, 4, (31, ':has_choice', 1),
(1747, ':agent', ':item'), 3, 3])
Exporting strings...
Exporting skills...
Exporting tracks...
Exporting animations...
Exporting meshes...
Exporting sounds...
Exporting skins...
Exporting map icons...
Creating new tag_uses.txt file...
Creating new quick_strings.txt file...
Exporting faction data...
Exporting item data...
Exporting scene data...
Exporting troops data
Exporting particle data...
Exporting scene props...
Exporting tableau materials data...
Exporting presentations...
Exporting party_template data...
Exporting parties
Exporting quest data...
Exporting info_page data...
Exporting scripts...
Traceback (most recent call last):
  File "process_scripts.py", line 35, in <module>
    save_python_header()
  File "process_scripts.py", line 29, in save_python_header
    file.write("script_%s = %d\n"%(convert_to_identifier(scripts[i_script][0]),i
_script))
  File "H:\Program Files\Mount&Blade Warband\Modules\OSPW - Native 1.127a Module
_system\process_common.py", line 5, in convert_to_identifier
    s1 = string.replace(s0," ","_")
  File "C:\Python27\lib\string.py", line 519, in replace
    return s.replace(old, new, maxsplit)
AttributeError: 'int' object has no attribute 'replace'
Exporting mission_template data...
Exporting game menus data...
exporting simple triggers...
exporting triggers...
exporting dialogs...
Checking global variable usages...
WARNING: Global variable never used: npc_with_personality_match
WARNING: Global variable never used: g_battle_simulation_cancel_for_party
WARNING: Global variable never used: g_include_diplo_explanation
WARNING: Global variable never used: debug_message_in_queue
WARNING: Global variable never used: g_multiplayer_next_team_1_faction
WARNING: Global variable never used: g_multiplayer_next_team_2_faction
WARNING: Global variable never used: g_confirmation_result
WARNING: Global variable never used: romantic_attraction_seed
WARNING: Global variable never used: g_multiplayer_factions_voteable
WARNING: Global variable never used: g_multiplayer_maps_voteable
WARNING: Global variable never used: g_multiplayer_kick_voteable
WARNING: Global variable never used: g_multiplayer_ban_voteable
WARNING: Global variable never used: g_multiplayer_player_respawn_as_bot
WARNING: Global variable never used: g_multiplayer_battle_earnings_multiplier
WARNING: Global variable never used: g_election_date
WARNING: Global variable never used: g_training_ground_training_num_gourds_to_de
stroy
WARNING: Global variable never used: pout_party
WARNING: Global variable never used: g_player_party_morale_modifier_leadership
WARNING: Global variable never used: g_player_party_morale_modifier_food
WARNING: Global variable never used: g_player_party_morale_modifier_debt
WARNING: Global variable never used: g_player_current_own_troop_kills
WARNING: Global variable never used: num_routed_allies
WARNING: Global variable never used: belfry_rotating_objects_begin
WARNING: Global variable never used: last_belfry_object_pos
WARNING: Global variable never used: g_minister_notification_quest
WARNING: Global variable never used: any_allies_at_the_last_battle
WARNING: Global variable never used: number_of_npc_slots
WARNING: Global variable never used: npc_grievance_string
WARNING: Global variable never used: npc_praise_not_complaint
WARNING: Global variable never used: g_custom_battle_team1_death_count
WARNING: Global variable never used: g_custom_battle_team2_death_count
WARNING: Global variable never used: total_political_events
WARNING: Global variable never used: g_wedding_bishop_troop
WARNING: Global variable never used: newglob_total_prosperity_from_villageloot
WARNING: Global variable never used: newglob_total_prosperity_from_bandits
WARNING: Global variable never used: newglob_total_prosperity_losses
WARNING: Global variable never used: newglob_total_prosperity_gains
WARNING: Global variable never used: total_vassal_days_on_campaign
WARNING: Global variable never used: total_feast_changes
WARNING: Global variable never used: g_use_current_ai_object_as_s8
WARNING: Global variable never used: g_is_quick_battle
WARNING: Global variable never used: player_marshal_ai_state
WARNING: Global variable never used: player_marshal_ai_object
WARNING: Global variable never used: g_player_affiliated_time
WARNING: Global variable never used: add_1000
WARNING: Global variable never used: g_persuasion_success_limit
WARNING: Global variable never used: server_mission_timer_while_player_joined
WARNING: Global variable never used: g_hp_bar_dis_limit
WARNING: Global variable never used: g_hp_bar_ally
WARNING: Global variable never used: g_hp_bar_enemy
WARNING: Global variable never used: g_encumbrance_penalty
WARNING: Global variable never used: g_multiplayer_max_num_bots
WARNING: Global variable never used: g_twice_consum_food
Exporting postfx_params...

______________________________

Script processing has ended.
Press any key to exit. . .
 

CryptoCactus

Sergeant Knight
M&BWB
Best answers
0
Panzercracker said:
Which module I should put this code in? In module_scripts, it is just error.
module_mission_templates.

For example, if you want to put it in open field battles, look for lead_charge and put it somewhere in there.
 

Panzercracker

Sergeant at Arms
Best answers
0
CryptoCactus said:
Panzercracker said:
Which module I should put this code in? In module_scripts, it is just error.
module_mission_templates.

For example, if you want to put it in open field battles, look for lead_charge and put it somewhere in there.
For goodness, you should say earlier,  how I know without name of module.
BTW, thank you
 

CryptoCactus

Sergeant Knight
M&BWB
Best answers
0
Added a simple ranged check to the second part of the code. Without it, it was possible for a bot to get 'stuck' switching back and forth between lance and ranged weapon in melee.

(neg|is_between, ":item", "itm_hunting_bow","itm_torch"), # not ranged - adjust as needed


Note here that lance-wielding bots on foot can still switch to and use ranged weapons per native AI, they just won't be forced to by this code.




I'm looking into adding some code to account for mounted ranged units (as in horse archers) as well, but running into some issues. I can easily force units with ranged weapons to wield them at the start of battle, but they still retardedly charge directly into the thick of battle, and I don't know if there's anything I can do about that. I was hoping maybe if they pulled out a bow or whatever at the very beginning of battle they'd behave a little less stupidly, but no. Of course, on the player's side, the player can still have them follow him, or F1 them all over the field by hand, but in that case they'll usually pull out ranged and start shooting anyway, rendering my code irrelevant.

WB really needs some skirmishing AI. Something that tells the units to try to maintain distance while firing with ranged (and, ideally for horse archers, maintain distance as well as forward movement, aka circling).



Panzercracker said:
For goodness, you should say earlier, due I so tired, I not click many new links.

BTW, thank you
Hopefully it's more clear now. :smile:
 

Panzercracker

Sergeant at Arms
Best answers
0
CryptoCactus said:
I said "toss it into lead_charge" in the first post, though I guess I shouldn't just expect everyone to know where that is. :razz:
There are also many non-pro module-modifier, like me never open mission_templates, how I know :sad:.
 

CryptoCactus

Sergeant Knight
M&BWB
Best answers
0
Panzercracker said:
CryptoCactus said:
I said "toss it into lead_charge" in the first post, though I guess I shouldn't just expect everyone to know where that is. :razz:
There are also many non-pro module-modifier, like me never open mission_templates, how I know :sad:.
I know, I realized that sounded somewhat condescending, but I didn't mean it to. I meant I just forgot to say where it is. I'm definitely not a pro myself. My fault, not yours!
 

Hatonastick

I'm looking to expand this.  Don't let that put you off working on it CryptoCactus because the ideas I have are going to be a little difficult if there's no way to get the agent ID of an enemy the player/NPC has targeted, and if the lack of response of my topic is anything to go by I'm not holding onto hope that there is such a command.  Nothing springs out at me when I go through header_operations.py or when I wander through the forums on here -- haven't been through the source code yet though it's a bit of a needle in a haystack. :smile:

I'm really holding onto the hope that Taleworlds is planning to continue to open up more things to the script/mod side of things with every patch.  Currently I have several ideas on hold (including my mod) because there are things I've hit that can't be gotten around with the current patch.

One such thing that would help a lot of modders is if they allowed attack triggers to work with melee weapons, as currently they still only work with ranged.  Another thing I'd like to see is that blood colour used in skins is also used by the engine when it paints blood on weapons and armour.  Or even the ability to turn blood off for some skins eg. automaton, golem etc.

Erm but anyway I'm now massively off topic, sorry.  Glad to see this script grow/develop further.  Honestly didn't think anyone was interested in my original script.
 

CryptoCactus

Sergeant Knight
M&BWB
Best answers
0
Hatonastick said:
I'm looking to expand this.  Don't let that put you off working on it CryptoCactus because the ideas I have are going to be a little difficult if there's no way to get the agent ID of an enemy the player/NPC has targeted, and if the lack of response of my topic is anything to go by I'm not holding onto hope that there is such a command.  Nothing springs out at me when I go through header_operations.py or when I wander through the forums on here -- haven't been through the source code yet though it's a bit of a needle in a haystack. :smile:
I bet you and I have had some of the same thoughts, because I was cruising header_operations last night looking for a way to get the agent ID of the chosen target as well, heh. IIRC there's an agent_look_target or something like that, but I'm not sure what it does. Although I wonder... hm. Of course I'm at work right now (working hard, obviously >_>), so I can't get to the modsys atm.
 

Hatonastick

I suspect so. :smile:  As soon as you are able to get the targets agent ID you can do all sorts of things from intelligently determining what equipment to use, to influencing the behavior/movement of the NPC.  Without it I'm not sure we can take the script that much further.

That's why I asked about it in another thread.  The only reply I've had so far was from Somebody (erm thats his/her user name btw :smile:).  And I quote:

"It's kind of weird. Try using agent_get_look_position in conjunction with the new function position_has_line_of_sight_to_position. You'd probably need a try_for_agent loop though.
agent_set_look_target_agent makes the agent face eachother. At least, that's what that its use in the tutorial mission templates suggest. If you get closer than 4 meters, they turn towards you."
 

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
Yeah, he's an ambiguous bastard. Using agent_get_look_position does return the appropriate view, at least if you use it in conjunction with deathcam on bots to directly view it. It pretty much locks on a target agent and changes instantaneously to another one if the current one dies or is out of range (no transition or turning during this switch).

Anyways, I did use Hatonastick's original code in TEATRC for tournaments, but implementing it for the field is kind of difficult and laggy. So instead, I arbitrarily removed lances from the troops (not agents) before sieges (and other close-quarter encounters) and gave them swords, removing them after the encounter.

Also, your code will only find the first appropriate sidearm listed, and then quits the loop. And it doesn't check if it's one of 4 items (agent_has_item_equipped) that any agent can have, so it's fortunate that Native's troops work for their intended purposes.
 

Hatonastick

Yeah well the tournament version isn't exactly optimised for speed.  It just spams running through every single agent on the battlefield every second.  You can get away with that in tournaments due to the low number of agents on the battlefield, hence why I didn't see the point in optimisation.

The version of the script I was working on (before I got sidetracked) for the battlefield was a bit different to this.  Unfortunately I'm not sure what I've done with it.  Not that it matters.  If this goes the direction I'm thinking of, it will become somewhat more complicated and also far more optimised than it is currently -- I play the game on a 3 year old laptop, so if it ends up being optimised enough for me, others may find it useful.  Maybe.  As you point out, it's probably not going to suit everybody.

In any case, if CryptoCactus doesn't mind, I'm going to work with him on this and see what we can come up with.
 

xenoargh

Grandmaster Knight
Best answers
0
I can see the point of yanking the lances from the troop before initiating a siege, but this situation comes up a lot on the battlefield.

Perhaps adding a slot that will automatically drop Agents from the first step of a loop if true might be a good performance enhancement?  I.E., some troops never should get checked; therefore, just agent_set_slot, blah... and lancers that have been de-horsed are given this state afterward, making the loop shorter and shorter?
 

Hatonastick

Excellent suggestion.  Believe it or not I only found out about slots and being able to set your own 10 minutes ago while searching through the forums for something else.  Going to massively change the way I code some of my other scripts too.

I guess I've got to keep in mind that anything I write is only going to be as efficient as the knowledge I have on the workings of Warband will allow me to be, and as that knowledge grows (hopefully) my coding will improve...

Changing my old script to use this knowledge.  Any other suggestions/tricks/optimisations are welcome.
 

CryptoCactus

Sergeant Knight
M&BWB
Best answers
0
Yeah, hat, I have absolutely no problem with you or anyone else altering this code (like I say, it's 98% yours anyway :p), and besides that I'm *really* a rank amateur. I only did this because it annoyed me personally to have lancers be so goofy, so.. yeah. And then I thought I should share it. :smile:

So alter it, scrap it entirely, whatever works. I'll help with anything I can, but I don't know how much help I'll be.


I had figured that it would be pretty laggy in bigger battles, especially on older machines. I built my computer a year or two back (it has an 8800GT, which was king to everything but the GTX at the time iirc), and it works fine for me on max sized (non-tweaked/non-battlesizer'd) battles, but beyond that I'm sure it'll start hiccuping.

But... I couldn't think of a better way to get it done at the time.

xenoargh said:
I can see the point of yanking the lances from the troop before initiating a siege, but this situation comes up a lot on the battlefield.
Yeah, that's the problem. The conditions on the battlefield can change so often and so quickly I can't see any way to do something like this without running regular checks (and thus adding potential for lag).

xenoargh said:
Perhaps adding a slot that will automatically drop Agents from the first step of a loop if true might be a good performance enhancement?  I.E., some troops never should get checked; therefore, just agent_set_slot, blah... and lancers that have been de-horsed are given this state afterward, making the loop shorter and shorter?
This is probably a stupid question, but even if we set an agent slot (let's say we set a new slot_agent_is_lancer to 1), we'd still have to check every troop on the battlefield for the value of that slot, right? I can see that being faster than grinding through the actual equipment for each troop to determine what to do with it, but we'll still have to run a check on every single unit.. right? I'm just wondering how much of a difference that'd make. It could be quite significant, I just don't know.

Somebody said:
Also, your code will only find the first appropriate sidearm listed, and then quits the loop. And it doesn't check if it's one of 4 items (agent_has_item_equipped) that any agent can have, so it's fortunate that Native's troops work for their intended purposes.
Yeah, that's because of the limits of my knowledge. >_>

I actually noted agent_has_item_equipped, but I wasn't sure what exactly it meant by "equipped". Which sounds kinda stupid when I type it out. I mean I was thinking of wielded vs equipped.. eh, nevermind.

As for finding the first available sidearm and breaking the loop, yeah, I knew that wouldn't be the ideal situation (for instance if it finds a dagger before a greatsword or what have you), but I don't really know how to run through each item and choose the best one.


HEY. I just noticed agent_unequip_item. Could we use that to just strip any unhorsed units of their lances for the duration of the battle? Of course (and I'm kinda just editing this as I go along and talking it out to myself), if we had an agent that *only* had a lance, then they'd be helpless... hrm. Oh, wait, no, because we'd only remove the lance after we've gone through and checked for an alternate weapon, of course. Duh.
 

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
agent_unequip_item makes it magically poof out of the agent's hand. Not exactly ideal if you wanted them to remount or anything - but who does that anyways?

Anyways, what you can do is set up the agent slots as xenoargh suggested in ti_on_agent_spawn (you can probably enforce lancers here too), so that only lancers (or whatever) are affected, and tick it whenver you switch weapons.

To optimize the sidearm selection, what you can do is define them in a certain order (preferably in the first few slots {I'm not sure if the order gets chewed up in-game}) and use (store_random_in_range, ":weapon", 10, 10+<#sidearms>), storing the #sidearm in troop slots or whatever beforehand. Keep in mind that inventory slots < 10 are reserved for NPC equipment. This won't be reflected in the agent though, which in most cases is referenced from a regular troop. For NPCs you can probably just check ek_item_0 to ek_head for the 4 he/she will definitely have equipped.