THE QUAKECUSTOM SYSTEM
Version 1.0

Copyright:

QuakeCustom is written by Jeff Epler <jepler@inetnebr.com> 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' <return>
	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"' <return>
	Type 'bind h "impulse 15"' <return>
	Type 'bind 9 "impulse 21"' <return>
	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