OSP Code SP [WB] Improved Siege Tower mechanics

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
This simple code replacement improves the belfry mechanics during sieges. It increases the amount of siege attackers pushing the tower and makes some changes to the mechanic by which the siege progresses. In addition, the player can also contribute to pushing (as long as the character is not standing on top of the siege tower, as in multiplayer), an effect which is increased with the troop's strength. The selection now also attempts to pick soldiers with some sort of shield instead of the first available agent, and allows archers to push as well when their ammunition is consumed. When the grunts pushing the siege tower are injured, they gradually retreat occurs towards their original entry point and fresher troops are selected instead.

Comment out common_siege_rotate_belfry, common_siege_attacker_do_not_stall from usage in native siege templates as they are redundant.
Code:
common_siege_init_ai_and_belfry = (
  0, 0, ti_once,
  [
    (call_script, "script_siege_init_ai_and_belfry"),
    ], [])

#run every frame for smooth wheel rotation
common_siege_move_belfry = (
  0, 0, ti_once,
  [
    (call_script, "script_cf_siege_move_belfry"),
    ], [ #modified from native
    (call_script, "script_siege_rot_belfry_platform")
    ])

# #this is run when the tower is in the final position
# #(eq, "$belfry_positioned", 1),
# common_siege_rotate_belfry = (
  # 3, 2, ti_once,
  # [(call_script, "script_cf_siege_rotate_belfry_platform")],
  # [(assign, "$belfry_positioned", 3)])
# #native belfry has two stages at most - (assign, "$belfry_positioned", 2), is called from cf script

#the belfry siege mechanism allows 20 seconds after mission start before archer are also allowed to push
#the middle agent loop allows for infantry or cavalry to contribute
#identical destinations allow for a tighter formation of 12, 2 at front, 1 mid, and 3 back
#the player is also allowed to push (higher strenght = faster), although standing on any of the belfry props will cause this to fail
common_siege_assign_men_to_belfry = (
  4, 0, ti_once,
  [(call_script, "script_cf_siege_assign_men_to_belfry")], 
    [
      (try_for_agents, ":cur_agent"),
        (agent_is_active, ":cur_agent"),
        (agent_is_alive, ":cur_agent"),
        (agent_is_human, ":cur_agent"),
        (agent_clear_scripted_mode, ":cur_agent"),
        #turns out the stall script really doesn't do anything
        (agent_ai_set_always_attack_in_melee, ":cur_agent", 0),
        (agent_set_speed_limit, ":cur_agent", 100),
      (try_end),
      (set_show_messages, 0),
      #clears out stand ground order
      (team_give_order, "$attacker_team", grc_everyone, mordr_charge),
      #makes sure they do not stall on ladder
      (team_give_order, "$attacker_team", grc_everyone, mordr_use_melee_weapons),
      (set_show_messages, 1),
    ])
Instead of the fixed number of 6 bots pushing the siege ladder, up to 12 are allocated - two in the first row, one in the middle, and three in the back.
Code:
  # script_cf_siege_move_belfry
  # Input: none
  # Output: none (required for siege mission templates)
  ("cf_siege_move_belfry",
   [(neq, "$last_belfry_object_pos", slot_scene_belfry_props_begin),
    (entry_point_get_position,pos1,50),
    (entry_point_get_position,pos4,55),
    (get_distance_between_positions, ":total_distance", pos4, pos1),
    (store_current_scene, ":cur_scene"),
    (scene_get_slot, ":first_belfry_object", ":cur_scene", slot_scene_belfry_props_begin),
    (prop_instance_get_position, pos2, ":first_belfry_object"),
    (entry_point_get_position,pos1,"$cur_belfry_pos"),
    (position_transform_position_to_parent, pos3, pos1, pos_belfry_begin),
    (position_transform_position_to_parent, pos5, pos4, pos_belfry_begin),
    (get_distance_between_positions, ":cur_distance", pos2, pos3),
    (get_distance_between_positions, ":distance_left", pos2, pos5),
    (try_begin),
      (le, ":cur_distance", 10),
      (val_add, "$cur_belfry_pos", 1),
      (entry_point_get_position,pos1,"$cur_belfry_pos"),
      (position_transform_position_to_parent, pos3, pos1, pos_belfry_begin),
      (get_distance_between_positions, ":cur_distance", pos2, pos3),
    (try_end),
    (neq, "$cur_belfry_pos", 50),

    (assign, ":base_speed", 20),
    (store_div, ":slow_range", ":total_distance", 60),
    (store_sub, ":distance_moved", ":total_distance", ":distance_left"),

    (try_begin),
      (lt, ":distance_moved", ":slow_range"),
      (store_mul, ":base_speed", ":distance_moved", -60),
      (val_div, ":base_speed", ":slow_range"),
      (val_add, ":base_speed", 80),
    (else_try),
      (lt, ":distance_left", ":slow_range"),
      (store_mul, ":base_speed", ":distance_left", -60),
      (val_div, ":base_speed", ":slow_range"),
      (val_add, ":base_speed", 80),
    (try_end),
    (store_mul, ":belfry_speed", ":cur_distance", ":base_speed"),
    (try_begin),
      (eq, "$belfry_num_men_pushing", 0),
      (assign, ":belfry_speed", 1000000),
    (else_try),
      (val_mul, ":belfry_speed", 15), #assuming an average strength per man
      (val_div, ":belfry_speed", "$belfry_num_men_pushing"),
    (try_end),

    (try_begin),
      (le, "$cur_belfry_pos", 55),
      (init_position, pos3),
      (position_rotate_x, pos3, ":distance_moved"),
      (scene_get_slot, ":base_belfry_object", ":cur_scene", slot_scene_belfry_props_begin),
      (prop_instance_get_position, pos4, ":base_belfry_object"),
      (entry_point_get_position,pos1,"$cur_belfry_pos"),
      (try_for_range, ":i_belfry_object_pos", slot_scene_belfry_props_begin, "$last_belfry_object_pos"),
        (scene_get_slot, ":cur_belfry_object", ":cur_scene", ":i_belfry_object_pos"),
        (try_begin),
          (ge, ":i_belfry_object_pos", "$belfry_rotating_objects_begin"),
          (prop_instance_get_starting_position, pos5, ":base_belfry_object"),
          (prop_instance_get_starting_position, pos6, ":cur_belfry_object"),
          (position_transform_position_to_local, pos7, pos5, pos6),
          (position_transform_position_to_parent, pos5, pos4, pos7),
          (position_transform_position_to_parent, pos6, pos5, pos3),
          (prop_instance_set_position, ":cur_belfry_object", pos6),
        (else_try),
          (assign, ":pos_no", pos_belfry_begin),
          (val_add, ":pos_no", ":i_belfry_object_pos"),
          (val_sub, ":pos_no", slot_scene_belfry_props_begin),
          (position_transform_position_to_parent, pos2, pos1, ":pos_no"),
          (prop_instance_animate_to_position, ":cur_belfry_object", pos2, ":belfry_speed"),
        (try_end),
      (try_end),
    (try_end),
    (gt, "$cur_belfry_pos", 55),
    (assign, "$belfry_positioned", 1),
  ]),

  # script_cf_siege_rotate_belfry_platform
  # Input: none
  # Output: none (required for siege mission templates)
  ("cf_siege_rotate_belfry_platform",
   [(eq, "$belfry_positioned", 1),
    (scene_prop_get_instance, ":belfry_object", "spr_belfry_platform_a", 0),
    (prop_instance_get_position, pos1, ":belfry_object"),
    (position_rotate_x, pos1, -90),
    (prop_instance_animate_to_position, ":belfry_object", pos1, 400),
    (assign, "$belfry_positioned", 2),
  ]),

  # script_siege_rotate_belfry_platform
  # Input: none
  # Output: none ($belfry_positioned assigned)
  ("siege_rot_belfry_platform",
   [
    (try_begin),
    (scene_prop_get_instance, ":belfry_object", "spr_belfry_platform_a", 0),
    (prop_instance_get_position, pos1, ":belfry_object"),
    (position_rotate_x, pos1, -90),
    (prop_instance_animate_to_position, ":belfry_object", pos1, 400),
    (assign, "$belfry_positioned", 3),
    (try_end),
  ]),


  # script_cf_siege_assign_men_to_belfry
  # Input: none
  # Output: none (required for siege mission templates)
  ("cf_siege_assign_men_to_belfry",
   [
    (neq, "$last_belfry_object_pos", slot_scene_belfry_props_begin),
    (assign, ":end_trigger", 0),
    (try_begin),
      (lt, "$belfry_positioned", 3),
      (get_player_agent_no, ":player_agent"),
      (store_current_scene, ":cur_scene"),
      (scene_get_slot, ":first_belfry_object", ":cur_scene", slot_scene_belfry_props_begin),
      (prop_instance_get_position, pos2, ":first_belfry_object"),
      (assign, ":slot_1_positioned", 0),
      (assign, ":slot_2_positioned", 0),
      (assign, ":slot_3_positioned", 0),
      (assign, ":slot_4_positioned", 0),
      (assign, ":slot_5_positioned", 0),
      (assign, ":slot_6_positioned", 0),
      (assign, "$belfry_num_slots_positioned", 0),
      #this has been modified to allow the player to help push
      
      (try_begin), #the player can try to help
        (get_player_agent_no, ":player_agent"),
        (agent_is_alive, ":player_agent"), #while still alive
        #can't push from inside/on top - makes no physical sense
        (assign, ":belfry_pos", "$last_belfry_object_pos"),
        (try_for_range, ":i_belfry_object_pos", slot_scene_belfry_props_begin, ":belfry_pos"),
          (scene_get_slot, ":belfry_object", ":cur_scene", ":i_belfry_object_pos"),
          (scene_prop_has_agent_on_it, ":belfry_object", ":player_agent"),
          (assign, ":belfry_pos", -1),
        (try_end), #if the player is stepping on top, loop is broken
        (eq, ":belfry_pos", "$last_belfry_object_pos"),
        #although bots tend to get stuck and exert force, ignoring this physical impossiblity
        (agent_get_team, ":agent_team", ":player_agent"),
        (this_or_next|eq, ":agent_team", "$attacker_team"),
        (eq, ":agent_team", "$attacker_team_2"),
        (init_position, pos3),
        (position_move_y, pos3, -600),
        (position_transform_position_to_parent, pos5, pos2, pos3),
        (agent_get_position, pos3, ":player_agent"),
        (get_distance_between_positions, ":target_distance", pos5, pos3),
        (lt, ":target_distance", 600), #same as other distance
        (agent_get_troop_id, ":player_troop", ":player_agent"),
        (store_attribute_level, "$belfry_num_men_pushing", ":player_troop", ca_strength),
        # (assign, reg1, ":target_distance"),
        # (display_message, "@player is pushing from {reg1} cm away"),
      (else_try),
        (assign, "$belfry_num_men_pushing", 0),
      (try_end),
      #this is a maintenance loop - it will be triggered most often and repositions pushing agents
      (try_for_agents, ":cur_agent"),
        (agent_is_active, ":cur_agent"),
        (agent_is_alive, ":cur_agent"),
        (agent_is_human, ":cur_agent"),
        (try_begin),
          (agent_get_slot, ":x_pos", ":cur_agent", slot_agent_target_x_pos),
          (try_begin), #remove wounded agents from the belfry pusher list
            (this_or_next|eq, ":x_pos", 600),
            (eq, ":x_pos", -600),
            (store_agent_hit_points, ":health", ":cur_agent", 0),
            (le, ":health", 33), #wounded
            (agent_clear_scripted_mode, ":cur_agent"),
            
            (agent_force_rethink, ":cur_agent"),
            (agent_get_entry_no, ":entry", ":cur_agent"),
            (entry_point_get_position, pos1, ":entry"), #spawn point
            # (str_store_agent_name, s1, ":cur_agent"),
            # (assign, reg1, ":entry"),
            # (display_message, "@{s1} to be replaced at {reg1}", 0xff0000),
            #do not bother re-raising speed limit - pretend they are limping
            (agent_set_scripted_destination, ":cur_agent", pos1),
            (agent_set_slot, ":cur_agent", slot_agent_target_x_pos, 0),
            (agent_set_slot, ":cur_agent", slot_agent_target_y_pos, 0),
            (assign, ":x_pos", 0),
          (try_end),
          (neq, ":x_pos", 0), #do some level-sorting here as opposed to agent id selector?
          (agent_get_slot, ":y_pos", ":cur_agent", slot_agent_target_y_pos),
          (try_begin),
            (eq, ":x_pos", -600),
            (try_begin),
              (eq, ":y_pos", 0),
              (val_add, ":slot_1_positioned", 1),
            (else_try),
              (eq, ":y_pos", -200),
              (val_add, ":slot_2_positioned", 1),
            (else_try),
              (val_add, ":slot_3_positioned", 1),
            (try_end),
          (else_try),
            (try_begin),
              (eq, ":y_pos", 0),
              (val_add, ":slot_4_positioned", 1),
            (else_try),
              (eq, ":y_pos", -200),
              (val_add, ":slot_5_positioned", 1),
            (else_try),
              (val_add, ":slot_6_positioned", 1),
            (try_end),
          (try_end),
          (val_add, "$belfry_num_slots_positioned", 1),
          (init_position, pos1),
          (position_move_x, pos1, ":x_pos"),
          (position_move_y, pos1, ":y_pos"),
          (init_position, pos3),
          (position_move_x, pos3, ":x_pos"),
          (position_move_y, pos3, -1000),
          (position_transform_position_to_parent, pos4, pos2, pos1),
          (position_transform_position_to_parent, pos5, pos2, pos3),
          (agent_get_position, pos6, ":cur_agent"),
          (get_distance_between_positions, ":target_distance", pos6, pos4),
          (get_distance_between_positions, ":waypoint_distance", pos6, pos5),
          (try_begin),
            (this_or_next|lt, ":target_distance", ":waypoint_distance"),
            (lt, ":waypoint_distance", 600),
            (agent_set_scripted_destination, ":cur_agent", pos4, 1),
          (else_try),
            (agent_set_scripted_destination, ":cur_agent", pos5, 1),
          (try_end),
          (try_begin),
            (le, ":target_distance", 300),
            (agent_get_troop_id, ":cur_troop", ":cur_agent"),
            (store_attribute_level, ":strength", ":cur_troop", ca_strength),
            (store_agent_hit_points, ":health", ":cur_agent", 0),
            (val_mul, ":strength", ":health"),
            (val_div, ":strength", 100),
            (val_add, "$belfry_num_men_pushing", ":strength"),
            # (str_store_agent_name, s1, ":cur_agent"),
            # (assign, reg1, ":x_pos"),
            # (assign, reg2, ":y_pos"),
            # (assign, reg3, ":health"),
            # (assign, reg4, "$belfry_num_slots_positioned"),
            # (assign, reg0, "$belfry_num_men_pushing"),
            # (display_message, "@loop {reg0}/{reg4}: {s1} pushing at {reg1},{reg2} with {reg3} health",0xffffff),
          (try_end),
        (try_end),
      (try_end),
      (try_begin), #assigns non-pushing agents?
        #2 front, 1 mid, 3 back
        (lt, "$belfry_num_slots_positioned", 12),
        (try_for_agents, ":cur_agent"),
          (lt, "$belfry_num_slots_positioned", 12),
          (agent_is_alive, ":cur_agent"),
          (agent_get_team, ":cur_agent_team", ":cur_agent"),
          (this_or_next|eq, "$attacker_team", ":cur_agent_team"),
          (eq, "$attacker_team_2", ":cur_agent_team"),
          (neq, ":player_agent", ":cur_agent"),
          (agent_get_class, ":agent_class", ":cur_agent"),
          (try_begin), #allow archers to push
            (eq,  ":agent_class", grc_archers),
            (agent_get_ammo, ":ammo", ":cur_agent"),
            (le, ":ammo", 0),
            (assign, ":agent_class", grc_everyone),
          (try_end),
          (neq, ":agent_class", grc_archers),
          # (this_or_next|eq, ":agent_class", grc_infantry),
          # (eq, ":agent_class", grc_cavalry),
          # (agent_get_wielded_item, ":shield", ":cur_agent", 1),
          (assign, ":shield", -1),
          (try_for_range, ":slots", ek_item_0, ek_head),
            (agent_get_item_slot, ":item", ":cur_agent", ":slots"),
            (gt, ":item", 0),
            (item_get_type, ":type", ":item"),
            (eq, ":type", itp_type_shield),
            (assign, ":shield", ":item"),
          (try_end),
          #we want there to be a greater chance of tower pushers having shields in use
          (this_or_next|gt, ":shield", 0),
          (lt, "$belfry_num_slots_positioned", 6), #this is for troops without shields, ranged units without ammo as seen above, etc.
          (store_agent_hit_points, ":health", ":cur_agent", 0),
          (eq, ":health", 100), #healthy
          (agent_get_slot, ":x_pos", ":cur_agent", 1),
          (eq, ":x_pos", 0),
          (assign, ":y_pos", 0),
          #restructured - symmetry and increased quantity
          (try_begin),
            (eq, ":slot_5_positioned", 0),
            (assign, ":x_pos", 600),
            (assign, ":y_pos", -200),
            (val_add, ":slot_5_positioned", 1),
          (else_try),
            (eq, ":slot_2_positioned", 0),
            (assign, ":x_pos", -600),
            (assign, ":y_pos", -200),
            (val_add, ":slot_2_positioned", 1),
          (else_try),
            (lt, ":slot_1_positioned", 2),
            (assign, ":x_pos", -600),
            (assign, ":y_pos", 0),
            (val_add, ":slot_1_positioned", 1),
          (else_try),
            (lt, ":slot_4_positioned", 2),
            (assign, ":x_pos", 600),
            (assign, ":y_pos", 0),
            (val_add, ":slot_4_positioned", 1),
          (else_try),
            (lt, ":slot_3_positioned", 3),
            (assign, ":x_pos", -600),
            (assign, ":y_pos", -400),
            (val_add, ":slot_3_positioned", 1),
          (else_try),
            (lt, ":slot_6_positioned", 3),
            (assign, ":x_pos", 600),
            (assign, ":y_pos", -400),
            (val_add, ":slot_6_positioned", 1),
          (try_end),
          (val_add, "$belfry_num_slots_positioned", 1),
          (agent_set_slot, ":cur_agent", slot_agent_target_x_pos, ":x_pos"),
          (agent_set_slot, ":cur_agent", slot_agent_target_y_pos, ":y_pos"),
          # (str_store_agent_name, s1, ":cur_agent"),
          # (try_begin),
            # (gt, ":shield", 0),
            # (str_store_item_name, s2, ":shield"),
          # (else_try),
            # (str_store_string, s2, "@beefy arms"),
          # (try_end),
          # (assign, reg1, ":x_pos"),
          # (assign, reg2, ":y_pos"),
          # (assign, reg0, "$belfry_num_slots_positioned"),
          # (display_message, "@middle loop {reg0}: {s1} pushing wiith {s2} at {reg1},{reg2}", 0xffff00),
          #this really doesn't seem to work
          (agent_ai_set_always_attack_in_melee, ":cur_agent", 1),
          (try_begin),
            (gt, ":shield", 0),
            (agent_get_wielded_item, ":item", ":cur_agent", 1),
            (neq, ":shield", ":item"),
            (agent_set_wielded_item, ":cur_agent", ":shield"),
          (try_end),
          (agent_set_speed_limit, ":cur_agent", 3),
        (try_end),
      (try_end),
    (else_try),
      (assign, ":end_trigger", 1),
    (try_end),
    (eq, ":end_trigger", 1),
  ]),


 

Lumos

Section Moderator
WBWF&SNWM&BVC
Best answers
0
Somebody is on fire! :razz:

Great work, as always.
But that screenshot, argh, that screenshot. Please tell me it was tweaked and that you don't normally play like this.
 

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
Lumos said:
Great work, as always.
But that screenshot, argh, that screenshot. Please tell me it was tweaked and that you don't normally play like this.
Had to blur out the previous screenshot message, tossed on a few filters just for fun.
 

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
Michadr said:
Would this interfere with a formation overhaul? (or formation changes)
I am not quite sure why you would have formations during sieges, but probably.
 

Federick421

Squire
Best answers
0
I would really like it if the Single Player Siege Tower is replaced by the multi-player siege tower.Small groups of the attacker's archers would try to place them selves on the top platform of the tower giving them the advantage of height over the defender's archers, While a group of Heavy Assault Infantry acting as the 'Forlorn Hope' would place themselves on the boarding ramp level so when the tower reaches the wall and lowers the ramp, the defenders would be immediately be attacked by the assault team, in attempt to soften the defender's position for the rest of the attacking infantry
 

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
Unfortunately giving bots orders while they are on a moving scene prop results in them getting stuck in mid-air. You can already see this when bots traverse the bottom of the siege ramp to get to the other side, or just go jump on a siege ladder and press Ctrl+F5 to let the AI take over, resulting in your character levitating.
 

Deathwhisper

Sergeant Knight at Arms
WB
Best answers
0
Hi,

I'm getting this error when compiling :

Code:
E:\Dev suite new\Floris Dev Suite 2.55\util_scripts.py:84: UserWarning: Error in
jecting code into: cf_siege_assign_men_to_belfry
  warnings.warn("Error injecting code into: %s" % (cur_directive[1]) )
There seems to be something wrong with the assign_men_to_belfry script. What have I done wrong?

 

Somebody

Code Pope
Baron
WBWF&S
Best answers
1
You appear to be trying to inject it with modmerger, just rename it to something else and reference it in the mission template.
Also found a bug where some bots seem to get stuck advancing after the belfry arrives, only seems to happen in quick battle mode though.
 

Windyplains

Grandmaster Knight
WB
Best answers
0
Odds are you need to look at the PBOD kit as you may have altered a section of native/Floris code it was looking for to inject its PBOD code into.  Look for that script name down at the bottom of PBOD_scripts in the script_directives section and you may find it referenced there.