• Register
Post tutorial RSS Quake c - .think() and .nextthink

The quake-c entity .think() function pointer is an engine managed timing concept that is used on nearly every entity in the game.

Posted by on - Basic Server Side Coding

What is .think() ?

The quake-c definition is found in "defs.qc":

.void() think;
.float nextthink;

If you peruse: Moddb.com , you realize ".think()" is a per-entity stored pointer to some function call and ".nextthink" is a float number.

What do they do?

These variables are set by quake-c code, and controlled by the engine. The concept is simple, however, the effect on quake-c coding is profound.

A function call value is assigned to ".think". Then ".nextthink" is assigned some time value in the future.
The server accumulates the current time in seconds as a global variable "time". Each iteration (server tick) every entities ".nextthink" value is compared to "time". While ".nextthink" is greater than "time" nothing occurs.

When ".nextthink" becomes less than or equal to time (since time is always accumulating value), the engine causes the function in ".think()" to be called during the next quake-c operation.

Three things are required to use this timer concept:

1. A valid entity.
2. A valid function pointer stored in ".think()"
3. A time value greater than the current global "time" stored in ".nextthink"

Here is an example from "misc.qc":

void () misc_fireball =
precache_model ("progs/lavaball.mdl");
self.classname = "fireball";
self.nextthink = time + (random() * 5);
self.think = fire_fly;
if (!self.speed)
self.speed = 1000; // Cataboligne 9.17.3 - fixed this compiler warning

"misc_fireball" is a map spawn function. It creates a lavaball launcher. These are often found in lava pools. The quake start map has a couple in the hall leading to episode 3.

What this code does is tells the server to make a function call to "fire_fly" with the launcher entity assigned to the "self" pointer in current "time" + 0 - 5 seconds. The launcher then fires off a lava ball and reloads ".nextthink" with some future time value. This think continues until the map is ended.

If you look through quake-c code you will find many instances of ".think()" used to control elements of game flow. It is not necessary to have an entity that is visible in game. Non model entities can be set up as timers (like the example above, the launcher is invisible, having no model assigned. The precache is so the lavaball model can be used for launches later.)

".think()" is used for monster ai, mega health rot, environmental effects, artifact countdown, and many other game elements.

The possibilities of this timer control method are almost limitless in quake-c. The brilliance of the ".think()" concept is code can be run exactly when needed and continuous loops and constant checks are not needed every quake-c operation.

The biggest code fault associated with ".think()" is failing to assign ".nextthink" a proper value:

self.nextthink = 10; // this will only happen at 10 seconds after map load. It does not add 10 seconds to "time"

self.nextthink = time + 10; // this will cause a ".think()" to happen every 10 seconds

You are now ready to code up some "thinking" entities!

Post a comment
Sign in or join with:

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.