Prop Interactions

From Valve Developer Community
Jump to navigation Jump to search
English (en)Translate (Translate)

Prop interactions, also known as propdata interactions, are a group of independent KeyValues blocks that control how props handle interaction from outside forces, like being launched by the gravity gun, and how they should employ fire, like igniting after taking enough damage. They are technically part of prop_data, but they are usually treated separately and embedded in a model with the $keyvalues QC command. Most are defined in props_shared.h and props_shared.cpp.

Warning.pngWarning:Having more than one key of the same name (e.g. "onfirstimpact break" and "onfirstimpact paintsplat") in the same block may cause some or all involved keyvalues to stop working.

Examples

They are usually embedded in a model like this:

$keyvalues
{
	physgun_interactions
	{
	 	preferred_carryangles 	"-90 0 0"
	 	onworldimpact 		stick
	 	onlaunch 		spin_zaxis
	 	onbreak 		explode_fire
	}
	fire_interactions
	{
	 	ignite			halfhealth
 	 	explosive_resist	yes
 	 	flammable		yes
	}
}

Putting them under prop_data is also possible:

$keyvalues
{
	prop_data
	{
		physgun_interactions
		{
		 	preferred_carryangles 	"-90 0 0"
		 	onfirstimpact 		break
		 	allow_overhead		yes
		}
		fire_interactions
		{
	 	 	explosive_resist	yes
	 	 	flammable		yes
		}
	}
}

You can even use them in propdata base types:

PropData.txt
{
	"Wooden.Base"
	{
		"dmg.bullets"		"0.75"
		"dmg.club"		"2.0"
		"dmg.explosive" 	"1.5"
		
		physgun_interactions
		{
			"onfirstimpact"	"paintsplat"
		}
	}
}


Physgun Interactions

The physgun_interactions block affects how a prop or ragdoll is handled by and responds to the Gravity Gun. Despite the name, some interactions, like explode_fire, can be triggered by forces not related to the gravity gun.

Options

preferred_carryangles <angles>
Sets the angles, relative to the player's orientation, at which the prop will be held when carried.
This is used with the sawblades and propellers in Ravenholm.
Note.pngNote:npc_turret_floor is held upright automatically and the model does not need to use this keyvalue.
carry_distance_offset <float>
Adds or subtracts to the prop's distance from whoever is carrying it.
Todo: Confirm and clarify
onworldimpact <choices>
When we hit the world after being thrown by the gravity gun.
stick
Sticks to what we hit if we're traveling fast enough.
Note.pngNote:Using the gravity gun to pick up a sticking prop automatically turns the prop into debris.
onfirstimpact <choices>
When we first hit something after being thrown by the gravity gun.
break
Breaks on colliding with anything after being launched.
Tip.pngTip:Use this with explosive props that should break after the player launches them.
paintsplat
Applies paint decals to the first thing we hit after being launched.
They come in 3 colors (Red, Green, Blue) by default, but each instance of this prop should stick to one color.
impale
Sometimes impales the first NPC we hit and sticks it to the wall.
Note.pngNote:This interaction has several bugs. Use with caution.
Warning.pngWarning:This interaction is actually executed against all high-speed collisions regardless of whether it's the first impact or not, so the interaction occasionally runs several times per impact. This can cause various bugs, including spawning extra ragdolls and/or crashing the game. This also has the stick interaction built-in, causing the prop to sometimes disappear due to the aforementioned collision rules.
Icon-Bug.pngBug:NPCs killed by impalement will not play their death sound(s).  [todo tested in?]
bloodsplat
Ragdolls only. Creates a blood decal on the first thing we hit.
alienbloodsplat
Ragdolls only. Identical to bloodsplat, but creates alien blood instead.
onlaunch <choices>
When we are flung by the gravity gun.
Default
Physgun applies random angular velocity to the prop as it launches.
spin_none
Prop does not spin when launched.
Icon-Bug.pngBug:This interaction doesn't work as expected since Source 2007.  [todo tested in?]
spin_zaxis
Prop should spin around the Z axis when launched by the physcannon. (e.g. Ravenholm propellers.)
Tip.pngTip:This automatically adds slash damage to the prop, allowing it to slice zombies.
onbreak <choices>
When we are broken by any means. (or after being flung by the gravity gun)
explode_fire
Explodes in a fireball and ignites nearby enemies (that are flammable)
Note.pngNote:Explosive properties must be defined in prop_data in order to use this interaction.
Tip.pngTip:Use this in conjunction with fire_interactions to allow the prop to ignite before exploding and use prop_data to control damage and radius.
damage none
Prop does not deal impact damage, meaning it cannot deal any damage directly from hitting anything else. The prop can still deal damage with other interactions.
allow_overhead yes
Allows the gravity gun to carry the prop directly above the wielder.
Note.pngNote:Some entities don't need this interaction to allow the prop to be carried overhead. See CGrabController::IsObjectAllowedOverhead().
onpickup <choices>
When we are picked up by the gravity gun. (some interactions like create_flare can be triggered by +USE)
onpickup create_flare
Emits a flare from the "fuse" attachment when picked up.
Note.pngNote:HL2_EPISODIC DLLs only; see CBreakableProp::OnPhysGunPickup().
Icon-Bug.pngBug:Doesn't work in Black Mesa Black Mesa, use prop_flare instead.  [todo tested in?]
onpickup boogie
Ragdolls only. Begins ragdoll boogie when released, electrocuting the ragdoll and causing it to flail rapidly.
If the ragdoll was punted, the boogie lasts for 3 seconds. If it was released by other means, like being dropped, it lasts for 2 seconds.

Fire Interactions

The fire_interactions block defines flammability. It is not required to make NPCs flammable.

Note.pngNote:Unbreakable models (health 0) can still be made flammable, but they cannot be ignited by other fires or explosions and will not take any damage from the fire. They can only be ignited with the "Ignite" input.

Options

flammable yes
Allows the prop to be ignited by fire and explosions.
ignite halfhealth
Causes the prop to ignite spontaneously upon reaching 50% health.
explosive_resist yes
Causes the prop to ignite instead of breaking when it is damaged by an explosion.

Non-Functional Interactions

This section is for leftover interactions that are now nonfunctional or no longer exist at all.

Physgun Interactions

onfirstimpact <choices>
lose_energy
Can be found in models/props_junk/sawblade001a. No code exists in the SDK.

World Interactions

Unused code exists of a KV block known as world_interactions belonging to a single interaction.

onworldimpact <choices>
bloodsplat
This is declared and defined in the SDK, but it is not functional or used anywhere else. Not to be confused with the onfirstimpact variant for ragdolls.

Creating New Interactions

Creating new interactions in your own mod is relatively simple, depending on if you want to make them for props or ragdolls.

Props

Prop interactions are declared in props_shared.h starting at Line 49.

// Propdata defined interactions
enum propdata_interactions_t
{
	PROPINTER_PHYSGUN_WORLD_STICK,		// "onworldimpact"	"stick"
	PROPINTER_PHYSGUN_FIRST_BREAK,		// "onfirstimpact"	"break"
	PROPINTER_PHYSGUN_FIRST_PAINT,		// "onfirstimpact"	"paintsplat"
	PROPINTER_PHYSGUN_FIRST_IMPALE,		// "onfirstimpact"	"impale"
	PROPINTER_PHYSGUN_LAUNCH_SPIN_NONE,	// "onlaunch"		"spin_none"
	PROPINTER_PHYSGUN_LAUNCH_SPIN_Z,	// "onlaunch"		"spin_zaxis"
	PROPINTER_PHYSGUN_BREAK_EXPLODE,	// "onbreak"		"explode_fire"
	PROPINTER_PHYSGUN_DAMAGE_NONE,		// "damage"			"none"

	PROPINTER_FIRE_FLAMMABLE,			// "flammable"			"yes"
	PROPINTER_FIRE_EXPLOSIVE_RESIST,	// "explosive_resist"	"yes"
	PROPINTER_FIRE_IGNITE_HALFHEALTH,	// "ignite"				"halfhealth"

	PROPINTER_PHYSGUN_CREATE_FLARE,		// "onpickup"		"create_flare"

	PROPINTER_PHYSGUN_ALLOW_OVERHEAD,	// "allow_overhead"	"yes"

	PROPINTER_WORLD_BLOODSPLAT,			// "onworldimpact", "bloodsplat"
	
	PROPINTER_PHYSGUN_NOTIFY_CHILDREN,	// "onfirstimpact" cause attached flechettes to explode

	// If we get more than 32 of these, we'll need a different system

	PROPINTER_NUM_INTERACTIONS,
};

To create a new interaction, simply add another entry to this list. Do not put any interactions below PROPINTER_NUM_INTERACTIONS and try to avoid putting any below PROPINTER_PHYSGUN_NOTIFY_CHILDREN unless you know what you are doing. The order of this list is crucial later. The comment at the bottom of the list implies there can only be a maximum of 32 interactions. Commenting the keyvalue of your interaction is optional and has no negative consequences, nor does it do anything implement your interaction. In this example, we will create PROPINTER_WORLD_KILL, which would remove a prop the moment it hits the world. We will put it in between PROPINTER_WORLD_BLOODSPLAT and PROPINTER_PHYSGUN_NOTIFY_CHILDREN.

	PROPINTER_WORLD_KILL,			// "onworldimpact", "kill"

In order for $keyvalues to use it, its KeyValue must be defined in a separate list at Line 155 in props_shared.cpp:

propdata_interaction_s sPropdataInteractionSections[PROPINTER_NUM_INTERACTIONS] =
{
	{ "physgun_interactions", "onworldimpact", "stick" },		// PROPINTER_PHYSGUN_WORLD_STICK,
	{ "physgun_interactions", "onfirstimpact", "break" },		// PROPINTER_PHYSGUN_FIRST_BREAK,
	{ "physgun_interactions", "onfirstimpact", "paintsplat" },	// PROPINTER_PHYSGUN_FIRST_PAINT,
	{ "physgun_interactions", "onfirstimpact", "impale" },		// PROPINTER_PHYSGUN_FIRST_IMPALE,
	{ "physgun_interactions", "onlaunch", "spin_none" },		// PROPINTER_PHYSGUN_LAUNCH_SPIN_NONE,
	{ "physgun_interactions", "onlaunch", "spin_zaxis" },		// PROPINTER_PHYSGUN_LAUNCH_SPIN_Z,
	{ "physgun_interactions", "onbreak", "explode_fire" },		// PROPINTER_PHYSGUN_BREAK_EXPLODE,
	{ "physgun_interactions", "damage", "none" },				// PROPINTER_PHYSGUN_DAMAGE_NONE,
	
	{ "fire_interactions", "flammable", "yes" },				// PROPINTER_FIRE_FLAMMABLE,
	{ "fire_interactions", "explosive_resist", "yes" },			// PROPINTER_FIRE_EXPLOSIVE_RESIST,
	{ "fire_interactions", "ignite", "halfhealth" },			// PROPINTER_FIRE_IGNITE_HALFHEALTH,

	{ "physgun_interactions", "onpickup", "create_flare" },		// PROPINTER_PHYSGUN_CREATE_FLARE,

	{ "physgun_interactions", "allow_overhead", "yes" },	// 	PROPINTER_PHYSGUN_ALLOW_OVERHEAD,

	{ "world_interactions", "onworldimpact", "bloodsplat" },	// PROPINTER_WORLD_BLOODSPLAT,
};

Format: { Section Name, Key Name, Value }

You can use a different section name (e.g., world_interactions) without consequence, except that you'd need a whole new block separate from physgun_interactions or fire_interactions.

This list must be in the same order as the original enumerator.

PROPINTER_PHYSGUN_NOTIFY_CHILDREN is a unique exception to this list. It is defined as a physgun interaction, but apparently it has no keyvalues declared here and thus cannot be used in a model's QC keyvalues. It was suggested earlier that you should avoid placing interactions below it because it may confuse the new keyvalue with PROPINTER_PHYSGUN_NOTIFY_CHILDREN. In order to associate your new interaction with the correct keyvalues, you must either declare PROPINTER_PHYSGUN_NOTIFY_CHILDREN beforehand or just avoid placing any interactions below PROPINTER_PHYSGUN_NOTIFY_CHILDREN in the first place. PROPINTER_PHYSGUN_NOTIFY_CHILDREN is believed to be used by props with Hunter flechettes on them and was most likely never meant to be set via KeyValues.

For our example of PROPINTER_WORLD_KILL, we will place this at the bottom according to its position in the enum:

{ "physgun_interactions", "onworldimpact", "kill" },	// PROPINTER_WORLD_KILL,

Again, commenting its name is optional.

Now that our interaction has been created, we can put it into action. Three functions can be used to handle prop interactions:

bool		HasInteraction( propdata_interactions_t Interaction ) // Checks to see if this prop has a specific interaction
void		SetInteraction( propdata_interactions_t Interaction ) // Adds a specific interaction to this prop
void		RemoveInteraction( propdata_interactions_t Interaction ) // Removes a specific interaction from this prop

For our example, we will implement PROPINTER_WORLD_KILL in props.cpp under CPhysicsProp::HandleAnyCollisionInteractions.

	if ( HasInteraction(PROPINTER_WORLD_KILL) )
	{
		if (pEvent->pEntities[!index]->IsWorld())
		{
			UTIL_Remove(this);
		}
	}

Once this is complete, you should now be able to implement it in a model's QC keyvalues. For example:

$keyvalues
{
	physgun_interactions
	{
	 	onworldimpact 		kill
	}
}

Ragdolls

Creating new interactions in physics_prop_ragdoll.cpp is much simpler but it is also much more limited. It involves their keyvalues alone, meaning you cannot make anything like PROPINTER_PHYSGUN_NOTIFY_CHILDREN from the previous section, and they can only be used when under physgun_interactions.

They are only called by this function:

HasPhysgunInteraction( const char *pszKeyName, const char *pszValue )
// Example: HasPhysgunInteraction("onfirstimpact", "bloodsplat")

Other than adding the keyvalues to your ragdoll's QC file, all you need to do is call the function with your keyvalue in whatever code you want to use.


See also