Quake-C Manual

Quake-C Manual

Warning
The definitions below are valid only for Quake version 1.00 and 1.01. Future versions may differ.

Copyright
This document is part of the Unofficial Quake Specifications, Copyright (c) 1996 by Olivier Montanuy.
All the informations below are Copyright (c) 1996, id software. The content of this file was derived from code and comments released by John Carmack, of id software.

This is a very crude manual of Quake-C. It is rather incomplete.


Structure of Quake-C code

Language constructs

Comments

Those comments are the same as in C++ (and many C languages).

Names

Names are a maximum of 64 characters, must begin with A-Z,a-z, or _, and can continue with those characters or 0-9.

Definition of field types

The field types are used to put a name on the different fields of a C structure.

.type name

Beware of the dirty 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.

Definition of variables

type variable1, variable2;
where type is one of the pre-defined simple types.

You can also affect default values to variables, for instance:

type variable1 = value;

Scoping of variables

There are two levels of scoping: global, and function. The parameter list of a function and any vars declared inside a function with the "local" statement are only visible within that function,

Definitions of functions

The general structure of a function definition is:

type (type param1, typeparam2, ... ) function =
{
   ... code ...
};
Don't forget the ; after the brackets.

Here are some examples:

void()		think = {...};
entity()	FindTarget = {...};
void(vector destination, float speed, void() callback)	SUB_CalcMove = {...};

Function declaration

If you want to use a function before defining it, you must declare it, otherwise the Quake-C compiler will not be able to use it.

The general structure of a function declaration is:

type (type param1, typeparam2, ... ) function;

Controling the flow of execution

Conditional construct

if( expression )
{
  statements
}
else
{
  statements
}

Loop construct

while( expression )
{
  statements
}
or
do
{ 
  statements
}while( expression )

Operations

Function calls

Call a function:

function_name ( parameter1, parameter2,... )
The cannot be more than 8 parameters.

Return a value:

return( expression )

Logical operations

 !   (logical not)
 &&  (logical and)
 ||  (logical or)
Beware that in if() conditional expressions, the complete expression is always evaluated (like in Basic, and countrary to the C language).

Comparisons

 <=    <      >=     >  
 ==  (equal)
 !=  (not equal)  

Operations on floats or integer

 *  /  -  +
Use parenthesis to remove ambiguities.

Those operators perform bitwise operations on integers:

&   (bitwise and)
|   (bitwise or)
They are generally meant to be used with bit masks.


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. Seperate 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

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. Quake-C Manual

Type: entity

The reference of an entity in the game, like things, players, monsters.
The entity type is a structured type: a detailed description of each field is available.

There are two default entity references available to functions, as global variables:

  • self, that represent the entity who is subject to the function.
  • other, that represent another entity that caused the function.

Definition of models

(Derived from information published by Steve Tietze)

Here are a few definitions that are commonly found in the Quake-C code defining the behavior of animated models (monsters, players, etc...).

Most of this information is not interpreted by the Quake-C compiler, but it's useful for the program modelgen that generates the models.

Model name

$modelname name
name is the name of the model file defining the object.
ex: $name armor

directory

$cd dir
Specify the directory where your model file (.MDL) is located.
ex: $cd /evil/models/armor

Special animation flags

$flags  rotation
This field is not interpreted by Quake-C, but it's useful for the program modelgen that generates the models.
Rotation of the object.
ex: $flags 8
Possible values for the flags:
  • 8: the object keeps rotating, like armors.

Origin

$origin x y z
This field is not interpreted by Quake-C, but it's useful for the program modelgen that generates the models.
Location of the object within the bounding box, in the quake editor.
ex: $origin 0 0 8

Scale factor

$scale number
This field is not interpreted by Quake-C, but it's useful for the program modelgen that generates the models.
number comes from the texmake number that is generated.
You can use different values if you want.
ex: $scale 4

Base

$base  object
This field is not interpreted by Quake-C, but it's useful for the program modelgen that generates the models.
object is the name of a model file, that will be used as a kind of starting position, for animation.

Skin file

$skin  skinfile
This field is not interpreted by Quake-C, but it's useful for the program modelgen that generates the models.
skinfile is the name (without extension) of the .lbm file that defines the skin of the object, as generated by the program texmake.

Frame definitions

$frame  frame1 frame2 ...
This defines several animation frames of the object.
For every animation frame defined, you must also define a Quake-C function, that will be called during this animation frame. For instance:
$frame walk1 walk2 walk3 walk4
void() man_walk1 = [ $walk1, man_walk2 ] { ... some code ... };
void() man_walk2 = [ $walk2, man_walk3 ] { ... some code ... };
void() man_walk3 = [ $walk3, man_walk4 ] { ... some code ... };
void() man_walk4 = [ $walk4, man_walk1 ] { ... some code ... };

In the brackets, the first parameter defines the name of the frame (as found in the model file), and the second parameter defined the function that is to be executed in the next frame (by setting the value of self.nextthink).

Most of these functions do nothing special, but some can be very complex (for instance, the functions that are called when the monster tries to see a player).


Compilation of Quake-C

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.
The language is strongly typed and there are no casts.
Anything that is initialized is assumed to be constant, and will have immediates folded into it. If you change the value, your program will malfunction. All uninitialized globals will be saved to savegame files.
Functions cannot have more than eight parameters.
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.
void()	MyFunction;			// the prototype

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

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.

afunc ( 4, bfunc(1,2,3));
will fail because there is a shared parameter marshaling area, which will cause the 1 from bfunc to overwrite the 4 allready placed in parm0. When a function is called, it copies the parms from the globals into it's privately scoped variables, so there is no collision when calling another function.
total = factorial(3) + factorial(4);
Will fail because the return value from functions is held in a single global area. If this really gets on your nerves, tell me and I can work around it at a slight performance and space penalty by allocating a new register for the function call and copying it out.