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
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
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_glow2. The scientist can't target both directly. Quake and GoldSrc both have different ways of dealing with this, through the
multi_managerentities, respectively, but both solutions were incredibly clumsy.
multi_managerrequires the mapper to set custom keyvalues, and another
trigger_relaywould 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_glow3somewhere 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
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_light, but not
*_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
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
!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.