OSP Code Combat Form Musket Square

Users who are viewing this thread

rL-tz.jpg

A quick idea I came up with while reminiscing about algebra and geometry last night. Pretty self-explanatory -- upon pressing a key (default T), soldiers will form a hollow square formation, where the first script parameter determines the rank depth, up to 3 (although a modder with some knowledge of basic algebra could quite easily add as many ranks as they want).

The Basics
The script separates the infantry square into "ranks", which start from the outside and work their way in. Each rank is then created by cycling through agents in a given division, moving one space at a time in a square. Successive ranks are made smaller and placed "inside" the largest square. There's very basic maths and geometry involved, but the script is a little spaghetti-like as it's all within a single try_for_agents.

The algebra you see below is used to determine how wide a formation needs to be. A stands for the width of the first rank, and B refers to the number of men in the entire formation. The relationship between these helps make the process a lot easier, and thus dynamically sized infantry squares can be produced nice and easily.

Storing the position, size and "allegiance" of infantry squares
There are also some dummy troops for storing the size and positioning of an infantry square, meaning that by accessing these values, you can give bonuses to troops in squares or whatever. Here's how it works:

A unique "Square ID" is chosen for each infantry square. It is is created by (team x 10) + division + 1. for example, square 23 is an an enemy ranged square.
The square ID is then used as the slot number for the dummy troop, and the slot value is either pos_x, pos_y, or the number of men in the infantry square, depending on which troop you're accessing the slot of.

These values can be used to do stuff like the above, by trying for the range 0-51 (highest and lowest possible values for infantry squares, although in hindsight it's probably 41), and checking the slots of the troops to retrieve the values (size value 0 also means there is no current infantry square).

Slowing down horses
Every second, a trigger checks for all the horses in a scene, and slows them down by 80% if they get close to an infantry square. This effect can be cancelled or diminished by another trigger; if someone dies or routs and the infantry square gets smaller, this will affect its "radius", and eventually disband the square if there are too few guys.

This trigger is based off the Diplomacy Dynamic Horse Speed feature. Simply overwrite the trigger since I've added/changed almost all of it.

Code:
#script_cf_create_infantry_square
#Width of 1st rank = a+1
#Width of 2nd rank = a-1
#Width of 3rd rank = a-3
#Input: 1: ranks, 2: team, 3: unit
#Output: none
	("cf_create_infantry_square",
	[
		#creates an infantry square using quarter sections. creates first rank first, second rank, and then third, if specified. "leftover" troops are included as rounding calculation creates larger square than is present in selection.
		#One rank = 	(4a)
		#Two ranks = 	(8a-8)		4a+4(a-2)
		#Three ranks = 	(12a-32)	4a+4(a-2)+4(a-4)

		#One rank = 	b/4
		#Two ranks = 	b/8 + 1
		#Three ranks = 	b+32 / 12
		(set_fixed_point_multiplier, 1),

		(store_script_param, ":ranks", 1),
		(store_script_param, ":team", 2),
		(store_script_param, ":division", 3),
		(store_script_param, ":is_ally", 4),

		(call_script, "script_cf_team_get_average_position_of_agents_with_type_to_pos1", ":team", ":division"),
		(assign, ":unit_count", reg0),

		(set_fixed_point_multiplier, 1),

		(ge, ":unit_count", 32),



		#initialise value of "a" (formation width)
		(try_begin),
			(eq, ":ranks", 1),
			(store_div, ":value_a", ":unit_count", 4),
		(else_try),
			(eq, ":ranks", 2),
			(store_div, ":value_a", ":unit_count", 8),
			(val_add, ":value_a", 1),
		(else_try),
			(eq, ":ranks", 3),
			(store_add, ":value_a", ":unit_count", 32),
			(val_div, ":value_a", 12),
		(try_end),

		# (init_position, pos1),
		# (call_script, "script_battlegroup_get_position", pos1, ":team", ":class"),
		(init_position, pos2),
		(copy_position, pos2, pos1), #pos2 remains as original

		#(set_fixed_point_multiplier, 1),

		#move by a/2 in x and y so that square is centred on formation properly
		(store_div, ":value_a_half", ":value_a", 2),
		(store_sub, ":value_a_half", 0, ":value_a_half"), #make negative
		(val_mul, ":value_a_half", 120), #make negative
		(position_move_x, pos2, ":value_a_half", 1), 
		(position_move_y, pos2, ":value_a_half", 1), 


		#square ID is created by (team x 10) + division + 1. e.g. square 22 is an an enemy ranged square.
		(store_mul, ":square_id", ":team", 10),
		(val_add, ":square_id", ":division"),
		(val_add, ":square_id", 1),

		(position_get_x, ":pos_x", pos2),
		(position_get_y, ":pos_y", pos2),

		#housekeeping
		# (position_set_z_to_ground_level, pos2),
		# (particle_system_burst, "psys_flare", pos2, 900000),
		#(display_message, "@SQUARE"),
		#housekeeping

		(copy_position, pos1, pos2), #pos2 remains as original


		(troop_set_slot, "trp_jacobhinds_form_musket_square_pos_x", ":square_id", ":pos_x"),
		(troop_set_slot, "trp_jacobhinds_form_musket_square_pos_y", ":square_id", ":pos_y"),
		(troop_set_slot, "trp_jacobhinds_form_musket_square_size", ":square_id", ":unit_count"),
		(troop_set_slot, "trp_jacobhinds_form_musket_square_is_ally", ":square_id", ":is_ally"),

		#initialises shift variables from ranks
		# (assign, ":shifted_2", 0),
		# (assign, ":shifted_3", 0),

		(assign, "$square_formation_progress", 0),
		(assign, "$square_formation_section", r1s1),

		(try_for_agents, ":agent"),
			(agent_is_alive, ":agent"),
			(agent_is_human, ":agent"),
			(agent_slot_eq, ":agent", slot_agent_is_running_away, 0), #not routing, prevents huge formation holes

			#check if troop is in correct group/team
			(agent_get_division, ":agent_division", ":agent"),
			(agent_get_team, ":agent_team", ":agent"),
			(eq, ":agent_team", ":team"),
			(eq, ":agent_division", ":division"),

			#If $square_formation_progress has reached value_a + 1, val_add to $square_formation_section.
			#also rotate by 90 degrees to allow troops to face outwards.
			(try_begin),
				(gt, "$square_formation_progress", ":value_a"),
				(val_add, "$square_formation_section", 1),
				(assign, "$square_formation_progress", 1),
				(position_rotate_z, pos1, 90),
			(try_end),

			#First Rank:
			(try_begin),
				(eq, "$square_formation_section", r1s1),
				(agent_set_scripted_destination, ":agent", pos1, 1),
				(position_move_x, pos1, 120, 1),
				(val_add, "$square_formation_progress", 1),
			(else_try),
				(eq, "$square_formation_section", r1s2),
				(agent_set_scripted_destination, ":agent", pos1, 1),
				(position_move_y, pos1, 120, 1),
				(val_add, "$square_formation_progress", 1),
			(else_try),
				(eq, "$square_formation_section", r1s3),
				(agent_set_scripted_destination, ":agent", pos1, 1),
				(position_move_x, pos1, -120, 1),
				(val_add, "$square_formation_progress", 1),
			(else_try),
				(eq, "$square_formation_section", r1s4),
				(agent_set_scripted_destination, ":agent", pos1, 1),
				(position_move_y, pos1, -120, 1),
				(val_add, "$square_formation_progress", 1),
			(try_end),

			#first rank crouch if formation is two or more deep
			#possibly add to end when all troops are in position...?
			#perhaps save time by doing so after a time proportional to formation size
			(try_begin),
				(ge, ":ranks", 2),
				(is_between, "$square_formation_section", r1s1, r2s1),
				#(agent_set_crouch_mode, ":agent", 1),
			(try_end),

			#when first rank finished:
			#move position down and across by 1 "unit" (0.5 metres currently)
			#also sub 2 from value_a to accommodate narrower formation
			(try_begin),
				#(eq, ":shifted_2", 0),
				(eq, "$square_formation_section", r2s1),
				(eq, "$square_formation_progress", 1),
				#(copy_position, pos1, pos2),
				(position_move_y, pos1, 120, 1),
				(position_move_x, pos1, 120, 1),
				#(val_sub, ":value_a", 2),
				#(display_message, "@shifting..."),
			(try_end),

			#Second Rank:
			(try_begin),
				(ge, ":ranks", 2),
				(try_begin),
					(eq, "$square_formation_section", r2s1),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_x, pos1, 120, 1),
					(val_add, "$square_formation_progress", 1),
				(else_try),
					(eq, "$square_formation_section", r2s2),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_y, pos1, 120, 1),
					(val_add, "$square_formation_progress", 1),
				(else_try),
					(eq, "$square_formation_section", r2s3),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_x, pos1, -120, 1),
					(val_add, "$square_formation_progress", 1),
				(else_try),
					(eq, "$square_formation_section", r2s4),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_y, pos1, -120, 1),
					(val_add, "$square_formation_progress", 1),
				(try_end),
			(try_end),

			#when second rank finished:
			#move position down and across by 1 "unit" (0.5 metres currently)
			#also sub 2 from value_a to accommodate narrower formation
			(try_begin),
				#(eq, ":shifted_3", 0),
				(eq, "$square_formation_section", r3s1),
				(eq, "$square_formation_progress", 1),
				#(copy_position, pos1, pos2),
				(position_move_y, pos1, 120, 1),
				(position_move_x, pos1, 120, 1),
				(val_sub, ":value_a", 2),
				#(assign, ":shifted_2", 1),
				#(display_message, "@shifting..."),
			(try_end),

			#Third Rank:
			(try_begin),
				(ge, ":ranks", 3),
				(try_begin),
					(eq, "$square_formation_section", r3s1),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_x, pos1, 120, 1),
					(val_add, "$square_formation_progress", 1),
				(else_try),
					(eq, "$square_formation_section", r3s2),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_y, pos1, 120, 1),
					(val_add, "$square_formation_progress", 1),
				(else_try),
					(eq, "$square_formation_section", r3s3),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_x, pos1, -120, 1),
					(val_add, "$square_formation_progress", 1),
				(else_try),
					(eq, "$square_formation_section", r3s4),
					(agent_set_scripted_destination, ":agent", pos1, 1),
					(position_move_y, pos1, -120, 1),
					(val_add, "$square_formation_progress", 1),
				(try_end),
			(try_end),
		(try_end),

	]),

#script_end_infantry_square
#Input: 1: team, 2: unit
#Output: none
	("cf_end_infantry_square",
	[
		(set_fixed_point_multiplier, 1),

		(store_script_param, ":team", 1),
		(store_script_param, ":division", 2),

		(try_for_agents, ":agent"),
			(agent_is_alive, ":agent"),
			(agent_is_human, ":agent"),
			(agent_slot_eq, ":agent", slot_agent_is_running_away, 0), #not routing

			(agent_get_division, ":agent_division", ":agent"),
			(agent_get_team, ":agent_team", ":agent"),
			(eq, ":agent_team", ":team"),
			(eq, ":agent_division", ":division"),
			(agent_clear_scripted_mode, ":agent"),
		(try_end),

		#square ID is created by (team x 10) + division + 1. e.g. square 23 is an an enemy ranged square.
		(store_mul, ":square_id", ":team", 10),
		(val_add, ":square_id", ":division"),
		(val_add, ":square_id", 1),
		(troop_set_slot, "trp_jacobhinds_form_musket_square_pos_x", ":square_id", 0),
		(troop_set_slot, "trp_jacobhinds_form_musket_square_pos_y", ":square_id", 0),
		(troop_set_slot, "trp_jacobhinds_form_musket_square_size", ":square_id", 0),
		(troop_set_slot, "trp_jacobhinds_form_musket_square_is_ally", ":square_id", -1),
	]),

#script_agent_remove_from_square
#every time someone dies, flees or gets wounded,
#reduce the infantry square size until it's below 
#30, then remove the square.
#Input: 1: troop
#Output: none
	("agent_remove_from_square",[
	
		
		(store_script_param, ":agent_no", 1),

		#(agent_is_alive, ":agent_no"),
		(agent_is_human, ":agent_no"),
		(agent_get_team, ":team", ":agent_no"),
		(agent_get_division, ":division", ":agent_no"),

		#calculate square id
		(store_mul, ":square_id", ":team", 10),
		(val_add, ":square_id", ":division"),
		(val_add, ":square_id", 1),


		#get square size. if less than 32, remove square altogether. else, reduce size by 1.
		(troop_get_slot, ":square_size", "trp_jacobhinds_form_musket_square_size", ":square_id"),

		#make sure there's a square in the first place
		(neq, ":square_size", 0),

		#housekeeping
		#(assign, reg2, ":square_size"),
		#(assign, reg3, ":square_id"),

		(try_begin),
			(lt, ":square_size", 32),
			(call_script, "script_cf_end_infantry_square", ":team", ":division", 1),
			#(display_message, "@not enough dudes in square {reg3} ({reg2})"),
		(else_try),
			(val_sub, ":square_size", 1),
			(troop_set_slot, "trp_jacobhinds_form_musket_square_size", ":square_id", ":square_size"),
			#(display_message, "@someone left square number {reg3} ({reg2})"),
		(try_end),

	]),

#

Code:
#jacobhinds form musket square BEGIN

jacobhinds_form_square_init = (
	ti_before_mission_start, 0, 0, [],
	[
		(try_for_range, ":square_id", 0, 150), #clears dummy troop array for infantry squares
			(troop_set_slot, "trp_jacobhinds_form_musket_square_size", ":square_id", 0),
		(try_end),
	])

jacobhinds_form_square = (
	0, 0, 0, [(key_clicked, key_t)],
	[
		#find player team
		(get_player_agent_no, ":player"),
		(agent_get_team, ":playerteam", ":player"),

		(try_for_range, ":class", 0, 9),
			(class_is_listening_order, ":playerteam", ":class"),
			(store_mul, ":square_id", ":playerteam", 10),
			(val_add, ":square_id", ":class"),
			(val_add, ":square_id", 1),
			#make sure they aren't already in square, otherwise end
			(try_begin),
				(troop_slot_eq, "trp_jacobhinds_form_musket_square_size", ":square_id", 0), 
				(call_script, "script_cf_create_infantry_square", 3, ":playerteam", ":class", 1),
				(str_store_class_name, s1, ":class"),
				(display_message, "@Forming square with {s1}"),
			(else_try),
				(call_script, "script_cf_end_infantry_square", ":playerteam", ":class"),
				(str_store_class_name, s1, ":class"),
				(display_message, "@Disbanding square with {s1}"),
			(try_end),
		(try_end),
	],
						)


jacobhinds_infantry_square_check = (
  ti_on_agent_killed_or_wounded, 0, 0, [],

	#every time someone dies, flees or gets wounded,
	#reduce the infantry square size until it's below 
	#30, then remove the square.
	[
    (store_trigger_param_1, ":dead_agent_no"),
	(call_script, "script_agent_remove_from_square", ":dead_agent_no"),
	])

#jacobhinds form musket square END

Code:
#jacobhinds form musket square BEGIN

dplmc_horse_speed = (
  1.1, 0, 0, [], #was 1
  [
  (set_fixed_point_multiplier, 1),
  (try_for_agents, ":agent_no"),
    (agent_is_alive, ":agent_no"),
    (agent_is_human, ":agent_no"),
    (agent_get_horse, ":horse_agent", ":agent_no"),
    (try_begin),
      (ge, ":horse_agent", 0),
      (store_agent_hit_points, ":horse_hp",":horse_agent"),
      (store_sub, ":lost_hp", 100, ":horse_hp"),
      (try_begin),
        (le, ":lost_hp", 15),
        (val_div, ":lost_hp", 2),
        (store_add, ":speed_factor", 100, ":lost_hp"),
      (else_try),
        (val_mul, ":lost_hp", 2),
        (val_div, ":lost_hp", 3),
        (store_sub, ":speed_factor", 115, ":lost_hp"),
      (try_end),

	  
	  #makes horses slow near infantry squares
	  (agent_get_position, pos2, ":agent_no"),

	  #find horse speed
	  # (agent_get_speed, pos6, ":horse_agent"),
	  # (init_position, pos7),
	  # (get_distance_between_positions_in_meters, ":velocity", pos6, pos7),

	  #housekeeping
	  # (assign, reg1, ":velocity"),
	  # (display_message, "@{reg1}"),
	  #housekeeping

	  (try_for_range, ":square_id", 1, 51),
		(troop_get_slot, ":pos_x", "trp_jacobhinds_form_musket_square_pos_x", ":square_id"),
		(troop_get_slot, ":pos_y", "trp_jacobhinds_form_musket_square_pos_y", ":square_id"),
		(troop_get_slot, ":square_size", "trp_jacobhinds_form_musket_square_size",  ":square_id"),
		(troop_get_slot, ":is_ally", "trp_jacobhinds_form_musket_square_is_ally",  ":square_id"),
		(gt, ":square_size", 0),
		# (gt, ":pos_x", 0),
		# (gt, ":pos_y", 0),
		
		(assign, ":continue", 0),
 
		(try_begin),
			(agent_is_ally, ":agent_no", 1), #if agent and square are both allies
			(eq, ":is_ally", 1), 
			(assign, ":continue", 1), 
		(else_try),
			(agent_is_ally, ":agent_no", 0), #if neither agent nor square are allies
			(eq, ":is_ally", 0), 
			(assign, ":continue", 1), 
		(try_end),

		(eq, ":continue", 0),

		(position_set_x, pos1, ":pos_x"),
		(position_set_y, pos1, ":pos_y"),

		(get_distance_between_positions_in_meters, ":distance", pos1, pos2), 

		#find infantry square radius
		#if closer than (size+32/24)+5, slow down
		#(inverse of original algebra, halved to make sure it doesn't
		#extend too far on both sides, with a little room (5m) for horses)

		(store_add, ":square_radius", ":square_size", 32),
		(val_div, ":square_radius", 24),
		(val_add, ":square_radius", 5),
		#(val_add, ":square_radius", 50),
		(lt, ":distance", ":square_radius"),

		#housekeeping
		# (assign, reg1, ":distance"),
		# (assign, reg2, ":square_size"),
		# (display_message, "@slowing down"),
		#housekeeping

		(val_sub, ":speed_factor", 80),
		(val_max, ":speed_factor", 0),
		# (store_random_in_range, ":rand", 0, 6), #1/6 chance to rear horse
		# (try_begin),
			# (eq, ":rand", 0),
			# (agent_set_animation, ":horse_agent", "anim_horse_rear"),
			# (agent_set_animation, ":agent_no", "anim_ride_rear"),
		# (try_end),
	  (try_end),
	  

      (agent_get_troop_id, ":agent_troop", ":agent_no"),
      (store_skill_level, ":skl_level", skl_riding, ":agent_troop"),
      (store_mul, ":speed_multi", ":skl_level", 2),
      (val_add, ":speed_multi", 100),
      (val_mul, ":speed_factor", ":speed_multi"),
      (val_div, ":speed_factor", 100),
      (agent_set_horse_speed_factor, ":agent_no", ":speed_factor"),
    (try_end),
  (try_end),
  ])

#jacobhinds form musket square END

Code:
	  #jacobhinds form musket square BEGIN
	  #assigns counted agents to reg0 (saves time)
	  (assign, reg0, ":num_agents"),
	  #jacobhinds form musket square END


Code:
	#jacobhinds form musket square BEGIN
["jacobhinds_form_musket_square_pos_x", "{!} Current musket squares", "{!} Current musket squares", 0, no_scene, reserved, fac_commoners, [], def_attrib, 0, 0, merchant_face_1, merchant_face_2 ],
["jacobhinds_form_musket_square_pos_y", "{!} Current musket squares", "{!} Current musket squares", 0, no_scene, reserved, fac_commoners, [], def_attrib, 0, 0, merchant_face_1, merchant_face_2 ],
["jacobhinds_form_musket_square_size", "{!} Current musket squares", "{!} Current musket squares", 0, no_scene, reserved, fac_commoners, [], def_attrib, 0, 0, merchant_face_1, merchant_face_2 ],
["jacobhinds_form_musket_square_is_ally", "{!} Current musket squares", "{!} Current musket squares", 0, no_scene, reserved, fac_commoners, [], def_attrib, 0, 0, merchant_face_1, merchant_face_2 ],
#jacobhinds form musket square END

Code:
	  #jacobhinds form musket square BEGIN
r1s1 			= 1
r1s2 			= 2
r1s3 			= 3
r1s4 			= 4

r2s1 			= 5
r2s2 			= 6
r2s3 			= 7
r2s4 			= 8

r3s1 			= 9
r3s2 			= 10
r3s3 			= 11
r3s4 			= 12
	  #jacobhinds form musket square END

Remove Routing Troops From Squares
Find every instance of agent_start_running_away in your module system, and add:

(call_script, "script_agent_remove_from_square", ":cur_agent"), #jacobhinds infantry square script

In the line after it.

If you DON'T have diplomacy:
Find these mission templates:

lead_charge
village_attack_bandits
village_raid
quick_battle_battle

And add the names of these triggers somewhere in the consequence block:

jacobhinds_infantry_square_check,
jacobhinds_form_square_init,
jacobhinds_form_square,
dplmc_horse_speed,

If you DO have diplomacy:

Add these triggers to dplmc_battle_mode_triggers:

jacobhinds_infantry_square_check,
jacobhinds_form_square,
jacobhinds_form_square_init,

Currently it's not "functional" -- it's up to you to decide how to implement this script -- although I'm likely to add my own "functionality" scripts as I create them. For example I'm thinking of editing the diplomacy script that slows horses and applying it to an agent's distance from the centre of the infantry square -- w.r.t. the size of the formation, of course.

Currently functional. The "listening" group of troops will form a square upon pressing T, and disband it upon clicking again. "Enemy" horses will slow down if they get too close to the square. Works for the AI too, although it's up to you when to get them to use it:

Code:
(call_script, "script_cf_create_infantry_square", ":number_of_ranks", ":team_no", ":division", 1),

If the square is "allied" to the player, set the last parameter to 1, but if not, set it to 0. The script does the rest.
 
Thats dam fine work, well done! I always thought its impossible in warband because its..warband but you did it!
Will you implement a crouching system so the first row will kneel down so that both rows can shoot? That would make it epic!
 
I can't think of a way to do it that isn't cumbersome and unworkable. I can't just tell them to crouch immediately after the square is created, otherwise they stand up again right away to get to their places. But I can't tell when the agents arrive at their positions without doing the following:

1. Checking for every agent in the game every few seconds.
2. Checking to see how fast they're going.
3. If they've stopped, tell them to crouch and face the right way.
4. If they've already crouched since the last square they were in was formed, this cycle skips them (the hardest part).

OR

1. Give each troop a slot that tells it where it needs to be.
2. Check if it's in the right place, then crouch.

The problems with this:

- Troops often get stuck and stop moving, or keep moving after they're in the right place. The cycle will miss them and random guys will end up crouching for 0.1 seconds, while others might not crouch at all.
- Troops stop crouching the moment they feel like moving. If they get into melee, they stand up.
- Crouching troops aren't exactly supported by the engine, as enemies assume everyone is standing, and aim for where the head might be.
 
Greetings. This is interesting, cool stuff. Thought I'd chime in on crouching from crouching and ranked-fire orders I've done before (feel free to pillage the code if you so desire).

First, you're right, you'd have to check every agent nearly constantly to enforce the crouch. There's no real way around this that I'm aware of.
Code:
process_crouching = ( #in pbod_common_triggers
   0, 0, 0, [(team_slot_eq, 0, slot_team_mv_crouching, 1)],
   [
    (set_fixed_point_multiplier, 100),
	(try_for_agents, ":agent"),
		(agent_is_alive, ":agent"),
		(agent_is_human, ":agent"),
		(agent_get_slot, ":crouching", ":agent", slot_agent_crouching),
		(gt, ":crouching", 0),
		(agent_get_horse, reg0, ":agent"),
		(le, reg0, 0),
		(assign, ":forced_to_stand", 0),
		(agent_get_wielded_item, ":weapon", ":agent", 0),
		(try_begin),
			(le, ":weapon", itm_no_item),     
		(else_try),   
			(item_get_type, ":weapon_type", ":weapon"),     
			(eq, ":weapon_type", itp_type_crossbow),
			(agent_get_attack_action, ":action", ":agent"), 
			(try_begin),
				(eq, ":action", 5), # Loading Crossbow always standing
				(assign, ":forced_to_stand", 1),
			(else_try),
				(this_or_next|eq, ":action", 1), 
				(eq, ":action", 2),
				(agent_slot_ge, ":agent", slot_agent_deployed_pavise, 1),
				(call_script, "script_cf_agent_is_behind_pavise", ":agent"), #"spr_pavise_shield1"),
				(assign, ":forced_to_stand", 1),
			(try_end),
		(else_try),
			#(eq, ":weapon", itm_long_bow),                    # Attacking with Longbow always standing
			(eq, ":weapon_type", itp_type_bow),
			(item_has_capability, ":weapon", itcf_carry_bow_back), #bow large enough to be on back, not case as proxy for longbow ??
			(agent_get_attack_action, ":action", ":agent"),
			(this_or_next|eq, ":action", 1), 
			(eq, ":action", 2),
			(assign, ":forced_to_stand", 1),        
		(try_end),
		(agent_get_speed, pos6, ":agent"),
		(position_get_y,":speed",pos6), 
		(store_mul, ":speed2", ":speed",2), 
		(val_abs, ":speed2"),
		(position_get_x,":drift",pos6), 
		(assign, ":abs_drift", ":drift"), 
		(val_abs, ":abs_drift"), 
		(try_begin),
			(eq,":forced_to_stand", 1),
			(try_begin),
				(agent_get_animation, ":anim", ":agent",0),
				(eq, ":anim", "anim_stand_to_crouch"),
				(agent_set_animation, ":agent", "anim_crouch_to_low"),
				(agent_set_slot, ":agent", slot_agent_crouching, 3),
			(try_end),
		(else_try),
			(this_or_next|eq, ":abs_drift", 0),
			(lt, ":abs_drift", ":speed2"),
			(try_begin),        
				(eq, ":speed", 0),
				(try_begin),
					(eq, ":crouching", 3),
					(agent_set_animation, ":agent", "anim_stand_to_crouch"),
					(agent_set_slot, ":agent", slot_agent_crouching, 1),
				(try_end),  
			(else_try),
				(eq, ":crouching", 1),
				(agent_set_animation, ":agent", "anim_crouch_to_low"),
				(agent_set_slot, ":agent", slot_agent_crouching, 2),
			(else_try), 
				(agent_set_slot, ":agent", slot_agent_crouching, 3),     
				(is_between, ":speed", 1, 200),     
				(agent_set_animation, ":agent", "anim_walk_forward_crouch"),
			(else_try),
				(is_between, ":speed", -200, 0),     
				(agent_set_animation, ":agent", "anim_walk_backward_crouch"),
			(try_end),
		(else_try),
			(lt, ":abs_drift", 200),
			(agent_set_slot, ":agent", slot_agent_crouching, 3),
			(try_begin),
				(gt, ":drift", 0),
				(agent_set_animation, ":agent", "anim_walk_right_crouch"),
			(else_try),
				(agent_set_animation, ":agent", "anim_walk_left_crouch"),         
			(try_end),  
		(try_end),
	(try_end), 
   ])

However, I think most of the problems you present are work-around-able if you'd want. You can set an agent's speed limit to 0, for instance, and stop them from trying to move. If you have any desire of pursuing this and want to draw from some already-done ground work, you could look at my implementation in PBOD of motomataru's formations code which includes *lots* of agent, team, and division tracking. This can make keeping tabs on the position of agents and whether they are where they should be much easier, and includes marking a slot for what rank they're in. Similarly, it includes my crouching order and related its processing as well as ranked volley fire orders that could be blended into this.
 
Updated the OP with new scripts, mission_templates and troops (!) that make infantry square functional. Press T to form or de-form the square. This can be made to work with the AI as well, and I've actually tested it out, but the main problem is that the square takes a while to form and the AI can't be made to think that far in advance.

Caba`drin said:
Greetings. This is interesting, cool stuff. Thought I'd chime in on crouching from crouching and ranked-fire orders I've done before (feel free to pillage the code if you so desire).

First, you're right, you'd have to check every agent nearly constantly to enforce the crouch. There's no real way around this that I'm aware of.
Code:
process_crouching = ( #in pbod_common_triggers
   0, 0, 0, [(team_slot_eq, 0, slot_team_mv_crouching, 1)],
   [
    (set_fixed_point_multiplier, 100),
	(try_for_agents, ":agent"),
		(agent_is_alive, ":agent"),
		(agent_is_human, ":agent"),
		(agent_get_slot, ":crouching", ":agent", slot_agent_crouching),
		(gt, ":crouching", 0),
		(agent_get_horse, reg0, ":agent"),
		(le, reg0, 0),
		(assign, ":forced_to_stand", 0),
		(agent_get_wielded_item, ":weapon", ":agent", 0),
		(try_begin),
			(le, ":weapon", itm_no_item),     
		(else_try),   
			(item_get_type, ":weapon_type", ":weapon"),     
			(eq, ":weapon_type", itp_type_crossbow),
			(agent_get_attack_action, ":action", ":agent"), 
			(try_begin),
				(eq, ":action", 5), # Loading Crossbow always standing
				(assign, ":forced_to_stand", 1),
			(else_try),
				(this_or_next|eq, ":action", 1), 
				(eq, ":action", 2),
				(agent_slot_ge, ":agent", slot_agent_deployed_pavise, 1),
				(call_script, "script_cf_agent_is_behind_pavise", ":agent"), #"spr_pavise_shield1"),
				(assign, ":forced_to_stand", 1),
			(try_end),
		(else_try),
			#(eq, ":weapon", itm_long_bow),                    # Attacking with Longbow always standing
			(eq, ":weapon_type", itp_type_bow),
			(item_has_capability, ":weapon", itcf_carry_bow_back), #bow large enough to be on back, not case as proxy for longbow ??
			(agent_get_attack_action, ":action", ":agent"),
			(this_or_next|eq, ":action", 1), 
			(eq, ":action", 2),
			(assign, ":forced_to_stand", 1),        
		(try_end),
		(agent_get_speed, pos6, ":agent"),
		(position_get_y,":speed",pos6), 
		(store_mul, ":speed2", ":speed",2), 
		(val_abs, ":speed2"),
		(position_get_x,":drift",pos6), 
		(assign, ":abs_drift", ":drift"), 
		(val_abs, ":abs_drift"), 
		(try_begin),
			(eq,":forced_to_stand", 1),
			(try_begin),
				(agent_get_animation, ":anim", ":agent",0),
				(eq, ":anim", "anim_stand_to_crouch"),
				(agent_set_animation, ":agent", "anim_crouch_to_low"),
				(agent_set_slot, ":agent", slot_agent_crouching, 3),
			(try_end),
		(else_try),
			(this_or_next|eq, ":abs_drift", 0),
			(lt, ":abs_drift", ":speed2"),
			(try_begin),        
				(eq, ":speed", 0),
				(try_begin),
					(eq, ":crouching", 3),
					(agent_set_animation, ":agent", "anim_stand_to_crouch"),
					(agent_set_slot, ":agent", slot_agent_crouching, 1),
				(try_end),  
			(else_try),
				(eq, ":crouching", 1),
				(agent_set_animation, ":agent", "anim_crouch_to_low"),
				(agent_set_slot, ":agent", slot_agent_crouching, 2),
			(else_try), 
				(agent_set_slot, ":agent", slot_agent_crouching, 3),     
				(is_between, ":speed", 1, 200),     
				(agent_set_animation, ":agent", "anim_walk_forward_crouch"),
			(else_try),
				(is_between, ":speed", -200, 0),     
				(agent_set_animation, ":agent", "anim_walk_backward_crouch"),
			(try_end),
		(else_try),
			(lt, ":abs_drift", 200),
			(agent_set_slot, ":agent", slot_agent_crouching, 3),
			(try_begin),
				(gt, ":drift", 0),
				(agent_set_animation, ":agent", "anim_walk_right_crouch"),
			(else_try),
				(agent_set_animation, ":agent", "anim_walk_left_crouch"),         
			(try_end),  
		(try_end),
	(try_end), 
   ])

However, I think most of the problems you present are work-around-able if you'd want. You can set an agent's speed limit to 0, for instance, and stop them from trying to move. If you have any desire of pursuing this and want to draw from some already-done ground work, you could look at my implementation in PBOD of motomataru's formations code which includes *lots* of agent, team, and division tracking. This can make keeping tabs on the position of agents and whether they are where they should be much easier, and includes marking a slot for what rank they're in. Similarly, it includes my crouching order and related its processing as well as ranked volley fire orders that could be blended into this.

The mighty Caba' returns!

Thanks for that script, but the 0 and try_for_agents is a bit scary. Having to set crouching every single frame is a real pain in the back and I'm not sure it's worth it, since I'd have to coordinate it with all the other instances of agent_set_animation I have on the battlefield as well.

I guess I could:
1. Create another dummy troop.
2. Create a slot for each agent who's in the front rank of the square, and assign the value as the agent ID.
3. Every frame, check if the first slot has a value.
4. If not, ignore. If yes, try for the range of the size of the square, and make all the agents crouch.
5. When a square is removed, delete all the slots of the troop.

Of course this would limit the number of infantry squares to 1 per team, unless I go and create 41 troops, which doesn't seem like a very efficient solution to me.

Hopefully when bannerlord comes out we can do away with all this inefficient overhead. It's sort of frustrating that anything to do with agents has to come after a try_for_agents.
 
Updated. Infantry squares now repel cavalry charges (the horses are slowed down considerably if they get too close), and squares are disbanded if there are fewer than 32 non-routing, not-dead men in the group. Returning/reinforcing men don't become part of the infantry square. To "readjust" the size of the square, double-tap T.
 
thanks friend You're a genius, I worked, although it gave error when trying to put: in agent_start_running_away (call_script, "script_agent_remove_from_square", "cur_agent")

but still it worked  :grin:

note: I worked in single and multi
although I did not understand the last part,,,
where I put that?
Code:
(call_script, "script_cf_create_infantry_square", ":number_of_ranks", ":team_no", ":division", 1),
 
Back
Top Bottom