IndexPart I - Purpose and system explanation Part II - Implementing EBFS into the Quake engine Part III - Rules for registering new builtin functions to the QSG list Part IV - Four examples of implementing new builtin functions with EBFS (builtin functions: cmd_find, cvar_find, cvar_string, WriteFloat) Part V - Activating some useful builtin functions of the preliminary Quake 2 code (builtin functions: sin, cos, sqrt, etos) Part I - Purpose and system explanationAll QuakeC coders want to have new builtin functions to use in their addons. The problem is to keep a standard among all the different engine ports. That is where the Quake Standards Group came in. The QSG keeps a list of all additional builtin functions, so that every engine uses the same function numbers. But how can the QuakeC coder determine during run-time if the engine supports all the needed new builtin functions? That's what the Enhanced BuiltIn Function System (short EBFS) is for. With EBFS the new builtin function "builtin_find" is added to the engine. It returns the function number of a given function name. The result is zero when the function does not exist. As the EBFS function "builtin_find" is a new builtin function too, the above system doesn't work for it. Hence there is also a new cvar called "pr_builtin_find" needed which contains the function number of "builtin_find".
Some days after I send the first version of this tutorial to the QSG for
approval, Sander "FireStorm" van Rossen With EBFS the old builtin array is no more static, it is created from a new array on the fly when a PROGS.DAT is loaded. The run-time execution of the PROGS.DAT stays the same, so there are no performance penalties because of the dynamic way of using the builtin numbers. You can download this tutorial including "How_to_use_EBFS_in_QuakeC.txt" here. Some may point out that LordHavoc already introduced a similar system to check for new engine extensions. The EBFS does not replace his system as it is still useful for various other engine extensions, but the EBFS simplifies adding new builtin functions to the engine (no more padding with PF_FixMe), allows to check the actual function numbers for QuakeC coders and provides a remapping functionality for the user. Part II - Implementing EBFS into the Quake engineAdding EBFS to your engine is very easy, even if you did lots of changes to your own engine. The new builtin function "builtin_find" and the dynamic number assignment need additional data, so we need to define a new structure for it. Go into PROGS.H and after... typedef void (*builtin_t) (void); extern builtin_t *pr_builtins; extern int pr_numbuiltins; ... add ... // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start typedef struct ebfs_builtin_s { int default_funcno; char *funcname; builtin_t function; int funcno; } ebfs_builtin_t; extern ebfs_builtin_t pr_ebfs_builtins[]; extern int pr_ebfs_numbuiltins; #define PR_DEFAULT_FUNCNO_BUILTIN_FIND 100 extern cvar_t pr_builtin_find; extern cvar_t pr_builtin_remap; #define PR_DEFAULT_FUNCNO_EXTENSION_FIND 99 // 2001-10-20 Extension System by Lord Havoc/Maddes // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end This added the default function number, its name and the assigned number to the data. The assigned number will be used to build the run-time builtin array for QuakeC execution, and it is also needed for the return value of "builtin_find". The name will be used to find a function when searched with "builtin_find" or when remapping the functions from the PROGS.DAT data. Now you have to add the EBFS data to PR_CMDS.C. But first completely disable the pr_builtin[] array at the end of PR_CMDS.C and also remove the assignment of the two builtin variables. The result looks like this...
// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start /* builtin_t pr_builtin[] = { PF_Fixme, PF_makevectors, // void(entity e) makevectors = #1; . . . PF_setspawnparms }; */ builtin_t *pr_builtins; int pr_numbuiltins; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end ... after this add the new EBFS data... // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start // for builtin function definitions see Quake Standards Group at http://www.quakesrc.org/ ebfs_builtin_t pr_ebfs_builtins[] = { { 0, NULL, PF_Fixme }, // has to be first entry as it is needed for initialization in PR_LoadProgs() { 1, "makevectors", PF_makevectors }, // void(entity e) makevectors = #1; { 2, "setorigin", PF_setorigin }, // void(entity e, vector o) setorigin = #2; { 3, "setmodel", PF_setmodel }, // void(entity e, string m) setmodel = #3; { 4, "setsize", PF_setsize }, // void(entity e, vector min, vector max) setsize = #4; // { 5, "fixme", PF_Fixme }, // void(entity e, vector min, vector max) setabssize = #5; { 6, "break", PF_break }, // void() break = #6; { 7, "random", PF_random }, // float() random = #7; { 8, "sound", PF_sound }, // void(entity e, float chan, string samp) sound = #8; { 9, "normalize", PF_normalize }, // vector(vector v) normalize = #9; { 10, "error", PF_error }, // void(string e) error = #10; { 11, "objerror", PF_objerror }, // void(string e) objerror = #11; { 12, "vlen", PF_vlen }, // float(vector v) vlen = #12; { 13, "vectoyaw", PF_vectoyaw }, // float(vector v) vectoyaw = #13; { 14, "spawn", PF_Spawn }, // entity() spawn = #14; { 15, "remove", PF_Remove }, // void(entity e) remove = #15; { 16, "traceline", PF_traceline }, // float(vector v1, vector v2, float tryents) traceline = #16; { 17, "checkclient", PF_checkclient }, // entity() clientlist = #17; { 18, "find", PF_Find }, // entity(entity start, .string fld, string match) find = #18; { 19, "precache_sound", PF_precache_sound }, // void(string s) precache_sound = #19; { 20, "precache_model", PF_precache_model }, // void(string s) precache_model = #20; { 21, "stuffcmd", PF_stuffcmd }, // void(entity client, string s)stuffcmd = #21; { 22, "findradius", PF_findradius }, // entity(vector org, float rad) findradius = #22; { 23, "bprint", PF_bprint }, // void(string s) bprint = #23; { 24, "sprint", PF_sprint }, // void(entity client, string s) sprint = #24; { 25, "dprint", PF_dprint }, // void(string s) dprint = #25; { 26, "ftos", PF_ftos }, // void(string s) ftos = #26; { 27, "vtos", PF_vtos }, // void(string s) vtos = #27; { 28, "coredump", PF_coredump }, { 29, "traceon", PF_traceon }, { 30, "traceoff", PF_traceoff }, { 31, "eprint", PF_eprint }, // void(entity e) debug print an entire entity { 32, "walkmove", PF_walkmove }, // float(float yaw, float dist) walkmove // { 33, "fixme", PF_Fixme }, // float(float yaw, float dist) walkmove { 34, "droptofloor", PF_droptofloor }, { 35, "lightstyle", PF_lightstyle }, { 36, "rint", PF_rint }, { 37, "floor", PF_floor }, { 38, "ceil", PF_ceil }, // { 39, "fixme", PF_Fixme }, { 40, "checkbottom", PF_checkbottom }, { 41, "pointcontents", PF_pointcontents }, // { 42, "fixme", PF_Fixme }, { 43, "fabs", PF_fabs }, { 44, "aim", PF_aim }, { 45, "cvar", PF_cvar }, { 46, "localcmd", PF_localcmd }, { 47, "nextent", PF_nextent }, { 48, "particle", PF_particle }, { 49, "ChangeYaw", PF_changeyaw }, // { 50, "fixme", PF_Fixme }, { 51, "vectoangles", PF_vectoangles }, { 52, "WriteByte", PF_WriteByte }, { 53, "WriteChar", PF_WriteChar }, { 54, "WriteShort", PF_WriteShort }, { 55, "WriteLong", PF_WriteLong }, { 56, "WriteCoord", PF_WriteCoord }, { 57, "WriteAngle", PF_WriteAngle }, { 58, "WriteString", PF_WriteString }, { 59, "WriteEntity", PF_WriteEntity }, #ifdef QUAKE2 { 60, "sin", PF_sin }, { 61, "cos", PF_cos }, { 62, "sqrt", PF_sqrt }, { 63, "changepitch", PF_changepitch }, { 64, "TraceToss", PF_TraceToss }, { 65, "etos", PF_etos }, { 66, "WaterMove", PF_WaterMove }, #endif { 67, "movetogoal", SV_MoveToGoal }, { 68, "precache_file", PF_precache_file }, { 69, "makestatic", PF_makestatic }, { 70, "changelevel", PF_changelevel }, // { 71, "fixme", PF_Fixme }, { 72, "cvar_set", PF_cvar_set }, { 73, "centerprint", PF_centerprint }, { 74, "ambientsound", PF_ambientsound }, { 75, "precache_model2", PF_precache_model }, { 76, "precache_sound2", PF_precache_sound }, // precache_sound2 is different only for qcc { 77, "precache_file2", PF_precache_file }, { 78, "setspawnparms", PF_setspawnparms }, // { 81, "stof", PF_stof }, // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes // 2001-11-15 DarkPlaces general builtin functions by Lord Havoc start // not implemented yet /* { 90, "tracebox", PF_tracebox }, { 91, "randomvec", PF_randomvec }, { 92, "getlight", PF_GetLight }, // not implemented yet { 93, "cvar_create", PF_cvar_create }, // 2001-09-18 New BuiltIn Function: cvar_create() by Maddes { 94, "fmin", PF_fmin }, { 95, "fmax", PF_fmax }, { 96, "fbound", PF_fbound }, { 97, "fpow", PF_fpow }, { 98, "findfloat", PF_FindFloat }, { PR_DEFAULT_FUNCNO_EXTENSION_FIND, "extension_find", PF_extension_find }, // 2001-10-20 Extension System by Lord Havoc/Maddes { 0, "registercvar", PF_cvar_create }, // 0 indicates that this entry is just for remapping (because of name change) { 0, "checkextension", PF_extension_find }, */ // 2001-11-15 DarkPlaces general builtin functions by Lord Havoc end { PR_DEFAULT_FUNCNO_BUILTIN_FIND, "builtin_find", PF_builtin_find }, // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes // not implemented yet /* { 101, "cmd_find", PF_cmd_find }, // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes { 102, "cvar_find", PF_cvar_find }, // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes { 103, "cvar_string", PF_cvar_string }, // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes { 105, "cvar_free", PF_cvar_free }, // 2001-09-18 New BuiltIn Function: cvar_free() by Maddes { 106, "NVS_InitSVCMsg", PF_NVS_InitSVCMsg }, // 2000-05-02 NVS SVC by Maddes { 107, "WriteFloat", PF_WriteFloat }, // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes { 108, "etof", PF_etof }, // 2001-09-25 New BuiltIn Function: etof() by Maddes { 109, "ftoe", PF_ftoe }, // 2001-09-25 New BuiltIn Function: ftoe() by Maddes */ // 2001-09-20 QuakeC file access by FrikaC/Maddes start // not implemented yet /* { 110, "fopen", PF_fopen }, { 111, "fclose", PF_fclose }, { 112, "fgets", PF_fgets }, { 113, "fputs", PF_fputs }, { 0, "open", PF_fopen }, // 0 indicates that this entry is just for remapping (because of name and number change) { 0, "close", PF_fclose }, { 0, "read", PF_fgets }, { 0, "write", PF_fputs }, */ // 2001-09-20 QuakeC file access by FrikaC/Maddes end // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes start // not implemented yet /* { 114, "strlen", PF_strlen }, { 115, "strcat", PF_strcat }, { 116, "substring", PF_substring }, { 117, "stov", PF_stov }, { 118, "strzone", PF_strzone }, { 119, "strunzone", PF_strunzone }, { 0, "zone", PF_strzone }, // 0 indicates that this entry is just for remapping (because of name and number change) { 0, "unzone", PF_strunzone }, */ // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes end // 2001-11-15 DarkPlaces general builtin functions by Lord Havoc start // not implemented yet /* { 400, "copyentity", PF_... }, { 401, "setcolor", PF_... }, { 402, "findchain", PF_... }, { 403, "findchainfloat", PF_... }, { 404, "effect", PF_... }, { 405, "te_blood", PF_... }, { 406, "te_bloodshower", PF_... }, { 407, "te_explosionrgb", PF_... }, { 408, "te_particlecube", PF_... }, { 409, "te_particlerain", PF_... }, { 410, "te_particlesnow", PF_... }, { 411, "te_spark", PF_... }, { 412, "te_gunshotquad", PF_... }, { 413, "te_spikequad", PF_... }, { 414, "te_superspikequad", PF_... }, { 415, "te_explosionquad", PF_... }, { 416, "te_smallflash", PF_... }, { 417, "te_customflash", PF_... }, { 418, "te_gunshot", PF_... }, { 419, "te_spike", PF_... }, { 420, "te_superspike", PF_... }, { 421, "te_explosion", PF_... }, { 422, "te_tarexplosion", PF_... }, { 423, "te_wizspike", PF_... }, { 424, "te_knightspike", PF_... }, { 425, "te_lavasplash", PF_... }, { 426, "te_teleport", PF_... }, { 427, "te_explosion2", PF_... }, { 428, "te_lightning1", PF_... }, { 429, "te_lightning2", PF_... }, { 430, "te_lightning3", PF_... }, { 431, "te_beam", PF_... }, { 432, "vectorvectors", PF_... }, */ // 2001-11-15 DarkPlaces general builtin functions by Lord Havoc end }; int pr_ebfs_numbuiltins = sizeof(pr_ebfs_builtins)/sizeof(pr_ebfs_builtins[0]); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end I already added all registered functions (outcommented) including FrikaC's file access QSG tutorial, Dark Places 1.05 functions and more. The function "builtin_find", which we will implement later, is already active in the list. You also see that you can have different names for the same builtin function for remapping reasons. Note that the right name must have a default function number and all extra names must use the default function number zero (0).
As we disabled/removed the old pr_builtin array, we have to rebuild it
dynamically when a PROGS.DAT is loaded. This will be done by allocating enough
memory for it and putting the function pointer of all assigned functions into
the slot of their actual function number. All this has to be done in PR_EDICT.C where the PROGS.DAT is loaded, so we add the new cvars at the top of it first... // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start cvar_t pr_builtin_find = {"pr_builtin_find", "0", false, false}; cvar_t pr_builtin_remap = {"pr_builtin_remap", "0", false, false}; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end ... and add some more variables to the top of the PR_LoadProgs function... // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start int j; int funcno; char *funcname; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end ... search for the following code in PR_LoadProgs ... for (i=0 ; i ... and replace it with this code ... // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start // initialize function numbers for PROGS.DAT pr_numbuiltins = 0; pr_builtins = NULL; if (pr_builtin_remap.value) { // remove all previous assigned function numbers for ( j=1 ; j < pr_ebfs_numbuiltins; j++) { pr_ebfs_builtins[j].funcno = 0; } } else { // use default function numbers for ( j=1 ; j < pr_ebfs_numbuiltins; j++) { pr_ebfs_builtins[j].funcno = pr_ebfs_builtins[j].default_funcno; // determine highest builtin number (when NOT remapped) if (pr_ebfs_builtins[j].funcno > pr_numbuiltins) { pr_numbuiltins = pr_ebfs_builtins[j].funcno; } } } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end for (i=0 ; i ... in PR_Init add the following lines ... // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start Cvar_RegisterVariable (&pr_builtin_find); Cvar_RegisterVariable (&pr_builtin_remap); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end You also have to implement the new builtin function "builtin_find". Back in PR_CMDS.C add the following code anywhere before the builtin variables... // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start /* ================= PF_builtin_find float builtin_find (string) ================= */ void PF_builtin_find (void) { int j; float funcno; char *funcname; funcno = 0; funcname = G_STRING(OFS_PARM0); // search function name for ( j=1 ; j < pr_ebfs_numbuiltins ; j++) { if ((pr_ebfs_builtins[j].funcname) && (!(Q_strcasecmp(funcname,pr_ebfs_builtins[j].funcname)))) { break; // found } } if (j < pr_ebfs_numbuiltins) { funcno = pr_ebfs_builtins[j].funcno; } G_FLOAT(OFS_RETURN) = funcno; } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end The function searches for the given function name, and if it finds the function it returns the corresponding function number.
As some function numbers and names have changed when cleaning up the known
functions for the QSG registry, it is useful to point out the remapping
functionality to the user, when a builtin function wasn't found during execution
time. // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start char *funcname; char *remaphint; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end ... and change ... if (newf->first_statement < 0) { // negative statements are built in functions i = -newf->first_statement; if (i >= pr_numbuiltins) PR_RunError ("Bad builtin call number"); pr_builtins[i] (); break; } ... into ... if (newf->first_statement < 0) { // negative statements are built in functions i = -newf->first_statement; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start if ( (i >= pr_numbuiltins) || (pr_builtins[i] == pr_ebfs_builtins[0].function) ) { funcname = pr_strings + newf->s_name; if (pr_builtin_remap.value) { remaphint = NULL; } else { remaphint = "Try \"builtin remapping\" by setting PR_BUILTIN_REMAP to 1\n"; } PR_RunError ("Bad builtin call number %i for %s\nPlease contact the PROGS.DAT author\n Use BUILTINLIST to see all assigned builtin functions\n%s", i, funcname, remaphint); } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end pr_builtins[i] (); break; }
A nice addition is the new command "builtinlist", which lists all builtin functions
with their numbers and names. // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start /* ============= PR_BuiltInList_f For debugging, prints all builtin functions with assigned and default number ============= */ void PR_BuiltInList_f (void) { int i; char *partial; int len; int count; if (Cmd_Argc() > 1) { partial = Cmd_Argv (1); len = strlen(partial); } else { partial = NULL; len = 0; } count=0; for (i=1; i < pr_ebfs_numbuiltins; i++) { if (partial && Q_strncasecmp (partial, pr_ebfs_builtins[i].funcname, len)) { continue; } count++; Con_Printf ("%i(%i): %s\n", pr_ebfs_builtins[i].funcno, pr_ebfs_builtins[i].default_funcno, pr_ebfs_builtins[i].funcname); } Con_Printf ("------------\n"); if (partial) { Con_Printf ("%i beginning with \"%s\" out of ", count, partial); } Con_Printf ("%i builtin functions\n", i); } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end Then add this function as a command in PR_Init() with the following line... Cmd_AddCommand ("builtinlist", PR_BuiltInList_f); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes Now recompile the engine. Last but not least, do not forget to document the EBFS in your readme and add the file "How_to_use_EBFS_in_QuakeC.txt" to your engine documentation. This way QuakeC coders will know how to use EBFS with your engine. Download the textfile here. Part III - Rules for registering new builtin functions to the QSG listIf you want to implement a new builtin function it is highly recommended to check out the existing builtin functions on the QSG homepage before. If the functionality you are going to create is not listed, then start coding and give it a dummy function number of 9999 (and counting downwards for others). If you are done and have tested thoroughly, create a short tutorial for this function (similar to part IV) and register your function on the QSG homepage. Please include your name, email address (for questions), a description and the short tutorial. When your function is registered you will get it's real function number. Part IV - Four examples of implementing new builtin functions with EBFSa) The function is called "cmd_find" and it checks if an engine command is available. The return value is 1 if the command exists, otherwise it is 0. In PR_CMDS.C add the following code anywhere before the builtin variables... // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes start /* ================= PF_cmd_find float cmd_find (string) ================= */ void PF_cmd_find (void) { char *cmdname; float result; cmdname = G_STRING(OFS_PARM0); result = Cmd_Exists (cmdname); G_FLOAT(OFS_RETURN) = result; } // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes end ... and to the EBFS data array add ... { 101, "cmd_find", PF_cmd_find }, // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes Done. b) The function is called "cvar_find" and checks if a console variable is available. The return value is 1 if the cvar exists, otherwise it is 0. In PR_CMDS.C add the following code anywhere before the builtin variables... // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes start /* ================= PF_cvar_find float cvar_find (string) ================= */ void PF_cvar_find (void) { char *varname; float result; varname = G_STRING(OFS_PARM0); result = 0; if (Cvar_FindVar (varname)) { result = 1; } G_FLOAT(OFS_RETURN) = result; } // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes end ... and to the EBFS data array add ... { 102, "cvar_find", PF_cvar_find }, // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes Done. c) The function is called "cvar_string" and returns the string of a console variable. In PR_CMDS.C add the following code anywhere before the builtin variables... // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes start /* ================= PF_cvar_string string cvar_string (string) ================= */ void PF_cvar_string (void) { char *varname; cvar_t *var; varname = G_STRING(OFS_PARM0); var = Cvar_FindVar (varname); if (!var) { Con_DPrintf ("Cvar_String: variable \"%s\" not found\n", varname); // 2001-09-09 Made 'Cvar not found' a developer message by Maddes G_INT(OFS_RETURN) = OFS_NULL; } else { G_INT(OFS_RETURN) = var->string - pr_strings; } } // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes end ... and to the EBFS data array add ... { 103, "cvar_string", PF_cvar_string }, // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes Done. d) The function is called "WriteFloat" and is the missing Write function. In PR_CMDS.C add the following code anywhere before the builtin variables... // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes start /* PF_WriteFloat void (float to, float f) WriteFloat */ void PF_WriteFloat (void) { MSG_WriteFloat (WriteDest(), G_FLOAT(OFS_PARM1)); } // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes end ... and to the EBFS data array add ... { 107, "WriteFloat", PF_WriteFloat }, // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes Done. You see it it is very easy to add your own builtin functions to the system. If you do have new builtin functions then check out the previous part how to submit them to the QSG. Part V - Activating some useful builtin functions of the preliminary Quake 2 codeThere are some simple but useful builtin functions done for the preliminary Quake 2 code: sin, cos, sqrt and etos. First you have to activate the code for Classic Quake. Remove the "#ifdef QUAKE2" before and the "#endif" behind PF_etos(). Before PF_sin() place the following line... #endif // 2001-09-16 Quake 2 builtin functions: sin, cos, sqrt, etos by id/Maddes ... and remove the "#endif" behind PF_sqrt(). At last replace the following in the EBFS data array... #ifdef QUAKE2 { 60, "sin", PF_sin }, { 61, "cos", PF_cos }, { 62, "sqrt", PF_sqrt }, { 63, "changepitch", PF_changepitch }, { 64, "TraceToss", PF_TraceToss }, { 65, "etos", PF_etos }, { 66, "WaterMove", PF_WaterMove }, #endif ... with ... // 2001-09-16 Quake 2 builtin functions: sin, cos, sqrt, etos by id/Maddes start { 60, "sin", PF_sin }, { 61, "cos", PF_cos }, { 62, "sqrt", PF_sqrt }, #ifdef QUAKE2 { 63, "changepitch", PF_changepitch }, { 64, "TraceToss", PF_TraceToss }, #endif { 65, "etos", PF_etos }, #ifdef QUAKE2 { 66, "WaterMove", PF_WaterMove }, #endif // 2001-09-16 Quake 2 builtin functions: sin, cos, sqrt, etos by id/Maddes end Done. |