VERC: The Think, Touch, and Use functions. Last edited 4 years ago2019-04-19 12:12:36 UTC

You are viewing an older revision of this wiki page. The current revision may be more detailed and up-to-date. Click here to see the current revision of this page.
Almost all entities in Half-Life have at least two of the three primary functions: Think, Touch, and Use. These functions are key to how an entity works.

The Use Function

The Use function is used whenever an entity can be triggered. Most of the effects entities, such as an env_sprite, have a use function. So, let's break down a Use function.
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
The first parameter is CBaseEntity *pActivator. When the Use function is called, this parameter identifies the entity which activated this particular Use function. It does not have to be the entity which actually called this entity's Use function. For example, if a player walks into a trigger_once, it calls its target's Use function and passes the player pointer as the activator.

*pCaller is the entity which called the particular Use function. From the Use function, you can modify the variables and call more functions to perform actions on those entities as you see fit.

The third parameter is the use type. There are three major use types: USE_ON, USE_OFF, and USE_TOGGLE. You can check what useType is and then perform different actions based on its value.

Parameter four is basically unused in most entities. It's just a simple value that you can assign when the entity is fired. It may be used for special cases or something.

Ok, now we will learn one of the key things about these three functions. If you are declaring a Use, Touch, or Think function and its name is not Use, Touch, or Think, you must put EXPORT between the function type and function name.
void EXPORT UseFunction( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float  value );
If you forget the EXPORT, the function will not be remembered between level changes or level saves. It is vitally important that you check to make sure all your functions have this.

Now, the reason you would want to name it something other than Use is if you have multiple Use functions. The particular Use function that will be called is determined by using the SetUse( functionname ); command.
SetUse( UseFunction );
If you only have one Use function, just keep it named Use and it will automatically be set, no SetUse needed. To fire an entity's Use function, use the FireTargets function in an entity; this will fire its pev->target's Use function (assuming it has one).

So we can set up a use function right, but what if we want to remove one? Well, that's easy. In a case where you have a specific event happen and you don't want an entity to be usable any more, just throw in one simple line:
SetUse( NULL );
Bam! No more use function for it. And of course if you want it back, you can just call SetUse with an actual function instead of NULL.

The Touch Function

The Touch function is used whenever an entity is touched. Its primary use is for triggers like a trigger_once or trigger_multiple, but it also has many others. The other major uses include tracking when an entity hits a wall, bounces, reflects, etc. When an entity touches another, the engine calls the DispatchTouch function which in return calls the touched entity's Touch function with the pointer of the toucher.
void Touch( CBaseEntity *pOther );
CBaseEntity *pOther is the entity which touched the entity with the Touch function. Again, you can modify it however you like. The Touch function shares many of the same properties as the Use function. You MUST have EXPORT between the function type and name:
void TouchFunction( CBaseEntity *pOther );
If the name of the function is Touch, you do not need to use the SetTouch function either. Otherwise, you must use the SetTouch command in the same manner as the SetUse function.
SetTouch( TouchFunction );
Again like use functions you can also remove a touch function from being set. One simple line:
SetTouch( NULL );

The Think Function

Of the three primary functions, the Think function is the most important. A Think function is called every x seconds and executes code that gives the impression of "thinking." Almost every entity has a Think function: monsters, players, weapons, effects entities, all of them. Some Think functions call other functions while they think, giving a branching effect. For example, the apache flies around and hunts players using the HuntThink function. But, within the HuntThink function is called the Flight function, which controls the dynamic physics to tilt the chopper in the appropriate direction as it flies. Here is what a basic Think function looks like:
void Think( void );
Pretty plain. Now let's look at a Think function's actual body:
void CYourClass::Think( void )
{
    ALERT(at_console, "I am thinking!\n");
    pev->nextthink = gpGlobals->time + 0.1;
}
Ok, time to detail. The first line with the ALERT is just a simple alert to tell us that our Think function is being called. Now the second line is extremely important in any Think function. pev->nextthink is a variable that stores the time value for the engine to use. The engine uses this number to know when to trigger the think function again. In this case, we're taking the current global time (gpGlobals->time) and adding 0.1 seconds to that. Right, so we'll get the message "I am thinking!" about 10 times a second. It's a simple Think function and serves no practical purpose, but it is indeed a Think function.

Again, Think functions work like the Use and Touch functions. Generally, you'll use a SetThink call within an entity's Spawn function, such as:
SetThink( ThinkFunction );
Followed by:
pev->nextthink = gpGlobals->time + X.X;
Again, that tells the engine at what time to call our Think function. If it is just named Think, there is no need for a SetThink, but you still must set the pev->nextthink variable to a time value.

You can stop an entity from thinking in two manners. First, just like use and touch, you can do:
SetThink( NULL );
But, you can also set nextthink to something before the current global time, for example:
pev->nextthink = 0;
If set before the global time, the engine will never call the function again because that time has already passed. This is useful if you want to keep the think function attached to the entity, but might not want to think in specific cases.

Put It to Use!

Time to put what we've learned to use in a small example. Attached to this tutorial is a small example utilizing all of these functions in the code for a projectile entity. Read the comments to understand how it all comes together. You'll want to create the projectile with CBaseEntity::Create and set its angles to whatever. I hope you've learned just how important the Think, Touch, and Use functions are in creating everything and anything in Half-Life.
This article was originally published on the Valve Editing Resource Collective (VERC).
TWHL only archives articles from defunct websites. For more information on TWHL's archiving efforts, please visit the TWHL Archiving Project page.

Comments

You must log in to post a comment. You can login or register a new account.