Entity Interactions in Source's Input/Output System
(February 11, 2018)
In Quake and GoldSrc, interaction between monsters, lights, choreography, sprites, and other entities was done through the target
-targetname
system. Under it, one entity would be given a name and another would be set to "target" that name; when the second entity was triggered, any entities with that name would activate.
Let's start with a simple example to demonstrate. When a scientist dies, an env_sprite
should turn off. This is easy enough to do. The sprite is named by the mapper (say, cpu_glow1
) and the scientist is set to target cpu_glow1
.
The limitations of the target system
Targets works fine for basic interactions such as this, but the system is fraught with limitations. Consider the following:
- What action exactly "triggers" an entity is ill-defined and vague. An NPC triggers on death, but the mapper has no idea of that unless they have prior knowledge or a reference is used. What action occurs when the targeted entity is activated is also often unclear.
- Only one action can count as a trigger. The scientist can only trigger on death. If the mapper wants the sprite to turn off when the NPC's AI goes idle, code changes would be required, and dying would no longer trigger the sprite.
- An entity can only target a single name. Consider the case of two sprite entities,
cpu_glow1
andcpu_glow2
. The scientist can't target both directly. Quake and GoldSrc both have different ways of dealing with this, through thetrigger_relay
andmulti_manager
entities, respectively, but both solutions were incredibly clumsy.multi_manager
requires the mapper to set custom keyvalues, and anothertrigger_relay
would have to exist for each new item to be targeted. - The available editors won't show what's connected to what. Nonexistent targets would appear as warnings in the compile log, but if the scientist was targeting, say,
cpu_glow3
somewhere else on the map, the mapper would be left bewildered as to why the sprite wasn't turning off.
Introducing the I/O system
With Source, Valve removed the target
-targetname
system and replaced it with the input/output (I/O) system. Though it works under the same principle (an entity activates others when an action occurs), the I/O system is far more flexible and intuitive.
- Output events are numerous for each entity and clearly labeled for when they occur.
- The system is far more easily extendable, and as many output actions as a coder wants can be stacked onto a single entity.
- Different outputs for as many targets as the mapper wants can be attached to a single action.
- Hammer 4.0 is built from the ground up to show broken or misnamed interactions, and Hammer's Check for Problems dialog will pick them out before compile. All of the interactions an entity is involved in can be viewed in a neat, orderly grid at a distance.
- Targets can be dynamically chosen through keywords; if an NPC walks into a trigger, the trigger can dynamically respond to hiim without his targetname being explicitly set in the map file.
Adding a new output
To add a new output to an entity, go into the Object Properties dialog and switch to the Outputs tab. You should see a blank list with four dropdowns and five buttons. To add a new output, click the "Add..." button.
The first dropdown, "My output named", sets the action that will cause the entity to activate. For this trigger_multiple
entity, you can cause an event to occur when the player first enters the brush volume, leaves the brush volume, when the brush volume is triggered in general, and so on. For most purposes, OnTrigger
works fine.
The second dropdown, "Target entities named", displays a list of valid targetnames in the map. This field supports wildcards and keywords, which we'll discuss later. If an invalid (nonexistent) entity name is put into this field, it'll be displayed in red. If the targetname is shared by multiple entities, the field will become bold.
The last two dropdowns, "Via this input" and "With a parameter override of", are the values the output entity will pass to the target entity. For metrocops, they can be told to bring out their stunsticks, or to holster a weapon, or have their squad name set. A door can be told to open, close, toggle state, or lock, or a new skin can be set. Physics constraints can be broken at any time using the I/O system. This is where the true beauty of the I/O system lies; a vast, complex amount of interactions can happen between entities, without compromise and without hassle. For now, though, we'll just make a metrocop go limp.
On the flipside, the Inputs tab of the Object Properties dialog displays a list of all the entities which target the one currently being viewed. The metrocop would have the BecomeRagdoll
output listed under his inputs. Nothing can be set here, but clicking the Mark button will bring you to the originating source. (The Mark button in the Outputs tab will also bring you to the entity being targeted.)
Note the two icons at the bottom of each screenshot. The first icon (an arrow pointing to a ball) will appear if the current entity is being targeted, while the second icon (an arrow extending out from a ball) appears if the current entity is targeting another. If an output on the current entity is broken, the second icon will appear with a red line through it.
Keywords and wildcards
The I/O system allows an entity to target entities dynamically without needing to specify an actual target. This is done through keywords, which allow the entity to refer to an implicit, context-reliant target, or through wildcards, which can match multiple entities with similar names in one go. (These go in the "Target entities named" field in the Outputs tab.)
Trailing asterisks (*
) can be used as wildcards to match a number of targetnames in one go. The use for this is limited to wildcards at the end of names; door1_*
matches door1_trigger
and door1_light
, but not door2_light
, and *_light
doesn't work at all.
On the other hand, keywords allows you to target players or NPCs in choreography and scripted sequences. This is useful if you have a specific entity in mind, but not a specific targetname to be able to target it with.
Three in particular refer to different entities in an I/O chain. Consider the scenario of an antlion wandering into a trigger_once
that's set to fire a prop_thumper
. When the thumper is activated, it can refer to the antlion with the !activator
keyword (refers to the entity that started the I/O chain), the trigger with the !caller
keyword (the previous entity in the I/O chain), or itself with the !self
keyword.
Other keywords exist for other functions: !player
can be used to target the player in singleplayer (all players can be targeted in multiplayer using a simple player
targetname), !pvsplayer
picks the first player found in the same potentially visible set as the entity itself, and !speechtarget
is exclusively meant for choreography and targets the entity the actor is looking at.
User inputs and outputs
The final point of discussion, user inputs and outputs, are a bit more complicated and abstract than the normal actions in the I/O system, but are good to know nonetheless. These four outputs store an action and fire when the corresponding input is fired. Complex I/O chains can be simplified down using user inputs, and multiple entities with the same name can perform different actions when the one generic user input is fired.
A good example is in d2_coast_08
, where four seagulls with the same name can be told to do four different things with only one input. A logic_auto
fires the FireUser1
input for the four gulls, and the corresponding OnUser1
output is fired for each, causing two to fly off to one spot of the map at different times, and another two to fly off to another spot, also at different times. Again, all done with a single input.
The I/O system makes complex choreography, scripted sequences, physics interactions, and other such functionality of the Source engine remarkably easy to carry out.