OSP Code Combat Spike Trap

Users who are viewing this thread

Warning: to implement this OSP, coding skills aren't enough, you should also have at least basic scene editor experience. If you don't, hiner's tutorial is a great starting point.

I'm not much of a coder myself, so any part of this code could use improvements. You are free to improve this or to use as it is, just don't forget to give credits.



How does it work? The trap consists of 2 parts: point of impact and a trigger prop. When you hit a trigger prop (with any weapon, from any range), the spikes will come out around the impact point and damage all nearby agents. One trigger prop can activate multiple impact points, so you can add more of them if you want trap to cover wider area. With my poor skills I can't assign trigger prop to some exact imact points, so hitting one trigger will activate all traps on a scene, that's why it is recommended not to place more than one trap per scene. If anyone knows how to separate them and allow multiple traps, please let me know.



Now let's get to the implementation itself. There are 5 steps:

1) First of all, we need to determine which props will play a role of trigger and impact point. Open module_scene_props.py. For a trigger you should pick one that has collision, so ti_on_scene_prop_hit can actually work. How to check if a prop has collision? Look at its record, if some names in it have bo_ prefix - collision is there. If not - scroll along. For an impact point you should pick some invisible object, and better remove its collision (replace "bo_***" with "0"). When you are done chosing, duplicate their records and rename trigger prop to "st_trigger", impact point prop to "st_impact_point", and also duplicate spike_group_a and rename it to "st_spikes". I've chosen bucket_a as a trigger and barrier_capsule as an impact point, so here is my record:
IvycH30.png

2) Add this anywhere in module_constants.py:

Code:
slot_trap_time_left = 50

3) Add these mission triggers at the top of mission_templates.py and later on add their names on any needed template:

spike_trap_setup => spawns four groups of spikes around each impact point when mission starts. It will spawn them underground so when moving impact point don't forget to move the camera below and delete all the spikes from previous spawn
spike_trap_hide => hides the spikes and reloads the trap, preparing it for the next use
spike_trap_check => sort of a timer, every second decreases time left for next trap activation by 1

Code:
spike_trap_setup = (0, 0, ti_once, [],
[
     (assign, "$trap_used", 0),
     (try_for_prop_instances, ":instance_id", "spr_st_impact_point"),
      (prop_instance_get_starting_position, pos2, ":instance_id"),	      	   	
      (position_get_x, ":x", pos2),
      (position_get_y, ":y", pos2),
      (position_get_z, ":z", pos2),
      (position_rotate_x, pos2, 320), #spikes are tilted by default so we should rotate their X axis before spawn
      (val_sub, ":z", 240),
      (val_add, ":x", 120),      
      (position_set_z, pos2, ":z"),
      (position_set_x, pos2, ":x"),	 
      (set_spawn_position, pos2),
      (spawn_scene_prop, "spr_st_spikes"),
      (val_sub, ":x", 240),
      (position_set_x, pos2, ":x"),
      (set_spawn_position, pos2),
      (spawn_scene_prop, "spr_st_spikes"),
      (val_add, ":x", 120),  
      (val_add, ":y", 120),    
      (position_set_x, pos2, ":x"),
      (position_set_y, pos2, ":y"), 	  
      (set_spawn_position, pos2),
      (spawn_scene_prop, "spr_st_spikes"),
      (val_sub, ":y", 240),    
      (position_set_y, pos2, ":y"),
      (set_spawn_position, pos2),
      (spawn_scene_prop, "spr_st_spikes"),
     (try_end),
    ])

spike_trap_hide = (0, 2, 0, [(eq, "$trap_used", 1),],
[        
      (try_for_prop_instances, ":spike", "spr_st_spikes"), 
       (prop_instance_get_position, pos3, ":spike"), 
       (position_get_z, ":z2", pos3),       
       (val_sub, ":z2", 280), 
       (position_set_z, pos3, ":z2"),	  
       (prop_instance_animate_to_position, ":spike", pos3, 300), 
      (try_end),
      (assign, "$trap_used", 0),      
    ])
	
spike_trap_check = (1, 0, 0, [],
[  
      (try_for_prop_instances, ":trigger", "spr_st_trigger"), 
       (scene_prop_get_slot, ":time_left", ":trigger", slot_trap_time_left),
       (gt, ":time_left", 0),
       (val_sub, ":time_left", 1),
       (scene_prop_set_slot, ":trigger", slot_trap_time_left, ":time_left"),
      (try_end),      
    ])
Example for ai_training mission, the one that is called when entering the siege scenes via cheats

R8llhrh.png

4) Now we need to make our trigger prop do its job. Put this in its square brackets:

Code:
(ti_on_scene_prop_hit,
    [
	(store_trigger_param_1, ":prop"),
	(store_trigger_param_3, ":attacker"),
	
      (play_sound, "snd_dummy_hit"),
      (particle_system_burst, "psys_dummy_smoke", pos1, 3),
      (particle_system_burst, "psys_dummy_straw", pos1, 10),    

      (try_begin),
       (eq, "$trap_used", 0),	
       (scene_prop_slot_eq, ":prop", slot_trap_time_left, 0),
      (try_for_prop_instances, ":spike", "spr_st_spikes"), 
       (prop_instance_get_position, pos3, ":spike"), 
       (position_get_z, ":z2", pos3), 
       (val_add, ":z2", 280), 
       (position_set_z, pos3, ":z2"),	  
       (prop_instance_animate_to_position, ":spike", pos3, 30), 	   
      (try_end),
       (play_sound, "snd_metal_hit_high_armor_high_damage"),
      (try_for_prop_instances, ":instance_id", "spr_st_impact_point"),
       (prop_instance_get_position, pos5, ":instance_id"),	  
      (try_for_agents, ":agent"),
       (agent_is_alive, ":agent"),
       (agent_get_position, pos4, ":agent"),
       (get_distance_between_positions_in_meters, ":dist", pos5, pos4),
       (le, ":dist", 2.5),
       (agent_deliver_damage_to_agent, ":attacker", ":agent", 300, "itm_war_spear"), #I used spear to make spikes deal piercing damage
      (try_end),	  
       (position_get_z, ":z3", pos5), 
       (val_sub, ":z3", 400), 
       (position_set_z, pos5, ":z3"),
       (particle_system_burst, "psys_dummy_smoke_big", pos5, 30),
      (try_end),
       (assign, "$trap_used", 1),
       (scene_prop_set_slot, ":prop", slot_trap_time_left, 5), #How long trap should reload. To avoid bugs, don't lower it below 5
      (else_try),
       (display_message, "@Trap is reloading, please wait..."),
      (try_end),
    ]),

5) Now we are set, and ready to put the trap on our scene in-game. Open scene editor, and find our props:
2Y3lxC4.png
Place impact points anywhere you like, and then put trigger somewhere near:
CGLLzZz.jpg
That's all. No need to place spikes, they will be spawned automatically when you restart the mission.





This was really painful to create, but definitely worth it imo  :twisted:
 
UndeadDuke said:
With my poor skills I can't assign trigger prop to some exact imact points, so hitting one trigger will activate all traps on a scene, that's why it is recommended not to place more than one trap per scene. If anyone knows how to separate them and allow multiple traps, please let me know.
You could simply set the variation id's of the props (in editor) to link the traps to a specific trigger and then check for them using;
Code:
prop_instance_get_variation_id              = 1840  # (prop_instance_get_variation_id, <destination>, <scene_prop_id>),
                                                    # Retrieves the first variation ID number for the specified scene prop instance.
prop_instance_get_variation_id_2            = 1841  # (prop_instance_get_variation_id_2, <destination>, <scene_prop_id>),
                                                    # Retrieves the second variation ID number for the specified scene prop instance.
 
Yeah I considered them too but the problem is, spikes are placed not via editor so I can't set their variation ids. Even if I restart mission and set them after they spawn, they will be spawned again in the next restart, on top of those I assigned, and default var1 of new ones will still be 0. I tried comparing their positions to closest impact point as a possible workaround, but there turned out to be too much bugs to handle. Also the trap is too OP to place more than one anyways
 
When you spawn a prop it's id is stored in register 0, so you can always refer to it.

I quickly rewrote your code to support multiple traps, did some optimizations and added rudimentary multiplayer support.
However I didn't test it ingame, I just wrote it ad-hoc, so let me know if there are some issues.

Both trigger and impact points have to have the same variation id's.

This new version requires to set up a new slot.
Code:
slot_trap_trigger = 51

That goes into mission templates.
Code:
spike_trap_setup = (ti_after_mission_start, 0, 0, [],
[
	(this_or_next|multiplayer_is_server),
	(neg|game_in_multiplayer_mode),

	(try_for_prop_instances, ":trigger", "spr_st_trigger", somt_object),
		(prop_instance_get_variation_id, ":trigger_vid", ":trigger"),

		(try_for_prop_instances, ":impact_point", "spr_st_impact_point", somt_object),
			(prop_instance_get_variation_id, ":impact_vid", ":impact_point"),
			(eq, ":trigger_vid", ":impact_vid"),
			
			(scene_prop_set_slot, ":impact_point", slot_trap_trigger, ":trigger"),
			(prop_instance_get_position, pos0, ":impact_point"),
			(position_rotate_x, pos0, 320), #spikes are tilted by default so we should rotate their X axis before spawn
			(position_move_z, pos0, -240),

			(try_for_range, ":i", 0, 4),
				(try_begin),
					(eq, ":i", 0),
					(position_move_x, pos0, 120),
				(else_try),
					(eq, ":i", 1),
					(position_move_x, pos0, -240),
				(else_try),
					(eq, ":i", 2),
					(position_move_x, pos0, 120),  
					(position_move_y, pos0, 120),    
				(else_try),
					(position_move_y, pos0, -240),
				(try_end),
				(set_spawn_position, pos0),
				(spawn_scene_prop, "spr_st_spikes"),
				(scene_prop_set_slot, reg0, slot_trap_trigger, ":trigger"),
			(try_end),
		(try_end),
	(try_end),
])
	
spike_trap_check = (1, 0, 0, [],
[
	(this_or_next|multiplayer_is_server),
	(neg|game_in_multiplayer_mode),

	(try_for_prop_instances, ":spike", "spr_st_spikes", somt_temporary_object),
		(scene_prop_get_slot, ":trigger", ":spike", slot_trap_trigger),
		(prop_instance_is_valid, ":trigger"),

		(scene_prop_slot_ge, ":trigger", slot_trap_time_left, 1),#only check within cool down time
		(prop_instance_is_animating, ":is_animating", ":spike"),
		(eq, ":is_animating", 0),

		(prop_instance_get_position, pos0, ":spike"),
		(prop_instance_get_starting_position, pos1, ":spike"),
		(position_get_z, ":z0", pos0),
		(position_get_z, ":z1", pos1),
		(neq, ":z0", ":z1"),

		(prop_instance_animate_to_position, ":spike", pos1, 300),#animate back to spawn position
	(try_end),

	(try_for_prop_instances, ":trigger", "spr_st_trigger", somt_object), 
		(scene_prop_get_slot, ":time_left", ":trigger", slot_trap_time_left),
		(gt, ":time_left", 0),
		(val_sub, ":time_left", 1),
		(scene_prop_set_slot, ":trigger", slot_trap_time_left, ":time_left"),
	(try_end),
])

And finally the scene prop trigger.
Code:
(ti_on_scene_prop_hit,
[
	(store_trigger_param_1, ":trigger"),
	(store_trigger_param_3, ":attacker"),

	(set_fixed_point_multiplier, 100),
	
	(try_begin),#only client side
		(neg|multiplayer_is_dedicated_server),
		(play_sound, "snd_dummy_hit"),
		(particle_system_burst, "psys_dummy_smoke", pos1, 3),
		(particle_system_burst, "psys_dummy_straw", pos1, 10),
	(try_end),

	(this_or_next|multiplayer_is_server),
	(neg|game_in_multiplayer_mode),

	(try_begin),
		(scene_prop_slot_eq, ":trigger", slot_trap_time_left, 0),
		(scene_prop_set_slot, ":trigger", slot_trap_time_left, 5), #How long trap should reload. To avoid bugs, don't lower it below 5
		
		(try_for_prop_instances, ":spike", "spr_st_spikes", somt_temporary_object),
			(scene_prop_slot_eq, ":spike", slot_trap_trigger, ":trigger"),
			(prop_instance_get_position, pos3, ":spike"),
			(position_move_z, pos3, 280),  
			(prop_instance_animate_to_position, ":spike", pos3, 30), 	   
		(try_end),

		(try_for_prop_instances, ":impact_point", "spr_st_impact_point", somt_object),
			(scene_prop_slot_eq, ":impact_point", slot_trap_trigger, ":trigger"),
			(prop_instance_get_position, pos3, ":impact_point"),

			(try_for_agents, ":agent", pos3, 250),#check for agents within the damaging radius
				(agent_is_alive, ":agent"),
				(agent_deliver_damage_to_agent, ":attacker", ":agent", 300, "itm_war_spear"), #I used spear to make spikes deal piercing damage
			(try_end),

			(position_move_z, pos3, -400),
			(particle_system_burst, "psys_dummy_smoke_big", pos3, 30),
		(try_end),
       	
	(else_try),
		(display_message, "@Trap is reloading, please wait..."),
	(try_end),
]),
 
_Sebastian_ said:
When you spawn a prop it's id is stored in register 0, so you can always refer to it.
That's what put me in trouble in the first place. Lav's ms description states that it doesn't, so I started looking for another ways  :facepalm:
Code:
spawn_scene_prop = 1974  # (spawn_scene_prop, <scene_prop_id>),
# Spawns a new scene prop instance of the specified type at the position defined by the last call to (set_spawn_position). Operation was supposed to store the prop_instance_id of the spawned position in reg0, but does not do this at the moment.

Thanks a million for your code. I studied it carefully and discovered lots of possibilities I didn't know exist (for example ability to determine radius directly in try_for_agents loop without comparing positions).

_Sebastian_ said:
However I didn't test it ingame, I just wrote it ad-hoc, so let me know if there are some issues.
I tested it and it works, it supports multiple traps even if I hit another trigger during the reload of previous one. The only issues are positions, they are completely messed up somehow, and the sound is missing too. When I fix those I'll update the OP and credit you as a co-creator :party:
 
Back
Top Bottom