The Quake-C Basic Types

Simple Types

Type: void

An empty result, mostly used for definition of procedures (i.e. functions that return no result at all).

Type: float

A floating point value.

Floats are also used to store booleans (TRUE, FALSE) or integer values linke counters, or bit flags.

    Valid syntax: 12  1.6   0.5   -100  
    Invalid syntax: .5

A parsing ambiguity is present with negative constants. "a-5" will be parsed as "a", then "-5", causing an error. Separate the - from the digits with a space "a - 5" to get the proper behavior.

Type: vector

A vector, made of 3 float coordinates.
Used to represent positions or directions in 3D space.
Valid syntax: '0 0 0' or '20.5 -10 0.00001'

Note the simple quotes around the vector. Do not use double quotes, they are reserved for strings.

If you declare a vector foobar, then you can access it's x, y and z fields with: foobar_x, foobar_y,foobar_z.

Type: string

Represents a character string.
Used to indicate file names, or messages to be broadcast to players.
Valid syntax: "maps/jrwiz1.bsp" or "ouch!\n"
Use \n for newline.

Type: entity

The reference of an entity in the game, like things, players, monsters.
For instance, this is the type of the entities self and other.

The entity type is a structured type, made of fields.
A description of each field is available.


Field types

Countrary to the other types, the entity type is a reference to an instance of a structured object, that contains many informations of totally different kinds.

To access all these informations conveniently, they are stored as fields of the entity object, and each field is given a name and a type, that makes it distinct of the others.

Some of the fields do not store value, but instead they store the function to be executed in certain conditions. They are called the methods that can be aplied to the object.

If Quake-C was an object oriented programming language, those method functions and would be distinguished from the other fields. And, above all, you would be able to create new object types, with their own fields.

As Quake-C stands currently, all the field definitions are definitions of entity fields. So anywhere in your code you could add definition of new fields, and the compiler would interpret them as an extension of the entity definition.

Here are all the possible definitions of entity fields, with their types:

    .float field_name;
    .string field_name;
    .vector field_name;
    .entity field_name;

Reserved field types (beware of the hack!)

In the first file read by the Quake-C compiler, defs.qc, there must be a definition for the entity fields, and world fields. This definition is hard coded. You had better not touch it, or you will have to recompile Quake itself.

The globals are defined before the special definition void end_sys_globals;
The entity fields are defined before the special definition void end_sys_fields;

It's not important if you don't understand the nonsense above. It's an ugly hack. Just don't modify defs.qc before those two tags, and you won't be in trouble.


Compilation of Quake-C

The language is strongly typed and there are no casts.

Source files are processed sequentially without dumping any state, so if a defs file is the first one processed, the definitions will be available to all other files.

Error recovery during compilation is minimal. It will skip to the next global definition, so you will never see more than one error at a time in a given function. All compilation aborts after ten error messages.

Names can be defined multiple times until they are defined with an initialization, allowing functions to be prototyped before their definition.

    // in headers
    void()	MyFunction;	        // the prototype
    // later
    void()	MyFunction =		// the initialization
    { dprint ("we're here\n"); };

Beware of the Quake-C compiler

Here are some remarks by Adnan Zafar (zafar@hal-pc.org)

The compiler only catches syntax and other language-style errors. It will not warn you of things that generate run-time errors.. and definitely doesn't guarantee that things will work the way you want. It's been my experience that I have to compile and test something 6 or 8 times; the first few times it doesn't work at all, and the last few it doesn't work exactly right. [...] I just don't want you to think that all the code that compiles will have a good chance of working. In all likelihood, it won't.


Execution of Quake-C

Code execution is initiated by C code in quake from two main places: the timed think routines for periodic control, and the touch function when two objects impact each other.

Execution is also caused by a few uncommon events, like the addition of a new client to an existing server.

There is a runnaway counter that stops a program if 100000 statements are executed, assuming it is in an infinite loop.

It is acceptable to change the system set global variables. This is usually done to pose as another entity by changing self and calling a function.

The interpretation is fairly efficient, but it is still over an order of magnitude slower than compiled C code. All time consuming operations should be made into built in functions.

A profile counter is kept for each function, and incremented for each interpreted instruction inside that function. The "profile" console command in Quake will dump out the top 10 functions, then clear all the counters. The "profile all" command will dump sorted stats for every function that has been executed.