10. Quake-C Tips and Tricks

10.1 The quirks of Quake and Quake-C

Here are some characteristics of Quake-C that you had better be aware of.

The names of variable and functions must be unique

The names of functions, variables and fields must be unique. For instance, you cannot define a variable with the same name as a field. However, local variables can be defined more than once (they had better!).

Composition of function is not supported

Since all the functions use a single parameter marshaling area, and a single global variable to store their reture result, you should NEVER try to call a function within another function call.

Example: printing the coordinate of entity self

    sprintf(self, vtos( self.origin ));
will fail miserably (sending the message somewhere in hell), so it should be replaced by:
    text = vtos( self.origin );
    sprintf(self, text);

Unfortunately, this also applies to operators:

   sum = anglestovec( 45) + anglestovec( 90);
will fail an should be replaced by:
   sum = anglestovec( 45);
   sum = sum + anglestovec( 90);

Actually, Quake-C is rather lame as a compiler, and you will probably make hundred of little mistakes like that, that the compiler will not warn you of. But remember Quake-C was built for "performance", not ease of use. And also that it wasn't designed by people from the MIT. Remember also that you got it for free... you can always get gcc (the Gnu C Compiler) for the same price ;-)

You cannot initialise variable with default values.

If you give a default value to a quake-C variable, this variable will be considered as a constant. And since the value of constants is not supposed to change, your program may not work properly after that.

Coordinates are relative to the world.

All the geometry (coordinate positions, directions, angles) are relative to the world. They are never relative to a given object. To know the direction an object is facing, you have to require calculation of the v_front vector (respectively v_right and v_up for the right, and the top).


10.2 Frequently Asked Questions about Quake-C

How do I change the viewpoint?

You would like that a given player sees through the eyes of another entity. This commonly happens at the end of the level (all players see through a camera), or when the player head is severed (gibbed), or when a player is invisible (he only exists as his eyes).

But the example above work by changing the player entity, and what you want is probably just to see through a camera (Duke3D) or a missile (Descent).

This operation is known in the Quake network protocol as a setview message. But nowhere it's defined in Quake-C, and there's no function to change the view port. So the solution is to encode a set view port message, followed by a set view angles message (to take the orientation of the camera).

This works fine, but if the entity you use as a camera was not declared to the client, then the view port will be set to '0 0 0', which is usually omewhere in the void. Hence the need to send an explicit update message.

Also, the view might not be updated correctly, if the camera is not in the same area as the player (same potential visibility set, to be exact). That's because the Quake server only sends updates to entities that are visible to the player.

How do I teleport a player into another server

A trick by Steven Lang (tiger@ecis.com)

    // In the slipgate touch function
    // other = entity that touched
    if(other.classname == "player")
      stuffcmd(other, "connect server.address\n");  // send command
When the slipgate is touched, the entity jumps to another server.

Trouble: the player stats and weapons won't be preserved, and the player would be dumped to the console if the other server was full or not available.
That's why John Carmack, will rewrite the code of Quake.exe to implement his Quake World proposal, and advanced server with all kinds of goodies... permission lists, ability to block an IP, etc. (info from quake-c list).

How do I manipulate strings in Quake-C?

Well, you can have any kind of strings, so long as they can't be changed. "In Ford we trust" (Brave New World).

The Quake compiler only defines operations =, ==, != on strings. No concatenation is possible.

How to read string variables, or text messages?

Well, if you know, tell, that would make a nice addition to this specs.

How do I move an entity in Quake-C?

You have better not touch it's position, else some stuff in the C code might not be valid anymore. So you call the setposition() built-in function.

How to change the velocity of an entity (make it bounce off walls)?

Information by Greg Lewis.

It seems that an entity's velocity can't be changed in the Touch function of the entity. Making the calculations there will be of no use.
So just set entity.movetype to MOVETYPE_BOUNCE, entity.nextthink to 0.1 (to let it bounce off), and set entity.think to the name of a function that, when called 0.1 second later, will set entity.velocity to the right direction.

How to calculate the direction a player is facing?

Assuming the player is , the entity field self.angles contains the orientation angles of the player (as set by moving the mouse).

Then the function makeverctors( self.angles) will calculate three vectors, that point in the direction the player is facing, but also to the right of the player (strafing direction) and to the direction the player is standing.

Note that those vectors are normalised to 1, so if you want to know what lays 100 units in front of the player, use self.origin + 100 * facing.

How to send a message to a player when he logs in?

It has been noticed that using a sprint() in function ClientConnect just plain doesn't send any message at all. Maybe the client is not ready to receive messages at this point.

However, Doug Keenan (doug.keegan@tamu.edu) has reported he could send such a text message by putting the sprint() close to the begining of the ClientConnect function. It doesn't work at the end, apparently.

How to...?

Another unanswered question? Just ask on the quake-c mailing list.


10.3 Writing clean Quake-C code

Here are some suggestions that you should really consider when writing Quake-C code. Well, there are no obligations, but that would make life simpler for others when they read your code (and thus for you when you read theirs).

I assume here that you want to develop code that others can re-use, or that can be mixed seamlessly with codes written by others.

(If you are reinventing the whole world all by yourself, you hardly need any help or counsels. By the way, the first command is +light).
  1. Please put comments in your code.
    Of course, the real gurus don't need comments. They understand raw Quake-C, even compiled. They can even imagine all the parts of your code before they read them. Even before you write them. But actually, they seldom read your code. Only normal people do.
  2. Please tag the begining and end of your modifications, if you are fixing a code from someone else.
    Also put a date, and put a reason for the fix. Example:
           // Patch by Nezu The Unworthy  8/3/96 
           // Gimme a chance to win a deathmatch
           if(self.name != "nezu")
             self.frag = self.frag - 10;
           // Patch End
      
  3. Each time you create a new function, a new variable or a new field, please give it a name that will not conflict with function or variable defined by others.
    A rather sure way to do this is to prefix every name with some abvreviated module name, or you initials, or whatever rare combination of three or four letter. Example:
         void() NTU_think =  // Nezu The Unworthy starts thinking
         {
            self.skin = 1;   // turn red and boil
         };
         
  4. Each time you implement some set of related functions, you should create a new Quake-C module, and give it a name different from the existing ones.
    Please do not use one of the module names used by id software, this would be confusing. Try to be original, else we might end-up with two hundred modules called impulse.qc.
  5. When you want to distribute some modified Quake-C programs:
  6. You should compile and distribute a version of your code, as a single PROGS.DAT file, to be used by those who just wanna have fun. Don't forget them, they largely overnumber the people who directly deal with Quake-C.
  7. Upload your Quake-C patches to the primary quake ftp site at ftp.cdrom.com.
    Maybe if it's good enough it will also appear in the Quake-C code repository.

Using Diff and Patch

Information by Jeff Epler (jepler@cse.unl.edu)

You can find a DOS version of diff and patch on all the major ftp archives, for instance Simtelnet (mirrored at ftp://oak.oakland.edu/pub/simtelnet).

For a Win32 (windows95 and NT) version, see diffutils.zip and patch.zip.

The full documentation for diff and patch is available on www.ai.mit.edu, but here are some shortened instructions:

To make a diff:
. start with yourdir/ with an entire working set of .qc files, and v101qc/
with the virgin qc files from id.
. diff -ur --new-file v101qc yourdir > patchfil

To patch with a diff:
. copy everything in v101qc (or yourdir if you want to add these patches to
your already-customized setup) to newdir
. change to newdir
. patch -p1 < patchfil
. Look for any "rejected" patches and apply them by hand using your
favorite 2-window text editor.  These should be few or none if the author
kept his changes well-structured or you patched from exactly the same
source he diffed from.