PYTHON SCRIPT/SCHEME EXCHANGE

Currently viewing this thread:

Armor Damage Script (Singleplayer)




Step 1: Set up per-agent item durability via slots and fill them in ti_on_agent_spawn

In module_constants
Python:
# First among your agent slots
agent_armor_dur_head = 100 # whatever number is open for you
agent_armor_dur_hand = agent_armor_dur_head + 1
agent_armor_dur_feet = agent_armor_dur_head + 2
agent_armor_dur_main = agent_armor_dur_head + 3

# and in your item_slots
slot_damaged_variant = 100 # Or, again, whatever you have available

In mission_templates, attached to a common ti_on_agent_spawn
Python:
agent_spawn_effects = (
    ti_on_agent_spawn, 0, 0, [], [
    (store_trigger_param_1, ":agent"),

    (try_for_range, ":equipment_slot", 4, 8),
        (agent_get_item_slot, ":equipment", ":agent", ":equipment_slot"),
        (gt, ":equipment", 0), # Agent does have an armor equipped here

        (item_get_hit_points, ":hp", ":equipment"),

        (try_begin),
            (eq, ":equipment_slot", 4), # Head
            (agent_set_slot, ":agent", agent_armor_dur_head, ":hp"),
        (else_try),
            (eq, ":equipment_slot", 5),    # Body
            (agent_set_slot, ":agent", agent_armor_dur_main, ":hp"),
        (else_try),
            (eq, ":equipment_slot", 6),    # Leg
            (agent_set_slot, ":agent", agent_armor_dur_feet, ":hp"),
        (else_try),
            # Would be 7, Hand
            (agent_set_slot, ":agent", agent_armor_dur_hand, ":hp"),
        (try_end),
    (try_end),

    ]),

As you can tell, this assumes you are declaring hitpoints for EACH AND EVERY ARMOR, if you do not, every hit will be shattering these poor things.

You could also just make a flat hitpoint level by changing

(item_get_hit_points, ":hp", ":equipment"),
to
(assign, ":hp", 100),

Also you need to set up a script inside game_start and game_quick_start to assign the damaged variants to their original forms

Step 2: Locational Damage, Breaking and Swapping -
First, we need to determine where damage was on the agent.

In mission_templates, attached to a common ti_on_agent_hit
Python:
common_damage_system= (
ti_on_agent_hit, 0, 0, [],
[
    (set_fixed_point_multiplier, 100),
    (store_trigger_param, ":agent", 1),
    (store_trigger_param, ":attacker", 2),
    (store_trigger_param, ":damage", 3),
    (store_trigger_param, ":bodypart", 4),
    # (store_trigger_param, ":missile", 5),
    (agent_get_troop_id, ":troop", ":agent"),
    (agent_get_troop_id, ":attacker_troop", ":attacker"),
    (assign, ":attacker_weapon", reg0),

    (agent_is_alive, ":agent"),
    (agent_is_human, ":agent"),

    (store_agent_hit_points, ":health_remaining_actual", ":agent", 1),

    (store_troop_health, ":current_health", ":troop"),
    (troop_set_health, ":troop", 100),
    (store_troop_health, ":max_health_actual", ":troop", 1),
    (troop_set_health, ":troop", ":current_health"),

    (try_begin),
        (gt, ":attacker_weapon", 0),
        (item_get_swing_damage_type, ":damage_type", ":attacker_weapon"),
        # I dunno how to determine which attack was unleashed, but this will work
        # We can use this to make certain damage types, such as blunt, do more damage
        # to armor durability vs things like pierce which should nearly pass through
    (else_try),
        (assign, ":damage_type", 2), # If you don't have a weapon it's fists.
    (try_end),

    (try_begin),
        (eq, ":damage_type", 0),    # Cut
        (assign, ":dampen", 30),
    (else_try),
        (eq, ":damage_type", 1),    # Pierce
        (assign, ":dampen", 35),
    (else_try),
        (eq, ":damage_type", 2),    # Blunt
        (assign, ":dampen", 15),
    (try_end),

    (store_div, ":durability_hit", ":damage", ":dampen"),

    (agent_get_slot, ":head_durability", ":agent", agent_armor_dur_head),
    (agent_get_slot, ":hand_durability", ":agent", agent_armor_dur_hand),
    (agent_get_slot, ":feet_durability", ":agent", agent_armor_dur_feet),
    (agent_get_slot, ":main_durability", ":agent", agent_armor_dur_main),

    (agent_get_item_slot, ":head_armor", ":agent", 4),
    (agent_get_item_slot, ":hand_armor", ":agent", 7),
    (agent_get_item_slot, ":feet_armor", ":agent", 6),
    (agent_get_item_slot, ":main_armor", ":agent", 5),

    (item_get_slot, ":head_damaged_var", ":head_armor", slot_damaged_variant),
    (item_get_slot, ":hand_damaged_var", ":hand_armor", slot_damaged_variant),
    (item_get_slot, ":feet_damaged_var", ":feet_armor", slot_damaged_variant),
    (item_get_slot, ":main_damaged_var", ":main_armor", slot_damaged_variant),

    (try_begin),
        (this_or_next|eq, ":bodypart", 0), # Guts
        (is_between, ":bodypart", 7, 9), # Spine and Thorax

        (gt, ":main_armor", 0), # Has Armor

        (val_sub, ":main_durability", ":durability_hit"),
        (val_max, ":main_durability", 0), # Durability cannot be negative

        (agent_set_slot, ":agent", agent_armor_dur_main, ":main_durability"),
        (eq, ":main_durability", 0),
        (agent_unequip_item, ":agent", ":main_armor", 5),
        (neq, ":main_damaged_var", 0),
        (item_get_hit_points, ":hp", ":main_damaged_var"),
        (agent_set_slot, ":agent", agent_armor_dur_main, ":hp"),
        (agent_equip_item, ":agent", ":main_damaged_var", 5),
    (else_try),
        (is_between, ":bodypart", 1, 7), # Legs

        (gt, ":feet_armor", 0), # Has Armor

        (val_sub, ":feet_durability", ":durability_hit"),
        (val_max, ":feet_durability", 0), # Durability cannot be negative

        (agent_set_slot, ":agent", agent_armor_dur_feet, ":feet_durability"),
        (eq, ":feet_durability", 0),
        (agent_unequip_item, ":agent", ":feet_armor", 4),
        (neq, ":feet_damaged_var", 0),
        (item_get_hit_points, ":hp", ":feet_damaged_var"),
        (agent_set_slot, ":agent", agent_armor_dur_feet, ":hp"),
        (agent_equip_item, ":agent", ":feet_damaged_var", 4),
    (else_try),
        (eq, ":bodypart", 9), # head

        (gt, ":head_armor", 0), # Has Armor

        (val_sub, ":head_durability", ":durability_hit"),
        (val_max, ":head_durability", 0), # Durability cannot be negative

        (agent_set_slot, ":agent", agent_armor_dur_head, ":head_durability"),
        (eq, ":head_durability", 0),
        (agent_unequip_item, ":agent", ":head_armor", 5),
        (neq, ":head_damaged_var", 0),
        (item_get_hit_points, ":hp", ":head_damaged_var"),
        (agent_set_slot, ":agent", agent_armor_dur_head, ":hp"),
        (agent_equip_item, ":agent", ":head_damaged_var", 5),
    (else_try),
        (this_or_next|is_between, ":bodypart", 12, 14), # Left Forearms and Hand
        (gt, ":bodypart", 17), # Right Forearms and Hand

        (gt, ":hand_armor", 0), # Has Armor

        (val_sub, ":hand_durability", ":durability_hit"),
        (val_max, ":hand_durability", 0), # Durability cannot be negative

        (agent_set_slot, ":agent", agent_armor_dur_hand, ":hand_durability"),
        (eq, ":hand_durability", 0),
        (agent_unequip_item, ":agent", ":hand_armor", 5),
        (neq, ":hand_damaged_var", 0),
        (item_get_hit_points, ":hp", ":hand_damaged_var"),
        (agent_set_slot, ":agent", agent_armor_dur_hand, ":hp"),
        (agent_equip_item, ":agent", ":hand_damaged_var", 5),
    (try_end),

    (set_trigger_result, ":damage"),
])
That bad boy will also handle breaking, and equipping a damaged variant if applicable


Tweaks I would do to make it more satisfying:
Store the location of the bone, make a particle burst at the location.
Create a physics item for the armor in the mod and allow it to pop off into the scene if destroyed.
This could probably be nested in a way to pretty it up and shorten the script, but this works for readability.
You could also simply move the item quality modifier down a stage and include various levels of disrepair that way, while keeping your items nice and tidy
 
Last edited:
This is from out friend Veni Vidi Vici#9348 on the modding Discord.

Convert Angles to Floating Point Numbers

Essentially, we can rotate positions by floating point values, but, we have no operation that can get a positions rotation in fixed point, so this will convert rotational positional data into fixed point to allow for more exact mathematics for rotation.

Python:
     #("script_rvs_get_floating_angles", pos##),
     # ATTENTION!!!
     # This script is only working correctly when either X or Y rotation is approx.= 0. In other cases it produces different output which deviates from normal angles..
     # by +-, 90, 180, whatever degrees. I'm not very comfortable with rotation matrixes or whatever that is so if you could help me improve the script i would appreciate that :3
     # Input: any pos## for calculation
     # Output: reg41: Pitch (Y) reg42: Yaw (Z) reg43: Roll (X)
     # Alternative calc output: reg44: Pitch (Y) reg45: Yaw (Z) reg46: Roll (X)
  ("rvs_get_floating_angles",
    [
      (store_script_param, ":input_pos", 1),

      (set_fixed_point_multiplier,1000000),

      (copy_position,pos52,":input_pos"),
      (position_move_x,pos52,10000),
      (position_get_y,":root_y",":input_pos"),
      (position_get_y,":vector_y",pos52),
      (position_get_x,":root_x",":input_pos"),
      (position_get_x,":vector_x",pos52),
      (position_get_z,":root_z",":input_pos"),
      (position_get_z,":vector_z",pos52),
      (store_sub,":x_offset",":vector_x",":root_x"),
      (store_sub,":y_offset",":vector_y",":root_y"),
      (store_sub,":z_offset",":vector_z",":root_z"),
      (store_atan2,":yaw",":y_offset",":x_offset"),

      (store_div,":reduced_x",":x_offset",1000000),
      (store_div,":reduced_y",":y_offset",1000000),
      (store_mul,":sq_x",":reduced_x",":reduced_x"),
      (store_mul,":sq_y",":reduced_y",":reduced_y"),
      (store_add,":sq_sum",":sq_x",":sq_y"),
      (convert_to_fixed_point,":sq_sum"),
      (store_sqrt,":hyp_xy",":sq_sum"), # Pythagor
      (store_atan2,":pitch",":hyp_xy",":z_offset"),
      (store_sub,":pitch",90000000,":pitch"),

      (val_mul,":y_offset",1000),
      (val_div,":hyp_xy",1000),
      (store_div,":y_hypxy",":y_offset",":hyp_xy"),
      (store_asin,":yaw2",":y_hypxy"),
   #   (val_div,":y_offset",1000),
   #   (val_mul,":hyp_xy",1000),

      (convert_to_fixed_point,":z_offset"),
      (store_div,":z_magnitude",":z_offset",100000000),
      (store_asin,":pitch2",":z_magnitude"),
      (convert_from_fixed_point,":z_offset"),

      (try_begin),# Remove this bul****tery if you prefer <-180|180> angles over <0|360>
       (lt,":yaw",0),
       (store_add,":yaw",360000000,":yaw"),
      (try_end),
      (try_begin),
       (lt,":yaw2",0),
       (store_add,":yaw2",360000000,":yaw2"),
      (try_end),
      (val_mul,":pitch",-1),
      (val_mul,":pitch2",-1),

      (assign,reg41,":pitch"),
      (assign,reg42,":yaw"),
      (assign,reg44,":pitch2"),
      (assign,reg45,":yaw2"),

      # Debuggery
    #  (assign,reg45,":x_offset"),
    #  (assign,reg46,":y_offset"),
    #  (assign,reg47,":z_offset"),
    #  (assign,reg48,":hyp_xy"),
    #  (assign,reg49,":y_hypxy"),
    #  (assign,reg50,":z_magnitude"),
    #
    #  (assign,reg51,":root_x"),
    #  (assign,reg52,":root_y"),
    #  (assign,reg53,":root_z"),
    #  (assign,reg54,":vector_x"),
    #  (assign,reg55,":vector_y"),
    #  (assign,reg56,":vector_z"),
    #
    #  (assign,reg39,":sq_x"),
    #  (assign,reg40,":sq_y"),

      # Now for X!

      (copy_position,pos52,":input_pos"),
      (position_move_y,pos52,10000),
      (position_get_y,":root_y",":input_pos"),
      (position_get_y,":vector_y",pos52),
      (position_get_x,":root_x",":input_pos"),
      (position_get_x,":vector_x",pos52),
      (position_get_z,":root_z",":input_pos"),
      (position_get_z,":vector_z",pos52),
      (store_sub,":x_offset",":vector_x",":root_x"),
      (store_sub,":y_offset",":vector_y",":root_y"),
      (store_sub,":z_offset",":vector_z",":root_z"),

      (store_div,":reduced_x",":x_offset",1000000),
      (store_div,":reduced_y",":y_offset",1000000),
      (store_mul,":sq_x",":reduced_x",":reduced_x"),
      (store_mul,":sq_y",":reduced_y",":reduced_y"),
      (store_add,":sq_sum",":sq_x",":sq_y"),
      (convert_to_fixed_point,":sq_sum"),
      (store_sqrt,":hyp_xy",":sq_sum"), # Pythagor
      (store_atan2,":roll",":hyp_xy",":z_offset"),
      (store_sub,":roll",90000000,":roll"),

      (convert_to_fixed_point,":z_offset"),
      (store_div,":z_magnitude",":z_offset",100000000),
      (store_asin,":roll2",":z_magnitude"),
      (convert_from_fixed_point,":z_offset"),

      (try_begin),# Remove this bul****tery if you prefer <-180|180> angles over <0|360>
       (lt,":roll",0),
       (store_add,":roll",360000000,":roll"),
      (try_end),
      (try_begin),
       (lt,":roll2",0),
       (store_add,":roll2",360000000,":roll2"),
      (try_end),

      (assign,reg43,":roll"),
      (assign,reg46,":roll2"),
    ]),

My only tweak was divorcing it from a specific pos## register and allowing it to work with whatever you choose to input into it. If you can improve this, pop into the #wb-scripts channel of the Discord and lets hammer out an updated version.


I figured, since I was sharing someone else's work, I might as well tag in with something of my own that is somewhat related.

I originally made this about a year or two ago for myself, but then found out this function is already included in the VC module_system, so I wanted to share my version just for fun.

Lookat!

Rotate one position to look at a second position. (With +Y being the facing)
I actually use this all the time, so It should be useful for someone else


Python:
        #("script_lookat", pos##, pos##),
        #This script will take a position and rotate it such that +Y will face the target exactly.
        #It does this with some basic trig, I'll explain it in the comments inside the script.
        #INPUT:
        #    Param 1: Positional register of the pos## that will be rotated
        #    Param 2: Positional register of the pos## that will be targeted
        #OUTPUT:
        #    N/A

    ("lookat",
    [
    (store_script_param, ":looker", 1),        # This is the positional register that will turn to face the :target (+Y being forward)
    (store_script_param, ":target", 2),        # This is the positional register that will be targeted
    (assign, ":local", pos13),                # This just makes the local_var :local equivilent to pos13

    (init_position, pos1),                        # Get a clean positional register so. . .
    (position_copy_rotation, ":looker", pos1),    # We can scrub the rotational data from the :looker to simplify the math

    # The maths are pretty easy once we figure it out. Essentially, make two triangles with the two positions
    # Use those triangles to determine the angles that the :looker will need to rotate to face the :target

    (position_transform_position_to_local, ":local", ":looker", ":target"),    # We will use the local data to determine side lengths

    (position_get_z, ":l_z", ":looker"),        # :looker z value

    (position_get_z, ":t_z", ":target"),        # :target z value

    (position_get_x, ":local_x", ":local"),        # The opposite side of the first triangle
    (position_get_y, ":local_y", ":local"),        # The adjacent side of the first triangle

    (store_sub, ":z_dis", ":l_z", ":t_z"),        # The adjacent side of the second triangle

    (get_distance_between_positions, ":hypo", ":looker", ":target"),    # Distance between the positions will be the hypotenuse of both triangles

    (convert_to_fixed_point, ":z_dis"),        # When doing fixed point maths only one needs to be fixed point or else it won't convert from the fp correctly
    (convert_to_fixed_point, ":local_x"),    # When doing fixed point maths only one needs to be fixed point or else it won't convert from the fp correctly

        # SOH CAH TOA, sin(opp/hyp), cos(adj/hyp), tan(opp/adj)

    (store_div, ":adj_hyp", ":z_dis", ":hypo"),    # Use the second triangle's adjacent / hypotenuse

    (store_acos, ":x_angle", ":adj_hyp"),        # To get the angle we need to adjust the pitch
    (convert_from_fixed_point, ":x_angle"),        # Convert from fixed point to make it useable by the rotation
    (val_add, ":x_angle", 270),                    # Move it by 270 degrees to convert the angle into the correct quadrant

    (try_begin),
        (eq, ":local_y", 0),                    # If the adjacent side's length would be 0
        (assign, ":z_angle", 0),                # Set the yaw to 0 to prevent division by zero errors
    (else_try),
        (store_div, ":opp_adj", ":local_x", ":local_y"),    # Use the second triangle's opposite side / adjacent side
        (store_atan, ":z_angle", ":opp_adj"),                # To get the angle of the yaw
        (convert_from_fixed_point, ":z_angle"),                # Convert from fixed point to make it useable by the rotation
        (val_mul, ":z_angle", -1),                            # Mirror the yaw to change it from CCW to CW
    (try_end),

    (try_begin),
        (lt, ":local_y", 0),            # If the :target is behind the :looker
        (val_add, ":z_angle", 180),        # Move the yaw 180 degrees
    (try_end),


    (position_rotate_z, ":looker", ":z_angle"),    # Rotate left/right (yaw) first
    (position_rotate_x, ":looker", ":x_angle"),    # Then rotate up/down (pitch) last
    ]),

Addendum, we were discussing prop spawning on inclined surfaces, and an interesting use case presented itself.

This is a short script that uses lookat in conjunction with cast_ray to determine the normal of the surface.

Python:
[(ti_on_missile_hit, [
    (set_fixed_point_multiplier, 100),

  
(copy_position, pos2, pos1), # Make a duplicate of landing location
            (position_move_z, pos2, 50, 1), # Move it up
            (call_script, "script_lookat", pos2, pos1), # Look at that position
           
            (position_rotate_x, pos2, -90), # There seems to be an issue inside the ti_on_missile_hit where the axis are mislabeled somehow, requiring us to do this.
           
            (cast_ray, reg1, pos3, pos2), # Cast a ray, this should make a duplicate of pos1, but with the normals of the surface stored in the rotational values
           
            (position_rotate_x, pos3, 90), # This is interesting, it appears the axis' inside of ti_on_missile_hit are swapped.
      # THE REST HERE #
                ]),
            ],
 
Last edited:

Vetrogor

Sergeant at Arms
This is a basic script for debugging positions. It saves registers wich later are restored. It also prints fixed_point_multiplier as <fp>.
Python:
  # script_debug_pos
  # Input:  arg1=<pos>
  # Output: none
  # Note:  Print all values from position
  ("debug_pos", [
    (store_script_param_1, ":pos"),
    (assign, ":reg0_bac", reg0),
    (assign, ":reg1_bac", reg1),
    (assign, ":reg2_bac", reg2),
    (assign, ":reg3_bac", reg3),
    (assign, ":reg4_bac", reg4),
    (assign, ":reg5_bac", reg5),
    (assign, ":reg6_bac", reg6),
    (assign, ":reg7_bac", reg7),
    (position_get_x, reg0, ":pos"),
    (position_get_y, reg1, ":pos"),
    (position_get_z, reg2, ":pos"),
    (position_get_rotation_around_x, reg3, ":pos"),
    (position_get_rotation_around_y, reg4, ":pos"),
    (position_get_rotation_around_z, reg5, ":pos"),
    (assign, reg6, ":pos"),
    (assign, reg7, 1),
    (convert_to_fixed_point, reg7),
    (display_message, "@{!}pos{reg6} ({reg0},{reg1},{reg2}, {reg3},{reg4},{reg5}) fp={reg7}"),
    (assign, reg0, ":reg0_bac"),
    (assign, reg1, ":reg1_bac"),
    (assign, reg2, ":reg2_bac"),
    (assign, reg3, ":reg3_bac"),
    (assign, reg4, ":reg4_bac"),
    (assign, reg5, ":reg5_bac"),
    (assign, reg6, ":reg6_bac"),
    (assign, reg7, ":reg7_bac"),
  ]),
 
Last edited:

Dalion

Knight
Started Battles

Do you know that feel when you join a started battle between two parties and turns out they still have yet to clash? Almost like they have been waiting for your join before actully start a fight. Weird feel, I know. Well guess what, it's fixed now! When you join a battle, all troops except for your party members will spawn in random spot within 10m radius from the middle of the map. So you will see kill feed from the very beginning, which should create a feeling that the battle was indeed going on when you joined. Sure it means more casualties for allies, but it's far more realistic. Reinforcements however will come from usual entrypoints for both sides.

Just put this trigger in lead_charge mission:
Code:
(ti_on_agent_spawn, 0, 0,
  [
   (ge, "$g_ally_party", 0), # if player has joined already started battle
   (eq, "$attacker_reinforcement_stage", 0), # and there hasn't been any reinforcements yet
   (eq, "$defender_reinforcement_stage", 0),
  ],
  [
    (store_trigger_param_1, ":agent"),
    (assign, ":agent_to_move", -1),
    (try_begin),
      (agent_is_human, ":agent"),
      (agent_get_party_id, ":a_party", ":agent"),
      (neq, ":a_party", "p_main_party"),
      (agent_is_non_player, ":agent"),
      (assign, ":agent_to_move", ":agent"),
    (else_try), 
      (neg|agent_is_human, ":agent"),
      (agent_get_rider, ":rider", ":agent"),
      (agent_get_party_id, ":r_party", ":rider"),
      (neq, ":r_party", "p_main_party"),
      (agent_is_non_player, ":rider"),
      (assign, ":agent_to_move", ":agent"),
    (try_end),   
    (neq, ":agent_to_move", -1),
    (get_scene_boundaries, pos10, pos11),
    (set_fixed_point_multiplier, 100),
    (position_get_x, "$g_scene_max_x", pos11),
    (position_get_y, "$g_scene_max_y", pos11),
    (val_add, "$g_scene_max_x", 2400), # 2400 has been subtracted automatically because of barriers from outer terrain
    (val_add, "$g_scene_max_y", 2400),
    (store_div, ":pos_x", "$g_scene_max_x", 2),
    (store_div, ":pos_y", "$g_scene_max_x", 2),
    (init_position, pos22), # map center
    (store_random_in_range, ":x_shift", -1000, 1000),
    (store_random_in_range, ":y_shift", -1000, 1000),
    (val_add, ":pos_x", ":x_shift"),
    (val_add, ":pos_y", ":y_shift"),
    (position_set_x, pos22, ":pos_x"),
    (position_set_y, pos22, ":pos_y"),
    (agent_set_position, ":agent_to_move", pos22),
  ]),

Shoutouts to Tocan for pointing me in the right direction here.
 
Last edited:

KnowsCount

Regular
I formerly wrote a script that allows AI to kick which is worth sharing:

Python:
AI_kick_enhancement =  (
    2, 0, 0,
    [], [
    (get_player_agent_no,":player"),
    (try_for_agents, ":agent"),
        (neq, ":agent", ":player"),
        (agent_is_alive, ":agent"),
        (agent_is_human, ":agent"),
        (agent_is_active, ":agent"),
        (agent_slot_eq, ":agent", slot_agent_is_running_away, 0),
        ##He's an eligible human.  Now see if he's in a position to kick.
        (agent_get_attack_action, ":attack_action", ":agent"), # return value: spare - 0, prepare - 1, attack - 2, hit - 3, was defended - 4,reload - 5, release - 6, cancel - 7
        (agent_get_defend_action, ":defend_action", ":agent"),
        (this_or_next|eq,":attack_action",4),
        (this_or_next|eq,":defend_action",1), # defend enemy
        ##So he'll only try to kick if he just parried an enemy attack, or his own attack just got parried.
        (agent_get_team, ":team", ":agent"),
        (assign, ":maximum_distance", 100),
        # get target
        (agent_ai_get_look_target,":suspect",":agent"),
        (gt,":suspect",0),
        (agent_is_alive, ":suspect"),
        (agent_is_human, ":suspect"),
        (agent_is_active, ":suspect"),
        (agent_get_team, ":suspect_team", ":suspect"),
        (neq, ":suspect_team", ":team"),
        (agent_get_position, pos1, ":agent"), # distance check
        (agent_get_position, pos2, ":suspect"),
        (neg|position_is_behind_position, pos2, pos1), #enemy cannot be behind player
        (get_distance_between_positions, ":distance", pos1, pos2),
        (le, ":distance", ":maximum_distance"),
        (store_random_in_range,":kickchance", 1, 10),
        (try_begin),
            (eq,":kickchance",1),
                (display_message, "@Agent kicks."),
                (agent_set_animation, ":agent", "anim_prepare_kick_0"),
                (agent_deliver_damage_to_agent, ":agent", ":suspect", 3),
                (agent_set_animation, ":suspect", "anim_strike3_abdomen_front"),
            (try_end),
       (try_end),
       ])

I never used this because I totally forgot about this until I took a look into my google drive..
 
Last edited:

Vetrogor

Sergeant at Arms
This scripts will help to point missile from one position to another adjusted with missile speed and gravitation without air resistance. I don't know exact warband formulas. So this can be different a bit.

Example: (call_script, "script_point_missile_position", pos3, pos4, ":speed"),

PHP:
  # script_cf_quadratic_roots
  # Input:  <a_fixed_point>, <b_fixed_point>, <c_fixed_point> : ax^2+bx+c=0
  # Output: <reg0> - number of roots, <reg1_fixed_point> - first root, <reg2_fixed_point> - second root
  # Note:   Fixed_point_multiplier can be any. It will be converted for precise calculation.
  ("cf_quadratic_roots", [
    (store_script_param, ":a", 1),
    (store_script_param, ":b", 2),
    (store_script_param, ":c", 3),

    (assign, ":save_fpm", 1),
    (convert_to_fixed_point, ":save_fpm"),
    (set_fixed_point_multiplier, 100000), # precise calculation
    (convert_to_fixed_point, ":a"),
    (convert_to_fixed_point, ":b"),
    (convert_to_fixed_point, ":c"),
    (val_div, ":a", ":save_fpm"),
    (val_div, ":b", ":save_fpm"),
    (val_div, ":c", ":save_fpm"),

    (store_mul, ":b2", ":b", ":b"),
    (convert_from_fixed_point, ":b2"),
    (val_mul, ":c", ":a"),
    (convert_from_fixed_point, ":c"),
    (val_mul, ":c", 4),
    (val_sub, ":b2", ":c"),
   
    (try_begin),
      (lt, ":b2", 0),
      (assign, reg0, 0),
      (assign, reg1, -1),
      (assign, reg2, -1),
    (else_try),
      (eq, ":b2", 0),
      (assign, reg0, 1),
      (assign, reg2, -1),
      (store_mul, reg1, ":b", -1),
      (val_mul, ":a", 2),
      (convert_to_fixed_point, reg1),
      (val_div, reg1, ":a"),
      (val_mul, reg1, ":save_fpm"),
      (convert_from_fixed_point, reg1),
    (else_try),
      (assign, reg0, 2),
      (store_sqrt, reg1, ":b2"),
      (store_mul, reg2, reg1, -1),
      (val_sub, reg1, ":b"),
      (val_sub, reg2, ":b"),
      (val_mul, ":a", 2),
      (convert_to_fixed_point, reg1),
      (convert_to_fixed_point, reg2),
      (val_div, reg1, ":a"),
      (val_div, reg2, ":a"),
      (val_mul, reg1, ":save_fpm"),
      (val_mul, reg2, ":save_fpm"),
      (convert_from_fixed_point, reg1),
      (convert_from_fixed_point, reg2),
    (try_end),
    (set_fixed_point_multiplier, ":save_fpm"),
    (gt, reg0, 0), # There are no roots
  ]),

  # script_point_missile_position
  # Input:  <missile_pos>, <target_pos>, <speed_fixed_point>
  # Output: <missile_pos>
  ("point_missile_position", [
    (store_script_param, ":missile_pos", 1),
    (store_script_param, ":target_pos", 2),
    (store_script_param, ":speed", 3),

    (assign, ":save_fpm", 1),
    (convert_to_fixed_point, ":save_fpm"),
    (set_fixed_point_multiplier, 1000), # precise calculation
    (assign, ":g", 9807),
    (convert_to_fixed_point, ":speed"),
    (val_div, ":speed", ":save_fpm"),

    #remove current rotation
    (position_get_x, ":from_x", ":missile_pos"),
    (position_get_y, ":from_y", ":missile_pos"),
    (position_get_z, ":from_z", ":missile_pos"),
    (init_position, ":missile_pos"),
    (position_set_x, ":missile_pos", ":from_x"),
    (position_set_y, ":missile_pos", ":from_y"),
    (position_set_z, ":missile_pos", ":from_z"),
   
    #horizontal rotation - Yaw
    (position_get_x, ":change_in_x", ":target_pos"),
    (val_sub, ":change_in_x", ":from_x"),
    (position_get_y, ":change_in_y", ":target_pos"),
    (val_sub, ":change_in_y", ":from_y"),
   
    (try_begin),
      (this_or_next|neq, ":change_in_y", 0),
      (neq, ":change_in_x", 0),
      (store_atan2, ":theta", ":change_in_y", ":change_in_x"),
      (assign, ":ninety", 90),
      (convert_to_fixed_point, ":ninety"),
      (val_sub, ":theta", ":ninety"), #point Y axis at to position
      (position_rotate_z_floating, ":missile_pos", ":theta"),
    (try_end),

    #vertical rotation - Roll
    (get_distance_between_positions, ":distance_between", ":missile_pos", ":target_pos"),
    (try_begin),
      (gt, ":distance_between", 0),
      (position_get_z, ":z_distance", ":target_pos"),
      (position_get_z, ":z_missile", ":missile_pos"),
      (val_sub, ":z_distance", ":z_missile"),

      # Get plane distance
      (val_mul, ":change_in_x", ":change_in_x"),
      (convert_from_fixed_point, ":change_in_x"),
      (val_mul, ":change_in_y", ":change_in_y"),
      (convert_from_fixed_point, ":change_in_y"),
      (store_add, ":plane_distance", ":change_in_x", ":change_in_y"),
      (store_sqrt, ":plane_distance", ":plane_distance"),

      # A
      (store_mul, ":a", ":g", -1),
      (val_mul, ":a", ":plane_distance"),
      (convert_from_fixed_point, ":a"),
      (store_mul, ":divisor", ":speed", ":speed"),
      (convert_from_fixed_point, ":divisor"),
      (val_mul, ":divisor", 2),
      (convert_to_fixed_point, ":a"),
      (val_div, ":a", ":divisor"),

      # B
      (assign, ":b", 1),
      (convert_to_fixed_point, ":b"),

      # C
      (store_mul, ":c", ":z_distance", -1),
      (convert_to_fixed_point, ":c"),
      (val_div, ":c", ":plane_distance"),
      (val_add, ":c", ":a"),

      (try_begin),
        (call_script, "script_cf_quadratic_roots", ":a", ":b", ":c"),
        (try_begin),
          (eq, reg0, 1),
          (store_atan, ":theta", reg1),
        (else_try),
          (eq, reg0, 2),
          (store_atan, ":theta", reg1),
          (store_atan, ":theta2", reg2),
          (try_begin),
            (lt, ":theta2", ":theta"),
            (assign, ":theta", ":theta2"),
          (try_end),
        (try_end),
        (position_rotate_x_floating, ":missile_pos", ":theta"),
      (else_try),
        (gt, "$cheat_mode", 0),
        (display_message, "@Error: script_point_missile_position invalid roots"),
      (try_end),
    (try_end),

    (set_fixed_point_multiplier, ":save_fpm"),
  ]),
 

dariel

Sergeant Knight at Arms
Hi all, this is nothing earthshaking compared to the awesome code others have posted here, it's just a hack to let your character and NPCs look cooler in court scenes by giving them dress weapons. It can easily be adapted to have a character wear any weapon or clothing/headgear/footwear for court scenes as well.


be42493ecacab8c1a4f62ac87f61f33e.jpg



Here's a sample from my mod, every lord gets to wear a random dress dagger on appearing in a court scene. I made a few katars and other Indo-Persian daggers, but since they don't work well in combat they might as well just add a bit of atmosphere :smile:

To set up dress weapons or other court apparel:
  1. Enter your dress daggers or court apparel into your module_items file in sequence. For example, court_dagger_a to court_dagger_e. Take note of the item entry following the last dress dagger, say that's last_item.
  2. In module_scripts, find "enter_court"
  3. Between each instance of set_visitor and try_end, insert the following:
Python:
                (store_random_in_range,":court_dagger","itm_your_first_court_dagger","itm_entry_after_last_court_dagger"),
                    (mission_tpl_entry_add_override_item,"mt_visit_town_castle",":cur_pos",":court_dagger"),         
               (val_add,":cur_pos", 1),

If you want different factions to have different regalia, overwrite the part of enter_court from (call_script, "script_get_heroes_attached_to_center", to (assign, ":lady_meets_visitors", 0), with this:

Just change the items and faction checks as needed for your mod.

Python:
      (call_script, "script_get_heroes_attached_to_center", ":center_no", "p_temp_party"),
      (party_get_num_companion_stacks, ":num_stacks","p_temp_party"),
      (try_for_range, ":i_stack", 0, ":num_stacks"),
        (party_stack_get_troop_id, ":stack_troop","p_temp_party",":i_stack"),
        (lt, ":cur_pos", 32), # spawn up to entry point 32 - is it possible to add another 10 spots?
        (set_visitor, ":cur_pos", ":stack_troop"),
            #DRQ improved court dagger
            (store_faction_of_troop,":fac",":stack_troop"),
                (try_begin),
                    (neq,":fac","fac_kingdom_21"),
                            (store_random_in_range,":court_dagger","itm_jamdhar_katari","itm_katar_a_shield"),
                                (mission_tpl_entry_add_override_item,"mt_visit_town_castle",":cur_pos",":court_dagger"),
                            (try_begin),#failsafe - I'm seeing lords without daggers
                                (eq,":court_dagger",0),                                                            (mission_tpl_entry_add_override_item,"mt_visit_town_castle",":cur_pos","itm_jamdhar_katari"),
                            (try_end),
                    (else_try),
                        (eq,":fac","fac_kingdom_21"),
                                    (mission_tpl_entry_add_override_item,"mt_visit_town_castle",":cur_pos","itm_dagger"),#Portuguese lords get Native European style dagger
                                    (mission_tpl_entry_add_override_item,"mt_visit_town_castle",":cur_pos","itm_rapier"),#Portuguese lords get rapier too!
                    (try_end),
            #DRQ end improved court dagger
        (val_add,":cur_pos", 1),
      (try_end),
      (try_for_range, ":cur_troop", kingdom_ladies_begin, kingdom_ladies_end),
        (neq, ":cur_troop", "trp_knight_1_1_wife"), #The one who should not appear in game
        #(troop_slot_eq, ":cur_troop", slot_troop_occupation, slto_kingdom_lady),
        (troop_slot_eq, ":cur_troop", slot_troop_cur_center, ":center_no"),

        (assign, ":lady_meets_visitors", 0),

If you want to use my Indo-Persian daggers, they're here https://www.mbrepository.com/file.php?id=2383
 
Last edited by a moderator:
Top Bottom