2010-02-15 23:26:55 +00:00
/*
Copyright ( C ) 1996 - 2001 Id Software , Inc .
Copyright ( C ) 2002 - 2009 John Fitzgibbons and others
2014-09-22 08:55:46 +00:00
Copyright ( C ) 2010 - 2014 QuakeSpasm developers
2010-02-15 23:26:55 +00:00
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include "quakedef.h"
2018-07-07 14:05:34 +00:00
//#define STRINGTEMP_BUFFERS 16
//#define STRINGTEMP_LENGTH 1024
2010-02-17 11:03:34 +00:00
static char pr_string_temp [ STRINGTEMP_BUFFERS ] [ STRINGTEMP_LENGTH ] ;
static byte pr_string_tempindex = 0 ;
2017-09-17 02:12:53 +00:00
char * PR_GetTempString ( void )
2010-02-17 11:03:34 +00:00
{
return pr_string_temp [ ( STRINGTEMP_BUFFERS - 1 ) & + + pr_string_tempindex ] ;
}
2018-05-01 00:35:14 +00:00
# define RETURN_EDICT(e) (((int *)qcvm->globals)[OFS_RETURN] = EDICT_TO_PROG(e))
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
# define MSG_BROADCAST 0 // unreliable to all
# define MSG_ONE 1 // reliable to one (msg_entity)
# define MSG_ALL 2 // reliable to all
# define MSG_INIT 3 // write to the init string
# define MSG_EXT_MULTICAST 4 // temporary buffer that can be splurged more reliably / with more control.
# define MSG_EXT_ENTITY 5 // for csqc networking. we don't actually support this. I'm just defining it for completeness.
2011-12-12 08:56:25 +00:00
2010-02-15 23:26:55 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2011-12-12 08:56:25 +00:00
BUILT - IN FUNCTIONS
2010-02-15 23:26:55 +00:00
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
char * PF_VarString ( int first )
2010-02-15 23:26:55 +00:00
{
int i ;
PF_VarString: raise buffer size to 1024. I checked all buffers that the resulting string could end up in, and they're all at least 1024 bytes (even with an unmodified quake client).
PF_VarString is only called on the server, in:
- PF_error, PF_objerror, PF_dprint to print error / debugging messages
- PF_bprint, which calls SV_BroadcastPrintf which copies the string into a 1024-byte buffer, then sends that to clients in a svc_print message
- PF_sprint, which sends the string in a svc_print message using MSG_WriteString (without copying into an intermediate buffer)
- PF_centerprint, which sends the return value of PF_VarString in a svc_centerprint message (without copying into an intermediate buffer)
On the client:
- svc_print reads the payload string with MSG_ReadString (2048-byte buffer), then Con_Printf is called (4096-byte buffer)
- svc_centerprint uses MSG_ReadString then calls SCR_CenterPrint (1024-byte buffer)
I also checked the original quake source and the client-side buffers for MSG_ReadString, Con_Printf, and SCR_CenterPrint are all the same sizes as in QS.
Admittedly I want to support sock's ITS mod, but as far as I can see, it's a totally safe to increase this to 1024.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@985 af15c1b1-3010-417e-b628-4374ebc0bcbd
2014-09-01 07:30:23 +00:00
static char out [ 1024 ] ;
2012-05-15 14:41:29 +00:00
size_t s ;
2010-02-15 23:26:55 +00:00
out [ 0 ] = 0 ;
2012-05-15 14:41:29 +00:00
s = 0 ;
2018-05-01 00:35:14 +00:00
for ( i = first ; i < qcvm - > argc ; i + + )
2010-02-15 23:26:55 +00:00
{
2012-05-15 14:41:29 +00:00
s = q_strlcat ( out , G_STRING ( ( OFS_PARM0 + i * 3 ) ) , sizeof ( out ) ) ;
if ( s > = sizeof ( out ) )
2012-05-15 16:10:13 +00:00
{
2013-01-06 12:05:34 +00:00
Con_Warning ( " PF_VarString: overflow (string truncated) \n " ) ;
2012-05-15 16:10:13 +00:00
return out ;
}
2010-02-15 23:26:55 +00:00
}
2015-05-25 01:48:03 +00:00
if ( s > 255 )
2017-04-25 20:07:34 +00:00
{
if ( ! dev_overflows . varstring | | dev_overflows . varstring + CONSOLE_RESPAM_TIME < realtime )
{
Con_DWarning ( " PF_VarString: %i characters exceeds standard limit of 255 (max = %d). \n " , ( int ) s , ( int ) ( sizeof ( out ) - 1 ) ) ;
dev_overflows . varstring = realtime ;
}
}
2010-02-15 23:26:55 +00:00
return out ;
}
/*
= = = = = = = = = = = = = = = = =
2011-12-12 08:56:25 +00:00
PF_error
2010-02-15 23:26:55 +00:00
This is a TERMINAL error , which will kill off the entire server .
Dumps self .
error ( value )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_error ( void )
2010-02-15 23:26:55 +00:00
{
char * s ;
edict_t * ed ;
s = PF_VarString ( 0 ) ;
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
Con_Printf ( " ======SERVER ERROR in %s: \n %s \n " ,
2018-05-01 00:35:14 +00:00
PR_GetString ( qcvm - > xfunction - > s_name ) , s ) ;
2010-02-15 23:26:55 +00:00
ed = PROG_TO_EDICT ( pr_global_struct - > self ) ;
ED_Print ( ed ) ;
Host_Error ( " Program error " ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_objerror
Dumps out self , then an error message . The program is aborted and self is
removed , but the level can continue .
objerror ( value )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_objerror ( void )
2010-02-15 23:26:55 +00:00
{
char * s ;
edict_t * ed ;
s = PF_VarString ( 0 ) ;
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
Con_Printf ( " ======OBJECT ERROR in %s: \n %s \n " ,
2018-05-01 00:35:14 +00:00
PR_GetString ( qcvm - > xfunction - > s_name ) , s ) ;
2010-02-15 23:26:55 +00:00
ed = PROG_TO_EDICT ( pr_global_struct - > self ) ;
ED_Print ( ed ) ;
ED_Free ( ed ) ;
//Host_Error ("Program error"); //johnfitz -- by design, this should not be fatal
}
/*
= = = = = = = = = = = = = =
PF_makevectors
Writes new values for v_forward , v_up , and v_right based on angles
makevectors ( vector )
= = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_makevectors ( void )
2010-02-15 23:26:55 +00:00
{
AngleVectors ( G_VECTOR ( OFS_PARM0 ) , pr_global_struct - > v_forward , pr_global_struct - > v_right , pr_global_struct - > v_up ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_setorigin
2011-12-12 08:56:25 +00:00
This is the only valid way to move an object without using the physics
of the world ( setting velocity and waiting ) . Directly changing origin
will not set internal links correctly , so clipping would be messed up .
This should be called when an object is spawned , and then only if it is
teleported .
2010-02-15 23:26:55 +00:00
setorigin ( entity , origin )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_setorigin ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * e ;
float * org ;
e = G_EDICT ( OFS_PARM0 ) ;
org = G_VECTOR ( OFS_PARM1 ) ;
VectorCopy ( org , e - > v . origin ) ;
SV_LinkEdict ( e , false ) ;
}
2018-05-01 00:35:14 +00:00
void SetMinMaxSize ( edict_t * e , float * minvec , float * maxvec , qboolean rotate )
2010-02-15 23:26:55 +00:00
{
float * angles ;
vec3_t rmin , rmax ;
float bounds [ 2 ] [ 3 ] ;
float xvector [ 2 ] , yvector [ 2 ] ;
float a ;
vec3_t base , transformed ;
int i , j , k , l ;
2011-12-12 08:56:25 +00:00
for ( i = 0 ; i < 3 ; i + + )
2010-06-01 12:10:49 +00:00
if ( minvec [ i ] > maxvec [ i ] )
2010-02-15 23:26:55 +00:00
PR_RunError ( " backwards mins/maxs " ) ;
rotate = false ; // FIXME: implement rotation properly again
if ( ! rotate )
{
2010-06-01 12:10:49 +00:00
VectorCopy ( minvec , rmin ) ;
VectorCopy ( maxvec , rmax ) ;
2010-02-15 23:26:55 +00:00
}
else
{
// find min / max for rotations
angles = e - > v . angles ;
a = angles [ 1 ] / 180 * M_PI ;
xvector [ 0 ] = cos ( a ) ;
xvector [ 1 ] = sin ( a ) ;
yvector [ 0 ] = - sin ( a ) ;
yvector [ 1 ] = cos ( a ) ;
2010-06-01 12:10:49 +00:00
VectorCopy ( minvec , bounds [ 0 ] ) ;
VectorCopy ( maxvec , bounds [ 1 ] ) ;
2010-02-15 23:26:55 +00:00
2020-03-28 05:09:09 +00:00
rmin [ 0 ] = rmin [ 1 ] = rmin [ 2 ] = FLT_MAX ;
rmax [ 0 ] = rmax [ 1 ] = rmax [ 2 ] = - FLT_MAX ;
2010-02-15 23:26:55 +00:00
2011-12-12 08:56:25 +00:00
for ( i = 0 ; i < = 1 ; i + + )
2010-02-15 23:26:55 +00:00
{
base [ 0 ] = bounds [ i ] [ 0 ] ;
2011-12-12 08:56:25 +00:00
for ( j = 0 ; j < = 1 ; j + + )
2010-02-15 23:26:55 +00:00
{
base [ 1 ] = bounds [ j ] [ 1 ] ;
2011-12-12 08:56:25 +00:00
for ( k = 0 ; k < = 1 ; k + + )
2010-02-15 23:26:55 +00:00
{
base [ 2 ] = bounds [ k ] [ 2 ] ;
// transform the point
transformed [ 0 ] = xvector [ 0 ] * base [ 0 ] + yvector [ 0 ] * base [ 1 ] ;
transformed [ 1 ] = xvector [ 1 ] * base [ 0 ] + yvector [ 1 ] * base [ 1 ] ;
transformed [ 2 ] = base [ 2 ] ;
2011-12-12 08:56:25 +00:00
for ( l = 0 ; l < 3 ; l + + )
2010-02-15 23:26:55 +00:00
{
if ( transformed [ l ] < rmin [ l ] )
rmin [ l ] = transformed [ l ] ;
if ( transformed [ l ] > rmax [ l ] )
rmax [ l ] = transformed [ l ] ;
}
}
}
}
}
// set derived values
VectorCopy ( rmin , e - > v . mins ) ;
VectorCopy ( rmax , e - > v . maxs ) ;
2010-06-01 12:10:49 +00:00
VectorSubtract ( maxvec , minvec , e - > v . size ) ;
2010-02-15 23:26:55 +00:00
SV_LinkEdict ( e , false ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_setsize
the size box is rotated by the current angle
setsize ( entity , minvector , maxvector )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_setsize ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * e ;
2010-06-01 12:10:49 +00:00
float * minvec , * maxvec ;
2010-02-15 23:26:55 +00:00
e = G_EDICT ( OFS_PARM0 ) ;
2010-06-01 12:10:49 +00:00
minvec = G_VECTOR ( OFS_PARM1 ) ;
maxvec = G_VECTOR ( OFS_PARM2 ) ;
SetMinMaxSize ( e , minvec , maxvec , false ) ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = =
PF_setmodel
setmodel ( entity , model )
= = = = = = = = = = = = = = = = =
*/
2018-05-01 00:35:14 +00:00
cvar_t sv_gameplayfix_setmodelrealbox = { " sv_gameplayfix_setmodelrealbox " , " 1 " } ;
static void PF_sv_setmodel ( void )
2010-02-15 23:26:55 +00:00
{
2011-12-12 08:56:25 +00:00
int i ;
2010-08-29 02:22:55 +00:00
const char * m , * * check ;
2012-05-30 08:56:06 +00:00
qmodel_t * mod ;
2011-12-12 08:56:25 +00:00
edict_t * e ;
2010-02-15 23:26:55 +00:00
e = G_EDICT ( OFS_PARM0 ) ;
m = G_STRING ( OFS_PARM1 ) ;
// check to see if model was properly precached
2011-12-12 08:56:25 +00:00
for ( i = 0 , check = sv . model_precache ; * check ; i + + , check + + )
{
2010-02-15 23:26:55 +00:00
if ( ! strcmp ( * check , m ) )
break ;
2011-12-12 08:56:25 +00:00
}
2010-02-15 23:26:55 +00:00
if ( ! * check )
2011-12-12 08:56:25 +00:00
{
2017-09-17 02:12:53 +00:00
if ( pr_checkextension . value )
{ //Spike: so that func_illusionaries work with custom models even in vanilla.
if ( sv . state = = ss_loading )
Con_DWarning ( " PF_setmodel( \" %s \" ): Model was not precached \n " , m ) ;
else
{
// PR_PrintStatement(pr_statements + pr_xstatement);
// PR_StackTrace();
Con_Warning ( " PF_setmodel( \" %s \" ): Model was not precached \n " , m ) ;
}
i = SV_Precache_Model ( m ) ;
}
else
PR_RunError ( " no precache: %s " , m ) ;
2011-12-12 08:56:25 +00:00
}
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
e - > v . model = PR_SetEngineString ( * check ) ;
2010-02-15 23:26:55 +00:00
e - > v . modelindex = i ; //SV_ModelIndex (m);
mod = sv . models [ ( int ) e - > v . modelindex ] ; // Mod_ForName (m, true);
if ( mod )
//johnfitz -- correct physics cullboxes for bmodels
2018-05-01 00:35:14 +00:00
/* Spike -- THIS IS A HUGE CLUSTERFUCK.
the mins / maxs sizes of models in vanilla was always set to xyz - 16 / + 16.
which causes issues with clientside culling .
many engines fixed that , but not here .
which means that setmodel - without - setsize is now fucked .
the qc will usually do a setsize after setmodel anyway , so applying that fix here will do nothing .
you ' d need to apply the serverside version of the cull fix in SV_LinkEdict instead , which is where the pvs is calculated .
tracebox is limited to specific hull sizes . the traces are biased such that they ' re aligned to the mins point of the box , rather than the center of the trace .
so vanilla ' s ' - 16 - 16 - 16 ' / ' 16 16 16 ' is wrong for Z ( which is usually corrected for with gravity anyway ) , but X + Y will be correctly aligned for the resulting hull .
but traceboxes using models with - 12 or - 20 or whatever will be biased / offcenter in the X + Y axis ( as well as Z , but that ' s still mostly unimportant )
deciding whether to replicate the vanilla behaviour based upon model type sucks .
vanilla :
brush - always the models size
mdl - always [ - 16 , - 16 , - 16 ] , [ 16 , 16 , 16 ]
quakespasm :
brush - always the models size
mdl - always the models size
quakeworld :
* . bsp - always the models size ( matched by extension rather than type )
other - model isn ' t even loaded , setmodel does not do setsize at all .
fte default ( with nq mod ) :
* . bsp ( or sv_gameplayfix_setmodelrealbox ) - always the models size ( matched by extension rather than type )
other - always [ - 16 , - 16 , - 16 ] , [ 16 , 16 , 16 ]
fte ' s behaviour means :
a ) dedicated servers don ' t have to bother loading non - mdls .
b ) nq mods still work fine , where extensions are adhered to in the original qc .
c ) when replacement models are used for bsp models , things still work without them reverting to + / - 16.
*/
2010-02-15 23:26:55 +00:00
{
2018-05-01 00:35:14 +00:00
if ( mod - > type = = mod_brush | | ! sv_gameplayfix_setmodelrealbox . value )
2010-02-15 23:26:55 +00:00
SetMinMaxSize ( e , mod - > clipmins , mod - > clipmaxs , true ) ;
else
SetMinMaxSize ( e , mod - > mins , mod - > maxs , true ) ;
}
//johnfitz
else
SetMinMaxSize ( e , vec3_origin , vec3_origin , true ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_bprint
broadcast print to everyone on server
bprint ( value )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_bprint ( void )
2010-02-15 23:26:55 +00:00
{
char * s ;
s = PF_VarString ( 0 ) ;
SV_BroadcastPrintf ( " %s " , s ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_sprint
single print to a specific client
sprint ( clientent , value )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_sprint ( void )
2010-02-15 23:26:55 +00:00
{
char * s ;
client_t * client ;
2011-12-12 08:56:25 +00:00
int entnum ;
2010-02-15 23:26:55 +00:00
entnum = G_EDICTNUM ( OFS_PARM0 ) ;
s = PF_VarString ( 1 ) ;
if ( entnum < 1 | | entnum > svs . maxclients )
{
Con_Printf ( " tried to sprint to a non-client \n " ) ;
return ;
}
client = & svs . clients [ entnum - 1 ] ;
MSG_WriteChar ( & client - > message , svc_print ) ;
MSG_WriteString ( & client - > message , s ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_centerprint
single print to a specific client
centerprint ( clientent , value )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_centerprint ( void )
2010-02-15 23:26:55 +00:00
{
char * s ;
client_t * client ;
2011-12-12 08:56:25 +00:00
int entnum ;
2010-02-15 23:26:55 +00:00
entnum = G_EDICTNUM ( OFS_PARM0 ) ;
s = PF_VarString ( 1 ) ;
if ( entnum < 1 | | entnum > svs . maxclients )
{
Con_Printf ( " tried to sprint to a non-client \n " ) ;
return ;
}
client = & svs . clients [ entnum - 1 ] ;
MSG_WriteChar ( & client - > message , svc_centerprint ) ;
2011-12-12 08:56:25 +00:00
MSG_WriteString ( & client - > message , s ) ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = =
PF_normalize
vector normalize ( vector )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_normalize ( void )
2010-02-15 23:26:55 +00:00
{
float * value1 ;
vec3_t newvalue ;
2018-04-23 09:20:31 +00:00
double new_temp ;
2010-02-15 23:26:55 +00:00
value1 = G_VECTOR ( OFS_PARM0 ) ;
2018-04-23 09:20:31 +00:00
new_temp = ( double ) value1 [ 0 ] * value1 [ 0 ] + ( double ) value1 [ 1 ] * value1 [ 1 ] + ( double ) value1 [ 2 ] * value1 [ 2 ] ;
2011-12-12 08:56:25 +00:00
new_temp = sqrt ( new_temp ) ;
2010-02-15 23:26:55 +00:00
2010-05-31 06:39:09 +00:00
if ( new_temp = = 0 )
2010-02-15 23:26:55 +00:00
newvalue [ 0 ] = newvalue [ 1 ] = newvalue [ 2 ] = 0 ;
else
{
2011-12-12 08:56:25 +00:00
new_temp = 1 / new_temp ;
2010-05-31 06:39:09 +00:00
newvalue [ 0 ] = value1 [ 0 ] * new_temp ;
newvalue [ 1 ] = value1 [ 1 ] * new_temp ;
newvalue [ 2 ] = value1 [ 2 ] * new_temp ;
2010-02-15 23:26:55 +00:00
}
VectorCopy ( newvalue , G_VECTOR ( OFS_RETURN ) ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_vlen
scalar vlen ( vector )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_vlen ( void )
2010-02-15 23:26:55 +00:00
{
float * value1 ;
2018-01-17 07:44:26 +00:00
double new_temp ;
2010-02-15 23:26:55 +00:00
value1 = G_VECTOR ( OFS_PARM0 ) ;
2018-01-17 07:44:26 +00:00
new_temp = ( double ) value1 [ 0 ] * value1 [ 0 ] + ( double ) value1 [ 1 ] * value1 [ 1 ] + ( double ) value1 [ 2 ] * value1 [ 2 ] ;
2010-05-31 06:39:09 +00:00
new_temp = sqrt ( new_temp ) ;
2010-02-15 23:26:55 +00:00
2010-05-31 06:39:09 +00:00
G_FLOAT ( OFS_RETURN ) = new_temp ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = =
PF_vectoyaw
float vectoyaw ( vector )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_vectoyaw ( void )
2010-02-15 23:26:55 +00:00
{
float * value1 ;
float yaw ;
value1 = G_VECTOR ( OFS_PARM0 ) ;
if ( value1 [ 1 ] = = 0 & & value1 [ 0 ] = = 0 )
yaw = 0 ;
else
{
yaw = ( int ) ( atan2 ( value1 [ 1 ] , value1 [ 0 ] ) * 180 / M_PI ) ;
if ( yaw < 0 )
yaw + = 360 ;
}
G_FLOAT ( OFS_RETURN ) = yaw ;
}
/*
= = = = = = = = = = = = = = = = =
PF_vectoangles
vector vectoangles ( vector )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_vectoangles ( void )
2010-02-15 23:26:55 +00:00
{
float * value1 ;
float forward ;
float yaw , pitch ;
value1 = G_VECTOR ( OFS_PARM0 ) ;
if ( value1 [ 1 ] = = 0 & & value1 [ 0 ] = = 0 )
{
yaw = 0 ;
if ( value1 [ 2 ] > 0 )
pitch = 90 ;
else
pitch = 270 ;
}
else
{
yaw = ( int ) ( atan2 ( value1 [ 1 ] , value1 [ 0 ] ) * 180 / M_PI ) ;
if ( yaw < 0 )
yaw + = 360 ;
forward = sqrt ( value1 [ 0 ] * value1 [ 0 ] + value1 [ 1 ] * value1 [ 1 ] ) ;
pitch = ( int ) ( atan2 ( value1 [ 2 ] , forward ) * 180 / M_PI ) ;
if ( pitch < 0 )
pitch + = 360 ;
}
G_FLOAT ( OFS_RETURN + 0 ) = pitch ;
G_FLOAT ( OFS_RETURN + 1 ) = yaw ;
G_FLOAT ( OFS_RETURN + 2 ) = 0 ;
}
/*
= = = = = = = = = = = = = = = = =
PF_Random
2011-12-12 08:56:25 +00:00
Returns a number from 0 < = num < 1
2010-02-15 23:26:55 +00:00
random ( )
2017-09-17 02:12:53 +00:00
bug : vanilla could return 1 , contrary to the ( unchanged ) comment just above .
2010-02-15 23:26:55 +00:00
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_random ( void )
2010-02-15 23:26:55 +00:00
{
float num ;
2017-09-17 02:12:53 +00:00
num = ( rand ( ) & 0x7fff ) / ( ( float ) 0x8000 ) ;
2010-02-15 23:26:55 +00:00
G_FLOAT ( OFS_RETURN ) = num ;
}
/*
= = = = = = = = = = = = = = = = =
PF_particle
particle ( origin , color , count )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_particle ( void )
2010-02-15 23:26:55 +00:00
{
float * org , * dir ;
float color ;
float count ;
org = G_VECTOR ( OFS_PARM0 ) ;
dir = G_VECTOR ( OFS_PARM1 ) ;
color = G_FLOAT ( OFS_PARM2 ) ;
count = G_FLOAT ( OFS_PARM3 ) ;
SV_StartParticle ( org , dir , color , count ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_ambientsound
= = = = = = = = = = = = = = = = =
*/
2018-05-01 00:35:14 +00:00
static void PF_sv_ambientsound ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * samp , * * check ;
2010-02-15 23:26:55 +00:00
float * pos ;
2011-12-12 08:56:25 +00:00
float vol , attenuation ;
2017-09-17 02:12:53 +00:00
int soundnum ;
struct ambientsound_s * st ;
2010-02-15 23:26:55 +00:00
pos = G_VECTOR ( OFS_PARM0 ) ;
samp = G_STRING ( OFS_PARM1 ) ;
vol = G_FLOAT ( OFS_PARM2 ) ;
attenuation = G_FLOAT ( OFS_PARM3 ) ;
// check to see if samp was properly precached
2011-12-12 08:56:25 +00:00
for ( soundnum = 0 , check = sv . sound_precache ; * check ; check + + , soundnum + + )
{
if ( ! strcmp ( * check , samp ) )
2010-02-15 23:26:55 +00:00
break ;
2011-12-12 08:56:25 +00:00
}
2010-02-15 23:26:55 +00:00
if ( ! * check )
{
Con_Printf ( " no precache: %s \n " , samp ) ;
return ;
}
2017-09-17 02:12:53 +00:00
//generate data to splurge on a per-client basis in SV_SendAmbientSounds
if ( sv . num_ambients = = sv . max_ambients )
chase.c, cl_input.c, cl_parse.c, client.h, common.c, common.h, console.h,
cvar.h, draw.h, gl_draw.c, gl_fog.c, gl_mesh.c, gl_model.c, gl_model.h,
gl_rmain.c, gl_rmisc.c, gl_screen.c, gl_sky.c, gl_texmgr.c, glquake.h,
host.c, keys.c, keys.h, main.c, menu.c, menu.h, pr_cmds.c, quakedef.h,
r_alias.c, r_brush.c, r_part.c, r_sprite.c, r_world.c, sbar.c, sbar.h,
screen.h, snd_dma.c, snd_mem.c, snd_mix.c, sv_main.c, sys_sdl.c, vid.h,
view.h, world.c, world.h: Loads of warning fixes about missing function
prototypes, missing parens around &, missing braces leading to ambiguous
else statements and unused and uninitialized variables. There are still a
couple of unitialised variables here and there, but not much. The warnings
about strict aliasing violations need taking care of.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@21 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-16 12:01:07 +00:00
{
2017-09-17 02:12:53 +00:00
int nm = sv . max_ambients + 128 ;
struct ambientsound_s * n = ( nm * sizeof ( * n ) < sv . max_ambients * sizeof ( * n ) ) ? NULL : realloc ( sv . ambientsounds , nm * sizeof ( * n ) ) ;
if ( ! n )
PR_RunError ( " PF_ambientsound: out of memory " ) ; //shouldn't really happen.
sv . ambientsounds = n ;
memset ( sv . ambientsounds + sv . max_ambients , 0 , ( nm - sv . max_ambients ) * sizeof ( * n ) ) ;
sv . max_ambients = nm ;
chase.c, cl_input.c, cl_parse.c, client.h, common.c, common.h, console.h,
cvar.h, draw.h, gl_draw.c, gl_fog.c, gl_mesh.c, gl_model.c, gl_model.h,
gl_rmain.c, gl_rmisc.c, gl_screen.c, gl_sky.c, gl_texmgr.c, glquake.h,
host.c, keys.c, keys.h, main.c, menu.c, menu.h, pr_cmds.c, quakedef.h,
r_alias.c, r_brush.c, r_part.c, r_sprite.c, r_world.c, sbar.c, sbar.h,
screen.h, snd_dma.c, snd_mem.c, snd_mix.c, sv_main.c, sys_sdl.c, vid.h,
view.h, world.c, world.h: Loads of warning fixes about missing function
prototypes, missing parens around &, missing braces leading to ambiguous
else statements and unused and uninitialized variables. There are still a
couple of unitialised variables here and there, but not much. The warnings
about strict aliasing violations need taking care of.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@21 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-16 12:01:07 +00:00
}
2017-09-17 02:12:53 +00:00
st = & sv . ambientsounds [ sv . num_ambients + + ] ;
VectorCopy ( pos , st - > origin ) ;
st - > soundindex = soundnum ;
st - > volume = vol ;
st - > attenuation = attenuation ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = =
PF_sound
Each entity can have eight independant sound sources , like voice ,
weapon , feet , etc .
Channel 0 is an auto - allocate channel , the others override anything
2011-12-12 08:56:25 +00:00
already running on that entity / channel pair .
2010-02-15 23:26:55 +00:00
An attenuation of 0 will play full volume everywhere in the level .
Larger attenuations will drop off .
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_sound ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * sample ;
2011-12-12 08:56:25 +00:00
int channel ;
2010-02-15 23:26:55 +00:00
edict_t * entity ;
2011-12-12 08:56:25 +00:00
int volume ;
float attenuation ;
2010-02-15 23:26:55 +00:00
entity = G_EDICT ( OFS_PARM0 ) ;
channel = G_FLOAT ( OFS_PARM1 ) ;
sample = G_STRING ( OFS_PARM2 ) ;
volume = G_FLOAT ( OFS_PARM3 ) * 255 ;
attenuation = G_FLOAT ( OFS_PARM4 ) ;
2017-09-17 02:12:53 +00:00
/* Spike -- these checks are redundant
2010-02-15 23:26:55 +00:00
if ( volume < 0 | | volume > 255 )
2012-11-15 17:30:43 +00:00
Host_Error ( " SV_StartSound: volume = %i " , volume ) ;
2010-02-15 23:26:55 +00:00
if ( attenuation < 0 | | attenuation > 4 )
2012-11-15 17:30:43 +00:00
Host_Error ( " SV_StartSound: attenuation = %f " , attenuation ) ;
2010-02-15 23:26:55 +00:00
if ( channel < 0 | | channel > 7 )
2012-11-15 17:30:43 +00:00
Host_Error ( " SV_StartSound: channel = %i " , channel ) ;
2017-09-17 02:12:53 +00:00
*/
SV_StartSound ( entity , NULL , channel , sample , volume , attenuation ) ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = =
PF_break
break ( )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_break ( void )
2010-02-15 23:26:55 +00:00
{
2011-12-12 08:56:25 +00:00
Con_Printf ( " break statement \n " ) ;
* ( int * ) - 4 = 0 ; // dump to debugger
2010-02-15 23:26:55 +00:00
// PR_RunError ("break statement");
}
/*
= = = = = = = = = = = = = = = = =
PF_traceline
Used for use tracing and shot targeting
Traces are blocked by bbox and exact bsp entityes , and also slide box entities
if the tryents flag is set .
traceline ( vector1 , vector2 , tryents )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_traceline ( void )
2010-02-15 23:26:55 +00:00
{
float * v1 , * v2 ;
trace_t trace ;
2011-12-12 08:56:25 +00:00
int nomonsters ;
2010-02-15 23:26:55 +00:00
edict_t * ent ;
v1 = G_VECTOR ( OFS_PARM0 ) ;
v2 = G_VECTOR ( OFS_PARM1 ) ;
nomonsters = G_FLOAT ( OFS_PARM2 ) ;
ent = G_EDICT ( OFS_PARM3 ) ;
2010-08-14 21:02:31 +00:00
/* FIXME FIXME FIXME: Why do we hit this with certain progs.dat ?? */
2010-08-14 20:06:06 +00:00
if ( developer . value ) {
if ( IS_NAN ( v1 [ 0 ] ) | | IS_NAN ( v1 [ 1 ] ) | | IS_NAN ( v1 [ 2 ] ) | |
IS_NAN ( v2 [ 0 ] ) | | IS_NAN ( v2 [ 1 ] ) | | IS_NAN ( v2 [ 2 ] ) ) {
2015-10-06 04:08:29 +00:00
Con_Warning ( " NAN in traceline: \n v1(%f %f %f) v2(%f %f %f) \n entity %d \n " ,
2010-08-14 20:50:31 +00:00
v1 [ 0 ] , v1 [ 1 ] , v1 [ 2 ] , v2 [ 0 ] , v2 [ 1 ] , v2 [ 2 ] , NUM_FOR_EDICT ( ent ) ) ;
2010-08-14 20:06:06 +00:00
}
2015-10-06 04:08:29 +00:00
}
if ( IS_NAN ( v1 [ 0 ] ) | | IS_NAN ( v1 [ 1 ] ) | | IS_NAN ( v1 [ 2 ] ) )
2010-08-14 03:59:29 +00:00
v1 [ 0 ] = v1 [ 1 ] = v1 [ 2 ] = 0 ;
2015-10-06 04:08:29 +00:00
if ( IS_NAN ( v2 [ 0 ] ) | | IS_NAN ( v2 [ 1 ] ) | | IS_NAN ( v2 [ 2 ] ) )
2010-08-14 03:59:29 +00:00
v2 [ 0 ] = v2 [ 1 ] = v2 [ 2 ] = 0 ;
2015-10-06 04:08:29 +00:00
2010-02-15 23:26:55 +00:00
trace = SV_Move ( v1 , vec3_origin , vec3_origin , v2 , nomonsters , ent ) ;
pr_global_struct - > trace_allsolid = trace . allsolid ;
pr_global_struct - > trace_startsolid = trace . startsolid ;
pr_global_struct - > trace_fraction = trace . fraction ;
pr_global_struct - > trace_inwater = trace . inwater ;
pr_global_struct - > trace_inopen = trace . inopen ;
VectorCopy ( trace . endpos , pr_global_struct - > trace_endpos ) ;
VectorCopy ( trace . plane . normal , pr_global_struct - > trace_plane_normal ) ;
pr_global_struct - > trace_plane_dist = trace . plane . dist ;
if ( trace . ent )
pr_global_struct - > trace_ent = EDICT_TO_PROG ( trace . ent ) ;
else
2018-05-01 00:35:14 +00:00
pr_global_struct - > trace_ent = EDICT_TO_PROG ( qcvm - > edicts ) ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = =
PF_checkpos
Returns true if the given entity can move to the given position from it ' s
current position by walking or rolling .
FIXME : make work . . .
scalar checkpos ( entity , vector )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
#if 0
static void PF_checkpos ( void )
2010-02-15 23:26:55 +00:00
{
}
2011-12-12 08:56:25 +00:00
# endif
2010-02-15 23:26:55 +00:00
//============================================================================
2017-07-26 04:27:16 +00:00
static byte * checkpvs ; //ericw -- changed to malloc
static int checkpvs_capacity ;
2010-02-15 23:26:55 +00:00
2011-12-12 08:56:25 +00:00
static int PF_newcheckclient ( int check )
2010-02-15 23:26:55 +00:00
{
int i ;
byte * pvs ;
edict_t * ent ;
mleaf_t * leaf ;
vec3_t org ;
2017-07-26 04:27:16 +00:00
int pvsbytes ;
2010-02-15 23:26:55 +00:00
// cycle to the next one
if ( check < 1 )
check = 1 ;
if ( check > svs . maxclients )
check = svs . maxclients ;
if ( check = = svs . maxclients )
i = 1 ;
else
i = check + 1 ;
for ( ; ; i + + )
{
if ( i = = svs . maxclients + 1 )
i = 1 ;
ent = EDICT_NUM ( i ) ;
if ( i = = check )
break ; // didn't find anything else
if ( ent - > free )
continue ;
if ( ent - > v . health < = 0 )
continue ;
if ( ( int ) ent - > v . flags & FL_NOTARGET )
continue ;
// anything that is a client, or has a client as an enemy
break ;
}
// get the PVS for the entity
VectorAdd ( ent - > v . origin , ent - > v . view_ofs , org ) ;
2018-05-01 00:35:14 +00:00
leaf = Mod_PointInLeaf ( org , qcvm - > worldmodel ) ;
pvs = Mod_LeafPVS ( leaf , qcvm - > worldmodel ) ;
2017-07-26 04:27:16 +00:00
2018-05-01 00:35:14 +00:00
pvsbytes = ( qcvm - > worldmodel - > numleafs + 7 ) > > 3 ;
2017-07-26 04:27:16 +00:00
if ( checkpvs = = NULL | | pvsbytes > checkpvs_capacity )
{
checkpvs_capacity = pvsbytes ;
checkpvs = ( byte * ) realloc ( checkpvs , checkpvs_capacity ) ;
if ( ! checkpvs )
Sys_Error ( " PF_newcheckclient: realloc() failed on %d bytes " , checkpvs_capacity ) ;
}
memcpy ( checkpvs , pvs , pvsbytes ) ;
2010-02-15 23:26:55 +00:00
return i ;
}
/*
= = = = = = = = = = = = = = = = =
PF_checkclient
Returns a client ( or object that has a client enemy ) that would be a
valid target .
If there are more than one valid options , they are cycled each frame
If ( self . origin + self . viewofs ) is not in the PVS of the current target ,
it is not returned at all .
name checkclient ( )
= = = = = = = = = = = = = = = = =
*/
# define MAX_CHECK 16
2011-12-12 08:56:25 +00:00
static int c_invis , c_notvis ;
2018-05-01 00:35:14 +00:00
static void PF_sv_checkclient ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ent , * self ;
mleaf_t * leaf ;
int l ;
vec3_t view ;
// find a new check if on a new frame
2018-05-01 00:35:14 +00:00
if ( qcvm - > time - sv . lastchecktime > = 0.1 )
2010-02-15 23:26:55 +00:00
{
sv . lastcheck = PF_newcheckclient ( sv . lastcheck ) ;
2018-05-01 00:35:14 +00:00
sv . lastchecktime = qcvm - > time ;
2010-02-15 23:26:55 +00:00
}
// return check if it might be visible
ent = EDICT_NUM ( sv . lastcheck ) ;
if ( ent - > free | | ent - > v . health < = 0 )
{
2018-05-01 00:35:14 +00:00
RETURN_EDICT ( qcvm - > edicts ) ;
2010-02-15 23:26:55 +00:00
return ;
}
// if current entity can't possibly see the check entity, return 0
self = PROG_TO_EDICT ( pr_global_struct - > self ) ;
VectorAdd ( self - > v . origin , self - > v . view_ofs , view ) ;
2018-05-01 00:35:14 +00:00
leaf = Mod_PointInLeaf ( view , qcvm - > worldmodel ) ;
l = ( leaf - qcvm - > worldmodel - > leafs ) - 1 ;
2011-12-12 08:56:25 +00:00
if ( ( l < 0 ) | | ! ( checkpvs [ l > > 3 ] & ( 1 < < ( l & 7 ) ) ) )
2010-02-15 23:26:55 +00:00
{
2011-12-12 08:56:25 +00:00
c_notvis + + ;
2018-05-01 00:35:14 +00:00
RETURN_EDICT ( qcvm - > edicts ) ;
2010-02-15 23:26:55 +00:00
return ;
}
// might be able to see it
2011-12-12 08:56:25 +00:00
c_invis + + ;
2010-02-15 23:26:55 +00:00
RETURN_EDICT ( ent ) ;
}
//============================================================================
/*
= = = = = = = = = = = = = = = = =
PF_stuffcmd
Sends text over to the client ' s execution buffer
stuffcmd ( clientent , value )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_stuffcmd ( void )
2010-02-15 23:26:55 +00:00
{
int entnum ;
2010-08-29 02:22:55 +00:00
const char * str ;
2010-02-15 23:26:55 +00:00
client_t * old ;
entnum = G_EDICTNUM ( OFS_PARM0 ) ;
if ( entnum < 1 | | entnum > svs . maxclients )
PR_RunError ( " Parm 0 not a client " ) ;
str = G_STRING ( OFS_PARM1 ) ;
old = host_client ;
host_client = & svs . clients [ entnum - 1 ] ;
Host_ClientCommands ( " %s " , str ) ;
host_client = old ;
}
/*
= = = = = = = = = = = = = = = = =
PF_localcmd
Sends text over to the client ' s execution buffer
localcmd ( string )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_localcmd ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * str ;
2010-02-15 23:26:55 +00:00
str = G_STRING ( OFS_PARM0 ) ;
Cbuf_AddText ( str ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_cvar
float cvar ( string )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_cvar ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * str ;
2010-02-15 23:26:55 +00:00
str = G_STRING ( OFS_PARM0 ) ;
G_FLOAT ( OFS_RETURN ) = Cvar_VariableValue ( str ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_cvar_set
float cvar ( string )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_cvar_set ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * var , * val ;
2010-02-15 23:26:55 +00:00
var = G_STRING ( OFS_PARM0 ) ;
val = G_STRING ( OFS_PARM1 ) ;
Cvar_Set ( var , val ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_findradius
Returns a chain of entities that have origins within a spherical area
findradius ( origin , radius )
= = = = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_findradius ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ent , * chain ;
float rad ;
float * org ;
vec3_t eorg ;
2011-12-12 08:56:25 +00:00
int i , j ;
2010-02-15 23:26:55 +00:00
2018-05-01 00:35:14 +00:00
chain = ( edict_t * ) qcvm - > edicts ;
2010-02-15 23:26:55 +00:00
org = G_VECTOR ( OFS_PARM0 ) ;
rad = G_FLOAT ( OFS_PARM1 ) ;
2018-05-01 00:35:14 +00:00
ent = NEXT_EDICT ( qcvm - > edicts ) ;
for ( i = 1 ; i < qcvm - > num_edicts ; i + + , ent = NEXT_EDICT ( ent ) )
2010-02-15 23:26:55 +00:00
{
if ( ent - > free )
continue ;
if ( ent - > v . solid = = SOLID_NOT )
continue ;
2011-12-12 08:56:25 +00:00
for ( j = 0 ; j < 3 ; j + + )
eorg [ j ] = org [ j ] - ( ent - > v . origin [ j ] + ( ent - > v . mins [ j ] + ent - > v . maxs [ j ] ) * 0.5 ) ;
2010-08-01 19:22:46 +00:00
if ( VectorLength ( eorg ) > rad )
2010-02-15 23:26:55 +00:00
continue ;
ent - > v . chain = EDICT_TO_PROG ( chain ) ;
chain = ent ;
}
RETURN_EDICT ( chain ) ;
}
/*
= = = = = = = = =
PF_dprint
= = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_dprint ( void )
2010-02-15 23:26:55 +00:00
{
Con_DPrintf ( " %s " , PF_VarString ( 0 ) ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_ftos ( void )
2010-02-15 23:26:55 +00:00
{
float v ;
2010-02-17 11:03:34 +00:00
char * s ;
2011-12-12 08:56:25 +00:00
2010-02-15 23:26:55 +00:00
v = G_FLOAT ( OFS_PARM0 ) ;
2010-02-17 11:03:34 +00:00
s = PR_GetTempString ( ) ;
2010-02-15 23:26:55 +00:00
if ( v = = ( int ) v )
2010-02-17 11:03:34 +00:00
sprintf ( s , " %d " , ( int ) v ) ;
2010-02-15 23:26:55 +00:00
else
2010-02-17 11:03:34 +00:00
sprintf ( s , " %5.1f " , v ) ;
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
G_INT ( OFS_RETURN ) = PR_SetEngineString ( s ) ;
2010-02-15 23:26:55 +00:00
}
2011-12-12 08:56:25 +00:00
static void PF_fabs ( void )
2010-02-15 23:26:55 +00:00
{
float v ;
v = G_FLOAT ( OFS_PARM0 ) ;
G_FLOAT ( OFS_RETURN ) = fabs ( v ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_vtos ( void )
2010-02-15 23:26:55 +00:00
{
2010-02-17 11:03:34 +00:00
char * s ;
s = PR_GetTempString ( ) ;
sprintf ( s , " '%5.1f %5.1f %5.1f' " , G_VECTOR ( OFS_PARM0 ) [ 0 ] , G_VECTOR ( OFS_PARM0 ) [ 1 ] , G_VECTOR ( OFS_PARM0 ) [ 2 ] ) ;
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
G_INT ( OFS_RETURN ) = PR_SetEngineString ( s ) ;
2010-02-15 23:26:55 +00:00
}
2011-12-12 08:56:25 +00:00
static void PF_Spawn ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ed ;
2011-12-12 08:56:25 +00:00
2010-02-15 23:26:55 +00:00
ed = ED_Alloc ( ) ;
2011-12-12 08:56:25 +00:00
2010-02-15 23:26:55 +00:00
RETURN_EDICT ( ed ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_Remove ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ed ;
ed = G_EDICT ( OFS_PARM0 ) ;
ED_Free ( ed ) ;
}
// entity (entity start, .string field, string match) find = #5;
2011-12-12 08:56:25 +00:00
static void PF_Find ( void )
2010-02-15 23:26:55 +00:00
{
int e ;
int f ;
2010-08-29 02:22:55 +00:00
const char * s , * t ;
2010-02-15 23:26:55 +00:00
edict_t * ed ;
e = G_EDICTNUM ( OFS_PARM0 ) ;
f = G_INT ( OFS_PARM1 ) ;
s = G_STRING ( OFS_PARM2 ) ;
if ( ! s )
PR_RunError ( " PF_Find: bad search string " ) ;
2018-05-01 00:35:14 +00:00
for ( e + + ; e < qcvm - > num_edicts ; e + + )
2010-02-15 23:26:55 +00:00
{
ed = EDICT_NUM ( e ) ;
if ( ed - > free )
continue ;
t = E_STRING ( ed , f ) ;
if ( ! t )
continue ;
if ( ! strcmp ( t , s ) )
{
RETURN_EDICT ( ed ) ;
return ;
}
}
2018-05-01 00:35:14 +00:00
RETURN_EDICT ( qcvm - > edicts ) ;
2010-02-15 23:26:55 +00:00
}
2011-12-12 08:56:25 +00:00
static void PR_CheckEmptyString ( const char * s )
2010-02-15 23:26:55 +00:00
{
if ( s [ 0 ] < = ' ' )
PR_RunError ( " Bad string " ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_precache_file ( void )
2010-02-15 23:26:55 +00:00
{ // precache_file is only used to copy files with qcc, it does nothing
G_INT ( OFS_RETURN ) = G_INT ( OFS_PARM0 ) ;
}
2017-09-17 02:12:53 +00:00
int SV_Precache_Sound ( const char * s )
{ //must be a persistent string.
int i ;
for ( i = 0 ; i < MAX_SOUNDS ; i + + )
{
if ( ! sv . sound_precache [ i ] )
{
if ( sv . state ! = ss_loading ) //spike -- moved this so that there's no actual error any more.
{
Con_Warning ( " PF_precache_sound( \" %s \" ): Precache should only be done in spawn functions \n " , s ) ;
//let existing clients know about it
MSG_WriteByte ( & sv . reliable_datagram , svcdp_precache ) ;
MSG_WriteShort ( & sv . reliable_datagram , i | 0x8000 ) ;
MSG_WriteString ( & sv . reliable_datagram , s ) ;
}
sv . sound_precache [ i ] = s ;
return i ;
}
if ( ! strcmp ( sv . sound_precache [ i ] , s ) )
{
if ( sv . state ! = ss_loading & & ! pr_checkextension . value )
Con_Warning ( " PF_precache_sound( \" %s \" ): Precache should only be done in spawn functions \n " , s ) ;
return i ;
}
}
return 0 ;
}
2018-05-01 00:35:14 +00:00
static void PF_sv_precache_sound ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * s ;
2010-02-15 23:26:55 +00:00
s = G_STRING ( OFS_PARM0 ) ;
G_INT ( OFS_RETURN ) = G_INT ( OFS_PARM0 ) ;
PR_CheckEmptyString ( s ) ;
2017-09-17 02:12:53 +00:00
if ( ! SV_Precache_Sound ( s ) )
PR_RunError ( " PF_precache_sound: overflow " ) ;
}
int SV_Precache_Model ( const char * s )
{
size_t i ;
for ( i = 0 ; i < MAX_MODELS ; i + + )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
if ( ! sv . model_precache [ i ] )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
if ( sv . state ! = ss_loading )
{
//let existing clients know about it
MSG_WriteByte ( & sv . reliable_datagram , svcdp_precache ) ;
2020-06-24 01:49:35 +00:00
MSG_WriteShort ( & sv . reliable_datagram , i | 0x0000 ) ;
2017-09-17 02:12:53 +00:00
MSG_WriteString ( & sv . reliable_datagram , s ) ;
}
sv . model_precache [ i ] = s ;
sv . models [ i ] = Mod_ForName ( s , i = = 1 ) ;
return i ;
2010-02-15 23:26:55 +00:00
}
2017-09-17 02:12:53 +00:00
if ( ! strcmp ( sv . model_precache [ i ] , s ) )
return i ;
2010-02-15 23:26:55 +00:00
}
2017-09-17 02:12:53 +00:00
return 0 ;
2010-02-15 23:26:55 +00:00
}
2018-05-01 00:35:14 +00:00
static void PF_sv_precache_model ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * s ;
2010-02-15 23:26:55 +00:00
int i ;
s = G_STRING ( OFS_PARM0 ) ;
G_INT ( OFS_RETURN ) = G_INT ( OFS_PARM0 ) ;
PR_CheckEmptyString ( s ) ;
2011-12-12 08:56:25 +00:00
for ( i = 0 ; i < MAX_MODELS ; i + + )
2010-02-15 23:26:55 +00:00
{
if ( ! sv . model_precache [ i ] )
{
2017-09-17 02:12:53 +00:00
if ( sv . state ! = ss_loading )
{
Con_Warning ( " PF_precache_model( \" %s \" ): Precache should only be done in spawn functions \n " , s ) ;
//let existing clients know about it
MSG_WriteByte ( & sv . reliable_datagram , svcdp_precache ) ;
MSG_WriteShort ( & sv . reliable_datagram , i | 0x8000 ) ;
MSG_WriteString ( & sv . reliable_datagram , s ) ;
}
2010-02-15 23:26:55 +00:00
sv . model_precache [ i ] = s ;
2017-09-17 02:12:53 +00:00
sv . models [ i ] = Mod_ForName ( s , i = = 1 ) ;
2010-02-15 23:26:55 +00:00
return ;
}
if ( ! strcmp ( sv . model_precache [ i ] , s ) )
2017-09-17 02:12:53 +00:00
{
if ( sv . state ! = ss_loading & & ! pr_checkextension . value )
Con_Warning ( " PF_precache_model( \" %s \" ): Precache should only be done in spawn functions \n " , s ) ;
2010-02-15 23:26:55 +00:00
return ;
2017-09-17 02:12:53 +00:00
}
2010-02-15 23:26:55 +00:00
}
PR_RunError ( " PF_precache_model: overflow " ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_coredump ( void )
2010-02-15 23:26:55 +00:00
{
ED_PrintEdicts ( ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_traceon ( void )
2010-02-15 23:26:55 +00:00
{
2018-05-01 00:35:14 +00:00
qcvm - > trace = true ;
2010-02-15 23:26:55 +00:00
}
2011-12-12 08:56:25 +00:00
static void PF_traceoff ( void )
2010-02-15 23:26:55 +00:00
{
2018-05-01 00:35:14 +00:00
qcvm - > trace = false ;
2010-02-15 23:26:55 +00:00
}
2011-12-12 08:56:25 +00:00
static void PF_eprint ( void )
2010-02-15 23:26:55 +00:00
{
ED_PrintNum ( G_EDICTNUM ( OFS_PARM0 ) ) ;
}
/*
= = = = = = = = = = = = = = =
PF_walkmove
float ( float yaw , float dist ) walkmove
= = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_walkmove ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ent ;
float yaw , dist ;
vec3_t move ;
dfunction_t * oldf ;
2011-12-12 08:56:25 +00:00
int oldself ;
2010-02-15 23:26:55 +00:00
ent = PROG_TO_EDICT ( pr_global_struct - > self ) ;
yaw = G_FLOAT ( OFS_PARM0 ) ;
dist = G_FLOAT ( OFS_PARM1 ) ;
if ( ! ( ( int ) ent - > v . flags & ( FL_ONGROUND | FL_FLY | FL_SWIM ) ) )
{
G_FLOAT ( OFS_RETURN ) = 0 ;
return ;
}
2011-12-12 08:56:25 +00:00
yaw = yaw * M_PI * 2 / 360 ;
2010-02-15 23:26:55 +00:00
2011-12-12 08:56:25 +00:00
move [ 0 ] = cos ( yaw ) * dist ;
move [ 1 ] = sin ( yaw ) * dist ;
2010-02-15 23:26:55 +00:00
move [ 2 ] = 0 ;
// save program state, because SV_movestep may call other progs
2018-05-01 00:35:14 +00:00
oldf = qcvm - > xfunction ;
2010-02-15 23:26:55 +00:00
oldself = pr_global_struct - > self ;
G_FLOAT ( OFS_RETURN ) = SV_movestep ( ent , move , true ) ;
// restore program state
2018-05-01 00:35:14 +00:00
qcvm - > xfunction = oldf ;
2010-02-15 23:26:55 +00:00
pr_global_struct - > self = oldself ;
}
/*
= = = = = = = = = = = = = = =
PF_droptofloor
void ( ) droptofloor
= = = = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_droptofloor ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ent ;
vec3_t end ;
trace_t trace ;
ent = PROG_TO_EDICT ( pr_global_struct - > self ) ;
VectorCopy ( ent - > v . origin , end ) ;
end [ 2 ] - = 256 ;
trace = SV_Move ( ent - > v . origin , ent - > v . mins , ent - > v . maxs , end , false , ent ) ;
if ( trace . fraction = = 1 | | trace . allsolid )
G_FLOAT ( OFS_RETURN ) = 0 ;
else
{
VectorCopy ( trace . endpos , ent - > v . origin ) ;
SV_LinkEdict ( ent , false ) ;
ent - > v . flags = ( int ) ent - > v . flags | FL_ONGROUND ;
ent - > v . groundentity = EDICT_TO_PROG ( trace . ent ) ;
G_FLOAT ( OFS_RETURN ) = 1 ;
}
}
/*
= = = = = = = = = = = = = = =
PF_lightstyle
void ( float style , string value ) lightstyle
= = = = = = = = = = = = = = =
*/
2018-05-01 00:35:14 +00:00
static void PF_sv_lightstyle ( void )
2010-02-15 23:26:55 +00:00
{
int style ;
2010-08-29 02:22:55 +00:00
const char * val ;
2010-02-15 23:26:55 +00:00
client_t * client ;
2011-12-12 08:56:25 +00:00
int j ;
2010-02-15 23:26:55 +00:00
style = G_FLOAT ( OFS_PARM0 ) ;
val = G_STRING ( OFS_PARM1 ) ;
2017-04-01 21:31:07 +00:00
// bounds check to avoid clobbering sv struct
if ( style < 0 | | style > = MAX_LIGHTSTYLES )
2017-06-22 03:32:14 +00:00
{
Con_DWarning ( " PF_lightstyle: invalid style %d \n " , style ) ;
return ;
}
2017-04-01 21:31:07 +00:00
2010-02-15 23:26:55 +00:00
// change the string in sv
sv . lightstyles [ style ] = val ;
// send message to all clients on this server
if ( sv . state ! = ss_active )
return ;
2011-12-12 08:56:25 +00:00
for ( j = 0 , client = svs . clients ; j < svs . maxclients ; j + + , client + + )
{
2010-02-15 23:26:55 +00:00
if ( client - > active | | client - > spawned )
{
2019-09-10 14:41:11 +00:00
if ( style > 0xff )
{
MSG_WriteByte ( & client - > message , svc_stufftext ) ;
MSG_WriteString ( & client - > message , va ( " //ls %i \" %s \" \n " , style , val ) ) ;
}
else
{
MSG_WriteChar ( & client - > message , svc_lightstyle ) ;
MSG_WriteChar ( & client - > message , style ) ;
MSG_WriteString ( & client - > message , val ) ;
}
2010-02-15 23:26:55 +00:00
}
2011-12-12 08:56:25 +00:00
}
2010-02-15 23:26:55 +00:00
}
2011-12-12 08:56:25 +00:00
static void PF_rint ( void )
2010-02-15 23:26:55 +00:00
{
float f ;
f = G_FLOAT ( OFS_PARM0 ) ;
if ( f > 0 )
G_FLOAT ( OFS_RETURN ) = ( int ) ( f + 0.5 ) ;
else
G_FLOAT ( OFS_RETURN ) = ( int ) ( f - 0.5 ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_floor ( void )
2010-02-15 23:26:55 +00:00
{
G_FLOAT ( OFS_RETURN ) = floor ( G_FLOAT ( OFS_PARM0 ) ) ;
}
2011-12-12 08:56:25 +00:00
static void PF_ceil ( void )
2010-02-15 23:26:55 +00:00
{
G_FLOAT ( OFS_RETURN ) = ceil ( G_FLOAT ( OFS_PARM0 ) ) ;
}
/*
= = = = = = = = = = = = =
PF_checkbottom
= = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_checkbottom ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ent ;
ent = G_EDICT ( OFS_PARM0 ) ;
G_FLOAT ( OFS_RETURN ) = SV_CheckBottom ( ent ) ;
}
/*
= = = = = = = = = = = = =
PF_pointcontents
= = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_pointcontents ( void )
2010-02-15 23:26:55 +00:00
{
float * v ;
v = G_VECTOR ( OFS_PARM0 ) ;
G_FLOAT ( OFS_RETURN ) = SV_PointContents ( v ) ;
}
/*
= = = = = = = = = = = = =
PF_nextent
entity nextent ( entity )
= = = = = = = = = = = = =
*/
2011-12-12 08:56:25 +00:00
static void PF_nextent ( void )
2010-02-15 23:26:55 +00:00
{
int i ;
edict_t * ent ;
i = G_EDICTNUM ( OFS_PARM0 ) ;
while ( 1 )
{
i + + ;
2018-05-01 00:35:14 +00:00
if ( i = = qcvm - > num_edicts )
2010-02-15 23:26:55 +00:00
{
2018-05-01 00:35:14 +00:00
RETURN_EDICT ( qcvm - > edicts ) ;
2010-02-15 23:26:55 +00:00
return ;
}
ent = EDICT_NUM ( i ) ;
if ( ! ent - > free )
{
RETURN_EDICT ( ent ) ;
return ;
}
}
}
/*
= = = = = = = = = = = = =
PF_aim
Pick a vector for the player to shoot along
vector aim ( entity , missilespeed )
= = = = = = = = = = = = =
*/
2014-08-07 18:47:37 +00:00
cvar_t sv_aim = { " sv_aim " , " 1 " , CVAR_NONE } ; // ericw -- turn autoaim off by default. was 0.93
2011-12-12 08:56:25 +00:00
static void PF_aim ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ent , * check , * bestent ;
vec3_t start , dir , end , bestdir ;
int i , j ;
trace_t tr ;
float dist , bestdist ;
float speed ;
ent = G_EDICT ( OFS_PARM0 ) ;
speed = G_FLOAT ( OFS_PARM1 ) ;
2011-04-12 12:36:04 +00:00
( void ) speed ; /* variable set but not used */
2010-02-15 23:26:55 +00:00
VectorCopy ( ent - > v . origin , start ) ;
start [ 2 ] + = 20 ;
// try sending a trace straight
VectorCopy ( pr_global_struct - > v_forward , dir ) ;
VectorMA ( start , 2048 , dir , end ) ;
tr = SV_Move ( start , vec3_origin , vec3_origin , end , false , ent ) ;
if ( tr . ent & & tr . ent - > v . takedamage = = DAMAGE_AIM
2011-12-12 08:56:25 +00:00
& & ( ! teamplay . value | | ent - > v . team < = 0 | | ent - > v . team ! = tr . ent - > v . team ) )
2010-02-15 23:26:55 +00:00
{
VectorCopy ( pr_global_struct - > v_forward , G_VECTOR ( OFS_RETURN ) ) ;
return ;
}
// try all possible entities
VectorCopy ( dir , bestdir ) ;
bestdist = sv_aim . value ;
bestent = NULL ;
2018-05-01 00:35:14 +00:00
check = NEXT_EDICT ( qcvm - > edicts ) ;
for ( i = 1 ; i < qcvm - > num_edicts ; i + + , check = NEXT_EDICT ( check ) )
2010-02-15 23:26:55 +00:00
{
if ( check - > v . takedamage ! = DAMAGE_AIM )
continue ;
if ( check = = ent )
continue ;
if ( teamplay . value & & ent - > v . team > 0 & & ent - > v . team = = check - > v . team )
continue ; // don't aim at teammate
2011-12-12 08:56:25 +00:00
for ( j = 0 ; j < 3 ; j + + )
end [ j ] = check - > v . origin [ j ] + 0.5 * ( check - > v . mins [ j ] + check - > v . maxs [ j ] ) ;
2010-02-15 23:26:55 +00:00
VectorSubtract ( end , start , dir ) ;
VectorNormalize ( dir ) ;
dist = DotProduct ( dir , pr_global_struct - > v_forward ) ;
if ( dist < bestdist )
continue ; // to far to turn
tr = SV_Move ( start , vec3_origin , vec3_origin , end , false , ent ) ;
if ( tr . ent = = check )
{ // can shoot at this one
bestdist = dist ;
bestent = check ;
}
}
if ( bestent )
{
VectorSubtract ( bestent - > v . origin , ent - > v . origin , dir ) ;
dist = DotProduct ( dir , pr_global_struct - > v_forward ) ;
VectorScale ( pr_global_struct - > v_forward , dist , end ) ;
end [ 2 ] = dir [ 2 ] ;
VectorNormalize ( end ) ;
VectorCopy ( end , G_VECTOR ( OFS_RETURN ) ) ;
}
else
{
VectorCopy ( bestdir , G_VECTOR ( OFS_RETURN ) ) ;
}
}
/*
= = = = = = = = = = = = = =
PF_changeyaw
This was a major timewaster in progs , so it was converted to C
= = = = = = = = = = = = = =
*/
void PF_changeyaw ( void )
{
edict_t * ent ;
float ideal , current , move , speed ;
ent = PROG_TO_EDICT ( pr_global_struct - > self ) ;
current = anglemod ( ent - > v . angles [ 1 ] ) ;
ideal = ent - > v . ideal_yaw ;
speed = ent - > v . yaw_speed ;
if ( current = = ideal )
return ;
move = ideal - current ;
if ( ideal > current )
{
if ( move > = 180 )
move = move - 360 ;
}
else
{
if ( move < = - 180 )
move = move + 360 ;
}
if ( move > 0 )
{
if ( move > speed )
move = speed ;
}
else
{
if ( move < - speed )
move = - speed ;
}
ent - > v . angles [ 1 ] = anglemod ( current + move ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
MESSAGE WRITING
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
sizebuf_t * WriteDest ( void )
2010-02-15 23:26:55 +00:00
{
int entnum ;
int dest ;
edict_t * ent ;
dest = G_FLOAT ( OFS_PARM0 ) ;
switch ( dest )
{
case MSG_BROADCAST :
return & sv . datagram ;
case MSG_ONE :
ent = PROG_TO_EDICT ( pr_global_struct - > msg_entity ) ;
entnum = NUM_FOR_EDICT ( ent ) ;
if ( entnum < 1 | | entnum > svs . maxclients )
PR_RunError ( " WriteDest: not a client " ) ;
return & svs . clients [ entnum - 1 ] . message ;
case MSG_ALL :
return & sv . reliable_datagram ;
case MSG_INIT :
return & sv . signon ;
2017-09-17 02:12:53 +00:00
case MSG_EXT_MULTICAST :
return & sv . multicast ;
2010-02-15 23:26:55 +00:00
default :
PR_RunError ( " WriteDest: bad destination " ) ;
break ;
}
return NULL ;
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteByte ( void )
2010-02-15 23:26:55 +00:00
{
MSG_WriteByte ( WriteDest ( ) , G_FLOAT ( OFS_PARM1 ) ) ;
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteChar ( void )
2010-02-15 23:26:55 +00:00
{
MSG_WriteChar ( WriteDest ( ) , G_FLOAT ( OFS_PARM1 ) ) ;
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteShort ( void )
2010-02-15 23:26:55 +00:00
{
MSG_WriteShort ( WriteDest ( ) , G_FLOAT ( OFS_PARM1 ) ) ;
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteLong ( void )
2010-02-15 23:26:55 +00:00
{
MSG_WriteLong ( WriteDest ( ) , G_FLOAT ( OFS_PARM1 ) ) ;
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteAngle ( void )
2010-02-15 23:26:55 +00:00
{
2016-06-24 06:15:41 +00:00
MSG_WriteAngle ( WriteDest ( ) , G_FLOAT ( OFS_PARM1 ) , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteCoord ( void )
2010-02-15 23:26:55 +00:00
{
2016-06-24 06:15:41 +00:00
MSG_WriteCoord ( WriteDest ( ) , G_FLOAT ( OFS_PARM1 ) , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteString ( void )
2010-02-15 23:26:55 +00:00
{
MSG_WriteString ( WriteDest ( ) , G_STRING ( OFS_PARM1 ) ) ;
}
2018-05-01 00:35:14 +00:00
static void PF_sv_WriteEntity ( void )
2010-02-15 23:26:55 +00:00
{
2020-06-19 22:11:22 +00:00
extern unsigned int sv_protocol_pext2 ; //spike -- this ought to be client-specific, but we can't cope with that, so just live with the problems when ents>32768 (which QS doesn't support anyway)
MSG_WriteEntity ( WriteDest ( ) , G_EDICTNUM ( OFS_PARM1 ) , sv_protocol_pext2 ) ;
2010-02-15 23:26:55 +00:00
}
//=============================================================================
2018-05-01 00:35:14 +00:00
static void PF_sv_makestatic ( void )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
entity_state_t * st ;
2011-12-12 08:56:25 +00:00
edict_t * ent ;
2010-02-15 23:26:55 +00:00
ent = G_EDICT ( OFS_PARM0 ) ;
2017-09-17 02:12:53 +00:00
if ( sv . num_statics = = sv . max_statics )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
int nm = sv . max_statics + 128 ;
entity_state_t * n = ( nm * sizeof ( * n ) < sv . max_statics * sizeof ( * n ) ) ? NULL : realloc ( sv . static_entities , nm * sizeof ( * n ) ) ;
if ( ! n )
PR_RunError ( " PF_makestatic: out of memory " ) ; //shouldn't really happen.
sv . static_entities = n ;
memset ( sv . static_entities + sv . max_statics , 0 , ( nm - sv . max_statics ) * sizeof ( * n ) ) ;
sv . max_statics = nm ;
2010-02-15 23:26:55 +00:00
}
2017-09-17 02:12:53 +00:00
st = & sv . static_entities [ sv . num_statics ] ;
SV_BuildEntityState ( ent , st ) ;
if ( st - > alpha = = ENTALPHA_ZERO )
; //no point
2010-02-15 23:26:55 +00:00
else
2017-09-17 02:12:53 +00:00
sv . num_statics + + ;
2010-02-15 23:26:55 +00:00
// throw the entity away now
ED_Free ( ent ) ;
}
//=============================================================================
/*
= = = = = = = = = = = = = =
PF_setspawnparms
= = = = = = = = = = = = = =
*/
2018-05-01 00:35:14 +00:00
static void PF_sv_setspawnparms ( void )
2010-02-15 23:26:55 +00:00
{
edict_t * ent ;
int i ;
client_t * client ;
ent = G_EDICT ( OFS_PARM0 ) ;
i = NUM_FOR_EDICT ( ent ) ;
if ( i < 1 | | i > svs . maxclients )
PR_RunError ( " Entity is not a client " ) ;
// copy spawn parms out of the client_t
client = svs . clients + ( i - 1 ) ;
2019-05-02 03:25:41 +00:00
for ( i = 0 ; i < NUM_BASIC_SPAWN_PARMS ; i + + )
2010-02-15 23:26:55 +00:00
( & pr_global_struct - > parm1 ) [ i ] = client - > spawn_parms [ i ] ;
2019-05-02 03:25:41 +00:00
if ( pr_checkextension . value )
{ //extended spawn parms
for ( ; i < NUM_TOTAL_SPAWN_PARMS ; i + + )
{
ddef_t * g = ED_FindGlobal ( va ( " parm%i " , i + 1 ) ) ;
if ( g )
qcvm - > globals [ g - > ofs ] = client - > spawn_parms [ i ] ;
}
}
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = =
PF_changelevel
= = = = = = = = = = = = = =
*/
2018-05-01 00:35:14 +00:00
static void PF_sv_changelevel ( void )
2010-02-15 23:26:55 +00:00
{
2010-08-29 02:22:55 +00:00
const char * s ;
2010-02-15 23:26:55 +00:00
// make sure we don't issue two changelevels
if ( svs . changelevel_issued )
return ;
svs . changelevel_issued = true ;
s = G_STRING ( OFS_PARM0 ) ;
Cbuf_AddText ( va ( " changelevel %s \n " , s ) ) ;
}
2017-09-17 02:12:53 +00:00
void PF_Fixme ( void ) ;
//{
// PR_RunError ("unimplemented builtin");
//}
void PR_spawnfunc_misc_model ( edict_t * self )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
eval_t * val ;
if ( ! self - > v . model & & ( val = GetEdictFieldValue ( self , ED_FindFieldOffset ( " mdl " ) ) ) )
self - > v . model = val - > string ;
if ( ! * PR_GetString ( self - > v . model ) ) //must have a model, because otherwise various things will assume its not valid at all.
self - > v . model = PR_SetEngineString ( " *null " ) ;
if ( self - > v . angles [ 1 ] < 0 ) //mimic AD. shame there's no avelocity clientside.
self - > v . angles [ 1 ] = ( rand ( ) * ( 360.0f / RAND_MAX ) ) ;
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
//make sure the model is precached, to avoid errors.
G_INT ( OFS_PARM0 ) = self - > v . model ;
2018-05-01 00:35:14 +00:00
PF_sv_precache_model ( ) ;
2017-09-17 02:12:53 +00:00
//and lets just call makestatic instead of worrying if it'll interfere with the rest of the qc.
G_INT ( OFS_PARM0 ) = EDICT_TO_PROG ( self ) ;
2018-05-01 00:35:14 +00:00
PF_sv_makestatic ( ) ;
2017-09-17 02:12:53 +00:00
}
2010-02-15 23:26:55 +00:00
2018-05-01 00:35:14 +00:00
builtin_t pr_ssqcbuiltins [ ] =
2010-02-15 23:26:55 +00:00
{
2011-12-12 08:56:25 +00:00
PF_Fixme ,
PF_makevectors , // void(entity e) makevectors = #1
PF_setorigin , // void(entity e, vector o) setorigin = #2
2018-05-01 00:35:14 +00:00
PF_sv_setmodel , // void(entity e, string m) setmodel = #3
2011-12-12 08:56:25 +00:00
PF_setsize , // void(entity e, vector min, vector max) setsize = #4
PF_Fixme , // void(entity e, vector min, vector max) setabssize = #5
PF_break , // void() break = #6
PF_random , // float() random = #7
PF_sound , // void(entity e, float chan, string samp) sound = #8
PF_normalize , // vector(vector v) normalize = #9
PF_error , // void(string e) error = #10
PF_objerror , // void(string e) objerror = #11
PF_vlen , // float(vector v) vlen = #12
PF_vectoyaw , // float(vector v) vectoyaw = #13
PF_Spawn , // entity() spawn = #14
PF_Remove , // void(entity e) remove = #15
PF_traceline , // float(vector v1, vector v2, float tryents) traceline = #16
2018-05-01 00:35:14 +00:00
PF_sv_checkclient , // entity() clientlist = #17
2011-12-12 08:56:25 +00:00
PF_Find , // entity(entity start, .string fld, string match) find = #18
2018-05-01 00:35:14 +00:00
PF_sv_precache_sound , // void(string s) precache_sound = #19
PF_sv_precache_model , // void(string s) precache_model = #20
2011-12-12 08:56:25 +00:00
PF_stuffcmd , // void(entity client, string s)stuffcmd = #21
PF_findradius , // entity(vector org, float rad) findradius = #22
PF_bprint , // void(string s) bprint = #23
PF_sprint , // void(entity client, string s) sprint = #24
PF_dprint , // void(string s) dprint = #25
PF_ftos , // void(string s) ftos = #26
PF_vtos , // void(string s) vtos = #27
PF_coredump ,
PF_traceon ,
PF_traceoff ,
PF_eprint , // void(entity e) debug print an entire entity
PF_walkmove , // float(float yaw, float dist) walkmove
PF_Fixme , // float(float yaw, float dist) walkmove
PF_droptofloor ,
2018-05-01 00:35:14 +00:00
PF_sv_lightstyle ,
2011-12-12 08:56:25 +00:00
PF_rint ,
PF_floor ,
PF_ceil ,
PF_Fixme ,
PF_checkbottom ,
PF_pointcontents ,
PF_Fixme ,
PF_fabs ,
PF_aim ,
PF_cvar ,
PF_localcmd ,
PF_nextent ,
PF_particle ,
PF_changeyaw ,
PF_Fixme ,
PF_vectoangles ,
2018-05-01 00:35:14 +00:00
PF_sv_WriteByte ,
PF_sv_WriteChar ,
PF_sv_WriteShort ,
PF_sv_WriteLong ,
PF_sv_WriteCoord ,
PF_sv_WriteAngle ,
PF_sv_WriteString ,
PF_sv_WriteEntity ,
2011-12-12 08:56:25 +00:00
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
SV_MoveToGoal ,
PF_precache_file ,
2018-05-01 00:35:14 +00:00
PF_sv_makestatic ,
2011-12-12 08:56:25 +00:00
2018-05-01 00:35:14 +00:00
PF_sv_changelevel ,
2011-12-12 08:56:25 +00:00
PF_Fixme ,
PF_cvar_set ,
PF_centerprint ,
2018-05-01 00:35:14 +00:00
PF_sv_ambientsound ,
2011-12-12 08:56:25 +00:00
2018-05-01 00:35:14 +00:00
PF_sv_precache_model ,
PF_sv_precache_sound , // precache_sound2 is different only for qcc
2011-12-12 08:56:25 +00:00
PF_precache_file ,
2018-05-01 00:35:14 +00:00
PF_sv_setspawnparms
2010-02-15 23:26:55 +00:00
} ;
2018-05-01 00:35:14 +00:00
int pr_ssqcnumbuiltins = sizeof ( pr_ssqcbuiltins ) / sizeof ( pr_ssqcbuiltins [ 0 ] ) ;
2010-02-15 23:26:55 +00:00
2018-07-07 14:05:34 +00:00
static void PF_cl_sound ( void )
{
const char * sample ;
int channel ;
edict_t * entity ;
int volume ;
float attenuation ;
int entnum ;
entity = G_EDICT ( OFS_PARM0 ) ;
channel = G_FLOAT ( OFS_PARM1 ) ;
sample = G_STRING ( OFS_PARM2 ) ;
volume = G_FLOAT ( OFS_PARM3 ) * 255 ;
attenuation = G_FLOAT ( OFS_PARM4 ) ;
entnum = NUM_FOR_EDICT ( entity ) ;
//fullcsqc fixme: if (entity->v->entnum)
entnum * = - 1 ;
S_StartSound ( entnum , channel , S_PrecacheSound ( sample ) , entity - > v . origin , volume , attenuation ) ;
}
static void PF_cl_precache_sound ( void )
{
const char * s ;
s = G_STRING ( OFS_PARM0 ) ;
G_INT ( OFS_RETURN ) = G_INT ( OFS_PARM0 ) ;
PR_CheckEmptyString ( s ) ;
//precache sounds are optional in quake's sound system. NULL is a valid response so don't check.
S_PrecacheSound ( s ) ;
}
int CL_Precache_Model ( const char * name )
{
int i ;
if ( ! * name )
return 0 ;
//if the server precached the model then we don't need to do anything.
for ( i = 1 ; i < MAX_MODELS ; i + + )
{
if ( ! * cl . model_name [ i ] )
break ; //no more
if ( ! strcmp ( cl . model_name [ i ] , name ) )
return i ;
}
PR_RunError ( " CL_Precache_Model: implementme " ) ;
return 0 ;
}
static void PF_cl_precache_model ( void )
{
const char * s ;
int i ;
s = G_STRING ( OFS_PARM0 ) ;
G_INT ( OFS_RETURN ) = G_INT ( OFS_PARM0 ) ;
PR_CheckEmptyString ( s ) ;
//if the server precached the model then we don't need to do anything.
for ( i = 1 ; i < MAX_MODELS ; i + + )
{
if ( ! * cl . model_name [ i ] )
break ; //no more
if ( ! strcmp ( cl . model_name [ i ] , s ) )
return ;
}
PR_RunError ( " PF_cl_precache_model: implementme " ) ;
}
static void PF_cl_setmodel ( void )
{
int i ;
const char * m ;
qmodel_t * mod ;
edict_t * e ;
e = G_EDICT ( OFS_PARM0 ) ;
m = G_STRING ( OFS_PARM1 ) ;
i = CL_Precache_Model ( m ) ;
mod = qcvm - > GetModel ( i ) ;
e - > v . model = mod ? PR_SetEngineString ( mod - > name ) : 0 ; //I believe this to be safe in QS.
e - > v . modelindex = i ;
if ( mod )
//johnfitz -- correct physics cullboxes for bmodels
/* Spike -- THIS IS A HUGE CLUSTERFUCK.
the mins / maxs sizes of models in vanilla was always set to xyz - 16 / + 16.
which causes issues with clientside culling .
many engines fixed that , but not here .
which means that setmodel - without - setsize is now fucked .
the qc will usually do a setsize after setmodel anyway , so applying that fix here will do nothing .
you ' d need to apply the serverside version of the cull fix in SV_LinkEdict instead , which is where the pvs is calculated .
tracebox is limited to specific hull sizes . the traces are biased such that they ' re aligned to the mins point of the box , rather than the center of the trace .
so vanilla ' s ' - 16 - 16 - 16 ' / ' 16 16 16 ' is wrong for Z ( which is usually corrected for with gravity anyway ) , but X + Y will be correctly aligned for the resulting hull .
but traceboxes using models with - 12 or - 20 or whatever will be biased / offcenter in the X + Y axis ( as well as Z , but that ' s still mostly unimportant )
deciding whether to replicate the vanilla behaviour based upon model type sucks .
vanilla :
brush - always the models size
mdl - always [ - 16 , - 16 , - 16 ] , [ 16 , 16 , 16 ]
quakespasm :
brush - always the models size
mdl - always the models size
quakeworld :
* . bsp - always the models size ( matched by extension rather than type )
other - model isn ' t even loaded , setmodel does not do setsize at all .
fte default ( with nq mod ) :
* . bsp ( or sv_gameplayfix_setmodelrealbox ) - always the models size ( matched by extension rather than type )
other - always [ - 16 , - 16 , - 16 ] , [ 16 , 16 , 16 ]
fte ' s behaviour means :
a ) dedicated servers don ' t have to bother loading non - mdls .
b ) nq mods still work fine , where extensions are adhered to in the original qc .
c ) when replacement models are used for bsp models , things still work without them reverting to + / - 16.
*/
{
if ( mod - > type = = mod_brush | | ! sv_gameplayfix_setmodelrealbox . value )
SetMinMaxSize ( e , mod - > clipmins , mod - > clipmaxs , true ) ;
else
SetMinMaxSize ( e , mod - > mins , mod - > maxs , true ) ;
}
//johnfitz
else
SetMinMaxSize ( e , vec3_origin , vec3_origin , true ) ;
}
2018-05-01 00:35:14 +00:00
# define PF_NoCSQC PF_Fixme
# define PF_CSQCToDo PF_Fixme
builtin_t pr_csqcbuiltins [ ] =
{
PF_Fixme ,
PF_makevectors , // void(entity e) makevectors = #1
PF_setorigin , // void(entity e, vector o) setorigin = #2
2018-07-07 14:05:34 +00:00
PF_cl_setmodel , // void(entity e, string m) setmodel = #3
2018-05-01 00:35:14 +00:00
PF_setsize , // void(entity e, vector min, vector max) setsize = #4
PF_Fixme , // void(entity e, vector min, vector max) setabssize = #5
PF_break , // void() break = #6
PF_random , // float() random = #7
2018-07-07 14:05:34 +00:00
PF_cl_sound , // void(entity e, float chan, string samp) sound = #8
2018-05-01 00:35:14 +00:00
PF_normalize , // vector(vector v) normalize = #9
PF_error , // void(string e) error = #10
PF_objerror , // void(string e) objerror = #11
PF_vlen , // float(vector v) vlen = #12
PF_vectoyaw , // float(vector v) vectoyaw = #13
PF_Spawn , // entity() spawn = #14
PF_Remove , // void(entity e) remove = #15
PF_traceline , // float(vector v1, vector v2, float tryents) traceline = #16
PF_NoCSQC , // entity() checkclient (was: clientlist, apparently) = #17
PF_Find , // entity(entity start, .string fld, string match) find = #18
2018-07-07 14:05:34 +00:00
PF_cl_precache_sound , // void(string s) precache_sound = #19
PF_cl_precache_model , // void(string s) precache_model = #20
2018-05-01 00:35:14 +00:00
PF_NoCSQC , // void(entity client, string s)stuffcmd = #21
PF_findradius , // entity(vector org, float rad) findradius = #22
PF_NoCSQC , // void(string s) bprint = #23
PF_NoCSQC , // void(entity client, string s) sprint = #24
PF_dprint , // void(string s) dprint = #25
PF_ftos , // void(string s) ftos = #26
PF_vtos , // void(string s) vtos = #27
PF_coredump ,
PF_traceon ,
PF_traceoff ,
PF_eprint , // void(entity e) debug print an entire entity
PF_walkmove , // float(float yaw, float dist) walkmove
PF_Fixme , // float(float yaw, float dist) walkmove
PF_droptofloor ,
PF_CSQCToDo , //PF_cl_lightstyle,
PF_rint ,
PF_floor ,
PF_ceil ,
PF_Fixme ,
PF_checkbottom ,
PF_pointcontents ,
PF_Fixme ,
PF_fabs ,
PF_NoCSQC , //PF_aim,
PF_cvar ,
PF_localcmd ,
PF_nextent ,
PF_CSQCToDo , //PF_cl_particle,
PF_changeyaw ,
PF_Fixme ,
PF_vectoangles ,
PF_NoCSQC , //PF_WriteByte,
PF_NoCSQC , //PF_WriteChar,
PF_NoCSQC , //PF_WriteShort,
PF_NoCSQC , //PF_WriteLong,
PF_NoCSQC , //PF_WriteCoord,
PF_NoCSQC , //PF_WriteAngle,
PF_NoCSQC , //PF_WriteString,
PF_NoCSQC , //PF_WriteEntity,
2010-02-15 23:26:55 +00:00
2018-05-01 00:35:14 +00:00
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
PF_Fixme ,
SV_MoveToGoal ,
PF_precache_file ,
PF_CSQCToDo , //PF_cl_makestatic,
PF_NoCSQC , //PF_changelevel,
PF_Fixme ,
PF_cvar_set ,
PF_NoCSQC , //PF_centerprint,
PF_CSQCToDo , //PF_ambientsound,
2018-07-07 14:05:34 +00:00
PF_cl_precache_model ,
PF_cl_precache_sound ,
2018-05-01 00:35:14 +00:00
PF_precache_file ,
PF_NoCSQC , //PF_setspawnparms
} ;
int pr_csqcnumbuiltins = sizeof ( pr_csqcbuiltins ) / sizeof ( pr_csqcbuiltins [ 0 ] ) ;