PYTHON SCRIPT/SCHEME EXCHANGE

Currently viewing this thread:

SupaNinjaMan

Baron
M&BWBWF&SNWVC
Best answers
20
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:

SupaNinjaMan

Baron
M&BWBWF&SNWVC
Best answers
20
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
Best answers
0
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: