The following code allows the player to order their archers and crossbow men to synchronize their shots into volleys. (The general principle could be applied to AIs as well, but has not been here. Similarly, the item type checks could be extended to pistols or muskets, but again, are not marked as such below.) Archers fire a shot every 3 seconds and crossbowmen get a shot off every 5 seconds...given this adds some delay to archers, this could be useful for preservation of arrows, but in my tests crossbowmen find it much more effective.
The order is given to divisions/groups of troops as Native orders and will scroll on screen as native orders do. (This has not, however been worked into the panel with the map.) Give the volley order once and your selected divisions will be ordered to "Prepare to Volley". Tap the key again with groups already ordered to volley will be ordered to "End their Volley". Giving the volley order to a mixed set of groups (some ordered to volley, others not) will order all selected groups to volley fire. Only troops currently ordered to hold position or stand ground can respond to a volley command.
This should be compatible with most any other code bits/mods/what have you, as it does not add any globals nor does it monkey with the original source code. (It uses team slots--see the Constants code--to track orders to battle divisions; as these are unused in single player, there is no conflict.) Two possible compatibility issues to pay attention to: one will be to find a suitable, unused key for the order. Currently, it is set to use F8. Second, ensure that the agent slot is unused elsewhere in constants. (For a more elegant integration with my other order codes, see the source for Pre-Battle Orders & Deployment.)
EXAMPLE VIDEOS
Without further ado, the code:
Two scripts: the check for an active order and code for actually giving the order to begin/stop volley fire and display the appropriate text on screen. Place where ever within scripts = [... The beginning works just fine.
Stick them in each of the actual mission templates you want to be able to give the order without the "order_skirmish_triggers" title and brackets, or stick them toward the top of the file (I put them after the multiplayer_* and before the common_* around line 630) and then just include the "order_volley_triggers" title in the actual mission templates. I tend to install extra triggers as follows:
Add to bottom of module_constants.py, after customizing the key used to your taste and ensuring no conflicts.
This has been tested and is working as described.
Comments and suggestions, etc, are--as always--welcome.
The order is given to divisions/groups of troops as Native orders and will scroll on screen as native orders do. (This has not, however been worked into the panel with the map.) Give the volley order once and your selected divisions will be ordered to "Prepare to Volley". Tap the key again with groups already ordered to volley will be ordered to "End their Volley". Giving the volley order to a mixed set of groups (some ordered to volley, others not) will order all selected groups to volley fire. Only troops currently ordered to hold position or stand ground can respond to a volley command.
This should be compatible with most any other code bits/mods/what have you, as it does not add any globals nor does it monkey with the original source code. (It uses team slots--see the Constants code--to track orders to battle divisions; as these are unused in single player, there is no conflict.) Two possible compatibility issues to pay attention to: one will be to find a suitable, unused key for the order. Currently, it is set to use F8. Second, ensure that the agent slot is unused elsewhere in constants. (For a more elegant integration with my other order codes, see the source for Pre-Battle Orders & Deployment.)
EXAMPLE VIDEOS
Without further ado, the code:
Code:
# script_cf_order_volley_check
# Input: Nothing
# Output: Nothing
# Check for an active Volley Fire order
("cf_order_volley_check", [
(assign, ":active", 0),
(assign, ":end", 4),
(assign, ":end2", slot_team_d0_order_volley + 9),
(try_for_range, ":team", 0, ":end"),
(try_for_range, ":i", slot_team_d0_order_volley, ":end2"),
(team_slot_ge, ":team", ":i", 1),
(assign, ":active", 1),
(assign, ":end", 0),
(assign, ":end2", slot_team_d0_order_volley),
(try_end),
(try_end),
(eq, ":active", 1),
]),
Code:
# script_order_volley_begin_end
# Input: Nothing
# Output: Nothing
# On key depression, determine if beginning or ending volley fire
# Display appropriate order text on screen.
("order_volley_begin_end", [
(get_player_agent_no, ":player"),
(agent_get_team, ":playerteam", ":player"),
(assign, ":volley", 0),
(try_for_range, ":class", 0, 8),
(class_is_listening_order, ":playerteam", ":class"), #Listening to Order
(store_add, ":class_ordered", slot_team_d0_order_volley, ":class"),
(team_slot_eq, ":playerteam", ":class_ordered", 0),
(team_set_slot, ":playerteam", ":class_ordered", 1),
(assign, ":volley", 1),
(str_store_string, s1, "@prepare to volley"),
(try_begin), #Mark class as selected--used for display text
(eq, ":class", 0),
(assign, ":group0_is_selected", 1),
(else_try),
(eq, ":class", 1),
(assign, ":group1_is_selected", 1),
(else_try),
(eq, ":class", 2),
(assign, ":group2_is_selected", 1),
(else_try),
(eq, ":class", 3),
(assign, ":group3_is_selected", 1),
(else_try),
(eq, ":class", 4),
(assign, ":group4_is_selected", 1),
(else_try),
(eq, ":class", 5),
(assign, ":group5_is_selected", 1),
(else_try),
(eq, ":class", 6),
(assign, ":group6_is_selected", 1),
(else_try),
(eq, ":class", 7),
(assign, ":group7_is_selected", 1),
(else_try),
(eq, ":class", 8),
(assign, ":group8_is_selected", 1),
(try_end),
(try_end), #Class Loop
(try_for_agents, ":agent"),
(agent_is_alive, ":agent"),
(agent_is_human, ":agent"),
(agent_is_non_player, ":agent"),
(agent_get_team, ":team", ":agent"),
(eq, ":team", ":playerteam"), #On Player's side?
(agent_get_division, ":class", ":agent"),
(try_begin), #Mark class as in battle--used for display text
(eq, ":class", 0),
(assign, ":group0_in_battle", 1),
(else_try),
(eq, ":class", 1),
(assign, ":group1_in_battle", 1),
(else_try),
(eq, ":class", 2),
(assign, ":group2_in_battle", 1),
(else_try),
(eq, ":class", 3),
(assign, ":group3_in_battle", 1),
(else_try),
(eq, ":class", 4),
(assign, ":group4_in_battle", 1),
(else_try),
(eq, ":class", 5),
(assign, ":group5_in_battle", 1),
(else_try),
(eq, ":class", 6),
(assign, ":group6_in_battle", 1),
(else_try),
(eq, ":class", 7),
(assign, ":group7_in_battle", 1),
(else_try),
(eq, ":class", 8),
(assign, ":group8_in_battle", 1),
(try_end),
(class_is_listening_order, ":team", ":class"), #Is the agent's division selected?
(try_begin),
(eq, ":volley", 0),
(store_add, ":class_ordered", slot_team_d0_order_volley, ":class"),
(team_set_slot, ":team", ":class_ordered", 0),
(try_begin),
(agent_slot_ge, ":agent", slot_agent_volley_fire, 1),
(agent_set_slot, ":agent", slot_agent_volley_fire, 0),
(agent_set_attack_action, ":agent", 0, 0), #Release (if holding)
(try_end),
(str_store_string, s1, "@end volley"),
(try_begin), #Mark class as selected--used for display text
(eq, ":class", 0),
(assign, ":group0_is_selected", 1),
(else_try),
(eq, ":class", 1),
(assign, ":group1_is_selected", 1),
(else_try),
(eq, ":class", 2),
(assign, ":group2_is_selected", 1),
(else_try),
(eq, ":class", 3),
(assign, ":group3_is_selected", 1),
(else_try),
(eq, ":class", 4),
(assign, ":group4_is_selected", 1),
(else_try),
(eq, ":class", 5),
(assign, ":group5_is_selected", 1),
(else_try),
(eq, ":class", 6),
(assign, ":group6_is_selected", 1),
(else_try),
(eq, ":class", 7),
(assign, ":group7_is_selected", 1),
(else_try),
(eq, ":class", 8),
(assign, ":group8_is_selected", 1),
(try_end),
(else_try),
(eq, ":volley", 1),
(agent_get_horse, ":horse", ":agent"),
(le, ":horse", 0), #Not Mounted
(assign, ":volley_wpn_type", -1),
(try_begin),
(team_get_movement_order, ":mordr", ":team", ":class"),
(this_or_next|eq, ":mordr", mordr_hold),
(eq, ":mordr", mordr_stand_ground),
(agent_get_wielded_item, ":wielded", ":agent", 0),
(ge, ":wielded", 0),
(item_get_type, ":type", ":wielded"),
(try_begin),
(this_or_next|eq, ":type", itp_type_bow),
(eq, ":type", itp_type_crossbow),
(assign, ":volley_wpn_type", ":type"),
(else_try),
(assign, ":end", ek_head),
(try_for_range, ":i_slot", ek_item_0, ":end"),
(agent_get_item_slot, ":item", ":agent", ":i_slot"),
(ge, ":item", 0),
(item_get_type, ":type", ":item"),
(this_or_next|eq, ":type", itp_type_bow),
(eq, ":type", itp_type_crossbow),
(agent_set_wielded_item, ":agent", ":item"),
(assign, ":volley_wpn_type", ":type"),
(assign, ":end", ek_item_0),
(try_end),
(try_end),
(try_end),
(agent_set_slot, ":agent", slot_agent_volley_fire, ":volley_wpn_type"),
(try_end), #Volley End or Begin
(try_end), #Agent loop
(str_clear, s2),
(str_clear, s3),
(assign, ":count_possible", 0),
(assign, ":count_selected", 0),
(try_begin),
(eq, ":group0_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group0_is_selected", 1),
(val_add, ":count_selected", 1),
(str_store_class_name, s2, 0),
(try_end),
(try_begin),
(eq, ":group1_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group1_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 1),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 1),
(try_end),
(try_end),
(try_begin),
(eq, ":group2_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group2_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 2),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 2),
(try_end),
(try_end),
(try_begin),
(eq, ":group3_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group3_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 3),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 3),
(try_end),
(try_end),
(try_begin),
(eq, ":group4_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group4_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 4),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 4),
(try_end),
(try_end),
(try_begin),
(eq, ":group5_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group5_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 5),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 5),
(try_end),
(try_end),
(try_begin),
(eq, ":group6_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group6_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 6),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 6),
(try_end),
(try_end),
(try_begin),
(eq, ":group7_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group7_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 7),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 7),
(try_end),
(try_end),
(try_begin),
(eq, ":group8_in_battle", 1),
(val_add, ":count_possible", 1),
(eq, ":group8_is_selected", 1),
(val_add, ":count_selected", 1),
(try_begin),
(neg|str_is_empty, s2),
(str_store_class_name, s3, 8),
(str_store_string, s2, "@{!}{s2}, {s3}"),
(else_try),
(str_store_class_name, s2, 8),
(try_end),
(try_end),
(try_begin),
(eq, ":count_selected", ":count_possible"),
(str_store_string, s2, "@Everyone"),
(try_end),
(try_begin),
(gt, ":count_selected", 0),
(display_message, "@{!}{s2}, {s1}!", 0xFFDDDD66),
(try_end),
(str_clear, s2),
(str_clear, s3),
]),
Code:
order_volley_triggers = [
(0, 0, 1, [(key_clicked, key_for_volley)], [(call_script, "script_order_volley_begin_end")]),
(1, 0, 0, [(call_script, "script_cf_order_volley_check")], [
(try_for_range, ":team", 0, 4),
(try_for_range, ":division", 0, 9),
(store_add, ":slot", slot_team_d0_order_volley, ":division"),
(team_slot_ge, ":team", ":slot", 1),
(team_get_slot, ":volley_counter", ":team", ":slot"),
(val_add, ":volley_counter", 1),
(team_set_slot, ":team", ":slot", ":volley_counter"),
(try_end),
(try_end),
(try_for_agents, ":agent"),
(agent_is_non_player, ":agent"),
(agent_slot_ge, ":agent", slot_agent_volley_fire, 1),
(agent_get_ammo, ":ammo", ":agent", 1),
(gt, ":ammo", 0),
(agent_get_team, ":team", ":agent"),
(agent_get_division, ":division", ":agent"),
(store_add, ":slot", slot_team_d0_order_volley, ":division"),
(team_get_slot, ":volley_counter", ":team", ":slot"),
(agent_get_slot, ":volley_wpn_type", ":agent", slot_agent_volley_fire),
(try_begin),
(eq, ":volley_wpn_type", itp_type_bow),
(assign, ":delay", 3),
(else_try),
(eq, ":volley_wpn_type", itp_type_crossbow),
(assign, ":delay", 5),
(try_end),
(agent_get_combat_state, ":cs", ":agent"),
(this_or_next|eq, ":cs", 1),
(eq, ":cs", 3),
(store_mod, reg0, ":volley_counter", ":delay"),
(try_begin),
(eq, reg0, 0),
(agent_set_attack_action, ":agent", 0, 0), #Fire
(else_try),
(agent_set_attack_action, ":agent", 0, 1), #Ready and Aim
(try_end),
(try_end),
]),
(1, 0, ti_once, [(neq, "$g_battle_result", 0)], [ #Disable Volley @ end of battle
(try_for_range, ":team", 0, 4),
(try_for_range, ":slot", slot_team_d0_order_volley, slot_team_d0_order_volley + 9),
(team_set_slot, ":team", ":slot", 0),
(try_end),
(try_end)
]),
]
Code:
(
"lead_charge",mtf_battle_mode,charge,
"You lead your men to battle.",
[
// bunch of stuff...all the way to the very end
] + order_volley_triggers,
),
Code:
slot_agent_volley_fire = 33
slot_team_d0_order_volley = 10 #plus 8 more for the other divisions
from header_triggers import *
key_for_volley = key_f8
This has been tested and is working as described.
Comments and suggestions, etc, are--as always--welcome.