QuakeMap Explorer

Using QuakeC


Informations about the QuakeC compiler found in QuakeMap

This document introduces the QuakeC language and highlights the differences between usual QuakeC programming and QuakeMap-oriented programming.


Index

  1. Introduction
  2. Basics
  3. Enhancements in the QuakeC syntax
  4. Abstract from EXPLORER.TXT
  5. Using another base Progs.dat
  6. Incompatibilities
  7. More about it

Introduction

The QuakeC language is a simplified C language for use with Quake. A large part of the behaviours found in Quake - doors, monsters, and much more - has been made in this QuakeC language, and this means it can be modified. id Software released the QuakeC source code (last version : 1.06), as well as the compiler they used. When compiled, QuakeC is a pseudo-code that can be interpreted by Quake. The compiled code is always put in a file named Progs.dat.

The traditional way of making changes in this code is to change parts of the source files and recompile the whole thing. This approach is neither practical - need to redistribute whole, modified files - nor modular - cannot easily mix several, unrelated code changes, as soon as two of them require changes in the same source file.

QuakeMap presents a solution to this problem : its integrated patch compiler. It is a full-featured QuakeC compiler, with the difference that it can't create Progs.dat files by itself. It works by patching - i.e. modifying - an existing Progs.dat file. This approach has the following advantages : you can apply several patches consecutively to any given Progs.dat file; and all the source files for the original Progs.dat are not required. This means you can directly take any Progs.dat and add any number of QuakeMap Add-ons to it. As source Progs.dat, you can take either id Software's original one, which is QuakeMap's default behaviour, or any other one found on the Internet - even if source code is not available !


Basics

First, you should download id Software's QuakeC source code. In theory, you don't need it, but when you are trying to enhance or change some behaviour, the first thing to do is to figure out where you must do these changes.

The difference between standard QuakeC programming and QuakeMap programming is that, in the first case, you can simply change parts of id Software's code, and in the second one you must write a new function to patch the old one, the one that holds the code you want to change. In QuakeMap, you can write a function exactly as you normally would, replacing any existing function just by writing a new version of it. However, the object of modulary is not to rewrite the entire function. What you will often be doing is writing patch functions. In this case, you give your function the same name as the patched function, but, to reuse the original's function behaviour, you don't need to rewrite it - you call it with the command : inherited();

The following example should make it clear. When a player is killed, the function ClientObituary is called, which displays the appropriate message based on who killed the player (e.g. ...was destroyed by an ogre). To change the message, or add new conditions and corresponding new messages (e.g. when creating a new monster type), you write a new ClientObituary function, testing the new conditions and reacting to them if they occur, and if they don't, you just call the inherited function, i.e. the old ClientObituary, which will handle all the other cases itself. This approach lets any number of patches enhance the same ClientObituary function : each one checks for particular conditions, and passes the message to the next one if it doesn't know how to handle this message itself. Practical example :

void(entity targ, attacker) ClientObituary =
{
  if (targ.classname == "player" && attacker.classname == "monster_army")
  {   // handle this case
     bprint (targ.netname);   // display the player's name
     bprint (" has stupidly been shot by a dummy grunt\n");
  }
  else   // all other cases
     inherted ();   // pass the message to the old ClientObituary
};

Too bad it is not always so easy to figure out how to patch a function to change or enhance its behaviour. Look for another example in file ROCKETS.QME that comes with QuakeMap.


Enhancements in the QuakeC syntax

QuakeMap gives you a way to bind keys with actions. As usual, this is done with Impulses. The difference is that Impulse numbers, which are limited to the range 0 to 255, are assigned dynamically by QuakeMap. You can write, in your code :

  bind "f", "Fire my special grenade", FireSpecialGrenade;

This allocates an unused Impulse number and assigns it to the call of your function FireSpecialGrenade. It also directs QuakeMap to prompt the user to choose the key he wants, when playing with this patch. "f" is the default key, which can be changed. Then, QuakeMap writes a Quake configuration file that binds the choosen key to the Impulse number. The result is that whenever the player press on the key he choosed, your function is called.

There is a variation : if the assigned function (here FireSpecialGrenade) has been defined as void(float KeyDown), it will be called both when the key is pressed and when it is released. The parameter KeyDown can be used to determine whether the key was pressed or released.

There is another special QuakeC command : autoexec. Any text you specify after this command is added to the configuration file that gets executed at Quake start-up. For example :

  autoexec "alias abc def";
  autoexec "alias abc \"+attack;wait;-attack\"";
  autoexec "alias abc \"", FireSpecialGrenade, ";wait;echo Fired grenade\"";

Note that \" means a quote, like \n means an end of line. Also note that you can put function names after autoexec : the function name is translated into the corresponding impulse xxx command, meaning that the function will be called at this point.


Abstract from EXPLORER.TXT

  - The integrated QC compiler is a patch compiler - that is, it takes the
    original Progs.dat file, modifies it, and writes a new one. You don't
    have to recompile all the .qc files released by id Software. Anyway,
    you couldn't, because this compiler does not support some operations
    that are essential to produce a Progs.dat but that make no sense for
    patches, like modifying the system variables.

  - Unlike what you do usually with the .qc files, you should not distribute
    a whole, modified .qc file as a patch. You should only write a modified
    version of one or a few functions - only the ones that needs changes,
    and not a whole .qc file !

  - To let the user play with multiple patches from multiple sources, you
    should, whenever possible, modify functions by only adding something
    at its begin or at its end. This is done trough the special "inherited"
    variable, which contains the address of the original function when you
    are overwriting one. See Rockets.qme for examples of this.

Using another base Progs.dat

As explained above, you can use with QuakeMap any compiled Progs.dat file, and add other code patches to it. You can't, however, mix several already compiled Progs.dat, so keep in mind that you have better use the source code, if available, as often as possible.

To use an existing Progs.dat, choose New, Import Wizard ; click on the button near the text Import an already compiled Progs.dat file, and select the file. Then, because QuakeMap will automatically assign Impulse numbers to the other code patches (if any), you must tell him which Impulse codes are already used in this Progs.dat. In case of doubt, try entering some unusual number like 160. At this stage, the Progs.dat is imported in QuakeMap. You don't need the original file any more.

If you want to add other QuakeMap Add-ons and mix them with the code you just imported, create file links to them (New, Make File links) and press the big GO! button to test your work. If, on the contrary, you are creating a QuakeMap Add-on like a new monster, you must import the other files related to this code, e.g. Models or sounds, and save the whole as a stand-alone Add-on. This operation is described in the document "New Monsters".

You can have troubles adding other code patches to some Progs.dat found on the Internet. If they made too many changes, QuakeMap won't be able to compile code patches relying on variables or functions no longer present. For example, TeamFortress releases Progs.dat with no constants in them, so any other code patch using standard constants - that is, almost any code patch - won't compile. Indeed, the constants are usually stored in the Progs.dat itself, so QuakeMap can usually rely on it to extract that kind of information. In the above case, the constants become Unkown identifiers. The solution is to rewrite the constants in a code patch and put it before the other ones. You can download ConstsQm.zip with this code patch and make a file link to it. Don't forget to put it first.


Incompatibilities

I hope my QuakeC compiler is mostly compatible with QCC, the standard one. However, here are the few changes you should be aware of :


More about it


Back to the main page
Date: 27.01.97, 2.02.97, by Armin Rigo