OSP Code MP Seismic Mace w/ Projectile-Simulated Knockback [WSE]

Users who are viewing this thread


A while back I was trying to access the game's built in ragdoll physics, but it turns out it's hardcoded and inaccessible. I wanted to create a knockback system, and I'd like to share with everyone my less than elegant solution. The idea is simple: when an agent is hit with a specific weapon, an invisible projectile is launched from the hit agent and their position is fixed to it (while the native knockdown animation runs).

It's not pretty, but it's a lot of fun. To do this, you're gonna need WSE in order to access some special triggers, but the code itself isn't too bad--let's get started.

Please note--this tutorial is for multiplayer. If anyone is interested in a single player version please lmk.

All you'll need resource-wise is a weapon you'd like to inflict knockback (can be native), an empty mesh, and a particle effect if you're feeling fancy (I use a simple white circle).

Let's start with the particle system: in module_particle_systems, you can add the following entry or create your own:
    ("smack", psf_billboard_3d|psf_global_emit_dir|psf_always_emit|psf_randomize_size, "YOURPARTICLEHERE",
     20, 0.5, 0, 0, 0, 0,
     (0, 1), (0.5, 0),
     (0, 0.9), (0.5, 0.9),
     (0, 0.9), (0.5, 0.9),
     (0, 0.9), (0.5, 0.9),
     (0, 0.01),   (0.5, 1.5),
     (0.01, 0.01, 0.01),
     (0, 0, 0),
Replace YOURPARTICLEHERE with the name of your particle mesh.

Next, head to module_constants. We need to create a new agent slot. Find the list of entries that start with slot_agent_ and add this to the end:
slot_knockback = 49
Replace 49 with 1 + whatever the previous number is. Mine happened to be 49.

Next, head to header_common and find the list of entries starting with multiplayer_event_. If you're modding single player, you can ignore this and use regular scripts instead of events. If anyone's interested in a tutorial for that lmk. Anyways, add these:
multiplayer_event_knockback = 121
multiplayer_event_play_animation = 122
Where again, 121 is 1 + the last entry and 122 is +2.

Next head to module_mission_templates. Here we're gonna create two new triggers in the list at the top. The first is an on-hit trigger, and the second checks to see if anyone is currently being flung and needs their position updated:
hit_checks = (ti_on_agent_hit, 0, 0, [(multiplayer_is_server),],
    (store_trigger_param_1, ":agent_no"),
    (store_trigger_param_2, ":attacker_no"),

        (eq, reg0, "itm_YOURITEMHERE"),
        (agent_is_human, ":agent_no"),
        (agent_is_alive, ":agent_no"),
        (particle_system_burst, "psys_smack", pos0, 3),
        (multiplayer_send_2_int_to_server, multiplayer_event_knockback, ":agent_no", ":attacker_no"),

knockback = (0.05, 0, 0, [(multiplayer_is_server),], [
(try_for_agents, ":cur_agent"),
        (agent_get_slot, ":knockback", ":cur_agent", slot_knockback),
        (neq, ":knockback", 0),
        (missile_is_valid, ":knockback"),
        (missile_get_cur_position, pos1, ":knockback"),
        (agent_set_position, ":cur_agent", pos1),
        (agent_set_slot, ":cur_agent", slot_knockback, 0),
Replace YOURITEMHERE with whatever item you want to apply knockback. Don't forget to add these to any mission templates you want this feature active in! Just add:
Inside the second '[ ]' of the mission template.

Next, head to module_scripts. Find the script 'game_receive_network_message' and add two new entries to the try block:
        (eq, ":event_type", multiplayer_event_play_animation),
        (store_script_param, ":agent", 3),
        (store_script_param, ":animation", 4),
        (store_script_param, ":body_part", 5),
        (store_script_param, ":progress", 6),

        (agent_set_animation, ":agent", ":animation", ":body_part"),
        (agent_set_animation_progress, ":agent", ":progress", ":body_part"),
      # Knockback
        (eq, ":event_type", multiplayer_event_knockback),
        (store_script_param, ":agent", 3),
        (store_script_param, ":attacker", 4),

        (agent_get_position, pos2, ":agent"),
        (position_get_x, ":x", pos2),
        (position_get_y, ":y", pos2),
        (position_get_z, ":z", pos2),

        (agent_get_look_position, pos1, ":attacker"),
        (position_set_x, pos1, ":x"),
        (position_set_y, pos1, ":y"),
        (position_set_z, pos1, ":z"),
        (position_get_rotation_around_x, ":x_rot", pos1),
        (val_mul, ":x_rot", -1),
        (position_rotate_x, pos1, ":x_rot"),
        (position_rotate_x, pos1, 25),

        (add_missile, ":agent", pos1, 750, "itm_blank", 0, "itm_blank", 0),
I added mine at the top, it doesn't really matter. If you're modding single player, feel free to just make these standalone scripts and call them that way.

Also in module_scripts add this new script:
    ("play_universal_animation", [
        (store_script_param, ":agent", 1),
        (store_script_param, ":animation", 2),
        (store_script_param, ":body_part", 3),
        (store_script_param, ":progress", 4),

        (agent_set_animation, ":agent", ":animation", ":body_part"),
        (agent_set_animation_progress, ":agent", ":progress", ":body_part"),

        (server_get_max_num_players, ":max_players"),
        (try_for_range, ":player", 0, ":max_players"),
            (player_is_active, ":player"),
            (multiplayer_send_4_int_to_player, ":player", multiplayer_event_play_animation, ":agent", ":animation", ":body_part", ":progress"),

This is only important for multiplayer.

Finally, we need a dud projectile. in module_items add the following:
["blank", "blank", [("empty",0)], itp_type_thrown|itp_primary, itcf_throw_stone, 0, weight(1)|difficulty(0)|spd_rtng(96)|shoot_speed(30)|thrust_damage(0, blunt)|max_ammo(1)|weapon_length(8), 0,
(ti_on_init_missile, [
    (store_trigger_param_1, ":agent"),
    (call_script, "script_play_universal_animation", ":agent", "anim_strike_fall_back_rise", 0, 0),
    (store_trigger_param, ":missile", 6),
    (agent_set_slot, ":agent", slot_knockback, ":missile"),
(ti_on_missile_hit, [
    (store_trigger_param_1, ":agent"),
    (agent_set_slot, ":agent", slot_knockback, 0),

And that's it! lmk if anyone is having trouble getting this to work or if I missed anything.
Last edited:
Thx good call. Stay tuned, next I'm going to try to apply this to an explosion script to cause radial blast knockback!
Top Bottom