THE QUAKECUSTOM SYSTEM Version 1.0 Copyright: QuakeCustom is written by Jeff Epler and derived from the QuakeC code written by Id Software. You are granted the right to create QuakeCustom modifications for personal or server use. If you distribute QuakeCustom modifications, you must offer a free method (Such as FTP or HTTP) to access the modifications, and you must at least offer the source of your QuakeCustom modifications. You may not distribute binary-only "progs.dat" created using QuakeCustom. If you wish to package QuakeCustom on a CD-ROM or other for-profit compilation method, you must first recieve my written permission. Table of contents: 1. Introduction 2. What you need 3. How to use it 4. A short introduction to the console 5. Writing a QuakeCustom modification (May contain technical QuakeC details) 5.1. QuakeCustom hooks 5.2. QuakeCustom directives 5.2.1. exclude 5.2.2. require 5.2.3. file 5.2.4. precache 5.2.5. impulse and impulseN (Where N is a series of digits) 5.2.6. object 5.2.7. flag 5.3. Example code 5.4. Future 6. Available QuakeCustom modifications 6.1. The included modifications 6.2. QuakeCustom modifications available elsewhere 7. About me 1. Introduction The QuakeCustom system is intended to make the addition of new items, player capabilities, and weapons easy. QuakeCustom is a combination of a modified QuakeC source code base and a script in the Python language. To add a new weapon, item, or morph, you just place the .qc file in the QuakeCustom directory and run the qcustom script. 2. What you need (I invite feedback on this section, since I use quake exclusively under the Linux environment, and have never tried these DOS versions of the tools I use in QuakeCustom) o Registered quake, which can be ordered from 1-800-ID-GAMES, or bought in software stores. http://www.idsoftware.com/ Install this by following the instructions. o QuakeCustom http://incolor.inetnebr.com/jepler/quake/ Install all the files from qcustom?.zip in the directory where you installed quake. Use -d with pkzip, or otherwise cause your unzip utility to create directories. o The QuakeC compiler (qcc). ftp://ftp.idgames.com/idstuff/source/qutils.zip (Source and win32 binaries) ftp://ftp.cdrom.com/pub/idgames2/utils/quakec (Other qcc versions, may be a DOS one there) Place this in a directory in your path, or in the QuakeCustom directory. o The Python interpreter. ftp://ftp.python.org/pub/python/pythonwin/index.html (Windows (95?) only) ftp://ftp.python.org/pub/python/wpy/ (Supposed to include DOS support, but directory permission is currently incorrect so I can't check) ftp://ftp.python.org/pub/python/src/python1.4.tar.gz (Source, compilable on most Unix; I don't know about using the python source with any DOS or Windows compiler) Install this by following the instructions. o Optional, other files created to work with QuakeCustom. None exist yet, but I hope that other QuakeC modification authors will make use of my work and make it easier to make "Compilations". I will also be seeking the permission of the authors of my favorite quakec work to release QuakeCustom versions of their changes. Unzip these files according to their directions. Often, this will require the '-d' option to pkunzip, so that directories are created. 3. How to use it This is the step that QuakeCustom makes much easier than the other schemes I've worked with. Drop some QuakeCustom-compatible .qc files in a subdirectory, and execute the qcustom script: C:\GAMES\QUAKE\QCUSTOM> python qcustom.py -r or use the batch file included, which will clean up junk files most versions of qcc leave around: C:\GAMES\QUAKE\QCUSTOM> runme (In other words, run the qcustom script with the python interpreter, in the qcustom directory. The script takes arguments: python qcustom.py [-r] [directories ...] -r specifies that .qc files should be sought recursively, and you can also specifically list directories that are to be searched for added .qc files. If no directories are specified, the current directory is used) You will see messages such as: m-demon.qc needs morph.qc. w-fishgu.qc needs m-fish.qc. [...] exclude w-spike.qc exclude w-templa.qc * Output from qcc starts here outputfile: progs.dat compiling defs.qc [...] compiling w-lshiel.qc compiling w-soulsw.qc writing progdefs.h 108848 strofs 25471 numstatements 2532 numfunctions 4946 numglobaldefs 239 numfielddefs 14204 numpr_globals 502124 TOTAL SIZE Now, you have a progs.dat which contains all the modifications you placed in the directory. If you see an error before the blank line, this means there was an error in the qcustom.py script, and you should first check for a newer version of QuakeCustom and then write me to report the bug. If you see an error below the blank line, it probably means that the author of the modification should be sent a bug report. If you see the message Run your QuakeC compiler now... then QuakeCustom wasn't able to run your QuakeC compiler by itself. You'll have to run it manually. This may indicate that the qcc executable isn't installed correctly. (Make sure it's called 'qcc.exe', not 'qccdos.exe', or 'advqcc.exe' or the like) Read the *.txt files created by the qcustom script for a description of impulses (impulses.txt), needed external files (needed.txt), and new objects (objects.txt). Especially make a note of the things in impulses.txt, as you'll need to type these impulses at the console or bind keys to them. Now, change directories to the main quake directory and run quake with a special argument: C:\GAMES\QUAKE\QCUSTOM> cd .. C:\GAMES\QUAKE\> quake -game qcustom (q95 for Windows 95 users. Make sure -game is lowercase) Start a game. You should now enjoy all the neat features of the QuakeCustom modifications you've installed. 4. A short introduction to the console Suppose that 15 is the impulse to morph into a Human, 12 is the impulse to become a Fiend, and 21 is the impulse to ready the Fish Gun. (This is the case with the default QuakeCustom files) Let's first turn into a fiend. Follow these steps: Type ~ (The 'console' pops down) Type 'impulse 12' Type ~ (the 'console' pops back up) You should see a teleportation-like flash, and the message 'Player has become a fiend.' Now attacking will claw, and jumping will jump forward damaging anything you run into. Now, let's bind some keys. H will turn us Human, F will make us a Fiend, and 9 will get out the Fish Gun: Type ~ (The 'console' pops down) Type 'bind f "impulse 12"' Type 'bind h "impulse 15"' Type 'bind 9 "impulse 21"' Type return, then ~ (the 'console' pops back up) Type h (you turn back to a human) If you exit Quake by choosing the 'quit' option, and not by rudely closing the window or turning off the computer, all the keys that you bind should be available the next time you run QuakeCustom. 5. Writing a QuakeCustom modification (May contain technical QuakeC details) You can two basic kinds of things in a QuakeCustom modification, Objects (items or monsters) or Impulses (weapons, special abilities, or anything else). In either case, you may need to precache more files than Quake does manually, so QuakeCustom permits you to add Precaches as well. 5.1. QuakeCustom hooks In QuakeCustom, there have been hooks (function pointers) added at many places in player.qc and client.qc, making it possible to modify most aspects of the player's behavior. Here is a list of the hooks, and what they do: .void() _stand; Called when the player is standing still. Responsible for performing animation frames, or any other activity. (For instance, regeneration when the player is a Zombie) .void(entity attacker, float take) _pain; Called when the player is injured. .void() _run; Called when the player is moving. Responsible for performing animation frames, or any other activity. .void() _impulse; Called when the player sends an impulse in the range 1..8, or greater than the highest defined impulse but less than 255. (Usually used to select weapons for a morph) .void() _attack; Called when the player presses attack, and time > self.attack_finished. Different from extra_fire(), _attack is checked before extra_fire. .void() _jump; Called before regular checks for 'able to jump' are made. Used for Scrag's flying. .void() _jump2; Called after regular checks for 'able to jump' are made. .void() _watermove; Calleed every frame, replaces drowning checks. .float(entity what, entity you) _can_get_p; Returns true if 'you' can pick up and use 'what' .float health_modifier; How much greater or less than the regular human this player's health can be. Among other things, this makes max health 100*self.health_modifier .float resist; .float immune; .float vulnerable; Tells if the player (or monster) resists (takes 1/2 damage from), is immune to (takes no damage from) or is vulnerable to (takes double damage from) a given sort of attack. See the ATTACK_* constants in defs.qc. .string(entity targ, entity attacker) _killmsg; .string(entity targ, entity attacker) _killmsg2; Death messages are of the form (dead player name)+_killmsg()+(killing player name)+_killmsg2() Modify them for morphs, use extra_killmsg{,2} for weapons .void() extra_fire; Called when a player has a special weapon armed. This is checked for after _attack, so that morphs cannot generally use special weapons. .string() extra_killmsg; .string() extra_killmsg2; Like _killmsg, _killmsg2 except for extra weapons. .void() extra_set_current_ammo; Called to set the ammo display for special weapons. In making a new morph, you generally need to deal with these hooks: _stand, _pain, _run, _impulse, _attack, _jump, _jump2, _watermove, _can_get_p, health_modifier, resist, immune, vulnerable, _killmsg, and _killmsg2. Setting any hook to SUB_Null means that the default human player action will be taken. In making a new weapon, you generally need to deal with these hooks: extra_killmsg, extra_killmsg2, extra_fire, extra_set_current_ammo You can reset all these to default by calling the functions reset_player_morph, reset_player_weapon or reset_player_all with the player entity you want to turn back to normal. You should use these functions to keep your code compatible, since future versions of QuakeCustom might add new hooks, and your code will be written assuming default player behavior. 5.2. Quakec Directives QuakeCustom uses specially formatted comments, called Directives, to create its list of objects, impulses, precaches, excluded and required files. The general format is: // keyword name comment ... The keywords can be: require file precache impulse impulseN (Where N is a series of digits) object exclude 5.2.1. exclude To exclude a file from consideration by QuakeCustom, make the first line read exactly "// exclude". No impulses, objects, or precaches will be read from this file. It will not be included in progs.src. If a file requires an excluded file, an error will be printed. 5.2.2. require Tell QuakeCustom that the current file requires that another .qc file be present and compiled before it. An example: // (quux.qc) // require foo // require bar.qc foo.qc and bar.qc will be required for compiling quux.qc file, and will be placed before it in the progs.src. Make sure that you do not have this situation: // (foo.qc) // require bar // (bar.qc) // require foo This will place QuakeCustom in an infinite loop, since it tries to place foo.qc before bar.qc and bar.qc in front of foo.qc. 5.2.3. file Tell QuakeCustom that an external file (.mdl, .wav, etc) is required by this file. As in QuakeC, use / for directories, not \. Example: // (m-dole.qc) // file progs/dole.mdl // file sound/dole/attack.wav Here, the Dole morph requires a model file and a sound file. QuakeCustom will issue an error if these files do not exist. 5.2.4. precache Tell QuakeCustom that a precache function needs to be called each time the server starts a level. // (m-dole.qc) // precache player_dole_precache void() player_dole_precache = { precache_model("progs/dole.mdl") precache_sound("dole/attack.wav") }; 5.2.5. impulse and impulseN (Where N is a series of digits) Tell QuakeCustom that an impulse function needs to be added. QuakeCustom will (currently) choose the impulse number for itself. // (m-dole.qc) // impulse player_dole_become Turn into Bob Dole // impulse3 choose_a_party Choose your political party void() player_dole_become = { // Do stuff to accomplish the morph here }; void(float party) choose_a_party = { bprint(self.netname); if(party==0) bprint("is a libertarian.\n"); else if(party==1) bprint("is a republican.\n"); else (party==2) bprint("is a democrat.\n"); }; The difference between impulse and impulseN is that for impulseN several impulses are handled in the same function, and their numbers are guaranteed to be contiguous. 5.2.6. object This currently does nothing except write an entry in objects.txt. However, this is a good way to create a list of valid 'classname's in QuakeCustom code. // (m-dole.qc) // object campaign_contribution A campaign contribution void() campaign_contribution = { precache_model("progs/money.mdl") // Code here to spawn the object -- Model like a check? }; 5.2.7 flag Allocate a flag which is saved from level to level. You must use the special functions 'getflag' and 'setflag' to access these flags. float getflag(entity object, float flagnum); // returns 0 if flag is not set on object, !=0 if it is set // note that 'getflag(self, FL_CHEESE) == TRUE' is // not the way to test things, since getflag can return // any nonzero number when a flag is set. void setflag(entity object, float flagnum, float set); // Sets flag flagnum on object // flag FL_CHEESE // impulse ToggleCheese void() ToggleCheese = { if (getflag(self, FL_CHEESE)) setflag(self, FL_CHEESE, 0); else setflag(self, FL_CHEESE, 1); } Do not define FL_CHEESE yourself, or use any functions besides getflag and setflag to access it. Qcustom will take care of saving these flags for you. All flags start with a value of 0. getflag and setflag can be used on non-player entities, but in that case the settings are (obviously) not saved from level to level. However, it is suggested that you use getflag and setflag only for player data that needs to remain from level to level. 5.3. Example code There is some sample code in m-templa.qc (A template for new morphs) and w-templa.qc. (A template for new weapons) 5.4. Future I hope to add some of the following to QuakeCustom in the future (the list below is in no particular order): o Allow the 'impulse' directive to ask for particular impulse numbers o Allow QuakeCustom to assemble a .pak file containing progs.dat and those files listed in the 'file' directive. o Add hooks in existing objects and monsters to change behavior o Add whatever hooks are necessary for bots o Add a new directive to allow random replacement of existing objects with new ones, perhaps: // replace monster_dog monster_cat 0.3 to replace monster_dog with monster_cat 30% of the time. o Add the ability to request non-flag information saved in player. Perhaps: // info ammo_cheese 0 100 1 to specify that the value of ammo_cheese should be saved, and that its values go from 0 to 100 with meaningful intervals of 1. Then qcustom has to figure out the best way to pack these into all the bits available for saving between levels... Unfortunately, this would quickly decrease the amount of 'flag's available. o Versions of my favorite quakec mods for QuakeCustom o Make currently built-in parts of quakec code removable (especially monsters) 6. Available QuakeCustom modifications 6.1. The included modifications Included are the ChaseCam, several new weapons and several morphs. convert/chasecam.qc // ChaseCam 3.3 by Rob Albin morphs/m-demon.qc // Morph into the Fiend morphs/m-fish.qc // Morph into the Rotfish morphs/m-hknigh.qc // Morph into the Hell Knight morphs/m-human.qc // Morph into the Human morphs/m-ogre.qc // Morph into the Ogre morphs/m-shalra.qc // Morph into the Vore morphs/m-shambl.qc // Morph into the Shambler morphs/m-wizard.qc // Morph into the Scrag morphs/m-zombie.qc // Morph into the Zombie weapons/w-fishgu.qc // Turns a player into a fish weapons/w-flame.qc // An ugly 'flame' weapon weapons/w-lbolt.qc // A lightning-bolt weapon, like Hexen weapons/w-lshiel.qc // An ugly 'lightning shield' weapons/w-soulsw.qc // Deathmatch-only, exchange souls with // your opponent (and weapons too) weapons/w-shotgu.qc // Sample, reimplements the shotguns weapons/w-spike.qc // Sample, reimplements the nailguns 6.2. QuakeCustom modifications available elsewhere There are none at this time. Mail me and I'll add things to this list. 7. About me I'm the author of several QuakeC mods. Currently, the best known seems to be my 'Morph' mod. [QuakeCustom includes all the goodies of Morph, plus more] I play and program for quake under Linux on a Pentium 133 with 32 megs memory and about 1 gig of disk. My modem is only 14.4, so I don't play deathmatch much. I am a second year computer science major at the University of Nebraska in Lincoln (USA). You can mail me with any questions about QuakeCustom you have, but I encourage you to seek out FAQs related to Quake and QuakeC first if you think your problem might not be QuakeCustom's fault. I especially seek other mods that are written to work with QuakeCustom, or examples of code that cannot currently be converted to QuakeCustom because of a capability I have not included. Unfortunately, I am unlikely to be able to answer questions where there is a DOS or Windows problem, since I do not use those operating systems. Please _do_ mail me success stories, especially windows and dos folks. I want to make sure that my instructions are usable on those operating systems even though I haven't been able to test them out myself. Jeff Epler jepler@inetnebr.com November 30, 1996