6fb100e8b5
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3071 fc73d0e0-1445-4013-8a0c-d673dee63da5
5135 lines
151 KiB
C
5135 lines
151 KiB
C
#include "quakedef.h"
|
|
|
|
/*
|
|
|
|
EXT_CSQC is the 'root' extension
|
|
EXT_CSQC_1 are a collection of additional features to cover omissions in the original spec
|
|
|
|
|
|
note the CHEAT_PARANOID define disables certain EXT_CSQC_1 features,
|
|
in an attempt to prevent the player from finding out where he/she is, thus preventing aimbots.
|
|
This is specifically targetted towards deathmatch mods where each player is a single player.
|
|
In reality, this paranoia provides nothing which could not be done with a cheat proxy.
|
|
Seeing as the client ensures hashes match in the first place, this paranoia gives nothing in the long run.
|
|
Unfortunatly EXT_CSQC was designed around this paranoia.
|
|
*/
|
|
|
|
#ifdef CSQC_DAT
|
|
|
|
#ifdef RGLQUAKE
|
|
#include "glquake.h" //evil to include this
|
|
#include "shader.h"
|
|
#endif
|
|
|
|
//#define CHEAT_PARANOID
|
|
|
|
#include "pr_common.h"
|
|
|
|
#define ANGLE2SHORT(x) ((x/360.0)*65535)
|
|
|
|
static progfuncs_t *csqcprogs;
|
|
|
|
typedef struct csqctreadstate_s {
|
|
float resumetime;
|
|
struct qcthread_s *thread;
|
|
int self;
|
|
int other;
|
|
|
|
struct csqctreadstate_s *next;
|
|
} csqctreadstate_t;
|
|
|
|
static unsigned int csqcchecksum;
|
|
static csqctreadstate_t *csqcthreads;
|
|
qboolean csqc_resortfrags;
|
|
qboolean csqc_drawsbar;
|
|
qboolean csqc_addcrosshair;
|
|
static int num_csqc_edicts;
|
|
static int csqc_fakereadbyte;
|
|
|
|
static int csqc_lplayernum;
|
|
|
|
#define CSQCPROGSGROUP "CSQC progs control"
|
|
cvar_t pr_csmaxedicts = SCVAR("pr_csmaxedicts", "3072");
|
|
cvar_t cl_csqcdebug = SCVAR("cl_csqcdebug", "0"); //prints entity numbers which arrive (so I can tell people not to apply it to players...)
|
|
cvar_t cl_nocsqc = SCVAR("cl_nocsqc", "0");
|
|
cvar_t pr_csqc_coreonerror = SCVAR("pr_csqc_coreonerror", "1");
|
|
|
|
|
|
#define MASK_DELTA 1
|
|
#define MASK_STDVIEWMODEL 2
|
|
|
|
|
|
// standard effect cvars/sounds
|
|
extern cvar_t r_explosionlight;
|
|
extern sfx_t *cl_sfx_wizhit;
|
|
extern sfx_t *cl_sfx_knighthit;
|
|
extern sfx_t *cl_sfx_tink1;
|
|
extern sfx_t *cl_sfx_ric1;
|
|
extern sfx_t *cl_sfx_ric2;
|
|
extern sfx_t *cl_sfx_ric3;
|
|
extern sfx_t *cl_sfx_r_exp3;
|
|
|
|
//If I do it like this, I'll never forget to register something...
|
|
#define csqcglobals \
|
|
globalfunction(init_function, "CSQC_Init"); \
|
|
globalfunction(worldloaded, "CSQC_WorldLoaded"); \
|
|
globalfunction(shutdown_function, "CSQC_Shutdown"); \
|
|
globalfunction(draw_function, "CSQC_UpdateView"); \
|
|
globalfunction(parse_stuffcmd, "CSQC_Parse_StuffCmd"); \
|
|
globalfunction(parse_centerprint, "CSQC_Parse_CenterPrint"); \
|
|
globalfunction(input_event, "CSQC_InputEvent"); \
|
|
globalfunction(input_frame, "CSQC_Input_Frame");/*EXT_CSQC_1*/ \
|
|
globalfunction(console_command, "CSQC_ConsoleCommand"); \
|
|
\
|
|
globalfunction(ent_update, "CSQC_Ent_Update"); \
|
|
globalfunction(ent_remove, "CSQC_Ent_Remove"); \
|
|
globalfunction(delta_update, "CSQC_Delta_Update");/*EXT_CSQC_1*/ \
|
|
globalfunction(delta_remove, "CSQC_Delta_Remove");/*EXT_CSQC_1*/ \
|
|
\
|
|
globalfunction(event_sound, "CSQC_Event_Sound"); \
|
|
globalfunction(serversound, "CSQC_ServerSound");/*obsolete, use event_sound*/ \
|
|
globalfunction(loadresource, "CSQC_LoadResource");/*EXT_CSQC_1*/ \
|
|
globalfunction(parse_tempentity, "CSQC_Parse_TempEntity");/*EXT_CSQC_ABSOLUTLY_VILE*/ \
|
|
\
|
|
/*These are pointers to the csqc's globals.*/ \
|
|
globalfloat(svtime, "time"); /*float Written before entering most qc functions*/ \
|
|
globalfloat(cltime, "cltime"); /*float Written before entering most qc functions*/ \
|
|
globalentity(self, "self"); /*entity Written before entering most qc functions*/ \
|
|
globalentity(other, "other"); /*entity Written before entering most qc functions*/ \
|
|
\
|
|
globalfloat(maxclients, "maxclients"); /*float max number of players allowed*/ \
|
|
\
|
|
globalvector(forward, "v_forward"); /*vector written by anglevectors*/ \
|
|
globalvector(right, "v_right"); /*vector written by anglevectors*/ \
|
|
globalvector(up, "v_up"); /*vector written by anglevectors*/ \
|
|
\
|
|
globalfloat(trace_allsolid, "trace_allsolid"); /*bool written by traceline*/ \
|
|
globalfloat(trace_startsolid, "trace_startsolid"); /*bool written by traceline*/ \
|
|
globalfloat(trace_fraction, "trace_fraction"); /*float written by traceline*/ \
|
|
globalfloat(trace_inwater, "trace_inwater"); /*bool written by traceline*/ \
|
|
globalfloat(trace_inopen, "trace_inopen"); /*bool written by traceline*/ \
|
|
globalvector(trace_endpos, "trace_endpos"); /*vector written by traceline*/ \
|
|
globalvector(trace_plane_normal, "trace_plane_normal"); /*vector written by traceline*/ \
|
|
globalfloat(trace_plane_dist, "trace_plane_dist"); /*float written by traceline*/ \
|
|
globalentity(trace_ent, "trace_ent"); /*entity written by traceline*/ \
|
|
globalfloat(trace_surfaceflags, "trace_surfaceflags"); /*float written by traceline*/ \
|
|
globalfloat(trace_endcontents, "trace_endcontents"); /*float written by traceline EXT_CSQC_1*/ \
|
|
\
|
|
globalfloat(clientcommandframe, "clientcommandframe"); /*float the next frame that will be sent*/ \
|
|
globalfloat(servercommandframe, "servercommandframe"); /*float the most recent frame received from the server*/ \
|
|
\
|
|
globalfloat(player_localentnum, "player_localentnum"); /*float the entity number of the local player*/ \
|
|
globalfloat(player_localnum, "player_localnum"); /*float the entity number of the local player*/ \
|
|
globalfloat(intermission, "intermission"); /*float set when the client receives svc_intermission*/ \
|
|
globalvector(view_angles, "view_angles"); /*float set to the view angles at the start of each new frame (EXT_CSQC_1)*/ \
|
|
globalfloat(dpcompat_sbshowscores, "sb_showscores"); /*float ask darkplaces people, its not part of the csqc standard */ \
|
|
\
|
|
globalvector(pmove_org, "pmove_org"); /*read/written by runplayerphysics*/ \
|
|
globalvector(pmove_vel, "pmove_vel"); /*read/written by runplayerphysics*/ \
|
|
globalvector(pmove_mins, "pmove_mins"); /*read/written by runplayerphysics*/ \
|
|
globalvector(pmove_maxs, "pmove_maxs"); /*read/written by runplayerphysics*/ \
|
|
globalfloat(pmove_jump_held, "pmove_jump_held"); /*read/written by runplayerphysics*/ \
|
|
globalfloat(pmove_waterjumptime, "pmove_waterjumptime"); /*read/written by runplayerphysics*/ \
|
|
\
|
|
globalfloat(input_timelength, "input_timelength"); /*float filled by getinputstate, read by runplayerphysics*/ \
|
|
globalvector(input_angles, "input_angles"); /*vector filled by getinputstate, read by runplayerphysics*/ \
|
|
globalvector(input_movevalues, "input_movevalues"); /*vector filled by getinputstate, read by runplayerphysics*/ \
|
|
globalfloat(input_buttons, "input_buttons"); /*float filled by getinputstate, read by runplayerphysics*/ \
|
|
globalfloat(input_impulse, "input_impulse"); /*float filled by getinputstate, read by runplayerphysics*/ \
|
|
globalfloat(input_lightlevel, "input_lightlevel"); /*float filled by getinputstate, read by runplayerphysics*/ \
|
|
globalfloat(input_weapon, "input_weapon"); /*float filled by getinputstate, read by runplayerphysics*/ \
|
|
globalfloat(input_servertime, "input_servertime"); /*float filled by getinputstate, read by runplayerphysics*/ \
|
|
globalfloat(input_clienttime, "input_clienttime"); /*float filled by getinputstate, read by runplayerphysics*/ \
|
|
\
|
|
globalfloat(movevar_gravity, "movevar_gravity"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_stopspeed, "movevar_stopspeed"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_maxspeed, "movevar_maxspeed"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_spectatormaxspeed,"movevar_spectatormaxspeed"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_accelerate, "movevar_accelerate"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_airaccelerate, "movevar_airaccelerate"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_wateraccelerate,"movevar_wateraccelerate"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_friction, "movevar_friction"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_waterfriction, "movevar_waterfriction"); /*float obtained from the server*/ \
|
|
globalfloat(movevar_entgravity, "movevar_entgravity"); /*float obtained from the server*/ \
|
|
|
|
|
|
typedef struct {
|
|
#define globalfloat(name,qcname) float *name
|
|
#define globalvector(name,qcname) float *name
|
|
#define globalentity(name,qcname) int *name
|
|
#define globalstring(name,qcname) string_t *name
|
|
#define globalfunction(name,qcname) func_t name
|
|
//These are the functions the engine will call to, found by name.
|
|
|
|
csqcglobals
|
|
|
|
#undef globalfloat
|
|
#undef globalvector
|
|
#undef globalentity
|
|
#undef globalstring
|
|
#undef globalfunction
|
|
} csqcglobals_t;
|
|
static csqcglobals_t csqcg;
|
|
|
|
static void CSQC_ChangeLocalPlayer(int lplayernum)
|
|
{
|
|
csqc_lplayernum = lplayernum;
|
|
if (csqcg.player_localentnum)
|
|
*csqcg.player_localentnum = cl.playernum[lplayernum]+1;
|
|
if (csqcg.player_localnum)
|
|
*csqcg.player_localnum = cl.playernum[lplayernum];
|
|
|
|
if (csqcg.view_angles)
|
|
{
|
|
csqcg.view_angles[0] = cl.viewangles[csqc_lplayernum][0];
|
|
csqcg.view_angles[1] = cl.viewangles[csqc_lplayernum][1];
|
|
csqcg.view_angles[2] = cl.viewangles[csqc_lplayernum][2];
|
|
}
|
|
|
|
}
|
|
|
|
static void CSQC_FindGlobals(void)
|
|
{
|
|
#define globalfloat(name,qcname) csqcg.name = (float*)PR_FindGlobal(csqcprogs, qcname, 0);
|
|
#define globalvector(name,qcname) csqcg.name = (float*)PR_FindGlobal(csqcprogs, qcname, 0);
|
|
#define globalentity(name,qcname) csqcg.name = (int*)PR_FindGlobal(csqcprogs, qcname, 0);
|
|
#define globalstring(name,qcname) csqcg.name = (string_t*)PR_FindGlobal(csqcprogs, qcname, 0);
|
|
#define globalfunction(name,qcname) csqcg.name = PR_FindFunction(csqcprogs,qcname,PR_ANY);
|
|
|
|
csqcglobals
|
|
|
|
#undef globalfloat
|
|
#undef globalvector
|
|
#undef globalentity
|
|
#undef globalstring
|
|
#undef globalfunction
|
|
|
|
if (csqcg.svtime)
|
|
*csqcg.svtime = cl.servertime;
|
|
if (csqcg.cltime)
|
|
*csqcg.cltime = cl.time;
|
|
|
|
CSQC_ChangeLocalPlayer(0);
|
|
|
|
if (csqcg.maxclients)
|
|
*csqcg.maxclients = MAX_CLIENTS;
|
|
}
|
|
|
|
|
|
|
|
//this is the list for all the csqc fields.
|
|
//(the #define is so the list always matches the ones pulled out)
|
|
#define csqcfields \
|
|
fieldfloat(entnum); \
|
|
fieldfloat(modelindex); \
|
|
fieldvector(origin); \
|
|
fieldvector(angles); \
|
|
fieldvector(velocity); \
|
|
fieldfloat(alpha); /*transparency*/ \
|
|
fieldfloat(scale); /*model scale*/ \
|
|
fieldfloat(fatness); /*expand models X units along their normals.*/ \
|
|
fieldfloat(skin); \
|
|
fieldfloat(colormap); \
|
|
fieldfloat(flags); \
|
|
fieldfloat(frame); \
|
|
fieldfloat(frame2); /*EXT_CSQC_1*/\
|
|
fieldfloat(frame1time); /*EXT_CSQC_1*/\
|
|
fieldfloat(frame2time); /*EXT_CSQC_1*/\
|
|
fieldfloat(lerpfrac); /*EXT_CSQC_1*/\
|
|
fieldfloat(renderflags);\
|
|
fieldfloat(forceshader);/*FTE_CSQC_SHADERS*/\
|
|
fieldfloat(dimension_hit); \
|
|
fieldfloat(dimension_solid); \
|
|
\
|
|
fieldfloat(baseframe); /*FTE_CSQC_BASEFRAME*/\
|
|
fieldfloat(baseframe2); /*FTE_CSQC_BASEFRAME*/\
|
|
fieldfloat(baseframe1time); /*FTE_CSQC_BASEFRAME*/\
|
|
fieldfloat(baseframe2time); /*FTE_CSQC_BASEFRAME*/\
|
|
fieldfloat(baselerpfrac); /*FTE_CSQC_BASEFRAME*/\
|
|
fieldfloat(basebone); /*FTE_CSQC_BASEFRAME*/\
|
|
\
|
|
fieldfloat(bonecontrol1); /*FTE_CSQC_HALFLIFE_MODELS*/\
|
|
fieldfloat(bonecontrol2); /*FTE_CSQC_HALFLIFE_MODELS*/\
|
|
fieldfloat(bonecontrol3); /*FTE_CSQC_HALFLIFE_MODELS*/\
|
|
fieldfloat(bonecontrol4); /*FTE_CSQC_HALFLIFE_MODELS*/\
|
|
fieldfloat(bonecontrol5); /*FTE_CSQC_HALFLIFE_MODELS*/\
|
|
fieldfloat(subblendfrac); /*FTE_CSQC_HALFLIFE_MODELS*/\
|
|
fieldfloat(basesubblendfrac); /*FTE_CSQC_HALFLIFE_MODELS*/\
|
|
\
|
|
fieldfloat(drawmask); /*So that the qc can specify all rockets at once or all bannanas at once*/ \
|
|
fieldfunction(predraw); /*If present, is called just before it's drawn.*/ \
|
|
\
|
|
fieldstring(model); \
|
|
fieldfloat(ideal_yaw); \
|
|
fieldfloat(ideal_pitch);\
|
|
fieldfloat(yaw_speed); \
|
|
fieldfloat(pitch_speed);\
|
|
\
|
|
fieldentity(chain); \
|
|
fieldentity(enemy); \
|
|
fieldentity(groundentity); \
|
|
fieldentity(owner); \
|
|
\
|
|
fieldfloat(solid); \
|
|
fieldvector(mins); \
|
|
fieldvector(maxs); \
|
|
fieldvector(size); \
|
|
fieldvector(absmin); \
|
|
fieldvector(absmax); \
|
|
fieldfloat(hull); /*(FTE_PEXT_HEXEN2)*/
|
|
|
|
|
|
//note: doesn't even have to match the clprogs.dat :)
|
|
typedef struct {
|
|
#define fieldfloat(name) float name
|
|
#define fieldvector(name) vec3_t name
|
|
#define fieldentity(name) int name
|
|
#define fieldstring(name) string_t name
|
|
#define fieldfunction(name) func_t name
|
|
csqcfields
|
|
#undef fieldfloat
|
|
#undef fieldvector
|
|
#undef fieldentity
|
|
#undef fieldstring
|
|
#undef fieldfunction
|
|
} csqcentvars_t;
|
|
|
|
typedef struct csqcedict_s
|
|
{
|
|
qboolean isfree;
|
|
float freetime; // sv.time when the object was freed
|
|
int entnum;
|
|
qboolean readonly; //world
|
|
csqcentvars_t *v;
|
|
|
|
//add whatever you wish here
|
|
trailstate_t *trailstate;
|
|
link_t area;
|
|
} csqcedict_t;
|
|
|
|
static csqcedict_t *csqc_edicts; //consider this 'world'
|
|
|
|
|
|
static void CSQC_InitFields(void)
|
|
{ //CHANGING THIS FUNCTION REQUIRES CHANGES TO csqcentvars_t
|
|
#define fieldfloat(name) PR_RegisterFieldVar(csqcprogs, ev_float, #name, (int)&((csqcentvars_t*)0)->name, -1)
|
|
#define fieldvector(name) PR_RegisterFieldVar(csqcprogs, ev_vector, #name, (int)&((csqcentvars_t*)0)->name, -1)
|
|
#define fieldentity(name) PR_RegisterFieldVar(csqcprogs, ev_entity, #name, (int)&((csqcentvars_t*)0)->name, -1)
|
|
#define fieldstring(name) PR_RegisterFieldVar(csqcprogs, ev_string, #name, (int)&((csqcentvars_t*)0)->name, -1)
|
|
#define fieldfunction(name) PR_RegisterFieldVar(csqcprogs, ev_function, #name, (int)&((csqcentvars_t*)0)->name, -1)
|
|
csqcfields //any *64->int32 casts are erroneous, it's biased off NULL.
|
|
#undef fieldfloat
|
|
#undef fieldvector
|
|
#undef fieldentity
|
|
#undef fieldstring
|
|
#undef fieldfunction
|
|
}
|
|
|
|
static csqcedict_t **csqcent;
|
|
static int maxcsqcentities;
|
|
|
|
static int csqcentsize;
|
|
|
|
static char *csqcmapentitydata;
|
|
static qboolean csqcmapentitydataloaded;
|
|
|
|
static model_t *CSQC_GetModelForIndex(int index);
|
|
static void CS_LinkEdict(csqcedict_t *ent, qboolean touchtriggers);
|
|
|
|
areanode_t cs_areanodes[AREA_NODES];
|
|
int cs_numareanodes;
|
|
areanode_t *CS_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs)
|
|
{
|
|
areanode_t *anode;
|
|
vec3_t size;
|
|
vec3_t mins1, maxs1, mins2, maxs2;
|
|
|
|
anode = &cs_areanodes[cs_numareanodes];
|
|
cs_numareanodes++;
|
|
|
|
ClearLink (&anode->trigger_edicts);
|
|
ClearLink (&anode->solid_edicts);
|
|
|
|
if (depth == AREA_DEPTH)
|
|
{
|
|
anode->axis = -1;
|
|
anode->children[0] = anode->children[1] = NULL;
|
|
return anode;
|
|
}
|
|
|
|
VectorSubtract (maxs, mins, size);
|
|
if (size[0] > size[1])
|
|
anode->axis = 0;
|
|
else
|
|
anode->axis = 1;
|
|
|
|
anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
|
|
VectorCopy (mins, mins1);
|
|
VectorCopy (mins, mins2);
|
|
VectorCopy (maxs, maxs1);
|
|
VectorCopy (maxs, maxs2);
|
|
|
|
maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
|
|
|
|
anode->children[0] = CS_CreateAreaNode (depth+1, mins2, maxs2);
|
|
anode->children[1] = CS_CreateAreaNode (depth+1, mins1, maxs1);
|
|
|
|
return anode;
|
|
}
|
|
|
|
void CS_ClearWorld (void)
|
|
{
|
|
int i;
|
|
|
|
memset (cs_areanodes, 0, sizeof(cs_areanodes));
|
|
cs_numareanodes = 0;
|
|
if (cl.worldmodel)
|
|
CS_CreateAreaNode (0, cl.worldmodel->mins, cl.worldmodel->maxs);
|
|
else
|
|
{
|
|
vec3_t mins, maxs;
|
|
int i;
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
mins[i] = -4096;
|
|
maxs[i] = 4096;
|
|
}
|
|
CS_CreateAreaNode (0, mins, maxs);
|
|
}
|
|
|
|
for (i = 1; i < num_csqc_edicts; i++)
|
|
CS_LinkEdict((csqcedict_t*)EDICT_NUM(csqcprogs, i), false);
|
|
}
|
|
|
|
static void CS_UnlinkEdict (csqcedict_t *ent)
|
|
{
|
|
if (!ent->area.prev)
|
|
return; // not linked in anywhere
|
|
RemoveLink (&ent->area);
|
|
ent->area.prev = ent->area.next = NULL;
|
|
}
|
|
|
|
static void CS_LinkEdict(csqcedict_t *ent, qboolean touchtriggers)
|
|
{
|
|
areanode_t *node;
|
|
|
|
if (ent->area.prev)
|
|
CS_UnlinkEdict (ent); // unlink from old position
|
|
|
|
if (ent == csqc_edicts)
|
|
return; // don't add the world
|
|
|
|
//FIXME: use some sort of area grid ?
|
|
VectorAdd(ent->v->origin, ent->v->mins, ent->v->absmin);
|
|
VectorAdd(ent->v->origin, ent->v->maxs, ent->v->absmax);
|
|
|
|
if ((int)ent->v->flags & FL_ITEM)
|
|
{
|
|
ent->v->absmin[0] -= 15;
|
|
ent->v->absmin[1] -= 15;
|
|
ent->v->absmax[0] += 15;
|
|
ent->v->absmax[1] += 15;
|
|
}
|
|
else
|
|
{ // because movement is clipped an epsilon away from an actual edge,
|
|
// we must fully check even when bounding boxes don't quite touch
|
|
ent->v->absmin[0] -= 1;
|
|
ent->v->absmin[1] -= 1;
|
|
ent->v->absmin[2] -= 1;
|
|
ent->v->absmax[0] += 1;
|
|
ent->v->absmax[1] += 1;
|
|
ent->v->absmax[2] += 1;
|
|
}
|
|
|
|
if (!ent->v->solid)
|
|
return;
|
|
|
|
// find the first node that the ent's box crosses
|
|
node = cs_areanodes;
|
|
while (1)
|
|
{
|
|
if (node->axis == -1)
|
|
break;
|
|
if (ent->v->absmin[node->axis] > node->dist)
|
|
node = node->children[0];
|
|
else if (ent->v->absmax[node->axis] < node->dist)
|
|
node = node->children[1];
|
|
else
|
|
break; // crosses the node
|
|
}
|
|
|
|
// link it in
|
|
|
|
if (ent->v->solid == SOLID_TRIGGER)
|
|
InsertLinkBefore (&ent->area, &node->trigger_edicts);
|
|
else
|
|
InsertLinkBefore (&ent->area, &node->solid_edicts);
|
|
}
|
|
|
|
typedef struct {
|
|
int type;
|
|
trace_t trace;
|
|
vec3_t boxmins; //mins/max of total move.
|
|
vec3_t boxmaxs;
|
|
vec3_t start;
|
|
vec3_t end;
|
|
vec3_t mins; //mins/max of ent
|
|
vec3_t maxs;
|
|
csqcedict_t *passedict;
|
|
} moveclip_t;
|
|
#define CSEDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,csqcedict_t,area)
|
|
static void CS_ClipToLinks ( areanode_t *node, moveclip_t *clip )
|
|
{
|
|
model_t *model;
|
|
trace_t tr;
|
|
link_t *l, *next;
|
|
csqcedict_t *touch;
|
|
|
|
//work out who they are first.
|
|
for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next)
|
|
{
|
|
next = l->next;
|
|
touch = (csqcedict_t*)CSEDICT_FROM_AREA(l);
|
|
if (touch->v->solid == SOLID_NOT)
|
|
continue;
|
|
if (touch == clip->passedict)
|
|
continue;
|
|
if (touch->v->solid == SOLID_TRIGGER || touch->v->solid == SOLID_LADDER)
|
|
continue;
|
|
|
|
if (clip->type & MOVE_NOMONSTERS && touch->v->solid != SOLID_BSP)
|
|
continue;
|
|
|
|
if (clip->passedict)
|
|
{
|
|
// don't clip corpse against character
|
|
if (clip->passedict->v->solid == SOLID_CORPSE && (touch->v->solid == SOLID_SLIDEBOX || touch->v->solid == SOLID_CORPSE))
|
|
continue;
|
|
// don't clip character against corpse
|
|
if (clip->passedict->v->solid == SOLID_SLIDEBOX && touch->v->solid == SOLID_CORPSE)
|
|
continue;
|
|
|
|
if (!((int)clip->passedict->v->dimension_hit & (int)touch->v->dimension_solid))
|
|
continue;
|
|
}
|
|
|
|
if (clip->boxmins[0] > touch->v->absmax[0]
|
|
|| clip->boxmins[1] > touch->v->absmax[1]
|
|
|| clip->boxmins[2] > touch->v->absmax[2]
|
|
|| clip->boxmaxs[0] < touch->v->absmin[0]
|
|
|| clip->boxmaxs[1] < touch->v->absmin[1]
|
|
|| clip->boxmaxs[2] < touch->v->absmin[2] )
|
|
continue;
|
|
|
|
if (clip->passedict && clip->passedict->v->size[0] && !touch->v->size[0])
|
|
continue; // points never interact
|
|
|
|
// might intersect, so do an exact clip
|
|
if (clip->trace.allsolid)
|
|
return;
|
|
if (clip->passedict)
|
|
{
|
|
if ((csqcedict_t*)PROG_TO_EDICT(csqcprogs, touch->v->owner) == clip->passedict)
|
|
continue; // don't clip against own missiles
|
|
if ((csqcedict_t*)PROG_TO_EDICT(csqcprogs, clip->passedict->v->owner) == touch)
|
|
continue; // don't clip against owner
|
|
}
|
|
|
|
|
|
if (!((int)clip->passedict->v->dimension_solid & (int)touch->v->dimension_hit))
|
|
continue;
|
|
|
|
model = CSQC_GetModelForIndex(touch->v->modelindex);
|
|
if (!model)
|
|
continue;
|
|
model->funcs.Trace(model, 0, 0, clip->start, clip->end, clip->mins, clip->maxs, &tr);
|
|
if (tr.fraction < clip->trace.fraction)
|
|
{
|
|
tr.ent = (void*)touch;
|
|
clip->trace = tr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static trace_t CS_Move(vec3_t v1, vec3_t mins, vec3_t maxs, vec3_t v2, float nomonsters, csqcedict_t *passedict)
|
|
{
|
|
moveclip_t clip;
|
|
|
|
if (cl.worldmodel)
|
|
{
|
|
cl.worldmodel->funcs.Trace(cl.worldmodel, 0, 0, v1, v2, mins, maxs, &clip.trace);
|
|
clip.trace.ent = (void*)csqc_edicts;
|
|
}
|
|
else
|
|
{
|
|
memset(&clip.trace, 0, sizeof(clip.trace));
|
|
clip.trace.fraction = 1;
|
|
VectorCopy(v2, clip.trace.endpos);
|
|
clip.trace.ent = (void*)csqc_edicts;
|
|
}
|
|
|
|
//why use trace.endpos instead?
|
|
//so that if we hit a wall early, we don't have a box covering the whole world because of a shotgun trace.
|
|
clip.boxmins[0] = ((v1[0] < clip.trace.endpos[0])?v1[0]:clip.trace.endpos[0]) - mins[0]-1;
|
|
clip.boxmins[1] = ((v1[1] < clip.trace.endpos[1])?v1[1]:clip.trace.endpos[1]) - mins[1]-1;
|
|
clip.boxmins[2] = ((v1[2] < clip.trace.endpos[2])?v1[2]:clip.trace.endpos[2]) - mins[2]-1;
|
|
clip.boxmaxs[0] = ((v1[0] > clip.trace.endpos[0])?v1[0]:clip.trace.endpos[0]) + maxs[0]+1;
|
|
clip.boxmaxs[1] = ((v1[1] > clip.trace.endpos[1])?v1[1]:clip.trace.endpos[1]) + maxs[1]+1;
|
|
clip.boxmaxs[2] = ((v1[2] > clip.trace.endpos[2])?v1[2]:clip.trace.endpos[2]) + maxs[2]+1;
|
|
|
|
VectorCopy(mins, clip.mins);
|
|
VectorCopy(maxs, clip.maxs);
|
|
VectorCopy(v1, clip.start);
|
|
VectorCopy(v2, clip.end);
|
|
clip.passedict = passedict;
|
|
|
|
CS_ClipToLinks(cs_areanodes, &clip);
|
|
return clip.trace;
|
|
}
|
|
|
|
static void CS_CheckVelocity(csqcedict_t *ent)
|
|
{
|
|
}
|
|
|
|
|
|
static void PF_cs_remove (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ed;
|
|
|
|
ed = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
|
|
if (ed->isfree)
|
|
{
|
|
Con_DPrintf("CSQC Tried removing free entity\n");
|
|
return;
|
|
}
|
|
|
|
pe->DelinkTrailstate(&ed->trailstate);
|
|
CS_UnlinkEdict(ed);
|
|
ED_Free (prinst, (void*)ed);
|
|
}
|
|
|
|
static void PF_cvar (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
cvar_t *var;
|
|
char *str;
|
|
|
|
str = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
{
|
|
var = Cvar_Get(str, "", 0, "csqc cvars");
|
|
if (var)
|
|
G_FLOAT(OFS_RETURN) = var->value;
|
|
else
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
}
|
|
}
|
|
|
|
//too specific to the prinst's builtins.
|
|
static void PF_Fixme (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
Con_Printf("\n");
|
|
|
|
prinst->RunError(prinst, "\nBuiltin %i not implemented.\nCSQC is not compatible.", prinst->lastcalledbuiltinnumber);
|
|
PR_BIError (prinst, "bulitin not implemented");
|
|
}
|
|
static void PF_NoCSQC (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
Con_Printf("\n");
|
|
|
|
prinst->RunError(prinst, "\nBuiltin %i does not make sense in csqc.\nCSQC is not compatible.", prinst->lastcalledbuiltinnumber);
|
|
PR_BIError (prinst, "bulitin not implemented");
|
|
}
|
|
|
|
static void PF_cl_cprint (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *str = PF_VarString(prinst, 0, pr_globals);
|
|
SCR_CenterPrint(csqc_lplayernum, str, true);
|
|
}
|
|
|
|
static void PF_cs_makevectors (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
if (!csqcg.forward || !csqcg.right || !csqcg.up)
|
|
Host_EndGame("PF_makevectors: one of v_forward, v_right or v_up was not defined\n");
|
|
AngleVectors (G_VECTOR(OFS_PARM0), csqcg.forward, csqcg.right, csqcg.up);
|
|
}
|
|
/*
|
|
void QuaternainToAngleMatrix(float *quat, vec3_t *mat)
|
|
{
|
|
float xx = quat[0] * quat[0];
|
|
float xy = quat[0] * quat[1];
|
|
float xz = quat[0] * quat[2];
|
|
float xw = quat[0] * quat[3];
|
|
float yy = quat[1] * quat[1];
|
|
float yz = quat[1] * quat[2];
|
|
float yw = quat[1] * quat[3];
|
|
float zz = quat[2] * quat[2];
|
|
float zw = quat[2] * quat[3];
|
|
mat[0][0] = 1 - 2 * ( yy + zz );
|
|
mat[0][1] = 2 * ( xy - zw );
|
|
mat[0][2] = 2 * ( xz + yw );
|
|
mat[1][0] = 2 * ( xy + zw );
|
|
mat[1][1] = 1 - 2 * ( xx + zz );
|
|
mat[1][2] = 2 * ( yz - xw );
|
|
mat[2][0] = 2 * ( xz - yw );
|
|
mat[2][1] = 2 * ( yz + xw );
|
|
mat[2][2] = 1 - 2 * ( xx + yy );
|
|
}
|
|
|
|
void quaternion_multiply(float *a, float *b, float *c)
|
|
{
|
|
#define x1 a[0]
|
|
#define y1 a[1]
|
|
#define z1 a[2]
|
|
#define w1 a[3]
|
|
#define x2 b[0]
|
|
#define y2 b[1]
|
|
#define z2 b[2]
|
|
#define w2 b[3]
|
|
c[0] = w1*x2 + x1*w2 + y1*z2 - z1*y2;
|
|
c[1] = w1*y2 + y1*w2 + z1*x2 - x1*z2;
|
|
c[2] = w1*z2 + z1*w2 + x1*y2 - y1*x2;
|
|
c[3] = w1*w2 - x1*x2 - y1*y2 - z1*z2;
|
|
}
|
|
|
|
void quaternion_rotation(float pitch, float roll, float yaw, float angle, float *quat)
|
|
{
|
|
float sin_a, cos_a;
|
|
|
|
sin_a = sin( angle / 360 );
|
|
cos_a = cos( angle / 360 );
|
|
quat[0] = pitch * sin_a;
|
|
quat[1] = yaw * sin_a;
|
|
quat[2] = roll * sin_a;
|
|
quat[3] = cos_a;
|
|
}
|
|
|
|
void EularToQuaternian(vec3_t angles, float *quat)
|
|
{
|
|
float x[4] = {sin(angles[2]/360), 0, 0, cos(angles[2]/360)};
|
|
float y[4] = {0, sin(angles[1]/360), 0, cos(angles[1]/360)};
|
|
float z[4] = {0, 0, sin(angles[0]/360), cos(angles[0]/360)};
|
|
float t[4];
|
|
quaternion_multiply(x, y, t);
|
|
quaternion_multiply(t, z, quat);
|
|
}
|
|
*/
|
|
#define CSQCRF_VIEWMODEL 1 //Not drawn in mirrors
|
|
#define CSQCRF_EXTERNALMODEL 2 //drawn ONLY in mirrors
|
|
#define CSQCRF_DEPTHHACK 4 //fun depthhack
|
|
#define CSQCRF_ADDITIVE 8 //add instead of blend
|
|
#define CSQCRF_USEAXIS 16 //use v_forward/v_right/v_up as an axis/matrix - predraw is needed to use this properly
|
|
#define CSQCRF_NOSHADOW 32 //don't cast shadows upon other entities (can still be self shadowing, if the engine wishes, and not additive)
|
|
#define CSQCRF_FRAMETIMESARESTARTTIMES 64 //EXT_CSQC_1: frame times should be read as (time-frametime).
|
|
|
|
static model_t *CSQC_GetModelForIndex(int index)
|
|
{
|
|
if (index == 0)
|
|
return NULL;
|
|
else if (index > 0 && index < MAX_MODELS)
|
|
return cl.model_precache[index];
|
|
else if (index < 0 && index > -MAX_CSQCMODELS)
|
|
return cl.model_csqcprecache[-index];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
|
|
{
|
|
int i;
|
|
model_t *model;
|
|
int rflags;
|
|
|
|
i = in->v->modelindex;
|
|
model = CSQC_GetModelForIndex(in->v->modelindex);
|
|
if (!model)
|
|
return false; //there might be other ent types later as an extension that stop this.
|
|
|
|
memset(out, 0, sizeof(*out));
|
|
out->model = model;
|
|
|
|
if (in->v->renderflags)
|
|
{
|
|
rflags = in->v->renderflags;
|
|
if (rflags & CSQCRF_VIEWMODEL)
|
|
out->flags |= Q2RF_DEPTHHACK|Q2RF_WEAPONMODEL;
|
|
if (rflags & CSQCRF_EXTERNALMODEL)
|
|
out->flags |= Q2RF_EXTERNALMODEL;
|
|
if (rflags & CSQCRF_DEPTHHACK)
|
|
out->flags |= Q2RF_DEPTHHACK;
|
|
if (rflags & CSQCRF_ADDITIVE)
|
|
out->flags |= Q2RF_ADDATIVE;
|
|
//CSQCRF_USEAXIS is below
|
|
if (rflags & CSQCRF_NOSHADOW)
|
|
out->flags |= RF_NOSHADOW;
|
|
//CSQCRF_FRAMETIMESARESTARTTIMES is below
|
|
}
|
|
else
|
|
rflags = 0;
|
|
|
|
//From here.
|
|
|
|
//FTE_CSQC_HALFLIFE_MODELS
|
|
#ifdef HALFLIFEMODELS
|
|
out->bonecontrols[0] = in->v->bonecontrol1;
|
|
out->bonecontrols[1] = in->v->bonecontrol2;
|
|
out->bonecontrols[2] = in->v->bonecontrol3;
|
|
out->bonecontrols[3] = in->v->bonecontrol4;
|
|
out->bonecontrols[4] = in->v->bonecontrol5;
|
|
out->subblendfrac = in->v->subblendfrac;
|
|
out->basesubblendfrac = in->v->basesubblendfrac;
|
|
#endif
|
|
|
|
//FTE_CSQC_BASEFRAME
|
|
out->basebone = in->v->basebone;
|
|
if (out->basebone)
|
|
{ //small optimisation.
|
|
out->baseframe1 = in->v->baseframe;
|
|
out->baseframe2 = in->v->baseframe2;
|
|
if (rflags & CSQCRF_FRAMETIMESARESTARTTIMES)
|
|
{
|
|
out->baseframe1time = *csqcg.svtime - in->v->baseframe1time;
|
|
out->baseframe2time = *csqcg.svtime - in->v->baseframe2time;
|
|
}
|
|
else
|
|
{
|
|
out->baseframe1time = in->v->baseframe1time;
|
|
out->baseframe2time = in->v->baseframe2time;
|
|
}
|
|
out->baselerpfrac = in->v->baselerpfrac;
|
|
}
|
|
|
|
//and the normal frames.
|
|
out->frame1 = in->v->frame;
|
|
out->frame2 = in->v->frame2;
|
|
out->lerpfrac = in->v->lerpfrac;
|
|
if (rflags & CSQCRF_FRAMETIMESARESTARTTIMES)
|
|
{
|
|
out->frame1time = *csqcg.svtime - in->v->frame1time;
|
|
out->frame2time = *csqcg.svtime - in->v->frame2time;
|
|
}
|
|
else
|
|
{
|
|
out->frame1time = in->v->frame1time;
|
|
out->frame2time = in->v->frame2time;
|
|
}
|
|
//to here... We read only frames and frame times... Yeah... Q1 originally had only a frame field. :D
|
|
|
|
VectorCopy(in->v->origin, out->origin);
|
|
if (rflags & CSQCRF_USEAXIS)
|
|
{
|
|
VectorCopy(csqcg.forward, out->axis[0]);
|
|
VectorNegate(csqcg.right, out->axis[1]);
|
|
VectorCopy(csqcg.up, out->axis[2]);
|
|
out->scale = 1;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(in->v->angles, out->angles);
|
|
out->angles[0]*=-1;
|
|
AngleVectors(out->angles, out->axis[0], out->axis[1], out->axis[2]);
|
|
VectorInverse(out->axis[1]);
|
|
|
|
if (!in->v->scale)
|
|
out->scale = 1;
|
|
else
|
|
out->scale = in->v->scale;
|
|
}
|
|
|
|
if (in->v->colormap > 0 && in->v->colormap <= MAX_CLIENTS)
|
|
{
|
|
#ifdef SWQUAKE
|
|
out->palremap = cl.players[(int)in->v->colormap-1].palremap;
|
|
#endif
|
|
out->scoreboard = &cl.players[(int)in->v->colormap-1];
|
|
} // TODO: DP COLORMAP extension?
|
|
|
|
out->shaderRGBAf[0] = 1;
|
|
out->shaderRGBAf[1] = 1;
|
|
out->shaderRGBAf[2] = 1;
|
|
if (!in->v->alpha)
|
|
out->shaderRGBAf[3] = 1;
|
|
else
|
|
out->shaderRGBAf[3] = in->v->alpha;
|
|
|
|
out->skinnum = in->v->skin;
|
|
out->fatness = in->v->fatness;
|
|
#ifdef Q3SHADERS
|
|
if (in->v->forceshader >= 1)
|
|
out->forcedshader = r_shaders + ((int)in->v->forceshader-1);
|
|
else
|
|
out->forcedshader = NULL;
|
|
#endif
|
|
|
|
out->keynum = -1;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void PF_cs_makestatic (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{ //still does a remove.
|
|
csqcedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);
|
|
entity_t *ent;
|
|
|
|
if (cl.num_statics >= MAX_STATIC_ENTITIES)
|
|
{
|
|
Con_Printf ("Too many static entities");
|
|
|
|
PF_cs_remove(prinst, pr_globals);
|
|
return;
|
|
}
|
|
|
|
ent = &cl_static_entities[cl.num_statics];
|
|
if (CopyCSQCEdictToEntity(in, ent))
|
|
{
|
|
cl.num_statics++;
|
|
R_AddEfrags(ent);
|
|
}
|
|
|
|
PF_cs_remove(prinst, pr_globals);
|
|
}
|
|
|
|
static void PF_R_AddEntity(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);
|
|
entity_t ent;
|
|
|
|
if (CopyCSQCEdictToEntity(in, &ent))
|
|
V_AddAxisEntity(&ent);
|
|
|
|
/*
|
|
{
|
|
float a[4];
|
|
float q[4];
|
|
float r[4];
|
|
EularToQuaternian(ent.angles, a);
|
|
|
|
QuaternainToAngleMatrix(a, ent.axis);
|
|
ent.origin[0] += 16;
|
|
V_AddEntity(&ent);
|
|
|
|
quaternion_rotation(0, 0, 1, cl.time*360, r);
|
|
quaternion_multiply(a, r, q);
|
|
QuaternainToAngleMatrix(q, ent.axis);
|
|
ent.origin[0] -= 32;
|
|
ent.angles[1] = cl.time;
|
|
V_AddEntity(&ent);
|
|
}
|
|
*/
|
|
}
|
|
|
|
static void PF_R_AddDynamicLight(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org = G_VECTOR(OFS_PARM0);
|
|
float radius = G_FLOAT(OFS_PARM1);
|
|
float *rgb = G_VECTOR(OFS_PARM2);
|
|
V_AddLight(org, radius, rgb[0]/5, rgb[1]/5, rgb[2]/5);
|
|
}
|
|
|
|
static void PF_R_AddEntityMask(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int mask = G_FLOAT(OFS_PARM0);
|
|
csqcedict_t *ent;
|
|
entity_t rent;
|
|
int e;
|
|
|
|
int oldself = *csqcg.self;
|
|
for (e=1; e < *prinst->parms->sv_num_edicts; e++)
|
|
{
|
|
ent = (void*)EDICT_NUM(prinst, e);
|
|
if (ent->isfree)
|
|
continue;
|
|
|
|
if ((int)ent->v->drawmask & mask)
|
|
{
|
|
if (ent->v->predraw)
|
|
{
|
|
*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);
|
|
PR_ExecuteProgram(prinst, ent->v->predraw);
|
|
|
|
if (ent->isfree)
|
|
continue; //bummer...
|
|
}
|
|
|
|
if (CopyCSQCEdictToEntity(ent, &rent))
|
|
V_AddAxisEntity(&rent);
|
|
}
|
|
}
|
|
*csqcg.self = oldself;
|
|
|
|
if (cl.worldmodel)
|
|
{
|
|
if (mask & MASK_STDVIEWMODEL)
|
|
{
|
|
CL_LinkViewModel ();
|
|
}
|
|
if (mask & MASK_DELTA)
|
|
{
|
|
CL_LinkPlayers ();
|
|
CL_LinkPacketEntities ();
|
|
CL_LinkProjectiles ();
|
|
CL_UpdateTEnts ();
|
|
}
|
|
}
|
|
}
|
|
|
|
qboolean csqc_rebuildmatricies;
|
|
float mvp[12];
|
|
float mvpi[12];
|
|
static void buildmatricies(void)
|
|
{
|
|
float modelview[16];
|
|
float proj[16];
|
|
|
|
Matrix4_ModelViewMatrix(modelview, r_refdef.viewangles, r_refdef.vieworg);
|
|
Matrix4_Projection2(proj, r_refdef.fov_x, r_refdef.fov_y, 4);
|
|
Matrix4_Multiply(proj, modelview, mvp);
|
|
Matrix4_Invert_Simple((matrix4x4_t*)mvpi, (matrix4x4_t*)mvp); //not actually used in this function.
|
|
|
|
csqc_rebuildmatricies = false;
|
|
}
|
|
static void PF_cs_project (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
if (csqc_rebuildmatricies)
|
|
buildmatricies();
|
|
|
|
|
|
{
|
|
float *in = G_VECTOR(OFS_PARM0);
|
|
float *out = G_VECTOR(OFS_RETURN);
|
|
float v[4], tempv[4];
|
|
|
|
v[0] = in[0];
|
|
v[1] = in[1];
|
|
v[2] = in[2];
|
|
v[3] = 1;
|
|
|
|
Matrix4_Transform4(mvp, v, tempv);
|
|
|
|
tempv[0] /= tempv[3];
|
|
tempv[1] /= tempv[3];
|
|
tempv[2] /= tempv[3];
|
|
|
|
out[0] = (1+tempv[0])/2;
|
|
out[1] = (1+tempv[1])/2;
|
|
out[2] = (1+tempv[2])/2;
|
|
|
|
out[0] = out[0]*r_refdef.vrect.width + r_refdef.vrect.x;
|
|
out[1] = out[1]*r_refdef.vrect.height + r_refdef.vrect.y;
|
|
}
|
|
}
|
|
static void PF_cs_unproject (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
if (csqc_rebuildmatricies)
|
|
buildmatricies();
|
|
|
|
|
|
{
|
|
float *in = G_VECTOR(OFS_PARM0);
|
|
float *out = G_VECTOR(OFS_RETURN);
|
|
|
|
float v[4], tempv[4];
|
|
|
|
out[0] = (out[0]-r_refdef.vrect.x)/r_refdef.vrect.width;
|
|
out[1] = (out[1]-r_refdef.vrect.y)/r_refdef.vrect.height;
|
|
|
|
v[0] = in[0]*2-1;
|
|
v[1] = in[1]*2-1;
|
|
v[2] = in[2]*2-1;
|
|
v[3] = 1;
|
|
|
|
Matrix4_Transform4(mvpi, v, tempv);
|
|
|
|
out[0] = tempv[0];
|
|
out[1] = tempv[1];
|
|
out[2] = tempv[2];
|
|
}
|
|
}
|
|
|
|
//float CalcFov (float fov_x, float width, float height);
|
|
//clear scene, and set up the default stuff.
|
|
static void PF_R_ClearScene (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
extern frame_t *view_frame;
|
|
extern player_state_t *view_message;
|
|
|
|
if (*prinst->callargc > 0)
|
|
CSQC_ChangeLocalPlayer(G_FLOAT(OFS_PARM0));
|
|
|
|
csqc_rebuildmatricies = true;
|
|
|
|
CL_DecayLights ();
|
|
|
|
if (cl.worldmodel)
|
|
{
|
|
//work out which packet entities are solid
|
|
CL_SetSolidEntities ();
|
|
|
|
// Set up prediction for other players
|
|
CL_SetUpPlayerPrediction(false);
|
|
|
|
// do client side motion prediction
|
|
CL_PredictMove ();
|
|
|
|
// Set up prediction for other players
|
|
CL_SetUpPlayerPrediction(true);
|
|
}
|
|
|
|
CL_SwapEntityLists();
|
|
|
|
view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
|
|
view_message = &view_frame->playerstate[cl.playernum[csqc_lplayernum]];
|
|
#ifdef NQPROT
|
|
if (cls.protocol == CP_NETQUAKE || !view_message->messagenum)
|
|
view_message->weaponframe = cl.stats[csqc_lplayernum][STAT_WEAPONFRAME];
|
|
#endif
|
|
V_CalcRefdef(csqc_lplayernum); //set up the defaults (for player 0)
|
|
/*
|
|
VectorCopy(cl.simangles[csqc_lplayernum], r_refdef.viewangles);
|
|
VectorCopy(cl.simorg[csqc_lplayernum], r_refdef.vieworg);
|
|
r_refdef.flags = 0;
|
|
|
|
r_refdef.vrect.x = 0;
|
|
r_refdef.vrect.y = 0;
|
|
r_refdef.vrect.width = vid.width;
|
|
r_refdef.vrect.height = vid.height;
|
|
|
|
r_refdef.fov_x = scr_fov.value;
|
|
r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
|
|
*/
|
|
|
|
csqc_addcrosshair = false;
|
|
csqc_drawsbar = false;
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
VF_MIN = 1,
|
|
VF_MIN_X = 2,
|
|
VF_MIN_Y = 3,
|
|
VF_SIZE = 4,
|
|
VF_SIZE_X = 5,
|
|
VF_SIZE_Y = 6,
|
|
VF_VIEWPORT = 7,
|
|
VF_FOV = 8,
|
|
VF_FOVX = 9,
|
|
VF_FOVY = 10,
|
|
VF_ORIGIN = 11,
|
|
VF_ORIGIN_X = 12,
|
|
VF_ORIGIN_Y = 13,
|
|
VF_ORIGIN_Z = 14,
|
|
VF_ANGLES = 15,
|
|
VF_ANGLES_X = 16,
|
|
VF_ANGLES_Y = 17,
|
|
VF_ANGLES_Z = 18,
|
|
VF_DRAWWORLD = 19,
|
|
VF_ENGINESBAR = 20,
|
|
VF_DRAWCROSSHAIR = 21,
|
|
VF_CARTESIAN_ANGLES = 22,
|
|
|
|
//this is a DP-compatibility hack.
|
|
VF_CL_VIEWANGLES_V = 33,
|
|
VF_CL_VIEWANGLES_X = 34,
|
|
VF_CL_VIEWANGLES_Y = 35,
|
|
VF_CL_VIEWANGLES_Z = 36,
|
|
|
|
#pragma message("FIXME: add cshift")
|
|
|
|
//33-36 used by DP...
|
|
VF_PERSPECTIVE = 200,
|
|
//201 used by DP... WTF? CLEARSCREEN
|
|
VF_LPLAYER = 202,
|
|
VF_AFOV = 203, //aproximate fov (match what the engine would normally use for the fov cvar). p0=fov, p1=zoom
|
|
} viewflags;
|
|
|
|
static void PF_R_SetViewFlag(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
viewflags parametertype = G_FLOAT(OFS_PARM0);
|
|
float *p = G_VECTOR(OFS_PARM1);
|
|
|
|
csqc_rebuildmatricies = true;
|
|
|
|
G_FLOAT(OFS_RETURN) = 1;
|
|
switch(parametertype)
|
|
{
|
|
case VF_FOV:
|
|
r_refdef.fov_x = p[0];
|
|
r_refdef.fov_y = p[1];
|
|
break;
|
|
|
|
case VF_FOVX:
|
|
r_refdef.fov_x = *p;
|
|
break;
|
|
|
|
case VF_FOVY:
|
|
r_refdef.fov_y = *p;
|
|
break;
|
|
|
|
case VF_AFOV:
|
|
{
|
|
float frustumx, frustumy;
|
|
frustumy = tan(p[0] * (M_PI/360)) * 0.75;
|
|
if (*prinst->callargc > 2)
|
|
frustumy *= G_FLOAT(OFS_PARM2);
|
|
frustumx = frustumy * vid.width / vid.height /* / vid.pixelheight*/;
|
|
r_refdef.fov_x = atan2(frustumx, 1) * (360/M_PI);
|
|
r_refdef.fov_y = atan2(frustumy, 1) * (360/M_PI);
|
|
}
|
|
break;
|
|
|
|
case VF_ORIGIN:
|
|
VectorCopy(p, r_refdef.vieworg);
|
|
cl.crouch[csqc_lplayernum] = 0;
|
|
break;
|
|
|
|
case VF_ORIGIN_Z:
|
|
cl.crouch[csqc_lplayernum] = 0;
|
|
case VF_ORIGIN_X:
|
|
case VF_ORIGIN_Y:
|
|
r_refdef.vieworg[parametertype-VF_ORIGIN_X] = *p;
|
|
break;
|
|
|
|
case VF_ANGLES:
|
|
VectorCopy(p, r_refdef.viewangles);
|
|
break;
|
|
case VF_ANGLES_X:
|
|
case VF_ANGLES_Y:
|
|
case VF_ANGLES_Z:
|
|
r_refdef.viewangles[parametertype-VF_ANGLES_X] = *p;
|
|
break;
|
|
|
|
case VF_CL_VIEWANGLES_V:
|
|
VectorCopy(p, cl.viewangles[csqc_lplayernum]);
|
|
break;
|
|
case VF_CL_VIEWANGLES_X:
|
|
case VF_CL_VIEWANGLES_Y:
|
|
case VF_CL_VIEWANGLES_Z:
|
|
cl.viewangles[csqc_lplayernum][parametertype-VF_CL_VIEWANGLES_X] = *p;
|
|
break;
|
|
|
|
case VF_CARTESIAN_ANGLES:
|
|
Con_Printf(CON_WARNING "WARNING: CARTESIAN ANGLES ARE NOT YET SUPPORTED!\n");
|
|
break;
|
|
|
|
case VF_VIEWPORT:
|
|
r_refdef.vrect.x = p[0];
|
|
r_refdef.vrect.y = p[1];
|
|
p+=3;
|
|
r_refdef.vrect.width = p[0];
|
|
r_refdef.vrect.height = p[1];
|
|
break;
|
|
|
|
case VF_SIZE_X:
|
|
r_refdef.vrect.width = *p;
|
|
break;
|
|
case VF_SIZE_Y:
|
|
r_refdef.vrect.height = *p;
|
|
break;
|
|
case VF_SIZE:
|
|
r_refdef.vrect.width = p[0];
|
|
r_refdef.vrect.height = p[1];
|
|
break;
|
|
|
|
case VF_MIN_X:
|
|
r_refdef.vrect.x = *p;
|
|
break;
|
|
case VF_MIN_Y:
|
|
r_refdef.vrect.y = *p;
|
|
break;
|
|
case VF_MIN:
|
|
r_refdef.vrect.x = p[0];
|
|
r_refdef.vrect.y = p[1];
|
|
break;
|
|
|
|
case VF_DRAWWORLD:
|
|
r_refdef.flags = (r_refdef.flags&~Q2RDF_NOWORLDMODEL) | (*p?0:Q2RDF_NOWORLDMODEL);
|
|
break;
|
|
case VF_ENGINESBAR:
|
|
csqc_drawsbar = *p;
|
|
break;
|
|
case VF_DRAWCROSSHAIR:
|
|
csqc_addcrosshair = *p;
|
|
break;
|
|
|
|
case VF_PERSPECTIVE:
|
|
r_refdef.useperspective = *p;
|
|
break;
|
|
|
|
default:
|
|
Con_DPrintf("SetViewFlag: %i not recognised\n", parametertype);
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void PF_R_GetViewFlag(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
viewflags parametertype = G_FLOAT(OFS_PARM0);
|
|
float *r = G_VECTOR(OFS_RETURN);
|
|
|
|
r[0] = 0;
|
|
r[1] = 0;
|
|
r[2] = 0;
|
|
|
|
switch(parametertype)
|
|
{
|
|
case VF_FOV:
|
|
r[0] = r_refdef.fov_x;
|
|
r[1] = r_refdef.fov_y;
|
|
break;
|
|
|
|
case VF_FOVX:
|
|
*r = r_refdef.fov_x;
|
|
break;
|
|
|
|
case VF_FOVY:
|
|
*r = r_refdef.fov_y;
|
|
break;
|
|
|
|
#pragma message("fixme: AFOV not retrievable")
|
|
case VF_AFOV:
|
|
*r = r_refdef.fov_x;
|
|
break;
|
|
|
|
case VF_ORIGIN:
|
|
#ifdef CHEAT_PARANOID
|
|
VectorClear(r);
|
|
#else
|
|
VectorCopy(r_refdef.vieworg, r);
|
|
#endif
|
|
break;
|
|
|
|
case VF_ORIGIN_Z:
|
|
case VF_ORIGIN_X:
|
|
case VF_ORIGIN_Y:
|
|
#ifdef CHEAT_PARANOID
|
|
*r = 0;
|
|
#else
|
|
*r = r_refdef.vieworg[parametertype-VF_ORIGIN_X];
|
|
#endif
|
|
break;
|
|
|
|
case VF_ANGLES:
|
|
VectorCopy(r_refdef.viewangles, r);
|
|
break;
|
|
case VF_ANGLES_X:
|
|
case VF_ANGLES_Y:
|
|
case VF_ANGLES_Z:
|
|
*r = r_refdef.viewangles[parametertype-VF_ANGLES_X];
|
|
break;
|
|
|
|
case VF_CL_VIEWANGLES_V:
|
|
VectorCopy(cl.viewangles[csqc_lplayernum], r);
|
|
break;
|
|
case VF_CL_VIEWANGLES_X:
|
|
case VF_CL_VIEWANGLES_Y:
|
|
case VF_CL_VIEWANGLES_Z:
|
|
*r = cl.viewangles[csqc_lplayernum][parametertype-VF_CL_VIEWANGLES_X];
|
|
break;
|
|
|
|
case VF_CARTESIAN_ANGLES:
|
|
Con_Printf(CON_WARNING "WARNING: CARTESIAN ANGLES ARE NOT YET SUPPORTED!\n");
|
|
break;
|
|
|
|
case VF_VIEWPORT:
|
|
r[0] = r_refdef.vrect.width;
|
|
r[1] = r_refdef.vrect.height;
|
|
break;
|
|
|
|
case VF_SIZE_X:
|
|
*r = r_refdef.vrect.width;
|
|
break;
|
|
case VF_SIZE_Y:
|
|
*r = r_refdef.vrect.height;
|
|
break;
|
|
case VF_SIZE:
|
|
r[0] = r_refdef.vrect.width;
|
|
r[1] = r_refdef.vrect.height;
|
|
break;
|
|
|
|
case VF_MIN_X:
|
|
*r = r_refdef.vrect.x;
|
|
break;
|
|
case VF_MIN_Y:
|
|
*r = r_refdef.vrect.y;
|
|
break;
|
|
case VF_MIN:
|
|
r[0] = r_refdef.vrect.x;
|
|
r[1] = r_refdef.vrect.y;
|
|
break;
|
|
|
|
case VF_DRAWWORLD:
|
|
*r = !(r_refdef.flags&Q2RDF_NOWORLDMODEL);;
|
|
break;
|
|
case VF_ENGINESBAR:
|
|
*r = csqc_drawsbar;
|
|
break;
|
|
case VF_DRAWCROSSHAIR:
|
|
*r = csqc_addcrosshair;
|
|
break;
|
|
|
|
case VF_PERSPECTIVE:
|
|
*r = r_refdef.useperspective;
|
|
break;
|
|
|
|
default:
|
|
Con_DPrintf("GetViewFlag: %i not recognised\n", parametertype);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void PF_R_RenderScene(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
if (cl.worldmodel)
|
|
R_PushDlights ();
|
|
|
|
#ifdef RGLQUAKE
|
|
if (qrenderer == QR_OPENGL)
|
|
{
|
|
gl_ztrickdisabled|=16;
|
|
qglDisable(GL_ALPHA_TEST);
|
|
qglDisable(GL_BLEND);
|
|
}
|
|
#endif
|
|
|
|
r_refdef.currentplayernum = csqc_lplayernum;
|
|
|
|
VectorCopy (r_refdef.vieworg, cl.viewent[csqc_lplayernum].origin);
|
|
CalcGunAngle(csqc_lplayernum);
|
|
|
|
R_RenderView();
|
|
|
|
#ifdef RGLQUAKE
|
|
if (qrenderer == QR_OPENGL)
|
|
{
|
|
gl_ztrickdisabled&=~16;
|
|
GL_Set2D ();
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
GL_TexEnv(GL_MODULATE);
|
|
}
|
|
#endif
|
|
|
|
#ifdef RGLQUAKE
|
|
if (qrenderer == QR_OPENGL)
|
|
{
|
|
qglDisable(GL_ALPHA_TEST);
|
|
qglEnable(GL_BLEND);
|
|
}
|
|
#endif
|
|
|
|
vid.recalc_refdef = 1;
|
|
|
|
if (csqc_drawsbar)
|
|
{
|
|
#ifdef PLUGINS
|
|
Plug_SBar();
|
|
#else
|
|
if (Sbar_ShouldDraw())
|
|
{
|
|
Sbar_Draw ();
|
|
Sbar_DrawScoreboard ();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (csqc_addcrosshair)
|
|
Draw_Crosshair();
|
|
}
|
|
|
|
static void PF_cs_getstatf(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int stnum = G_FLOAT(OFS_PARM0);
|
|
float val = *(float*)&cl.stats[csqc_lplayernum][stnum]; //copy float into the stat
|
|
G_FLOAT(OFS_RETURN) = val;
|
|
}
|
|
static void PF_cs_getstati(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{ //convert an int stat into a qc float.
|
|
|
|
int stnum = G_FLOAT(OFS_PARM0);
|
|
int val = cl.stats[csqc_lplayernum][stnum];
|
|
if (*prinst->callargc > 1)
|
|
{
|
|
int first, count;
|
|
first = G_FLOAT(OFS_PARM1);
|
|
if (*prinst->callargc > 2)
|
|
count = G_FLOAT(OFS_PARM2);
|
|
else
|
|
count = 1;
|
|
G_FLOAT(OFS_RETURN) = (((unsigned int)val)&(((1<<count)-1)<<first))>>first;
|
|
}
|
|
else
|
|
G_FLOAT(OFS_RETURN) = val;
|
|
}
|
|
static void PF_cs_getstats(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int stnum = G_FLOAT(OFS_PARM0);
|
|
char out[8];
|
|
|
|
//the network protocol byteswaps
|
|
|
|
((unsigned int*)out)[0] = LittleLong(cl.stats[csqc_lplayernum][stnum+0]);
|
|
((unsigned int*)out)[1] = LittleLong(cl.stats[csqc_lplayernum][stnum+1]);
|
|
((unsigned int*)out)[2] = LittleLong(cl.stats[csqc_lplayernum][stnum+2]);
|
|
((unsigned int*)out)[3] = LittleLong(cl.stats[csqc_lplayernum][stnum+3]);
|
|
((unsigned int*)out)[4] = 0; //make sure it's null terminated
|
|
|
|
RETURN_TSTRING(out);
|
|
}
|
|
|
|
static void PF_cs_SetOrigin(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);
|
|
float *org = G_VECTOR(OFS_PARM1);
|
|
|
|
VectorCopy(org, ent->v->origin);
|
|
|
|
CS_LinkEdict(ent, false);
|
|
}
|
|
|
|
static void PF_cs_SetSize(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);
|
|
float *mins = G_VECTOR(OFS_PARM1);
|
|
float *maxs = G_VECTOR(OFS_PARM2);
|
|
|
|
VectorCopy(mins, ent->v->mins);
|
|
VectorCopy(maxs, ent->v->maxs);
|
|
|
|
CS_LinkEdict(ent, false);
|
|
}
|
|
|
|
static void cs_settracevars(trace_t *tr)
|
|
{
|
|
*csqcg.trace_allsolid = tr->allsolid;
|
|
*csqcg.trace_startsolid = tr->startsolid;
|
|
*csqcg.trace_fraction = tr->fraction;
|
|
*csqcg.trace_inwater = tr->inwater;
|
|
*csqcg.trace_inopen = tr->inopen;
|
|
VectorCopy (tr->endpos, csqcg.trace_endpos);
|
|
VectorCopy (tr->plane.normal, csqcg.trace_plane_normal);
|
|
*csqcg.trace_plane_dist = tr->plane.dist;
|
|
if (csqcg.trace_surfaceflags)
|
|
*csqcg.trace_surfaceflags = tr->surface?tr->surface->flags:0;
|
|
if (csqcg.trace_endcontents)
|
|
*csqcg.trace_endcontents = tr->contents;
|
|
if (tr->ent)
|
|
*csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)tr->ent);
|
|
else
|
|
*csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)csqc_edicts);
|
|
}
|
|
|
|
static void PF_cs_traceline(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *v1, *v2, *mins, *maxs;
|
|
trace_t trace;
|
|
int nomonsters;
|
|
csqcedict_t *ent;
|
|
int savedhull;
|
|
|
|
v1 = G_VECTOR(OFS_PARM0);
|
|
v2 = G_VECTOR(OFS_PARM1);
|
|
nomonsters = G_FLOAT(OFS_PARM2);
|
|
ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM3);
|
|
|
|
// if (*prinst->callargc == 6)
|
|
// {
|
|
// mins = G_VECTOR(OFS_PARM4);
|
|
// maxs = G_VECTOR(OFS_PARM5);
|
|
// }
|
|
// else
|
|
{
|
|
mins = vec3_origin;
|
|
maxs = vec3_origin;
|
|
}
|
|
|
|
savedhull = ent->v->hull;
|
|
ent->v->hull = 0;
|
|
trace = CS_Move (v1, mins, maxs, v2, nomonsters, ent);
|
|
ent->v->hull = savedhull;
|
|
|
|
cs_settracevars(&trace);
|
|
}
|
|
static void PF_cs_tracebox(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *v1, *v2, *mins, *maxs;
|
|
trace_t trace;
|
|
int nomonsters;
|
|
csqcedict_t *ent;
|
|
int savedhull;
|
|
|
|
v1 = G_VECTOR(OFS_PARM0);
|
|
mins = G_VECTOR(OFS_PARM1);
|
|
maxs = G_VECTOR(OFS_PARM2);
|
|
v2 = G_VECTOR(OFS_PARM3);
|
|
nomonsters = G_FLOAT(OFS_PARM4);
|
|
ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM5);
|
|
|
|
savedhull = ent->v->hull;
|
|
ent->v->hull = 0;
|
|
trace = CS_Move (v1, mins, maxs, v2, nomonsters, ent);
|
|
ent->v->hull = savedhull;
|
|
|
|
*csqcg.trace_allsolid = trace.allsolid;
|
|
*csqcg.trace_startsolid = trace.startsolid;
|
|
*csqcg.trace_fraction = trace.fraction;
|
|
*csqcg.trace_inwater = trace.inwater;
|
|
*csqcg.trace_inopen = trace.inopen;
|
|
VectorCopy (trace.endpos, csqcg.trace_endpos);
|
|
VectorCopy (trace.plane.normal, csqcg.trace_plane_normal);
|
|
*csqcg.trace_plane_dist = trace.plane.dist;
|
|
if (trace.ent)
|
|
*csqcg.trace_ent = EDICT_TO_PROG(prinst, (void*)trace.ent);
|
|
else
|
|
*csqcg.trace_ent = EDICT_TO_PROG(prinst, (void*)csqc_edicts);
|
|
}
|
|
|
|
static trace_t CS_Trace_Toss (csqcedict_t *tossent, csqcedict_t *ignore)
|
|
{
|
|
int i;
|
|
int savedhull;
|
|
float gravity;
|
|
vec3_t move, end;
|
|
trace_t trace;
|
|
// float maxvel = Cvar_Get("sv_maxvelocity", "2000", 0, "CSQC physics")->value;
|
|
|
|
vec3_t origin, velocity;
|
|
|
|
// this has to fetch the field from the original edict, since our copy is truncated
|
|
gravity = 1;//tossent->v->gravity;
|
|
if (!gravity)
|
|
gravity = 1.0;
|
|
gravity *= Cvar_Get("sv_gravity", "800", 0, "CSQC physics")->value * 0.05;
|
|
|
|
VectorCopy (tossent->v->origin, origin);
|
|
VectorCopy (tossent->v->velocity, velocity);
|
|
|
|
CS_CheckVelocity (tossent);
|
|
|
|
savedhull = tossent->v->hull;
|
|
tossent->v->hull = 0;
|
|
for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
|
|
{
|
|
velocity[2] -= gravity;
|
|
VectorScale (velocity, 0.05, move);
|
|
VectorAdd (origin, move, end);
|
|
trace = CS_Move (origin, tossent->v->mins, tossent->v->maxs, end, MOVE_NORMAL, tossent);
|
|
VectorCopy (trace.endpos, origin);
|
|
|
|
CS_CheckVelocity (tossent);
|
|
|
|
if (trace.fraction < 1 && trace.ent && (void*)trace.ent != ignore)
|
|
break;
|
|
}
|
|
tossent->v->hull = savedhull;
|
|
|
|
trace.fraction = 0; // not relevant
|
|
return trace;
|
|
}
|
|
static void PF_cs_tracetoss (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
trace_t trace;
|
|
csqcedict_t *ent;
|
|
csqcedict_t *ignore;
|
|
|
|
ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
if (ent == csqc_edicts)
|
|
Con_DPrintf("tracetoss: can not use world entity\n");
|
|
ignore = (csqcedict_t*)G_EDICT(prinst, OFS_PARM1);
|
|
|
|
trace = CS_Trace_Toss (ent, ignore);
|
|
|
|
*csqcg.trace_allsolid = trace.allsolid;
|
|
*csqcg.trace_startsolid = trace.startsolid;
|
|
*csqcg.trace_fraction = trace.fraction;
|
|
*csqcg.trace_inwater = trace.inwater;
|
|
*csqcg.trace_inopen = trace.inopen;
|
|
VectorCopy (trace.endpos, csqcg.trace_endpos);
|
|
VectorCopy (trace.plane.normal, csqcg.trace_plane_normal);
|
|
*csqcg.trace_plane_dist = trace.plane.dist;
|
|
if (trace.ent)
|
|
*csqcg.trace_ent = EDICT_TO_PROG(prinst, trace.ent);
|
|
else
|
|
*csqcg.trace_ent = EDICT_TO_PROG(prinst, (void*)csqc_edicts);
|
|
}
|
|
|
|
static int CS_PointContents(vec3_t org)
|
|
{
|
|
if (!cl.worldmodel)
|
|
return FTECONTENTS_EMPTY;
|
|
return cl.worldmodel->funcs.PointContents(cl.worldmodel, org);
|
|
}
|
|
static void PF_cs_pointcontents(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *v;
|
|
int cont;
|
|
|
|
v = G_VECTOR(OFS_PARM0);
|
|
|
|
cont = CS_PointContents(v);
|
|
if (cont & FTECONTENTS_SOLID)
|
|
G_FLOAT(OFS_RETURN) = Q1CONTENTS_SOLID;
|
|
else if (cont & FTECONTENTS_SKY)
|
|
G_FLOAT(OFS_RETURN) = Q1CONTENTS_SKY;
|
|
else if (cont & FTECONTENTS_LAVA)
|
|
G_FLOAT(OFS_RETURN) = Q1CONTENTS_LAVA;
|
|
else if (cont & FTECONTENTS_SLIME)
|
|
G_FLOAT(OFS_RETURN) = Q1CONTENTS_SLIME;
|
|
else if (cont & FTECONTENTS_WATER)
|
|
G_FLOAT(OFS_RETURN) = Q1CONTENTS_WATER;
|
|
else
|
|
G_FLOAT(OFS_RETURN) = Q1CONTENTS_EMPTY;
|
|
}
|
|
|
|
static int FindModel(char *name, int *free)
|
|
{
|
|
int i;
|
|
|
|
*free = 0;
|
|
|
|
if (!name || !*name)
|
|
return 0;
|
|
|
|
for (i = 1; i < MAX_CSQCMODELS; i++)
|
|
{
|
|
if (!*cl.model_csqcname[i])
|
|
{
|
|
*free = -i;
|
|
break;
|
|
}
|
|
if (!strcmp(cl.model_csqcname[i], name))
|
|
return -i;
|
|
}
|
|
for (i = 1; i < MAX_MODELS; i++)
|
|
{
|
|
if (!strcmp(cl.model_name[i], name))
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void csqc_setmodel(progfuncs_t *prinst, csqcedict_t *ent, int modelindex)
|
|
{
|
|
model_t *model;
|
|
|
|
ent->v->modelindex = modelindex;
|
|
if (modelindex < 0)
|
|
{
|
|
if (modelindex <= -MAX_MODELS)
|
|
return;
|
|
ent->v->model = PR_SetString(prinst, cl.model_csqcname[-modelindex]);
|
|
if (!cl.model_csqcprecache[-modelindex])
|
|
cl.model_csqcprecache[-modelindex] = Mod_ForName(cl.model_csqcname[-modelindex], false);
|
|
model = cl.model_csqcprecache[-modelindex];
|
|
}
|
|
else
|
|
{
|
|
if (modelindex >= MAX_MODELS)
|
|
return;
|
|
ent->v->model = PR_SetString(prinst, cl.model_name[modelindex]);
|
|
model = cl.model_precache[modelindex];
|
|
}
|
|
if (model)
|
|
{
|
|
VectorCopy(model->mins, ent->v->mins);
|
|
VectorCopy(model->maxs, ent->v->maxs);
|
|
}
|
|
else
|
|
{
|
|
VectorClear(ent->v->mins);
|
|
VectorClear(ent->v->maxs);
|
|
}
|
|
}
|
|
|
|
static void PF_cs_SetModel(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);
|
|
char *modelname = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
int freei;
|
|
int modelindex = FindModel(modelname, &freei);
|
|
|
|
if (!modelindex && modelname && *modelname)
|
|
{
|
|
if (!freei)
|
|
Host_EndGame("CSQC ran out of model slots\n");
|
|
Con_DPrintf("Late caching model \"%s\"\n", modelname);
|
|
Q_strncpyz(cl.model_csqcname[-freei], modelname, sizeof(cl.model_csqcname[-freei])); //allocate a slot now
|
|
modelindex = freei;
|
|
|
|
cl.model_csqcprecache[-freei] = NULL;
|
|
}
|
|
|
|
csqc_setmodel(prinst, ent, modelindex);
|
|
}
|
|
static void PF_cs_SetModelIndex(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);
|
|
int modelindex = G_FLOAT(OFS_PARM1);
|
|
|
|
csqc_setmodel(prinst, ent, modelindex);
|
|
}
|
|
static void PF_cs_PrecacheModel(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int modelindex, freei;
|
|
char *modelname = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
int i;
|
|
|
|
for (i = 1; i < MAX_MODELS; i++) //Make sure that the server specified model is loaded..
|
|
{
|
|
if (!*cl.model_name[i])
|
|
break;
|
|
if (!strcmp(cl.model_name[i], modelname))
|
|
{
|
|
cl.model_precache[i] = Mod_ForName(cl.model_name[i], false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
modelindex = FindModel(modelname, &freei); //now load it
|
|
|
|
if (!modelindex)
|
|
{
|
|
if (!freei)
|
|
Host_EndGame("CSQC ran out of model slots\n");
|
|
Q_strncpyz(cl.model_csqcname[-freei], modelname, sizeof(cl.model_csqcname[-freei])); //allocate a slot now
|
|
modelindex = freei;
|
|
|
|
CL_CheckOrEnqueDownloadFile(modelname, modelname, 0);
|
|
cl.model_csqcprecache[-freei] = NULL;
|
|
}
|
|
|
|
G_FLOAT(OFS_RETURN) = modelindex;
|
|
}
|
|
static void PF_cs_PrecacheSound(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *soundname = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
S_PrecacheSound(soundname);
|
|
}
|
|
|
|
static void PF_cs_ModelnameForIndex(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int modelindex = G_FLOAT(OFS_PARM0);
|
|
|
|
if (modelindex < 0)
|
|
G_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.model_csqcname[-modelindex]);
|
|
else
|
|
G_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.model_name[modelindex]);
|
|
}
|
|
|
|
static void PF_ReadByte(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
if (csqc_fakereadbyte != -1)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = csqc_fakereadbyte;
|
|
csqc_fakereadbyte = -1;
|
|
}
|
|
else
|
|
{
|
|
G_FLOAT(OFS_RETURN) = MSG_ReadByte();
|
|
}
|
|
}
|
|
|
|
static void PF_ReadChar(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = MSG_ReadChar();
|
|
}
|
|
|
|
static void PF_ReadShort(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = MSG_ReadShort();
|
|
}
|
|
|
|
static void PF_ReadEntityNum(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
unsigned short val;
|
|
val = MSG_ReadShort();
|
|
if (val & 0x8000)
|
|
{ //our protocol only supports 15bits of revelent entity number (16th bit is used as 'remove').
|
|
//so warn with badly coded mods.
|
|
Con_Printf("ReadEntityNumber read bad entity number\n");
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
}
|
|
else
|
|
G_FLOAT(OFS_RETURN) = val;
|
|
}
|
|
|
|
static void PF_ReadLong(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = MSG_ReadLong();
|
|
}
|
|
|
|
static void PF_ReadCoord(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = MSG_ReadCoord();
|
|
}
|
|
|
|
static void PF_ReadFloat(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = MSG_ReadFloat();
|
|
}
|
|
|
|
static void PF_ReadString(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *read = MSG_ReadString();
|
|
|
|
RETURN_TSTRING(read);
|
|
}
|
|
|
|
static void PF_ReadAngle(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = MSG_ReadAngle();
|
|
}
|
|
|
|
|
|
static void PF_objerror (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *s;
|
|
struct edict_s *ed;
|
|
|
|
s = PF_VarString(prinst, 0, pr_globals);
|
|
/* Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name),s);
|
|
*/ ed = PROG_TO_EDICT(prinst, *csqcg.self);
|
|
/* ED_Print (ed);
|
|
*/
|
|
ED_Print(prinst, ed);
|
|
Con_Printf("%s", s);
|
|
|
|
if (developer.value)
|
|
(*prinst->pr_trace) = 2;
|
|
else
|
|
{
|
|
ED_Free (prinst, ed);
|
|
|
|
prinst->AbortStack(prinst);
|
|
|
|
PR_BIError (prinst, "Program error: %s", s);
|
|
}
|
|
}
|
|
|
|
static void PF_cs_setsensativityscaler (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
in_sensitivityscale = G_FLOAT(OFS_PARM0);
|
|
}
|
|
|
|
static void PF_cs_pointparticles (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int effectnum = G_FLOAT(OFS_PARM0)-1;
|
|
float *org = G_VECTOR(OFS_PARM1);
|
|
float *vel = G_VECTOR(OFS_PARM2);
|
|
float count = G_FLOAT(OFS_PARM3);
|
|
|
|
if (*prinst->callargc < 3)
|
|
vel = vec3_origin;
|
|
if (*prinst->callargc < 4)
|
|
count = 1;
|
|
|
|
P_RunParticleEffectType(org, vel, count, effectnum);
|
|
}
|
|
|
|
static void PF_cs_trailparticles (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int efnum = G_FLOAT(OFS_PARM0)-1;
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM1);
|
|
float *start = G_VECTOR(OFS_PARM2);
|
|
float *end = G_VECTOR(OFS_PARM3);
|
|
|
|
if (!ent->entnum) //world trails are non-state-based.
|
|
pe->ParticleTrail(start, end, efnum, NULL);
|
|
else
|
|
pe->ParticleTrail(start, end, efnum, &ent->trailstate);
|
|
}
|
|
|
|
static void PF_cs_particleeffectnum (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *effectname = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
|
|
//keep the effectinfo synced between server and client.
|
|
COM_Effectinfo_ForName(effectname);
|
|
|
|
G_FLOAT(OFS_RETURN) = pe->FindParticleType(effectname)+1;
|
|
}
|
|
|
|
static void cs_set_input_state (usercmd_t *cmd)
|
|
{
|
|
if (csqcg.input_timelength)
|
|
*csqcg.input_timelength = cmd->msec/1000.0f;
|
|
if (csqcg.input_angles)
|
|
{
|
|
csqcg.input_angles[0] = SHORT2ANGLE(cmd->angles[0]);
|
|
csqcg.input_angles[1] = SHORT2ANGLE(cmd->angles[1]);
|
|
csqcg.input_angles[2] = SHORT2ANGLE(cmd->angles[2]);
|
|
}
|
|
if (csqcg.input_movevalues)
|
|
{
|
|
csqcg.input_movevalues[0] = cmd->forwardmove;
|
|
csqcg.input_movevalues[1] = cmd->sidemove;
|
|
csqcg.input_movevalues[2] = cmd->upmove;
|
|
}
|
|
if (csqcg.input_buttons)
|
|
*csqcg.input_buttons = cmd->buttons;
|
|
|
|
if (csqcg.input_impulse)
|
|
*csqcg.input_impulse = cmd->impulse;
|
|
if (csqcg.input_lightlevel)
|
|
*csqcg.input_lightlevel = cmd->lightlevel;
|
|
if (csqcg.input_weapon)
|
|
*csqcg.input_weapon = cmd->weapon;
|
|
if (csqcg.input_servertime)
|
|
*csqcg.input_servertime = cmd->servertime/1000.0f;
|
|
if (csqcg.input_clienttime)
|
|
*csqcg.input_clienttime = cmd->fclienttime/1000.0f;
|
|
}
|
|
|
|
static void cs_get_input_state (usercmd_t *cmd)
|
|
{
|
|
if (csqcg.input_timelength)
|
|
cmd->msec = *csqcg.input_timelength*1000;
|
|
if (csqcg.input_angles)
|
|
{
|
|
cmd->angles[0] = ANGLE2SHORT(csqcg.input_angles[0]);
|
|
cmd->angles[1] = ANGLE2SHORT(csqcg.input_angles[1]);
|
|
cmd->angles[2] = ANGLE2SHORT(csqcg.input_angles[2]);
|
|
}
|
|
if (csqcg.input_movevalues)
|
|
{
|
|
cmd->forwardmove = csqcg.input_movevalues[0];
|
|
cmd->sidemove = csqcg.input_movevalues[1];
|
|
cmd->upmove = csqcg.input_movevalues[2];
|
|
}
|
|
if (csqcg.input_buttons)
|
|
cmd->buttons = *csqcg.input_buttons;
|
|
|
|
if (csqcg.input_impulse)
|
|
cmd->impulse = *csqcg.input_impulse;
|
|
if (csqcg.input_lightlevel)
|
|
cmd->lightlevel = *csqcg.input_lightlevel;
|
|
if (csqcg.input_weapon)
|
|
cmd->weapon = *csqcg.input_weapon;
|
|
if (csqcg.input_servertime)
|
|
cmd->servertime = *csqcg.input_servertime*1000;
|
|
}
|
|
|
|
//get the input commands, and stuff them into some globals.
|
|
static void PF_cs_getinputstate (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int f;
|
|
usercmd_t *cmd;
|
|
|
|
f = G_FLOAT(OFS_PARM0);
|
|
if (f >= cls.netchan.outgoing_sequence)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = false;
|
|
return;
|
|
}
|
|
if (f < cls.netchan.outgoing_sequence - UPDATE_MASK || f < 0)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = false;
|
|
return;
|
|
}
|
|
|
|
// save this command off for prediction
|
|
cmd = &cl.frames[f&UPDATE_MASK].cmd[csqc_lplayernum];
|
|
|
|
cs_set_input_state(cmd);
|
|
|
|
G_FLOAT(OFS_RETURN) = true;
|
|
}
|
|
|
|
//read lots of globals, run the default player physics, write lots of globals.
|
|
//not intended to affect client state at all
|
|
static void PF_cs_runplayerphysics (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
unsigned int msecs;
|
|
extern vec3_t player_mins;
|
|
extern vec3_t player_maxs;
|
|
|
|
if (!cl.worldmodel)
|
|
return; //urm..
|
|
/*
|
|
int sequence; // just for debugging prints
|
|
|
|
// player state
|
|
vec3_t origin;
|
|
vec3_t angles;
|
|
vec3_t velocity;
|
|
qboolean jump_held;
|
|
int jump_msec; // msec since last jump
|
|
float waterjumptime;
|
|
int pm_type;
|
|
int hullnum;
|
|
|
|
// world state
|
|
int numphysent;
|
|
physent_t physents[MAX_PHYSENTS]; // 0 should be the world
|
|
|
|
// input
|
|
usercmd_t cmd;
|
|
|
|
qboolean onladder;
|
|
|
|
// results
|
|
int numtouch;
|
|
int touchindex[MAX_PHYSENTS];
|
|
qboolean onground;
|
|
int groundent; // index in physents array, only valid
|
|
// when onground is true
|
|
int waterlevel;
|
|
int watertype;
|
|
} playermove_t;
|
|
|
|
typedef struct {
|
|
float gravity;
|
|
float stopspeed;
|
|
float maxspeed;
|
|
float spectatormaxspeed;
|
|
float accelerate;
|
|
float airaccelerate;
|
|
float wateraccelerate;
|
|
float friction;
|
|
float waterfriction;
|
|
float entgravity;
|
|
float bunnyspeedcap;
|
|
float ktjump;
|
|
qboolean slidefix;
|
|
qboolean airstep;
|
|
qboolean walljump;
|
|
|
|
|
|
*/
|
|
|
|
pmove.sequence = *csqcg.clientcommandframe;
|
|
pmove.pm_type = PM_NORMAL;
|
|
|
|
pmove.jump_msec = 0;//(cls.z_ext & Z_EXT_PM_TYPE) ? 0 : from->jump_msec;
|
|
if (csqcg.pmove_jump_held)
|
|
pmove.jump_held = *csqcg.pmove_jump_held;
|
|
if (csqcg.pmove_waterjumptime)
|
|
pmove.waterjumptime = *csqcg.pmove_waterjumptime;
|
|
|
|
//set up the movement command
|
|
msecs = *csqcg.input_timelength*1000 + 0.5f;
|
|
//precision inaccuracies. :(
|
|
pmove.cmd.angles[0] = ANGLE2SHORT(csqcg.input_angles[0]);
|
|
pmove.cmd.angles[1] = ANGLE2SHORT(csqcg.input_angles[1]);
|
|
pmove.cmd.angles[2] = ANGLE2SHORT(csqcg.input_angles[2]);
|
|
VectorCopy(csqcg.input_angles, pmove.angles);
|
|
|
|
pmove.cmd.forwardmove = csqcg.input_movevalues[0];
|
|
pmove.cmd.sidemove = csqcg.input_movevalues[1];
|
|
pmove.cmd.upmove = csqcg.input_movevalues[2];
|
|
pmove.cmd.buttons = *csqcg.input_buttons;
|
|
|
|
VectorCopy(csqcg.pmove_org, pmove.origin);
|
|
VectorCopy(csqcg.pmove_vel, pmove.velocity);
|
|
VectorCopy(csqcg.pmove_maxs, player_maxs);
|
|
VectorCopy(csqcg.pmove_mins, player_mins);
|
|
pmove.hullnum = 1;
|
|
|
|
CL_SetSolidEntities();
|
|
|
|
|
|
|
|
while(msecs) //break up longer commands
|
|
{
|
|
pmove.cmd.msec = msecs;
|
|
if (pmove.cmd.msec > 50)
|
|
pmove.cmd.msec = 50;
|
|
msecs -= pmove.cmd.msec;
|
|
PM_PlayerMove(1);
|
|
}
|
|
|
|
if (csqcg.pmove_jump_held)
|
|
*csqcg.pmove_jump_held = pmove.jump_held;
|
|
if (csqcg.pmove_waterjumptime)
|
|
*csqcg.pmove_waterjumptime = pmove.waterjumptime;
|
|
VectorCopy(pmove.origin, csqcg.pmove_org);
|
|
VectorCopy(pmove.velocity, csqcg.pmove_vel);
|
|
|
|
pmove.origin[0] = ((int)(pmove.origin[0]*8))/8.0f;
|
|
pmove.origin[1] = ((int)(pmove.origin[1]*8))/8.0f;
|
|
pmove.origin[2] = ((int)(pmove.origin[2]*8))/8.0f;
|
|
}
|
|
|
|
static void PF_cs_getentitytoken (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
if (!csqcmapentitydata)
|
|
{
|
|
//nothing more to parse
|
|
G_INT(OFS_RETURN) = 0;
|
|
}
|
|
else
|
|
{
|
|
com_tokentype = TTP_LINEENDING;
|
|
while(com_tokentype == TTP_LINEENDING)
|
|
{
|
|
csqcmapentitydata = COM_ParseToken(csqcmapentitydata, "{}()\'\":,");
|
|
}
|
|
RETURN_TSTRING(com_token);
|
|
}
|
|
}
|
|
|
|
static void CheckSendPings(void)
|
|
{ //quakeworld sends a 'pings' client command to retrieve the frequently updating stuff
|
|
if (realtime - cl.last_ping_request > 2)
|
|
{
|
|
cl.last_ping_request = realtime;
|
|
CL_SendClientCommand(true, "pings");
|
|
}
|
|
}
|
|
|
|
static void PF_cs_serverkey (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *keyname = PF_VarString(prinst, 0, pr_globals);
|
|
char *ret;
|
|
char adr[MAX_ADR_SIZE];
|
|
|
|
if (!strcmp(keyname, "ip"))
|
|
ret = NET_AdrToString(adr, sizeof(adr), cls.netchan.remote_address);
|
|
else if (!strcmp(keyname, "protocol"))
|
|
{ //using this is pretty acedemic, really. Not particuarly portable.
|
|
switch (cls.protocol)
|
|
{ //a tokenizable string
|
|
//first is the base game qw/nq
|
|
//second is branch (custom engine name)
|
|
//third is protocol version.
|
|
default:
|
|
case CP_UNKNOWN:
|
|
ret = "Unknown";
|
|
break;
|
|
case CP_QUAKEWORLD:
|
|
if (cls.fteprotocolextensions)
|
|
ret = "QuakeWorld FTE";
|
|
else if (cls.z_ext)
|
|
ret = "QuakeWorld ZQuake";
|
|
else
|
|
ret = "QuakeWorld";
|
|
break;
|
|
case CP_NETQUAKE:
|
|
switch (nq_dp_protocol)
|
|
{
|
|
default:
|
|
ret = "NetQuake";
|
|
break;
|
|
case 5:
|
|
ret = "NetQuake DarkPlaces 5";
|
|
break;
|
|
case 6:
|
|
ret = "NetQuake DarkPlaces 6";
|
|
break;
|
|
case 7:
|
|
ret = "NetQuake DarkPlaces 7";
|
|
break;
|
|
}
|
|
break;
|
|
case CP_QUAKE2:
|
|
ret = "Quake2";
|
|
break;
|
|
case CP_QUAKE3:
|
|
ret = "Quake3";
|
|
break;
|
|
case CP_PLUGIN:
|
|
ret = "External";
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = Info_ValueForKey(cl.serverinfo, keyname);
|
|
}
|
|
|
|
if (*ret)
|
|
RETURN_TSTRING(ret);
|
|
else
|
|
G_INT(OFS_RETURN) = 0;
|
|
}
|
|
|
|
//string(float pnum, string keyname)
|
|
static void PF_cs_getplayerkey (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char buffer[64];
|
|
char *ret;
|
|
int pnum = G_FLOAT(OFS_PARM0);
|
|
char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
if (pnum < 0)
|
|
{
|
|
if (csqc_resortfrags)
|
|
{
|
|
Sbar_SortFrags(false);
|
|
csqc_resortfrags = false;
|
|
}
|
|
if (pnum >= -scoreboardlines)
|
|
{//sort by
|
|
pnum = fragsort[-(pnum+1)];
|
|
}
|
|
}
|
|
|
|
if (pnum < 0 || pnum >= MAX_CLIENTS)
|
|
ret = "";
|
|
else if (!*cl.players[pnum].userinfo)
|
|
ret = ""; //player isn't on the server.
|
|
else if (!strcmp(keyname, "ping"))
|
|
{
|
|
CheckSendPings();
|
|
|
|
ret = buffer;
|
|
sprintf(ret, "%i", cl.players[pnum].ping);
|
|
}
|
|
else if (!strcmp(keyname, "frags"))
|
|
{
|
|
ret = buffer;
|
|
sprintf(ret, "%i", cl.players[pnum].frags);
|
|
}
|
|
else if (!strcmp(keyname, "pl")) //packet loss
|
|
{
|
|
CheckSendPings();
|
|
|
|
ret = buffer;
|
|
sprintf(ret, "%i", cl.players[pnum].pl);
|
|
}
|
|
else if (!strcmp(keyname, "entertime")) //packet loss
|
|
{
|
|
ret = buffer;
|
|
sprintf(ret, "%i", (int)cl.players[pnum].entertime);
|
|
}
|
|
else
|
|
{
|
|
ret = Info_ValueForKey(cl.players[pnum].userinfo, keyname);
|
|
}
|
|
if (*ret)
|
|
RETURN_TSTRING(ret);
|
|
else
|
|
G_INT(OFS_RETURN) = 0;
|
|
}
|
|
|
|
static void PF_checkextension (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *extname = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
int i;
|
|
for (i = 0; i < QSG_Extensions_count; i++)
|
|
{
|
|
if (!QSG_Extensions[i].name)
|
|
continue;
|
|
|
|
if (i < 32 && cls.protocol == CP_QUAKEWORLD)
|
|
if (!(cls.fteprotocolextensions & (1<<i)))
|
|
continue;
|
|
|
|
if (!strcmp(QSG_Extensions[i].name, extname))
|
|
{
|
|
G_FLOAT(OFS_RETURN) = true;
|
|
return;
|
|
}
|
|
}
|
|
G_FLOAT(OFS_RETURN) = false;
|
|
}
|
|
|
|
static void PF_cs_sound(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *sample;
|
|
int channel;
|
|
csqcedict_t *entity;
|
|
float volume;
|
|
float attenuation;
|
|
|
|
sfx_t *sfx;
|
|
|
|
entity = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
channel = G_FLOAT(OFS_PARM1);
|
|
sample = PR_GetStringOfs(prinst, OFS_PARM2);
|
|
volume = G_FLOAT(OFS_PARM3);
|
|
attenuation = G_FLOAT(OFS_PARM4);
|
|
|
|
sfx = S_PrecacheSound(sample);
|
|
if (sfx)
|
|
S_StartSound(-entity->entnum, channel, sfx, entity->v->origin, volume, attenuation);
|
|
};
|
|
|
|
void PF_cs_pointsound(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *sample;
|
|
float *origin;
|
|
float volume;
|
|
float attenuation;
|
|
|
|
sfx_t *sfx;
|
|
|
|
origin = G_VECTOR(OFS_PARM0);
|
|
sample = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
volume = G_FLOAT(OFS_PARM2);
|
|
attenuation = G_FLOAT(OFS_PARM3);
|
|
|
|
sfx = S_PrecacheSound(sample);
|
|
if (sfx)
|
|
S_StartSound(0, 0, sfx, origin, volume, attenuation);
|
|
}
|
|
|
|
static void PF_cs_particle(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org = G_VECTOR(OFS_PARM0);
|
|
float *dir = G_VECTOR(OFS_PARM1);
|
|
float colour = G_FLOAT(OFS_PARM2);
|
|
float count = G_FLOAT(OFS_PARM2);
|
|
|
|
pe->RunParticleEffect(org, dir, colour, count);
|
|
}
|
|
static void PF_cs_particle2(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org, *dmin, *dmax;
|
|
float colour;
|
|
float count;
|
|
float effect;
|
|
|
|
org = G_VECTOR(OFS_PARM0);
|
|
dmin = G_VECTOR(OFS_PARM1);
|
|
dmax = G_VECTOR(OFS_PARM2);
|
|
colour = G_FLOAT(OFS_PARM3);
|
|
effect = G_FLOAT(OFS_PARM4);
|
|
count = G_FLOAT(OFS_PARM5);
|
|
|
|
pe->RunParticleEffect2 (org, dmin, dmax, colour, effect, count);
|
|
}
|
|
|
|
static void PF_cs_particle3(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org, *box;
|
|
float colour;
|
|
float count;
|
|
float effect;
|
|
|
|
org = G_VECTOR(OFS_PARM0);
|
|
box = G_VECTOR(OFS_PARM1);
|
|
colour = G_FLOAT(OFS_PARM2);
|
|
effect = G_FLOAT(OFS_PARM3);
|
|
count = G_FLOAT(OFS_PARM4);
|
|
|
|
pe->RunParticleEffect3(org, box, colour, effect, count);
|
|
}
|
|
|
|
static void PF_cs_particle4(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org;
|
|
float radius;
|
|
float colour;
|
|
float count;
|
|
float effect;
|
|
|
|
org = G_VECTOR(OFS_PARM0);
|
|
radius = G_FLOAT(OFS_PARM1);
|
|
colour = G_FLOAT(OFS_PARM2);
|
|
effect = G_FLOAT(OFS_PARM3);
|
|
count = G_FLOAT(OFS_PARM4);
|
|
|
|
pe->RunParticleEffect4(org, radius, colour, effect, count);
|
|
}
|
|
|
|
|
|
void CL_SpawnSpriteEffect(vec3_t org, model_t *model, int startframe, int framecount, int framerate);
|
|
void PF_cl_effect(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org = G_VECTOR(OFS_PARM0);
|
|
char *name = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
float startframe = G_FLOAT(OFS_PARM2);
|
|
float endframe = G_FLOAT(OFS_PARM3);
|
|
float framerate = G_FLOAT(OFS_PARM4);
|
|
model_t *mdl;
|
|
|
|
mdl = Mod_ForName(name, false);
|
|
if (mdl)
|
|
CL_SpawnSpriteEffect(org, mdl, startframe, endframe, framerate);
|
|
else
|
|
Con_Printf("PF_cl_effect: Couldn't load model %s\n", name);
|
|
}
|
|
|
|
void PF_cl_ambientsound(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *samp;
|
|
float *pos;
|
|
float vol, attenuation;
|
|
|
|
pos = G_VECTOR (OFS_PARM0);
|
|
samp = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
vol = G_FLOAT(OFS_PARM2);
|
|
attenuation = G_FLOAT(OFS_PARM3);
|
|
|
|
S_StaticSound (S_PrecacheSound (samp), pos, vol, attenuation);
|
|
}
|
|
|
|
static void PF_cs_vectorvectors (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
VectorCopy(G_VECTOR(OFS_PARM0), csqcg.forward);
|
|
VectorNormalize(csqcg.forward);
|
|
VectorVectors(csqcg.forward, csqcg.right, csqcg.up);
|
|
}
|
|
|
|
static void PF_cs_lightstyle (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int stnum = G_FLOAT(OFS_PARM0);
|
|
char *str = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
int colourflags = 7;
|
|
|
|
if ((unsigned)stnum >= MAX_LIGHTSTYLES)
|
|
{
|
|
Con_Printf ("PF_cs_lightstyle: stnum > MAX_LIGHTSTYLES");
|
|
return;
|
|
}
|
|
cl_lightstyle[stnum].colour = colourflags;
|
|
Q_strncpyz (cl_lightstyle[stnum].map, str, sizeof(cl_lightstyle[stnum].map));
|
|
cl_lightstyle[stnum].length = Q_strlen(cl_lightstyle[stnum].map);
|
|
}
|
|
|
|
static void PF_cs_changeyaw (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent;
|
|
float ideal, current, move, speed;
|
|
|
|
ent = (void*)PROG_TO_EDICT(prinst, *csqcg.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);
|
|
}
|
|
static void PF_cs_changepitch (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent;
|
|
float ideal, current, move, speed;
|
|
|
|
ent = (void*)PROG_TO_EDICT(prinst, *csqcg.self);
|
|
current = anglemod( ent->v->angles[0] );
|
|
ideal = ent->v->ideal_pitch;
|
|
speed = ent->v->pitch_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[0] = anglemod (current + move);
|
|
}
|
|
|
|
static void PF_cs_findradius (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent, *chain;
|
|
float rad;
|
|
float *org;
|
|
vec3_t eorg;
|
|
int i, j;
|
|
|
|
chain = (csqcedict_t *)*prinst->parms->sv_edicts;
|
|
|
|
org = G_VECTOR(OFS_PARM0);
|
|
rad = G_FLOAT(OFS_PARM1);
|
|
|
|
for (i=1 ; i<*prinst->parms->sv_num_edicts ; i++)
|
|
{
|
|
ent = (void*)EDICT_NUM(prinst, i);
|
|
if (ent->isfree)
|
|
continue;
|
|
// if (ent->v->solid == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.value)
|
|
// continue;
|
|
for (j=0 ; j<3 ; j++)
|
|
eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);
|
|
if (Length(eorg) > rad)
|
|
continue;
|
|
|
|
ent->v->chain = EDICT_TO_PROG(prinst, (void*)chain);
|
|
chain = ent;
|
|
}
|
|
|
|
RETURN_EDICT(prinst, (void*)chain);
|
|
}
|
|
|
|
static void PF_cl_te_gunshot (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
float scaler = 1;
|
|
if (*prinst->callargc >= 2) //fte is a quakeworld engine
|
|
scaler = G_FLOAT(OFS_PARM1);
|
|
if (P_RunParticleEffectType(pos, NULL, scaler, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20*scaler);
|
|
}
|
|
static void PF_cl_te_bloodqw (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
float scaler = 1;
|
|
if (*prinst->callargc >= 2) //fte is a quakeworld engine
|
|
scaler = G_FLOAT(OFS_PARM1);
|
|
if (P_RunParticleEffectType(pos, NULL, scaler, ptqw_blood))
|
|
if (P_RunParticleEffectType(pos, NULL, scaler, ptdp_blood))
|
|
P_RunParticleEffect (pos, vec3_origin, 73, 20*scaler);
|
|
}
|
|
static void PF_cl_te_blooddp (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
float *dir = G_VECTOR(OFS_PARM1);
|
|
float scaler = G_FLOAT(OFS_PARM2);
|
|
|
|
if (P_RunParticleEffectType(pos, dir, scaler, ptdp_blood))
|
|
if (P_RunParticleEffectType(pos, dir, scaler, ptqw_blood))
|
|
P_RunParticleEffect (pos, dir, 73, 20*scaler);
|
|
}
|
|
static void PF_cl_te_lightningblood (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptqw_lightningblood))
|
|
P_RunParticleEffect (pos, vec3_origin, 225, 50);
|
|
}
|
|
static void PF_cl_te_spike (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 10);
|
|
}
|
|
static void PF_cl_te_superspike (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))
|
|
if (P_RunParticleEffectType(pos, NULL, 2, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20);
|
|
}
|
|
static void PF_cl_te_explosion (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
|
|
// light
|
|
if (r_explosionlight.value) {
|
|
dlight_t *dl;
|
|
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 1;
|
|
dl->decay = 300;
|
|
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.155;
|
|
dl->color[2] = 0.05;
|
|
dl->channelfade[0] = 0.196;
|
|
dl->channelfade[1] = 0.23;
|
|
dl->channelfade[2] = 0.12;
|
|
}
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
}
|
|
static void PF_cl_te_tarexplosion (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
P_RunParticleEffectType(pos, NULL, 1, pt_tarexplosion);
|
|
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
}
|
|
static void PF_cl_te_wizspike (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_wizspike))
|
|
P_RunParticleEffect (pos, vec3_origin, 20, 30);
|
|
|
|
S_StartSound (-2, 0, cl_sfx_knighthit, pos, 1, 1);
|
|
}
|
|
static void PF_cl_te_knightspike (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_knightspike))
|
|
P_RunParticleEffect (pos, vec3_origin, 226, 20);
|
|
|
|
S_StartSound (-2, 0, cl_sfx_knighthit, pos, 1, 1);
|
|
}
|
|
static void PF_cl_te_lavasplash (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
P_RunParticleEffectType(pos, NULL, 1, pt_lavasplash);
|
|
}
|
|
static void PF_cl_te_teleport (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
P_RunParticleEffectType(pos, NULL, 1, pt_teleportsplash);
|
|
}
|
|
static void PF_cl_te_gunshotquad (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectTypeString(pos, vec3_origin, 1, "te_gunshotquad"))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20);
|
|
}
|
|
static void PF_cl_te_spikequad (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectTypeString(pos, vec3_origin, 1, "te_spikequad"))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 10);
|
|
}
|
|
static void PF_cl_te_superspikequad (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectTypeString(pos, vec3_origin, 1, "te_superspikequad"))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))
|
|
if (P_RunParticleEffectType(pos, NULL, 2, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20);
|
|
}
|
|
static void PF_cl_te_explosionquad (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *pos = G_VECTOR(OFS_PARM0);
|
|
if (P_RunParticleEffectTypeString(pos, vec3_origin, 1, "te_explosionquad"))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
|
|
// light
|
|
if (r_explosionlight.value) {
|
|
dlight_t *dl;
|
|
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 1;
|
|
dl->decay = 300;
|
|
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.155;
|
|
dl->color[2] = 0.05;
|
|
dl->channelfade[0] = 0.196;
|
|
dl->channelfade[1] = 0.23;
|
|
dl->channelfade[2] = 0.12;
|
|
}
|
|
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
}
|
|
|
|
//void(vector org, float radius, float lifetime, vector color) te_customflash
|
|
static void PF_cl_te_customflash (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org = G_VECTOR(OFS_PARM0);
|
|
float radius = G_FLOAT(OFS_PARM1);
|
|
float lifetime = G_FLOAT(OFS_PARM2);
|
|
float *colour = G_VECTOR(OFS_PARM3);
|
|
|
|
dlight_t *dl;
|
|
// light
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (org, dl->origin);
|
|
dl->radius = radius;
|
|
dl->die = cl.time + lifetime;
|
|
dl->decay = dl->radius / lifetime;
|
|
dl->color[0] = colour[0]*0.5f;
|
|
dl->color[1] = colour[1]*0.5f;
|
|
dl->color[2] = colour[2]*0.5f;
|
|
}
|
|
|
|
static void PF_cl_te_bloodshower (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
}
|
|
static void PF_cl_te_particlecube (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *minb = G_VECTOR(OFS_PARM0);
|
|
float *maxb = G_VECTOR(OFS_PARM1);
|
|
float *vel = G_VECTOR(OFS_PARM2);
|
|
float howmany = G_FLOAT(OFS_PARM3);
|
|
float color = G_FLOAT(OFS_PARM4);
|
|
float gravity = G_FLOAT(OFS_PARM5);
|
|
float jitter = G_FLOAT(OFS_PARM6);
|
|
|
|
P_RunParticleCube(minb, maxb, vel, howmany, color, gravity, jitter);
|
|
}
|
|
static void PF_cl_te_spark (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
}
|
|
static void PF_cl_te_smallflash (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
}
|
|
static void PF_cl_te_explosion2 (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
}
|
|
static void PF_cl_te_lightning1 (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
float *start = G_VECTOR(OFS_PARM1);
|
|
float *end = G_VECTOR(OFS_PARM1);
|
|
|
|
CL_AddBeam(0, ent->entnum+MAX_EDICTS, start, end);
|
|
}
|
|
static void PF_cl_te_lightning2 (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
float *start = G_VECTOR(OFS_PARM1);
|
|
float *end = G_VECTOR(OFS_PARM1);
|
|
|
|
CL_AddBeam(1, ent->entnum+MAX_EDICTS, start, end);
|
|
}
|
|
static void PF_cl_te_lightning3 (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
float *start = G_VECTOR(OFS_PARM1);
|
|
float *end = G_VECTOR(OFS_PARM1);
|
|
|
|
CL_AddBeam(2, ent->entnum+MAX_EDICTS, start, end);
|
|
}
|
|
static void PF_cl_te_beam (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
float *start = G_VECTOR(OFS_PARM1);
|
|
float *end = G_VECTOR(OFS_PARM1);
|
|
|
|
CL_AddBeam(5, ent->entnum+MAX_EDICTS, start, end);
|
|
}
|
|
static void PF_cl_te_plasmaburn (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
}
|
|
static void PF_cl_te_explosionrgb (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *org = G_VECTOR(OFS_PARM0);
|
|
float *colour = G_VECTOR(OFS_PARM1);
|
|
|
|
dlight_t *dl;
|
|
|
|
if (P_RunParticleEffectType(org, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(org, NULL, 107, 1024); // should be 97-111
|
|
|
|
R_AddStain(org, -1, -1, -1, 100);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (org, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 0.5;
|
|
dl->decay = 300;
|
|
|
|
dl->color[0] = 0.4f*colour[0];
|
|
dl->color[1] = 0.4f*colour[1];
|
|
dl->color[2] = 0.4f*colour[2];
|
|
dl->channelfade[0] = 0;
|
|
dl->channelfade[1] = 0;
|
|
dl->channelfade[2] = 0;
|
|
}
|
|
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, org, 1, 1);
|
|
}
|
|
static void PF_cl_te_particlerain (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *min = G_VECTOR(OFS_PARM0);
|
|
float *max = G_VECTOR(OFS_PARM1);
|
|
float *vel = G_VECTOR(OFS_PARM2);
|
|
float howmany = G_FLOAT(OFS_PARM3);
|
|
float colour = G_FLOAT(OFS_PARM4);
|
|
|
|
P_RunParticleWeather(min, max, vel, howmany, colour, "rain");
|
|
}
|
|
static void PF_cl_te_particlesnow (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *min = G_VECTOR(OFS_PARM0);
|
|
float *max = G_VECTOR(OFS_PARM1);
|
|
float *vel = G_VECTOR(OFS_PARM2);
|
|
float howmany = G_FLOAT(OFS_PARM3);
|
|
float colour = G_FLOAT(OFS_PARM4);
|
|
|
|
P_RunParticleWeather(min, max, vel, howmany, colour, "snow");
|
|
}
|
|
|
|
void CSQC_RunThreads(void)
|
|
{
|
|
csqctreadstate_t *state = csqcthreads, *next;
|
|
float ctime = Sys_DoubleTime();
|
|
csqcthreads = NULL;
|
|
while(state)
|
|
{
|
|
next = state->next;
|
|
|
|
if (state->resumetime > ctime)
|
|
{ //not time yet, reform original list.
|
|
state->next = csqcthreads;
|
|
csqcthreads = state;
|
|
}
|
|
else
|
|
{ //call it and forget it ever happened. The Sleep biltin will recreate if needed.
|
|
|
|
|
|
*csqcg.self = EDICT_TO_PROG(csqcprogs, EDICT_NUM(csqcprogs, state->self));
|
|
*csqcg.other = EDICT_TO_PROG(csqcprogs, EDICT_NUM(csqcprogs, state->other));
|
|
|
|
csqcprogs->RunThread(csqcprogs, state->thread);
|
|
csqcprogs->parms->memfree(state->thread);
|
|
csqcprogs->parms->memfree(state);
|
|
}
|
|
|
|
state = next;
|
|
}
|
|
}
|
|
|
|
static void PF_cs_addprogs (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *s = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
if (!s || !*s)
|
|
G_FLOAT(OFS_RETURN) = -1;
|
|
else
|
|
G_FLOAT(OFS_RETURN) = PR_LoadProgs(prinst, s, 0, NULL, 0);
|
|
}
|
|
|
|
static void PF_cs_OpenPortal (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
#ifdef Q2BSPS
|
|
if (sv.worldmodel->fromgame == fg_quake2)
|
|
CMQ2_SetAreaPortalState(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
|
|
#endif
|
|
}
|
|
|
|
// #487 float(string name) gecko_create( string name )
|
|
static void PF_cs_gecko_create (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
cin_t *cin;
|
|
#ifdef Q3SHADERS
|
|
cin = R_ShaderGetCinematic(shader);
|
|
#else
|
|
cin = NULL;
|
|
#endif
|
|
|
|
if (!cin)
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
else
|
|
G_FLOAT(OFS_RETURN) = 1;
|
|
}
|
|
// #488 void(string name) gecko_destroy( string name )
|
|
static void PF_cs_gecko_destroy (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
}
|
|
// #489 void(string name) gecko_navigate( string name, string URI )
|
|
static void PF_cs_gecko_navigate (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
char *command = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
cin_t *cin;
|
|
#ifdef Q3SHADERS
|
|
cin = R_ShaderGetCinematic(shader);
|
|
#else
|
|
cin = NULL;
|
|
#endif
|
|
|
|
if (!cin)
|
|
return;
|
|
|
|
Media_Send_Command(cin, command);
|
|
}
|
|
// #490 float(string name) gecko_keyevent( string name, float key, float eventtype )
|
|
static void PF_cs_gecko_keyevent (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
int key = G_FLOAT(OFS_PARM1);
|
|
int eventtype = G_FLOAT(OFS_PARM2);
|
|
cin_t *cin;
|
|
#ifdef Q3SHADERS
|
|
cin = R_ShaderGetCinematic(shader);
|
|
#else
|
|
cin = NULL;
|
|
#endif
|
|
|
|
if (!cin)
|
|
return;
|
|
Media_Send_KeyEvent(cin, MP_TranslateDPtoFTECodes(key), eventtype);
|
|
}
|
|
// #491 void gecko_mousemove( string name, float x, float y )
|
|
static void PF_cs_gecko_mousemove (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
float posx = G_FLOAT(OFS_PARM1);
|
|
float posy = G_FLOAT(OFS_PARM2);
|
|
cin_t *cin;
|
|
#ifdef Q3SHADERS
|
|
cin = R_ShaderGetCinematic(shader);
|
|
#else
|
|
cin = NULL;
|
|
#endif
|
|
|
|
if (!cin)
|
|
return;
|
|
Media_Send_MouseMove(cin, posx, posy);
|
|
}
|
|
// #492 void gecko_resize( string name, float w, float h )
|
|
static void PF_cs_gecko_resize (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
float sizex = G_FLOAT(OFS_PARM1);
|
|
float sizey = G_FLOAT(OFS_PARM2);
|
|
cin_t *cin;
|
|
#ifdef Q3SHADERS
|
|
cin = R_ShaderGetCinematic(shader);
|
|
#else
|
|
cin = NULL;
|
|
#endif
|
|
if (!cin)
|
|
return;
|
|
Media_Send_Resize(cin, sizex, sizey);
|
|
}
|
|
// #493 vector gecko_get_texture_extent( string name )
|
|
static void PF_cs_gecko_get_texture_extent (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
|
|
float *ret = G_VECTOR(OFS_RETURN);
|
|
int sx, sy;
|
|
cin_t *cin;
|
|
#ifdef Q3SHADERS
|
|
cin = R_ShaderGetCinematic(shader);
|
|
#else
|
|
cin = NULL;
|
|
#endif
|
|
|
|
if (cin)
|
|
{
|
|
Media_Send_GetSize(cin, &sx, &sy);
|
|
}
|
|
else
|
|
{
|
|
sx = 0;
|
|
sy = 0;
|
|
}
|
|
ret[0] = sx;
|
|
ret[1] = sy;
|
|
ret[2] = 0;
|
|
}
|
|
|
|
static void PF_cs_droptofloor (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent;
|
|
vec3_t end;
|
|
vec3_t start;
|
|
trace_t trace;
|
|
|
|
ent = (csqcedict_t*)PROG_TO_EDICT(prinst, *csqcg.self);
|
|
|
|
VectorCopy (ent->v->origin, end);
|
|
end[2] -= 512;
|
|
|
|
VectorCopy (ent->v->origin, start);
|
|
trace = CS_Move (start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
|
|
|
|
if (trace.fraction == 1 || trace.allsolid)
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
else
|
|
{
|
|
VectorCopy (trace.endpos, ent->v->origin);
|
|
CS_LinkEdict (ent, false);
|
|
ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
|
|
ent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent);
|
|
G_FLOAT(OFS_RETURN) = 1;
|
|
}
|
|
}
|
|
|
|
static void PF_cs_copyentity (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *in, *out;
|
|
|
|
in = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
out = (csqcedict_t*)G_EDICT(prinst, OFS_PARM1);
|
|
|
|
memcpy(out->v, in->v, csqcentsize);
|
|
|
|
CS_LinkEdict (out, false);
|
|
}
|
|
|
|
static void PF_cl_playingdemo (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
G_FLOAT(OFS_RETURN) = !!cls.demoplayback;
|
|
}
|
|
|
|
static void PF_cl_runningserver (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
#ifdef CLIENTONLY
|
|
G_FLOAT(OFS_RETURN) = false;
|
|
#else
|
|
G_FLOAT(OFS_RETURN) = !!sv.active;
|
|
#endif
|
|
}
|
|
|
|
static void PF_cl_getlight (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
vec3_t ambient, diffuse, dir;
|
|
cl.worldmodel->funcs.LightPointValues(cl.worldmodel, G_VECTOR(OFS_PARM0), ambient, diffuse, dir);
|
|
VectorMA(ambient, 0.5, diffuse, G_VECTOR(OFS_RETURN));
|
|
}
|
|
|
|
/*
|
|
static void PF_Stub (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
Con_Printf("Obsolete csqc builtin (%i) executed\n", prinst->lastcalledbuiltinnumber);
|
|
}
|
|
*/
|
|
|
|
static void PF_rotatevectorsbytag (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
int tagnum = G_FLOAT(OFS_PARM1);
|
|
|
|
float *srcorg = ent->v->origin;
|
|
int modelindex = ent->v->modelindex;
|
|
int frame1 = ent->v->frame;
|
|
int frame2 = ent->v->frame2;
|
|
float lerp = ent->v->lerpfrac;
|
|
float frame1time = ent->v->frame1time;
|
|
float frame2time = ent->v->frame2time;
|
|
|
|
float *retorg = G_VECTOR(OFS_RETURN);
|
|
|
|
model_t *mod = CSQC_GetModelForIndex(modelindex);
|
|
float transforms[12];
|
|
float src[12];
|
|
float dest[12];
|
|
int i;
|
|
|
|
if (lerp < 0) lerp = 0;
|
|
if (lerp > 1) lerp = 1;
|
|
|
|
if (Mod_GetTag(mod, tagnum, frame1, frame2, lerp, frame1time, frame2time, transforms))
|
|
{
|
|
VectorCopy(csqcg.forward, src+0);
|
|
src[3] = 0;
|
|
VectorNegate(csqcg.right, src+4);
|
|
src[7] = 0;
|
|
VectorCopy(csqcg.up, src+8);
|
|
src[11] = 0;
|
|
|
|
if (ent->v->scale)
|
|
{
|
|
for (i = 0; i < 12; i+=4)
|
|
{
|
|
transforms[i+0] *= ent->v->scale;
|
|
transforms[i+1] *= ent->v->scale;
|
|
transforms[i+2] *= ent->v->scale;
|
|
transforms[i+3] *= ent->v->scale;
|
|
}
|
|
}
|
|
|
|
R_ConcatRotationsPad((void*)transforms, (void*)src, (void*)dest);
|
|
|
|
VectorCopy(dest+0, csqcg.forward);
|
|
VectorNegate(dest+4, csqcg.right);
|
|
VectorCopy(dest+8, csqcg.up);
|
|
|
|
VectorCopy(srcorg, retorg);
|
|
for (i = 0 ; i < 3 ; i++)
|
|
{
|
|
retorg[0] += transforms[i*4+3]*src[4*i+0];
|
|
retorg[1] += transforms[i*4+3]*src[4*i+1];
|
|
retorg[2] += transforms[i*4+3]*src[4*i+2];
|
|
}
|
|
return;
|
|
}
|
|
|
|
VectorCopy(srcorg, retorg);
|
|
}
|
|
|
|
static void EdictToTransform(csqcedict_t *ed, float *trans)
|
|
{
|
|
AngleVectors(ed->v->angles, trans+0, trans+4, trans+8);
|
|
VectorInverse(trans+4);
|
|
|
|
trans[3] = ed->v->origin[0];
|
|
trans[7] = ed->v->origin[1];
|
|
trans[11] = ed->v->origin[2];
|
|
}
|
|
|
|
static void PF_cs_gettaginfo (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
int tagnum = G_FLOAT(OFS_PARM1);
|
|
|
|
float *origin = G_VECTOR(OFS_RETURN);
|
|
|
|
int modelindex = ent->v->modelindex;
|
|
int frame1 = ent->v->frame;
|
|
int frame2 = ent->v->frame2;
|
|
float lerp = ent->v->lerpfrac;
|
|
float frame1time = ent->v->frame1time;
|
|
float frame2time = ent->v->frame2time;
|
|
|
|
model_t *mod = CSQC_GetModelForIndex(modelindex);
|
|
|
|
float transent[12];
|
|
float transforms[12];
|
|
float result[12];
|
|
|
|
#pragma message("PF_cs_gettaginfo: This function doesn't honour attachments (but setattachment isn't implemented yet anyway)")
|
|
if (!Mod_GetTag(mod, tagnum, frame1, frame2, lerp, frame1time, frame2time, transforms))
|
|
{
|
|
memset(transforms, 0, sizeof(transforms));
|
|
}
|
|
|
|
EdictToTransform(ent, transent);
|
|
R_ConcatTransforms((void*)transent, (void*)transforms, (void*)result);
|
|
|
|
origin[0] = result[3];
|
|
origin[1] = result[7];
|
|
origin[2] = result[11];
|
|
VectorCopy((result+0), csqcg.forward);
|
|
VectorCopy((result+4), csqcg.right);
|
|
VectorCopy((result+8), csqcg.up);
|
|
|
|
}
|
|
static void PF_cs_gettagindex (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
char *tagname = PR_GetStringOfs(prinst, OFS_PARM1);
|
|
|
|
model_t *mod = CSQC_GetModelForIndex(ent->v->modelindex);
|
|
G_FLOAT(OFS_RETURN) = Mod_TagNumForName(mod, tagname);
|
|
}
|
|
static void PF_rotatevectorsbyangles (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *ang = G_VECTOR(OFS_PARM0);
|
|
vec3_t src[3], trans[3], res[3];
|
|
ang[0]*=-1;
|
|
AngleVectors(ang, trans[0], trans[1], trans[2]);
|
|
ang[0]*=-1;
|
|
VectorInverse(trans[1]);
|
|
|
|
VectorCopy(csqcg.forward, src[0]);
|
|
VectorNegate(csqcg.right, src[1]);
|
|
VectorCopy(csqcg.up, src[2]);
|
|
|
|
R_ConcatRotations(trans, src, res);
|
|
|
|
VectorCopy(res[0], csqcg.forward);
|
|
VectorNegate(res[1], csqcg.right);
|
|
VectorCopy(res[2], csqcg.up);
|
|
}
|
|
static void PF_rotatevectorsbymatrix (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
vec3_t src[3], trans[3], res[3];
|
|
|
|
VectorCopy(G_VECTOR(OFS_PARM0), src[0]);
|
|
VectorNegate(G_VECTOR(OFS_PARM1), src[1]);
|
|
VectorCopy(G_VECTOR(OFS_PARM2), src[2]);
|
|
|
|
VectorCopy(csqcg.forward, src[0]);
|
|
VectorNegate(csqcg.right, src[1]);
|
|
VectorCopy(csqcg.up, src[2]);
|
|
|
|
R_ConcatRotations(trans, src, res);
|
|
|
|
VectorCopy(res[0], csqcg.forward);
|
|
VectorNegate(res[1], csqcg.right);
|
|
VectorCopy(res[2], csqcg.up);
|
|
}
|
|
static void PF_skinforname (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int modelindex = G_FLOAT(OFS_PARM0);
|
|
char *str = PF_VarString(prinst, 1, pr_globals);
|
|
model_t *mod = CSQC_GetModelForIndex(modelindex);
|
|
|
|
|
|
if (Mod_SkinForName)
|
|
G_FLOAT(OFS_RETURN) = Mod_SkinForName(mod, str);
|
|
else
|
|
G_FLOAT(OFS_RETURN) = -1;
|
|
}
|
|
static void PF_shaderforname (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *str = PF_VarString(prinst, 0, pr_globals);
|
|
#ifdef Q3SHADERS
|
|
shader_t *shad;
|
|
shad = R_RegisterSkin(str);
|
|
if (shad)
|
|
G_FLOAT(OFS_RETURN) = shad-r_shaders + 1;
|
|
else
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
#else
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
#endif
|
|
}
|
|
|
|
static qboolean CS_CheckBottom (csqcedict_t *ent)
|
|
{
|
|
int savedhull;
|
|
vec3_t mins, maxs, start, stop;
|
|
trace_t trace;
|
|
int x, y;
|
|
float mid, bottom;
|
|
|
|
if (!cl.worldmodel)
|
|
return false;
|
|
|
|
VectorAdd (ent->v->origin, ent->v->mins, mins);
|
|
VectorAdd (ent->v->origin, ent->v->maxs, maxs);
|
|
|
|
// if all of the points under the corners are solid world, don't bother
|
|
// with the tougher checks
|
|
// the corners must be within 16 of the midpoint
|
|
start[2] = mins[2] - 1;
|
|
for (x=0 ; x<=1 ; x++)
|
|
for (y=0 ; y<=1 ; y++)
|
|
{
|
|
start[0] = x ? maxs[0] : mins[0];
|
|
start[1] = y ? maxs[1] : mins[1];
|
|
if (!(CS_PointContents (start) & FTECONTENTS_SOLID))
|
|
goto realcheck;
|
|
}
|
|
|
|
// c_yes++;
|
|
return true; // we got out easy
|
|
|
|
realcheck:
|
|
// c_no++;
|
|
//
|
|
// check it for real...
|
|
//
|
|
start[2] = mins[2];
|
|
|
|
// the midpoint must be within 16 of the bottom
|
|
start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
|
|
start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
|
|
stop[2] = start[2] - 2*movevars.stepheight;
|
|
trace = CS_Move (start, vec3_origin, vec3_origin, stop, true, ent);
|
|
|
|
if (trace.fraction == 1.0)
|
|
return false;
|
|
mid = bottom = trace.endpos[2];
|
|
|
|
// the corners must be within 16 of the midpoint
|
|
for (x=0 ; x<=1 ; x++)
|
|
for (y=0 ; y<=1 ; y++)
|
|
{
|
|
start[0] = stop[0] = x ? maxs[0] : mins[0];
|
|
start[1] = stop[1] = y ? maxs[1] : mins[1];
|
|
|
|
savedhull = ent->v->hull;
|
|
ent->v->hull = 0;
|
|
trace = CS_Move (start, vec3_origin, vec3_origin, stop, true, ent);
|
|
ent->v->hull = savedhull;
|
|
|
|
if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
|
|
bottom = trace.endpos[2];
|
|
if (trace.fraction == 1.0 || mid - trace.endpos[2] > movevars.stepheight)
|
|
return false;
|
|
}
|
|
|
|
// c_yes++;
|
|
return true;
|
|
}
|
|
static void PF_cs_checkbottom (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent;
|
|
|
|
ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
|
|
|
|
G_FLOAT(OFS_RETURN) = CS_CheckBottom (ent);
|
|
}
|
|
|
|
static void PF_cs_break (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
Con_Printf ("break statement\n");
|
|
#ifdef TEXTEDITOR
|
|
(*prinst->pr_trace)++;
|
|
#endif
|
|
}
|
|
|
|
static qboolean CS_movestep (csqcedict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean set_trace)
|
|
{
|
|
float dz;
|
|
vec3_t oldorg, neworg, end;
|
|
trace_t trace;
|
|
int i;
|
|
csqcedict_t *enemy = csqc_edicts;
|
|
|
|
// try the move
|
|
VectorCopy (ent->v->origin, oldorg);
|
|
VectorAdd (ent->v->origin, move, neworg);
|
|
|
|
// flying monsters don't step up
|
|
if ( (int)ent->v->flags & (FL_SWIM | FL_FLY) )
|
|
{
|
|
// try one move with vertical motion, then one without
|
|
for (i=0 ; i<2 ; i++)
|
|
{
|
|
VectorAdd (ent->v->origin, move, neworg);
|
|
if (!noenemy)
|
|
{
|
|
enemy = (csqcedict_t*)PROG_TO_EDICT(csqcprogs, ent->v->enemy);
|
|
if (i == 0 && enemy != csqc_edicts)
|
|
{
|
|
dz = ent->v->origin[2] - ((csqcedict_t*)PROG_TO_EDICT(csqcprogs, ent->v->enemy))->v->origin[2];
|
|
if (dz > 40)
|
|
neworg[2] -= 8;
|
|
if (dz < 30)
|
|
neworg[2] += 8;
|
|
}
|
|
}
|
|
trace = CS_Move (ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);
|
|
if (set_trace)
|
|
cs_settracevars(&trace);
|
|
|
|
if (trace.fraction == 1)
|
|
{
|
|
if ( ((int)ent->v->flags & FL_SWIM) && !(CS_PointContents(trace.endpos) & FTECONTENTS_FLUID))
|
|
return false; // swim monster left water
|
|
|
|
VectorCopy (trace.endpos, ent->v->origin);
|
|
if (relink)
|
|
CS_LinkEdict (ent, true);
|
|
return true;
|
|
}
|
|
|
|
if (noenemy || enemy == csqc_edicts)
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// push down from a step height above the wished position
|
|
neworg[2] += movevars.stepheight;
|
|
VectorCopy (neworg, end);
|
|
end[2] -= movevars.stepheight*2;
|
|
|
|
trace = CS_Move (neworg, ent->v->mins, ent->v->maxs, end, false, ent);
|
|
if (set_trace)
|
|
cs_settracevars(&trace);
|
|
|
|
if (trace.allsolid)
|
|
return false;
|
|
|
|
if (trace.startsolid)
|
|
{
|
|
neworg[2] -= movevars.stepheight;
|
|
trace = CS_Move (neworg, ent->v->mins, ent->v->maxs, end, false, ent);
|
|
if (set_trace)
|
|
cs_settracevars(&trace);
|
|
if (trace.allsolid || trace.startsolid)
|
|
return false;
|
|
}
|
|
if (trace.fraction == 1)
|
|
{
|
|
// if monster had the ground pulled out, go ahead and fall
|
|
if ( (int)ent->v->flags & FL_PARTIALGROUND )
|
|
{
|
|
VectorAdd (ent->v->origin, move, ent->v->origin);
|
|
if (relink)
|
|
CS_LinkEdict (ent, true);
|
|
ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
|
|
// Con_Printf ("fall down\n");
|
|
return true;
|
|
}
|
|
|
|
return false; // walked off an edge
|
|
}
|
|
|
|
// check point traces down for dangling corners
|
|
VectorCopy (trace.endpos, ent->v->origin);
|
|
|
|
if (!CS_CheckBottom (ent))
|
|
{
|
|
if ( (int)ent->v->flags & FL_PARTIALGROUND )
|
|
{ // entity had floor mostly pulled out from underneath it
|
|
// and is trying to correct
|
|
if (relink)
|
|
CS_LinkEdict (ent, true);
|
|
return true;
|
|
}
|
|
VectorCopy (oldorg, ent->v->origin);
|
|
return false;
|
|
}
|
|
|
|
if ( (int)ent->v->flags & FL_PARTIALGROUND )
|
|
{
|
|
// Con_Printf ("back on ground\n");
|
|
ent->v->flags = (int)ent->v->flags & ~FL_PARTIALGROUND;
|
|
}
|
|
ent->v->groundentity = EDICT_TO_PROG(csqcprogs, trace.ent);
|
|
|
|
// the move is ok
|
|
if (relink)
|
|
CS_LinkEdict (ent, true);
|
|
return true;
|
|
}
|
|
|
|
static void PF_cs_walkmove (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
csqcedict_t *ent;
|
|
float yaw, dist;
|
|
vec3_t move;
|
|
// dfunction_t *oldf;
|
|
int oldself;
|
|
qboolean settrace;
|
|
|
|
ent = (csqcedict_t*)PROG_TO_EDICT(prinst, *csqcg.self);
|
|
yaw = G_FLOAT(OFS_PARM0);
|
|
dist = G_FLOAT(OFS_PARM1);
|
|
if (*prinst->callargc >= 3 && G_FLOAT(OFS_PARM2))
|
|
settrace = true;
|
|
else
|
|
settrace = false;
|
|
|
|
if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
|
|
{
|
|
G_FLOAT(OFS_RETURN) = 0;
|
|
return;
|
|
}
|
|
|
|
yaw = yaw*M_PI*2 / 360;
|
|
|
|
move[0] = cos(yaw)*dist;
|
|
move[1] = sin(yaw)*dist;
|
|
move[2] = 0;
|
|
|
|
// save program state, because CS_movestep may call other progs
|
|
oldself = *csqcg.self;
|
|
|
|
G_FLOAT(OFS_RETURN) = CS_movestep(ent, move, true, false, settrace);
|
|
|
|
// restore program state
|
|
*csqcg.self = oldself;
|
|
}
|
|
|
|
static void CS_ConsoleCommand_f(void)
|
|
{ //FIXME: unregister them.
|
|
char cmd[2048];
|
|
Q_snprintfz(cmd, sizeof(cmd), "%s %s", Cmd_Argv(0), Cmd_Args());
|
|
CSQC_ConsoleCommand(cmd);
|
|
}
|
|
static void PF_cs_registercommand (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
char *str = PF_VarString(prinst, 0, pr_globals);
|
|
if (!strcmp(str, "+showscores") || !strcmp(str, "-showscores") ||
|
|
!strcmp(str, "+showteamscores") || !strcmp(str, "-showteamscores"))
|
|
return;
|
|
Cmd_AddRemCommand(str, CS_ConsoleCommand_f);
|
|
}
|
|
|
|
static qboolean csqc_usinglistener;
|
|
qboolean CSQC_SettingListener(void)
|
|
{ //stops the engine from setting the listener positions.
|
|
if (csqc_usinglistener)
|
|
{
|
|
csqc_usinglistener = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
static void PF_cs_setlistener (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
float *origin = G_VECTOR(OFS_PARM0);
|
|
float *forward = G_VECTOR(OFS_PARM1);
|
|
float *right = G_VECTOR(OFS_PARM2);
|
|
float *up = G_VECTOR(OFS_PARM3);
|
|
csqc_usinglistener = true;
|
|
S_Update(origin, forward, right, up);
|
|
}
|
|
|
|
typedef struct oldcsqcpack_s
|
|
{
|
|
unsigned int numents;
|
|
unsigned int maxents;
|
|
unsigned short *entnum;
|
|
csqcedict_t **entptr;
|
|
} oldcsqcpack_t;
|
|
static oldcsqcpack_t loadedcsqcpack[2];
|
|
static int loadedcsqcpacknum;
|
|
static csqcedict_t *deltaedplayerents[MAX_CLIENTS];
|
|
|
|
#define RSES_NOLERP 1
|
|
#define RSES_NOROTATE 2
|
|
#define RSES_NOTRAILS 4
|
|
#define RSES_NOLIGHTS 8
|
|
|
|
packet_entities_t *CL_ProcessPacketEntities(float *servertime, qboolean nolerp);
|
|
void PF_ReadServerEntityState(progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
//read the arguments the csqc gave us
|
|
unsigned int flags = G_FLOAT(OFS_PARM0);
|
|
float servertime = G_FLOAT(OFS_PARM1);
|
|
|
|
//locals
|
|
packet_entities_t *pack;
|
|
csqcedict_t *ent;
|
|
entity_state_t *src;
|
|
unsigned int i;
|
|
lerpents_t *le;
|
|
csqcedict_t *oldent;
|
|
oldcsqcpack_t *oldlist, *newlist;
|
|
int oldidx = 0, newidx = 0;
|
|
model_t *model;
|
|
player_state_t *srcp;
|
|
|
|
//setup
|
|
servertime += cl.servertime;
|
|
pack = CL_ProcessPacketEntities(&servertime, (flags & RSES_NOLERP));
|
|
if (!pack)
|
|
return; //we're lagging. can't do anything, just don't update
|
|
|
|
for (i = 0; i < MAX_CLIENTS; i++)
|
|
{
|
|
srcp = &cl.frames[cl.validsequence&UPDATE_MASK].playerstate[i];
|
|
ent = deltaedplayerents[i];
|
|
if (srcp->messagenum == cl.validsequence && (i+1 >= maxcsqcentities || !csqcent[i+1]))
|
|
{
|
|
if (!ent)
|
|
{
|
|
ent = (csqcedict_t *)ED_Alloc(prinst);
|
|
deltaedplayerents[i] = ent;
|
|
G_FLOAT(OFS_PARM0) = true;
|
|
}
|
|
else
|
|
{
|
|
G_FLOAT(OFS_PARM0) = false;
|
|
}
|
|
|
|
ent->v->entnum = i+1;
|
|
|
|
if (cl.spectator && !Cam_DrawPlayer(0, i))
|
|
{
|
|
ent->v->modelindex = 0;
|
|
}
|
|
else
|
|
ent->v->modelindex = srcp->modelindex;
|
|
ent->v->skin = srcp->skinnum;
|
|
|
|
ent->v->frame1time = cl.time - cl.lerpplayers[i].framechange;
|
|
ent->v->frame2time = cl.time - cl.lerpplayers[i].oldframechange;
|
|
|
|
if (ent->v->frame != cl.lerpplayers[i].frame)
|
|
{
|
|
ent->v->frame2 = ent->v->frame;
|
|
ent->v->frame = cl.lerpplayers[i].frame;
|
|
}
|
|
|
|
ent->v->lerpfrac = 1-(cl.time - cl.lerpplayers[i].framechange)*10;
|
|
if (ent->v->lerpfrac > 1)
|
|
ent->v->lerpfrac = 1;
|
|
else if (ent->v->lerpfrac < 0)
|
|
{
|
|
ent->v->lerpfrac = 0;
|
|
}
|
|
VectorCopy(srcp->origin, ent->v->origin);
|
|
VectorCopy(srcp->velocity, ent->v->velocity);
|
|
VectorCopy(srcp->viewangles, ent->v->angles);
|
|
ent->v->angles[0] *= -0.333;
|
|
ent->v->colormap = i+1;
|
|
ent->v->scale = srcp->scale/16.0f;
|
|
//ent->v->fatness = srcp->fatness;
|
|
ent->v->alpha = srcp->alpha/255.0f;
|
|
|
|
// ent->v->colormod[0] = (srcp->colormod[0]/255.0f)*8;
|
|
// ent->v->colormod[1] = (srcp->colormod[1]/255.0f)*8;
|
|
// ent->v->colormod[2] = (srcp->colormod[2]/255.0f)*8;
|
|
// ent->v->effects = srcp->effects;
|
|
|
|
if (csqcg.delta_update)
|
|
{
|
|
*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);
|
|
PR_ExecuteProgram(prinst, csqcg.delta_update);
|
|
}
|
|
}
|
|
else if (ent)
|
|
{
|
|
*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);
|
|
PR_ExecuteProgram(prinst, csqcg.delta_remove);
|
|
deltaedplayerents[i] = NULL;
|
|
}
|
|
}
|
|
|
|
oldlist = &loadedcsqcpack[loadedcsqcpacknum];
|
|
loadedcsqcpacknum ^= 1;
|
|
newlist = &loadedcsqcpack[loadedcsqcpacknum];
|
|
newlist->numents = 0;
|
|
|
|
for (i = 0; i < pack->num_entities; i++)
|
|
{
|
|
src = &pack->entities[i];
|
|
// CL_LinkPacketEntities
|
|
|
|
#ifndef _MSC_VER
|
|
#warning what to do here?
|
|
#endif
|
|
// if (csqcent[src->number])
|
|
// continue; //don't add the entity if we have one sent specially via csqc protocols.
|
|
|
|
if (oldidx == oldlist->numents)
|
|
{ //reached the end of the old frame's ents
|
|
oldent = NULL;
|
|
}
|
|
else
|
|
{
|
|
while (oldidx < oldlist->numents && oldlist->entnum[oldidx] < src->number)
|
|
{
|
|
//this entity is stale, remove it.
|
|
oldent = oldlist->entptr[oldidx];
|
|
*csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent);
|
|
PR_ExecuteProgram(prinst, csqcg.delta_remove);
|
|
oldidx++;
|
|
}
|
|
|
|
if (src->number < oldlist->entnum[oldidx])
|
|
oldent = NULL;
|
|
else
|
|
{
|
|
oldent = oldlist->entptr[oldidx];
|
|
oldidx++;
|
|
}
|
|
}
|
|
|
|
if (src->number < maxcsqcentities && csqcent[src->number])
|
|
{
|
|
//in the csqc list
|
|
if (oldent)
|
|
{
|
|
*csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent);
|
|
PR_ExecuteProgram(prinst, csqcg.delta_remove);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//note: we don't delta the state here. we just replace the old.
|
|
//its already lerped
|
|
|
|
if (oldent)
|
|
ent = oldent;
|
|
else
|
|
ent = (csqcedict_t *)ED_Alloc(prinst);
|
|
|
|
le = &cl.lerpents[src->number];
|
|
|
|
//frames needs special handling
|
|
ent->v->frame = src->frame;
|
|
ent->v->frame2 = le->frame;
|
|
if (le->framechange == le->oldframechange)
|
|
ent->v->lerpfrac = 0;
|
|
else
|
|
{
|
|
ent->v->lerpfrac = 1-(servertime - le->framechange) / (le->framechange - le->oldframechange);
|
|
if (ent->v->lerpfrac > 1)
|
|
ent->v->lerpfrac = 1;
|
|
else if (ent->v->lerpfrac < 0)
|
|
{
|
|
ent->v->lerpfrac = 0;
|
|
}
|
|
}
|
|
|
|
model = cl.model_precache[src->modelindex];
|
|
if (!(flags & RSES_NOTRAILS))
|
|
{
|
|
if (oldent && model->particletrail >= 0)
|
|
{
|
|
if (pe->ParticleTrail (ent->v->origin, src->origin, model->particletrail, &(le->trailstate)))
|
|
pe->ParticleTrailIndex(ent->v->origin, src->origin, model->traildefaultindex, 0, &(le->trailstate));
|
|
}
|
|
}
|
|
|
|
ent->v->entnum = src->number;
|
|
ent->v->modelindex = src->modelindex;
|
|
// ent->v->bitmask = src->bitmask;
|
|
ent->v->flags = src->flags;
|
|
// ent->v->effects = src->effects;
|
|
ent->v->origin[0] = src->origin[0];
|
|
ent->v->origin[1] = src->origin[1];
|
|
ent->v->origin[2] = src->origin[2];
|
|
ent->v->angles[0] = src->angles[0];
|
|
ent->v->angles[1] = src->angles[1];
|
|
ent->v->angles[2] = src->angles[2];
|
|
|
|
//we ignore the q2 state fields
|
|
|
|
ent->v->colormap = src->colormap;
|
|
ent->v->skin = src->skinnum;
|
|
// ent->v->glowsize = src->glowsize;
|
|
// ent->v->glowcolor = src->glowcolour;
|
|
ent->v->scale = src->scale/16.0f;
|
|
ent->v->fatness = src->fatness/16.0f;
|
|
// ent->v->hexen2flags = src->hexen2flags;
|
|
// ent->v->abslight = src->abslight;
|
|
// ent->v->dpflags = src->dpflags;
|
|
// ent->v->colormod[0] = (src->colormod[0]/255.0f)*8;
|
|
// ent->v->colormod[1] = (src->colormod[1]/255.0f)*8;
|
|
// ent->v->colormod[2] = (src->colormod[2]/255.0f)*8;
|
|
ent->v->alpha = src->trans/255.0f;
|
|
// ent->v->lightstyle = src->lightstyle;
|
|
// ent->v->lightpflags = src->lightpflags;
|
|
// ent->v->solid = src->solid;
|
|
// ent->v->light[0] = src->light[0];
|
|
// ent->v->light[1] = src->light[1];
|
|
// ent->v->light[2] = src->light[2];
|
|
// ent->v->light[3] = src->light[3];
|
|
// ent->v->tagentity = src->tagentity;
|
|
// ent->v->tagindex = src->tagindex;
|
|
|
|
if (model)
|
|
{
|
|
if (!(flags & RSES_NOROTATE) && (model->flags & EF_ROTATE))
|
|
{
|
|
ent->v->angles[0] = 0;
|
|
ent->v->angles[1] = 100*servertime;
|
|
ent->v->angles[2] = 0;
|
|
}
|
|
}
|
|
|
|
if (csqcg.delta_update)
|
|
{
|
|
*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);
|
|
G_FLOAT(OFS_PARM0) = !oldent;
|
|
PR_ExecuteProgram(prinst, csqcg.delta_update);
|
|
}
|
|
|
|
if (newlist->maxents <= newidx)
|
|
{
|
|
newlist->maxents = newidx + 64;
|
|
newlist->entptr = BZ_Realloc(newlist->entptr, sizeof(*newlist->entptr)*newlist->maxents);
|
|
newlist->entnum = BZ_Realloc(newlist->entnum, sizeof(*newlist->entnum)*newlist->maxents);
|
|
}
|
|
newlist->entptr[newidx] = ent;
|
|
newlist->entnum[newidx] = src->number;
|
|
newidx++;
|
|
|
|
}
|
|
|
|
//remove any unreferenced ents stuck on the end
|
|
while (oldidx < oldlist->numents)
|
|
{
|
|
oldent = oldlist->entptr[oldidx];
|
|
*csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent);
|
|
PR_ExecuteProgram(prinst, csqcg.delta_remove);
|
|
oldidx++;
|
|
}
|
|
|
|
newlist->numents = newidx;
|
|
}
|
|
|
|
#define PF_FixTen PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme
|
|
|
|
//prefixes:
|
|
//PF_ - common, works on any vm
|
|
//PF_cs_ - works in csqc only (dependant upon globals or fields)
|
|
//PF_cl_ - works in csqc and menu (if needed...)
|
|
|
|
//these are the builtins that still need to be added.
|
|
#define PS_cs_setattachment PF_Fixme
|
|
|
|
#define PF_R_PolygonBegin PF_Fixme // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???)
|
|
#define PF_R_PolygonVertex PF_Fixme // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???)
|
|
#define PF_R_PolygonEnd PF_Fixme // #308 void() R_EndPolygon (EXT_CSQC_???)
|
|
|
|
//warning: functions that depend on globals are bad, mkay?
|
|
static struct {
|
|
char *name;
|
|
builtin_t bifunc;
|
|
int ebfsnum;
|
|
} BuiltinList[] = {
|
|
//0
|
|
{"makevectors", PF_cs_makevectors, 1}, // #1 void() makevectors (QUAKE)
|
|
{"setorigin", PF_cs_SetOrigin, 2}, // #2 void(entity e, vector org) setorigin (QUAKE)
|
|
{"setmodel", PF_cs_SetModel, 3}, // #3 void(entity e, string modl) setmodel (QUAKE)
|
|
{"setsize", PF_cs_SetSize, 4}, // #4 void(entity e, vector mins, vector maxs) setsize (QUAKE)
|
|
//5
|
|
{"debugbreak", PF_cs_break, 6}, // #6 void() debugbreak (QUAKE)
|
|
{"random", PF_random, 7}, // #7 float() random (QUAKE)
|
|
{"sound", PF_cs_sound, 8}, // #8 void(entity e, float chan, string samp, float vol, float atten) sound (QUAKE)
|
|
{"normalize", PF_normalize, 9}, // #9 vector(vector in) normalize (QUAKE)
|
|
//10
|
|
{"error", PF_error, 10}, // #10 void(string errortext) error (QUAKE)
|
|
{"objerror", PF_objerror, 11}, // #11 void(string errortext) onjerror (QUAKE)
|
|
{"vlen", PF_vlen, 12}, // #12 float(vector v) vlen (QUAKE)
|
|
{"vectoyaw", PF_vectoyaw, 13}, // #13 float(vector v) vectoyaw (QUAKE)
|
|
{"spawn", PF_Spawn, 14}, // #14 entity() spawn (QUAKE)
|
|
{"remove", PF_cs_remove, 15}, // #15 void(entity e) remove (QUAKE)
|
|
{"traceline", PF_cs_traceline, 16}, // #16 void(vector v1, vector v2, float nomonst, entity forent) traceline (QUAKE)
|
|
{"checkclient", PF_NoCSQC, 17}, // #17 entity() checkclient (QUAKE) (don't support)
|
|
{"findstring", PF_FindString, 18}, // #18 entity(entity start, .string fld, string match) findstring (QUAKE)
|
|
{"precache_sound", PF_cs_PrecacheSound, 19}, // #19 void(string str) precache_sound (QUAKE)
|
|
//20
|
|
{"precache_model", PF_cs_PrecacheModel, 20}, // #20 void(string str) precache_model (QUAKE)
|
|
{"stuffcmd", PF_NoCSQC, 21}, // #21 void(entity client, string s) stuffcmd (QUAKE) (don't support)
|
|
{"findradius", PF_cs_findradius, 22}, // #22 entity(vector org, float rad) findradius (QUAKE)
|
|
{"bprint", PF_NoCSQC, 23}, // #23 void(string s, ...) bprint (QUAKE) (don't support)
|
|
{"sprint", PF_NoCSQC, 24}, // #24 void(entity e, string s, ...) sprint (QUAKE) (don't support)
|
|
{"dprint", PF_dprint, 25}, // #25 void(string s, ...) dprint (QUAKE)
|
|
{"ftos", PF_ftos, 26}, // #26 string(float f) ftos (QUAKE)
|
|
{"vtos", PF_vtos, 27}, // #27 string(vector f) vtos (QUAKE)
|
|
{"coredump", PF_coredump, 28}, // #28 void(void) coredump (QUAKE)
|
|
{"traceon", PF_traceon, 29}, // #29 void() traceon (QUAKE)
|
|
//30
|
|
{"traceoff", PF_traceoff, 30}, // #30 void() traceoff (QUAKE)
|
|
{"eprint", PF_eprint, 31}, // #31 void(entity e) eprint (QUAKE)
|
|
{"walkmove", PF_cs_walkmove, 32}, // #32 float(float yaw, float dist) walkmove (QUAKE)
|
|
{"?", PF_Fixme, 33}, // #33
|
|
{"droptofloor", PF_cs_droptofloor, 34}, // #34
|
|
{"lightstyle", PF_cs_lightstyle, 35}, // #35 void(float lightstyle, string stylestring) lightstyle (QUAKE)
|
|
{"rint", PF_rint, 36}, // #36 float(float f) rint (QUAKE)
|
|
{"floor", PF_floor, 37}, // #37 float(float f) floor (QUAKE)
|
|
{"ceil", PF_ceil, 38}, // #38 float(float f) ceil (QUAKE)
|
|
// {"?", PF_Fixme, 39}, // #39
|
|
//40
|
|
{"checkbottom", PF_cs_checkbottom, 40}, // #40 float(entity e) checkbottom (QUAKE)
|
|
{"pointcontents", PF_cs_pointcontents, 41}, // #41 float(vector org) pointcontents (QUAKE)
|
|
// {"?", PF_Fixme, 42}, // #42
|
|
{"fabs", PF_fabs, 43}, // #43 float(float f) fabs (QUAKE)
|
|
{"aim", PF_NoCSQC, 44}, // #44 vector(entity e, float speed) aim (QUAKE) (don't support)
|
|
{"cvar", PF_cvar, 45}, // #45 float(string cvarname) cvar (QUAKE)
|
|
{"localcmd", PF_localcmd, 46}, // #46 void(string str) localcmd (QUAKE)
|
|
{"nextent", PF_nextent, 47}, // #47 entity(entity e) nextent (QUAKE)
|
|
{"particle", PF_cs_particle, 48}, // #48 void(vector org, vector dir, float colour, float count) particle (QUAKE)
|
|
{"changeyaw", PF_cs_changeyaw, 49}, // #49 void() changeyaw (QUAKE)
|
|
//50
|
|
// {"?", PF_Fixme, 50}, // #50
|
|
{"vectoangles", PF_vectoangles, 51}, // #51 vector(vector v) vectoangles (QUAKE)
|
|
// {"WriteByte", PF_Fixme, 52}, // #52 void(float to, float f) WriteByte (QUAKE)
|
|
// {"WriteChar", PF_Fixme, 53}, // #53 void(float to, float f) WriteChar (QUAKE)
|
|
// {"WriteShort", PF_Fixme, 54}, // #54 void(float to, float f) WriteShort (QUAKE)
|
|
|
|
// {"WriteLong", PF_Fixme, 55}, // #55 void(float to, float f) WriteLong (QUAKE)
|
|
// {"WriteCoord", PF_Fixme, 56}, // #56 void(float to, float f) WriteCoord (QUAKE)
|
|
// {"WriteAngle", PF_Fixme, 57}, // #57 void(float to, float f) WriteAngle (QUAKE)
|
|
// {"WriteString", PF_Fixme, 58}, // #58 void(float to, float f) WriteString (QUAKE)
|
|
// {"WriteEntity", PF_Fixme, 59}, // #59 void(float to, float f) WriteEntity (QUAKE)
|
|
|
|
//60
|
|
{"sin", PF_Sin, 60}, // #60 float(float angle) sin (DP_QC_SINCOSSQRTPOW)
|
|
{"cos", PF_Cos, 61}, // #61 float(float angle) cos (DP_QC_SINCOSSQRTPOW)
|
|
{"sqrt", PF_Sqrt, 62}, // #62 float(float value) sqrt (DP_QC_SINCOSSQRTPOW)
|
|
{"changepitch", PF_cs_changepitch, 63}, // #63 void(entity ent) changepitch (DP_QC_CHANGEPITCH)
|
|
{"tracetoss", PF_cs_tracetoss, 64}, // #64 void(entity ent, entity ignore) tracetoss (DP_QC_TRACETOSS)
|
|
|
|
{"etos", PF_etos, 65}, // #65 string(entity ent) etos (DP_QC_ETOS)
|
|
{"?", PF_Fixme, 66}, // #66
|
|
// {"movetogoal", PF_Fixme, 67}, // #67 void(float step) movetogoal (QUAKE)
|
|
{"precache_file", PF_NoCSQC, 68}, // #68 void(string s) precache_file (QUAKE) (don't support)
|
|
{"makestatic", PF_cs_makestatic, 69}, // #69 void(entity e) makestatic (QUAKE)
|
|
//70
|
|
{"changelevel", PF_NoCSQC, 70}, // #70 void(string mapname) changelevel (QUAKE) (don't support)
|
|
// {"?", PF_Fixme, 71}, // #71
|
|
{"cvar_set", PF_cvar_set, 72}, // #72 void(string cvarname, string valuetoset) cvar_set (QUAKE)
|
|
{"centerprint", PF_NoCSQC, 73}, // #73 void(entity ent, string text) centerprint (QUAKE) (don't support - cprint is supported instead)
|
|
{"ambientsound", PF_cl_ambientsound, 74}, // #74 void (vector pos, string samp, float vol, float atten) ambientsound (QUAKE)
|
|
|
|
{"precache_model2", PF_cs_PrecacheModel, 80}, // #75 void(string str) precache_model2 (QUAKE)
|
|
{"precache_sound2", PF_cs_PrecacheSound, 76}, // #76 void(string str) precache_sound2 (QUAKE)
|
|
{"precache_file2", PF_NoCSQC, 77}, // #77 void(string str) precache_file2 (QUAKE)
|
|
{"setspawnparms", PF_NoCSQC, 78}, // #78 void() setspawnparms (QUAKE) (don't support)
|
|
{"logfrag", PF_NoCSQC, 79}, // #79 void(entity killer, entity killee) logfrag (QW_ENGINE) (don't support)
|
|
|
|
//80
|
|
{"infokey", PF_NoCSQC, 80}, // #80 string(entity e, string keyname) infokey (QW_ENGINE) (don't support)
|
|
{"stof", PF_stof, 81}, // #81 float(string s) stof (FRIK_FILE or QW_ENGINE)
|
|
{"multicast", PF_NoCSQC, 82}, // #82 void(vector where, float set) multicast (QW_ENGINE) (don't support)
|
|
|
|
|
|
//90
|
|
{"tracebox", PF_cs_tracebox, 90},
|
|
{"randomvec", PF_randomvector, 91}, // #91 vector() randomvec (DP_QC_RANDOMVEC)
|
|
{"getlight", PF_cl_getlight, 92}, // #92 vector(vector org) getlight (DP_QC_GETLIGHT)
|
|
{"registercvar", PF_registercvar, 93}, // #93 void(string cvarname, string defaultvalue) registercvar (DP_QC_REGISTERCVAR)
|
|
{"min", PF_min, 94}, // #94 float(float a, floats) min (DP_QC_MINMAXBOUND)
|
|
|
|
{"max", PF_max, 95}, // #95 float(float a, floats) max (DP_QC_MINMAXBOUND)
|
|
{"bound", PF_bound, 96}, // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND)
|
|
{"pow", PF_pow, 97}, // #97 float(float value) pow (DP_QC_SINCOSSQRTPOW)
|
|
{"findfloat", PF_FindFloat, 98}, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)
|
|
{"checkextension", PF_checkextension, 99}, // #99 float(string extname) checkextension (EXT_CSQC)
|
|
|
|
//110
|
|
{"fopen", PF_fopen, 110}, // #110 float(string strname, float accessmode) fopen (FRIK_FILE)
|
|
{"fclose", PF_fclose, 111}, // #111 void(float fnum) fclose (FRIK_FILE)
|
|
{"fgets", PF_fgets, 112}, // #112 string(float fnum) fgets (FRIK_FILE)
|
|
{"fputs", PF_fputs, 113}, // #113 void(float fnum, string str) fputs (FRIK_FILE)
|
|
{"strlen", PF_strlen, 114}, // #114 float(string str) strlen (FRIK_FILE)
|
|
|
|
{"strcat", PF_strcat, 115}, // #115 string(string str1, string str2, ...) strcat (FRIK_FILE)
|
|
{"substring", PF_substring, 116}, // #116 string(string str, float start, float length) substring (FRIK_FILE)
|
|
{"stov", PF_stov, 117}, // #117 vector(string str) stov (FRIK_FILE)
|
|
{"strzone", PF_dupstring, 118}, // #118 string(string str) dupstring (FRIK_FILE)
|
|
{"strunzone", PF_forgetstring, 119}, // #119 void(string str) freestring (FRIK_FILE)
|
|
|
|
//200
|
|
{"precachemodel", PF_cs_PrecacheModel, 200},
|
|
{"eterncall", PF_externcall, 201},
|
|
{"addprogs", PF_cs_addprogs, 202},
|
|
{"externvalue", PF_externvalue, 203},
|
|
{"externset", PF_externset, 204},
|
|
|
|
{"externrefcall", PF_externrefcall, 205},
|
|
{"instr", PF_instr, 206},
|
|
{"openportal", PF_cs_OpenPortal, 207}, //q2bsps
|
|
{"registertempent", PF_NoCSQC, 208},//{"RegisterTempEnt", PF_RegisterTEnt, 0, 0, 0, 208},
|
|
{"customtempent", PF_NoCSQC, 209},//{"CustomTempEnt", PF_CustomTEnt, 0, 0, 0, 209},
|
|
//210
|
|
// {"fork", PF_Fixme, 210},//{"fork", PF_Fork, 0, 0, 0, 210},
|
|
{"abort", PF_Abort, 211}, //#211 void() abort (FTE_MULTITHREADED)
|
|
// {"sleep", PF_Fixme, 212},//{"sleep", PF_Sleep, 0, 0, 0, 212},
|
|
{"forceinfokey", PF_NoCSQC, 213},//{"forceinfokey", PF_ForceInfoKey, 0, 0, 0, 213},
|
|
{"chat", PF_NoCSQC, 214},//{"chat", PF_chat, 0, 0, 0, 214},// #214 void(string filename, float starttag, entity edict) SV_Chat (FTE_NPCCHAT)
|
|
|
|
{"particle2", PF_cs_particle2, 215}, //215 (FTE_PEXT_HEXEN2)
|
|
{"particle3", PF_cs_particle3, 216}, //216 (FTE_PEXT_HEXEN2)
|
|
{"particle4", PF_cs_particle4, 217}, //217 (FTE_PEXT_HEXEN2)
|
|
|
|
//EXT_DIMENSION_PLANES
|
|
{"bitshift", PF_bitshift, 218}, //#218 bitshift (EXT_DIMENSION_PLANES)
|
|
{"te_lightningblood", PF_cl_te_lightningblood, 219},// #219 te_lightningblood void(vector org) (FTE_TE_STANDARDEFFECTBUILTINS)
|
|
|
|
//220
|
|
// {"map_builtin", PF_Fixme, 220}, //like #100 - takes 2 args. arg0 is builtinname, 1 is number to map to.
|
|
{"strstrofs", PF_strstrofs, 221}, // #221 float(string s1, string sub) strstrofs (FTE_STRINGS)
|
|
{"str2chr", PF_str2chr, 222}, // #222 float(string str, float index) str2chr (FTE_STRINGS)
|
|
{"chr2str", PF_chr2str, 223}, // #223 string(float chr, ...) chr2str (FTE_STRINGS)
|
|
{"strconv", PF_strconv, 224}, // #224 string(float ccase, float redalpha, float redchars, string str, ...) strconv (FTE_STRINGS)
|
|
|
|
{"strpad", PF_strpad, 225}, // #225 string strpad(float pad, string str1, ...) strpad (FTE_STRINGS)
|
|
{"infoadd", PF_infoadd, 226}, // #226 string(string old, string key, string value) infoadd
|
|
{"infoget", PF_infoget, 227}, // #227 string(string info, string key) infoget
|
|
{"strncmp", PF_strncmp, 228}, // #228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
|
|
{"strcasecmp", PF_strcasecmp, 229}, // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
|
|
|
|
//230
|
|
{"strncasecmp", PF_strncasecmp, 230}, // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
|
|
{"clientstat", PF_NoCSQC, 231}, // #231 clientstat
|
|
{"runclientphys", PF_NoCSQC, 232}, // #232 runclientphys
|
|
{"isbackbuffered", PF_NoCSQC, 233}, // #233 float(entity ent) isbackbuffered
|
|
{"rotatevectorsbytag", PF_rotatevectorsbytag, 234}, // #234
|
|
|
|
{"rotatevectorsbyangle", PF_rotatevectorsbyangles, 235}, // #235
|
|
{"rotatevectorsbymatrix", PF_rotatevectorsbymatrix, 236}, // #236
|
|
{"skinforname", PF_skinforname, 237}, // #237
|
|
{"shaderforname", PF_shaderforname, 238}, // #238
|
|
{"te_bloodqw", PF_cl_te_bloodqw, 239}, // #239 void te_bloodqw(vector org[, float count]) (FTE_TE_STANDARDEFFECTBUILTINS)
|
|
|
|
{"stoi", PF_stoi, 259},
|
|
{"itos", PF_itos, 260},
|
|
{"stoh", PF_stoh, 261},
|
|
{"htos", PF_htos, 262},
|
|
|
|
//300
|
|
{"clearscene", PF_R_ClearScene, 300}, // #300 void() clearscene (EXT_CSQC)
|
|
{"addentities", PF_R_AddEntityMask, 301}, // #301 void(float mask) addentities (EXT_CSQC)
|
|
{"addentity", PF_R_AddEntity, 302}, // #302 void(entity ent) addentity (EXT_CSQC)
|
|
{"setproperty", PF_R_SetViewFlag, 303}, // #303 float(float property, ...) setproperty (EXT_CSQC)
|
|
{"renderscene", PF_R_RenderScene, 304}, // #304 void() renderscene (EXT_CSQC)
|
|
|
|
{"adddynamiclight", PF_R_AddDynamicLight, 305}, // #305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC)
|
|
|
|
{"R_BeginPolygon", PF_R_PolygonBegin, 306}, // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???)
|
|
{"R_PolygonVertex", PF_R_PolygonVertex, 307}, // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???)
|
|
{"R_EndPolygon", PF_R_PolygonEnd, 308}, // #308 void() R_EndPolygon (EXT_CSQC_???)
|
|
|
|
{"getproperty", PF_R_GetViewFlag, 309}, // #309 vector/float(float property) getproperty (EXT_CSQC_1)
|
|
|
|
//310
|
|
//maths stuff that uses the current view settings.
|
|
{"unproject", PF_cs_unproject, 310}, // #310 vector (vector v) unproject (EXT_CSQC)
|
|
{"project", PF_cs_project, 311}, // #311 vector (vector v) project (EXT_CSQC)
|
|
|
|
// {"?", PF_Fixme, 312}, // #312
|
|
// {"?", PF_Fixme, 313}, // #313
|
|
// {"?", PF_Fixme, 314}, // #314
|
|
|
|
//2d (immediate) operations
|
|
{"drawline", PF_CL_drawline, 315}, // #315 void(float width, vector pos1, vector pos2) drawline (EXT_CSQC)
|
|
{"iscachedpic", PF_CL_is_cached_pic, 316}, // #316 float(string name) iscachedpic (EXT_CSQC)
|
|
{"precache_pic", PF_CL_precache_pic, 317}, // #317 string(string name, float trywad) precache_pic (EXT_CSQC)
|
|
{"draw_getimagesize", PF_CL_drawgetimagesize, 318}, // #318 vector(string picname) draw_getimagesize (EXT_CSQC)
|
|
{"freepic", PF_CL_free_pic, 319}, // #319 void(string name) freepic (EXT_CSQC)
|
|
//320
|
|
{"drawcharacter", PF_CL_drawcharacter, 320}, // #320 float(vector position, float character, vector scale, vector rgb, float alpha [, float flag]) drawcharacter (EXT_CSQC, [EXT_CSQC_???])
|
|
{"drawstring", PF_CL_drawstring, 321}, // #321 float(vector position, string text, vector scale, vector rgb, float alpha [, float flag]) drawstring (EXT_CSQC, [EXT_CSQC_???])
|
|
{"drawpic", PF_CL_drawpic, 322}, // #322 float(vector position, string pic, vector size, vector rgb, float alpha [, float flag]) drawpic (EXT_CSQC, [EXT_CSQC_???])
|
|
{"drawfill", PF_CL_drawfill, 323}, // #323 float(vector position, vector size, vector rgb, float alpha [, float flag]) drawfill (EXT_CSQC, [EXT_CSQC_???])
|
|
{"drawsetcliparea", PF_CL_drawsetcliparea, 324}, // #324 void(float x, float y, float width, float height) drawsetcliparea (EXT_CSQC_???)
|
|
{"drawresetcliparea", PF_CL_drawresetcliparea, 325}, // #325 void(void) drawresetcliparea (EXT_CSQC_???)
|
|
|
|
{"drawcolorcodedstring", PF_CL_drawstring, 326}, // #326
|
|
{"stringwidth", PF_CL_stringwidth, 327}, // #327 EXT_CSQC_'DARKPLACES'
|
|
{"drawsubpic", PF_CL_drawsubpic, 328}, // #328 EXT_CSQC_'DARKPLACES'
|
|
// {"?", PF_Fixme, 329}, // #329 EXT_CSQC_'DARKPLACES'
|
|
|
|
//330
|
|
{"getstatf", PF_cs_getstatf, 330}, // #330 float(float stnum) getstatf (EXT_CSQC)
|
|
{"getstati", PF_cs_getstati, 331}, // #331 float(float stnum) getstati (EXT_CSQC)
|
|
{"getstats", PF_cs_getstats, 332}, // #332 string(float firststnum) getstats (EXT_CSQC)
|
|
{"setmodelindex", PF_cs_SetModelIndex, 333}, // #333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
|
|
{"modelnameforindex", PF_cs_ModelnameForIndex, 334}, // #334 string(float mdlindex) modelnameforindex (EXT_CSQC)
|
|
|
|
{"particleeffectnum", PF_cs_particleeffectnum, 335}, // #335 float(string effectname) particleeffectnum (EXT_CSQC)
|
|
{"trailparticles", PF_cs_trailparticles, 336}, // #336 void(entity ent, float effectnum, vector start, vector end) trailparticles (EXT_CSQC),
|
|
{"pointparticles", PF_cs_pointparticles, 337}, // #337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC)
|
|
|
|
{"cprint", PF_cl_cprint, 338}, // #338 void(string s) cprint (EXT_CSQC)
|
|
{"print", PF_print, 339}, // #339 void(string s) print (EXT_CSQC)
|
|
|
|
//340
|
|
{"keynumtostring", PF_cl_keynumtostring, 340}, // #340 string(float keynum) keynumtostring (EXT_CSQC)
|
|
{"stringtokeynum", PF_cl_stringtokeynum, 341}, // #341 float(string keyname) stringtokeynum (EXT_CSQC)
|
|
{"getkeybind", PF_cl_getkeybind, 342}, // #342 string(float keynum) getkeybind (EXT_CSQC)
|
|
|
|
// {"?", PF_Fixme, 343}, // #343
|
|
// {"?", PF_Fixme, 344}, // #344
|
|
|
|
{"getinputstate", PF_cs_getinputstate, 345}, // #345 float(float framenum) getinputstate (EXT_CSQC)
|
|
{"setsensitivityscaler", PF_cs_setsensativityscaler, 346}, // #346 void(float sens) setsensitivityscaler (EXT_CSQC)
|
|
|
|
{"runstandardplayerphysics", PF_cs_runplayerphysics, 347}, // #347 void() runstandardplayerphysics (EXT_CSQC)
|
|
|
|
{"getplayerkeyvalue", PF_cs_getplayerkey, 348}, // #348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
|
|
|
|
{"isdemo", PF_cl_playingdemo, 349}, // #349 float() isdemo (EXT_CSQC)
|
|
//350
|
|
{"isserver", PF_cl_runningserver, 350}, // #350 float() isserver (EXT_CSQC)
|
|
|
|
{"SetListener", PF_cs_setlistener, 351}, // #351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
|
|
{"registercommand", PF_cs_registercommand, 352}, // #352 void(string cmdname) registercommand (EXT_CSQC)
|
|
{"wasfreed", PF_WasFreed, 353}, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too)
|
|
|
|
{"serverkey", PF_cs_serverkey, 354}, // #354 string(string key) serverkey;
|
|
{"getentitytoken", PF_cs_getentitytoken, 355}, // #355 string() getentitytoken;
|
|
// {"?", PF_Fixme, 356}, // #356
|
|
// {"?", PF_Fixme, 357}, // #357
|
|
// {"?", PF_Fixme, 358}, // #358
|
|
// {"?", PF_Fixme, 359}, // #359
|
|
|
|
//360
|
|
//note that 'ReadEntity' is pretty hard to implement reliably. Modders should use a combination of ReadShort, and findfloat, and remember that it might not be known clientside (pvs culled or other reason)
|
|
{"readbyte", PF_ReadByte, 360}, // #360 float() readbyte (EXT_CSQC)
|
|
{"readchar", PF_ReadChar, 361}, // #361 float() readchar (EXT_CSQC)
|
|
{"readshort", PF_ReadShort, 362}, // #362 float() readshort (EXT_CSQC)
|
|
{"readlong", PF_ReadLong, 363}, // #363 float() readlong (EXT_CSQC)
|
|
{"readcoord", PF_ReadCoord, 364}, // #364 float() readcoord (EXT_CSQC)
|
|
|
|
{"readangle", PF_ReadAngle, 365}, // #365 float() readangle (EXT_CSQC)
|
|
{"readstring", PF_ReadString, 366}, // #366 string() readstring (EXT_CSQC)
|
|
{"readfloat", PF_ReadFloat, 367}, // #367 string() readfloat (EXT_CSQC)
|
|
|
|
{"readentitynum", PF_ReadEntityNum, 368}, // #368 float() readentitynum (EXT_CSQC)
|
|
{"readserverentitystate", PF_ReadServerEntityState, 369}, // #369 void(float flags, float simtime) readserverentitystate (EXT_CSQC_1)
|
|
|
|
//400
|
|
{"copyentity", PF_cs_copyentity, 400}, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY)
|
|
{"setcolors", PF_NoCSQC, 401}, // #401 void(entity cl, float colours) setcolors (DP_SV_SETCOLOR) (don't implement)
|
|
{"findchain", PF_findchain, 402}, // #402 entity(string field, string match) findchain (DP_QC_FINDCHAIN)
|
|
{"findchainfloat", PF_findchainfloat, 403}, // #403 entity(float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT)
|
|
{"effect", PF_cl_effect, 404}, // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
|
|
|
|
{"te_blood", PF_cl_te_blooddp, 405}, // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
|
|
{"te_bloodshower", PF_cl_te_bloodshower,406}, // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
|
|
{"te_explosionrgb", PF_cl_te_explosionrgb, 407}, // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
|
|
{"te_particlecube", PF_cl_te_particlecube,408}, // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
|
|
{"te_particlerain", PF_cl_te_particlerain, 409}, // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
|
|
|
|
{"te_particlesnow", PF_cl_te_particlesnow,410}, // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
|
|
{"te_spark", PF_cl_te_spark, 411}, // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK)
|
|
{"te_gunshotquad", PF_cl_te_gunshotquad, 412}, // #412 void(vector org) te_gunshotquad (DP_TE_QUADEFFECTS1)
|
|
{"te_spikequad", PF_cl_te_spikequad, 413}, // #413 void(vector org) te_spikequad (DP_TE_QUADEFFECTS1)
|
|
{"te_superspikequad", PF_cl_te_superspikequad,414}, // #414 void(vector org) te_superspikequad (DP_TE_QUADEFFECTS1)
|
|
|
|
{"te_explosionquad", PF_cl_te_explosionquad, 415}, // #415 void(vector org) te_explosionquad (DP_TE_QUADEFFECTS1)
|
|
{"te_smallflash", PF_cl_te_smallflash, 416}, // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
|
|
{"te_customflash", PF_cl_te_customflash, 417}, // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
|
|
{"te_gunshot", PF_cl_te_gunshot, 418}, // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_spike", PF_cl_te_spike, 419}, // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
|
|
|
|
{"te_superspike", PF_cl_te_superspike,420}, // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_explosion", PF_cl_te_explosion, 421}, // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_tarexplosion", PF_cl_te_tarexplosion,422}, // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_wizspike", PF_cl_te_wizspike, 423}, // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_knightspike", PF_cl_te_knightspike,424}, // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
|
|
|
|
{"te_lavasplash", PF_cl_te_lavasplash,425}, // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_teleport", PF_cl_te_teleport, 426}, // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_explosion2", PF_cl_te_explosion2,427}, // #427 void(vector org, float color, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_lightning1", PF_cl_te_lightning1, 428}, // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_lightning2", PF_cl_te_lightning2,429}, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
|
|
|
|
{"te_lightning3", PF_cl_te_lightning3,430}, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"te_beam", PF_cl_te_beam, 431}, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
|
|
{"vectorvectors", PF_cs_vectorvectors,432}, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS)
|
|
{"te_plasmaburn", PF_cl_te_plasmaburn,433}, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
|
|
// {"getsurfacenumpoints", PF_Fixme, 434}, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE)
|
|
|
|
// {"getsurfacepoint", PF_Fixme, 435}, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE)
|
|
// {"getsurfacenormal", PF_Fixme, 436}, // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE)
|
|
// {"getsurfacetexture", PF_Fixme, 437}, // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE)
|
|
// {"getsurfacenearpoint", PF_Fixme, 438}, // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)
|
|
// {"getsurfaceclippedpoint", PF_Fixme, 439}, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)
|
|
|
|
{"clientcommand", PF_NoCSQC, 440}, // #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIENTCOMMAND) (don't implement)
|
|
{"tokenize", PF_Tokenize, 441}, // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND)
|
|
{"argv", PF_ArgV, 442}, // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND)
|
|
{"setattachment", PS_cs_setattachment,443}, // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS)
|
|
{"search_begin", PF_search_begin, 444}, // #444 float search_begin(string pattern, float caseinsensitive, float quiet) (DP_QC_FS_SEARCH)
|
|
|
|
{"search_end", PF_search_end, 445}, // #445 void search_end(float handle) (DP_QC_FS_SEARCH)
|
|
{"search_getsize", PF_search_getsize, 446}, // #446 float search_getsize(float handle) (DP_QC_FS_SEARCH)
|
|
{"search_getfilename", PF_search_getfilename,447}, // #447 string search_getfilename(float handle, float num) (DP_QC_FS_SEARCH)
|
|
{"dp_cvar_string", PF_cvar_string, 448}, // #448 string(float n) cvar_string (DP_QC_CVAR_STRING)
|
|
{"findflags", PF_FindFlags, 449}, // #449 entity(entity start, .entity fld, float match) findflags (DP_QC_FINDFLAGS)
|
|
|
|
{"findchainflags", PF_findchainflags, 450}, // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS)
|
|
{"gettagindex", PF_cs_gettagindex, 451}, // #451 float(entity ent, string tagname) gettagindex (DP_MD3_TAGSINFO)
|
|
{"gettaginfo", PF_cs_gettaginfo, 452}, // #452 vector(entity ent, float tagindex) gettaginfo (DP_MD3_TAGSINFO)
|
|
{"dropclient", PF_NoCSQC, 453}, // #453 void(entity player) dropclient (DP_SV_BOTCLIENT) (don't implement)
|
|
{"spawnclient", PF_NoCSQC, 454}, // #454 entity() spawnclient (DP_SV_BOTCLIENT) (don't implement)
|
|
|
|
{"clienttype", PF_NoCSQC, 455}, // #455 float(entity client) clienttype (DP_SV_BOTCLIENT) (don't implement)
|
|
|
|
|
|
// {"WriteUnterminatedString",PF_WriteString2, 456}, //writestring but without the null terminator. makes things a little nicer.
|
|
|
|
//DP_TE_FLAMEJET
|
|
// {"te_flamejet", PF_te_flamejet, 457}, // #457 void(vector org, vector vel, float howmany) te_flamejet
|
|
|
|
//no 458 documented.
|
|
|
|
//DP_QC_EDICT_NUM
|
|
{"edict_num", PF_edict_for_num, 459}, // #459 entity(float entnum) edict_num
|
|
|
|
//DP_QC_STRINGBUFFERS
|
|
{"buf_create", PF_buf_create, 460}, // #460 float() buf_create
|
|
{"buf_del", PF_buf_del, 461}, // #461 void(float bufhandle) buf_del
|
|
{"buf_getsize", PF_buf_getsize, 462}, // #462 float(float bufhandle) buf_getsize
|
|
{"buf_copy", PF_buf_copy, 463}, // #463 void(float bufhandle_from, float bufhandle_to) buf_copy
|
|
{"buf_sort", PF_buf_sort, 464}, // #464 void(float bufhandle, float sortpower, float backward) buf_sort
|
|
{"buf_implode", PF_buf_implode, 465}, // #465 string(float bufhandle, string glue) buf_implode
|
|
{"bufstr_get", PF_bufstr_get, 466}, // #466 string(float bufhandle, float string_index) bufstr_get
|
|
{"bufstr_set", PF_bufstr_set, 467}, // #467 void(float bufhandle, float string_index, string str) bufstr_set
|
|
{"bufstr_add", PF_bufstr_add, 468}, // #468 float(float bufhandle, string str, float order) bufstr_add
|
|
{"bufstr_free", PF_bufstr_free, 469}, // #469 void(float bufhandle, float string_index) bufstr_free
|
|
|
|
//no 470 documented
|
|
|
|
//DP_QC_ASINACOSATANATAN2TAN
|
|
{"asin", PF_asin, 471}, // #471 float(float s) asin
|
|
{"acos", PF_acos, 472}, // #472 float(float c) acos
|
|
{"atan", PF_atan, 473}, // #473 float(float t) atan
|
|
{"atan2", PF_atan2, 474}, // #474 float(float c, float s) atan2
|
|
{"tan", PF_tan, 475}, // #475 float(float a) tan
|
|
|
|
|
|
////DP_QC_STRINGCOLORFUNCTIONS
|
|
{"strlennocol", PF_strlennocol, 476}, // #476 float(string s) strlennocol
|
|
{"strdecolorize", PF_strdecolorize, 477}, // #477 string(string s) strdecolorize
|
|
|
|
//DP_QC_STRFTIME
|
|
{"strftime", PF_strftime, 478}, // #478 string(float uselocaltime, string format, ...) strftime
|
|
|
|
//DP_QC_TOKENIZEBYSEPARATOR
|
|
{"tokenizebyseparator",PF_tokenizebyseparator, 479}, // #479 float(string s, string separator1, ...) tokenizebyseparator
|
|
|
|
//DP_QC_STRING_CASE_FUNCTIONS
|
|
{"strtolower", PF_strtolower, 480}, // #476 string(string s) strtolower
|
|
{"strtoupper", PF_strtoupper, 481}, // #476 string(string s) strlennocol
|
|
|
|
//DP_QC_CVAR_DEFSTRING
|
|
{"cvar_defstring", PF_cvar_defstring, 482}, // #482 string(string s) cvar_defstring
|
|
|
|
//DP_SV_POINTSOUND
|
|
{"pointsound", PF_cs_pointsound, 483}, // #483 void(vector origin, string sample, float volume, float attenuation) pointsound
|
|
|
|
//DP_QC_STRREPLACE
|
|
{"strreplace", PF_strreplace, 484}, // #484 string(string search, string replace, string subject) strreplace
|
|
{"strireplace", PF_strireplace, 485}, // #485 string(string search, string replace, string subject) strireplace
|
|
|
|
|
|
//DP_QC_GETSURFACEPOINTATTRIBUTE
|
|
{"getsurfacepointattribute",PF_getsurfacepointattribute, 486}, // #486vector(entity e, float s, float n, float a) getsurfacepointattribute
|
|
|
|
//DP_GECKO_SUPPORT
|
|
{"gecko_create", PF_cs_gecko_create, 487}, // #487 float(string name) gecko_create( string name )
|
|
{"gecko_destroy", PF_cs_gecko_destroy, 488}, // #488 void(string name) gecko_destroy( string name )
|
|
{"gecko_navigate", PF_cs_gecko_navigate, 489}, // #489 void(string name) gecko_navigate( string name, string URI )
|
|
{"gecko_keyevent", PF_cs_gecko_keyevent, 490}, // #490 float(string name) gecko_keyevent( string name, float key, float eventtype )
|
|
{"gecko_mousemove", PF_cs_gecko_mousemove, 491}, // #491 void gecko_mousemove( string name, float x, float y )
|
|
{"gecko_resize", PF_cs_gecko_resize, 492}, // #492 void gecko_resize( string name, float w, float h )
|
|
{"gecko_get_texture_extent",PF_cs_gecko_get_texture_extent, 493}, // #493 vector gecko_get_texture_extent( string name )
|
|
|
|
//DP_QC_CRC16
|
|
{"crc16", PF_crc16, 494}, // #494 float(float caseinsensitive, string s, ...) crc16
|
|
|
|
//DP_QC_CVAR_TYPE
|
|
{"cvar_type", PF_cvar_type, 495}, // #495 float(string name) cvar_type
|
|
|
|
//DP_QC_ENTITYDATA
|
|
{"numentityfields", PF_numentityfields, 496}, // #496 float() numentityfields
|
|
{"entityfieldname", PF_entityfieldname, 497}, // #497 string(float fieldnum) entityfieldname
|
|
{"entityfieldtype", PF_entityfieldtype, 498}, // #498 float(float fieldnum) entityfieldtype
|
|
{"getentityfieldstring",PF_getentityfieldstring, 499}, // #499 string(float fieldnum, entity ent) getentityfieldstring
|
|
{"putentityfieldstring",PF_putentityfieldstring, 500}, // #500 float(float fieldnum, entity ent, string s) putentityfieldstring
|
|
|
|
//DP_SV_WRITEPICTURE
|
|
{"WritePicture", PF_ReadPicture, 501}, // #501 void(float to, string s, float sz) WritePicture
|
|
|
|
//no 502 documented
|
|
|
|
//DP_QC_WHICHPACK
|
|
{"whichpack", PF_whichpack, 503}, // #503 string(string filename) whichpack
|
|
|
|
//DP_QC_URI_ESCAPE
|
|
{"uri_escape", PF_uri_escape, 510}, // #510 string(string in) uri_escape
|
|
{"uri_unescape", PF_uri_unescape, 511}, // #511 string(string in) uri_unescape = #511;
|
|
|
|
//DP_QC_NUM_FOR_EDICT
|
|
{"num_for_edict", PF_num_for_edict, 512}, // #512 float(entity ent) num_for_edict
|
|
|
|
//DP_QC_URI_GET
|
|
{"uri_get", PF_uri_get, 513}, // #513 float(string uril, float id) uri_get
|
|
|
|
{"keynumtostring", PF_cl_keynumtostring, 520}, // #520
|
|
{"findkeysforcommand", PF_cl_findkeysforcommand, 521}, // #521
|
|
|
|
{NULL}
|
|
};
|
|
|
|
static builtin_t pr_builtin[550];
|
|
|
|
|
|
|
|
|
|
static jmp_buf csqc_abort;
|
|
static progparms_t csqcprogparms;
|
|
|
|
|
|
//Any menu builtin error or anything like that will come here.
|
|
void VARGS CSQC_Abort (char *format, ...) //an error occured.
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr, format);
|
|
vsnprintf (string,sizeof(string)-1, format,argptr);
|
|
va_end (argptr);
|
|
|
|
Con_Printf("CSQC_Abort: %s\nShutting down csqc\n", string);
|
|
|
|
if (pr_csqc_coreonerror.value)
|
|
{
|
|
int size = 1024*1024*8;
|
|
char *buffer = BZ_Malloc(size);
|
|
csqcprogs->save_ents(csqcprogs, buffer, &size, 3);
|
|
COM_WriteFile("csqccore.txt", buffer, size);
|
|
BZ_Free(buffer);
|
|
}
|
|
|
|
Host_EndGame("csqc error");
|
|
}
|
|
|
|
void CSQC_ForgetThreads(void)
|
|
{
|
|
csqctreadstate_t *state = csqcthreads, *next;
|
|
csqcthreads = NULL;
|
|
while(state)
|
|
{
|
|
next = state->next;
|
|
|
|
csqcprogs->parms->memfree(state->thread);
|
|
csqcprogs->parms->memfree(state);
|
|
|
|
state = next;
|
|
}
|
|
}
|
|
|
|
void CSQC_Shutdown(void)
|
|
{
|
|
search_close_progs(csqcprogs, false);
|
|
if (csqcprogs)
|
|
{
|
|
CSQC_ForgetThreads();
|
|
CloseProgs(csqcprogs);
|
|
Con_Printf("Closed csqc\n");
|
|
}
|
|
csqcprogs = NULL;
|
|
|
|
in_sensitivityscale = 1;
|
|
}
|
|
|
|
//when the qclib needs a file, it calls out to this function.
|
|
qbyte *CSQC_PRLoadFile (char *path, void *buffer, int bufsize)
|
|
{
|
|
qbyte *file;
|
|
|
|
if (!strcmp(path, "csprogs.dat"))
|
|
{
|
|
char newname[MAX_QPATH];
|
|
snprintf(newname, MAX_QPATH, "csprogsvers/%x.dat", csqcchecksum);
|
|
|
|
if (csqcchecksum)
|
|
{
|
|
file = COM_LoadStackFile(newname, buffer, bufsize);
|
|
if (file)
|
|
{
|
|
if (cls.protocol == CP_NETQUAKE)
|
|
{
|
|
if (QCRC_Block(file, com_filesize) == csqcchecksum)
|
|
return file;
|
|
}
|
|
else
|
|
{
|
|
if (LittleLong(Com_BlockChecksum(file, com_filesize)) == csqcchecksum) //and the user wasn't trying to be cunning.
|
|
return file;
|
|
}
|
|
}
|
|
}
|
|
|
|
file = COM_LoadStackFile(path, buffer, bufsize);
|
|
if (file && !cls.demoplayback) //allow them to use csprogs.dat if playing a demo, and don't care about the checksum
|
|
{
|
|
if (csqcchecksum)
|
|
{
|
|
if (cls.protocol == CP_NETQUAKE)
|
|
{
|
|
if (QCRC_Block(file, com_filesize) != csqcchecksum)
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
if (LittleLong(Com_BlockChecksum(file, com_filesize)) != csqcchecksum)
|
|
return NULL; //not valid
|
|
}
|
|
|
|
//back it up
|
|
COM_WriteFile(newname, file, com_filesize);
|
|
}
|
|
}
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
return COM_LoadStackFile(path, buffer, bufsize);
|
|
}
|
|
|
|
int CSQC_PRFileSize (char *path)
|
|
{
|
|
qbyte *file;
|
|
|
|
if (!strcmp(path, "csprogs.dat"))
|
|
{
|
|
char newname[MAX_QPATH];
|
|
snprintf(newname, MAX_QPATH, "csprogsvers/%x.dat", csqcchecksum);
|
|
|
|
if (csqcchecksum)
|
|
{
|
|
file = COM_LoadTempFile (newname);
|
|
if (file)
|
|
{
|
|
if (cls.protocol == CP_NETQUAKE)
|
|
{
|
|
if (QCRC_Block(file, com_filesize) == csqcchecksum)
|
|
return com_filesize+1;
|
|
}
|
|
else
|
|
{
|
|
if (LittleLong(Com_BlockChecksum(file, com_filesize)) == csqcchecksum) //and the user wasn't trying to be cunning.
|
|
return com_filesize+1;
|
|
}
|
|
}
|
|
}
|
|
|
|
file = COM_LoadTempFile(path);
|
|
if (file && !cls.demoplayback) //allow them to use csprogs.dat if playing a demo, and don't care about the checksum
|
|
{
|
|
if (csqcchecksum)
|
|
{
|
|
if (cls.protocol == CP_NETQUAKE)
|
|
{
|
|
if (QCRC_Block(file, com_filesize) != csqcchecksum)
|
|
return -1; //not valid
|
|
}
|
|
else
|
|
{
|
|
if (LittleLong(Com_BlockChecksum(file, com_filesize)) != csqcchecksum)
|
|
return -1; //not valid
|
|
}
|
|
}
|
|
}
|
|
if (!file)
|
|
return -1;
|
|
|
|
return com_filesize;
|
|
}
|
|
|
|
return COM_FileSize(path);
|
|
}
|
|
|
|
double csqctime;
|
|
qboolean CSQC_Init (unsigned int checksum)
|
|
{
|
|
int i;
|
|
csqcedict_t *worldent;
|
|
csqcchecksum = checksum;
|
|
|
|
//its already running...
|
|
if (csqcprogs)
|
|
return false;
|
|
|
|
if (!qrenderer)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (cl_nocsqc.value)
|
|
return false;
|
|
|
|
for (i = 0; i < sizeof(pr_builtin)/sizeof(pr_builtin[0]); i++)
|
|
pr_builtin[i] = PF_Fixme;
|
|
for (i = 0; BuiltinList[i].bifunc; i++)
|
|
{
|
|
if (BuiltinList[i].ebfsnum)
|
|
pr_builtin[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc;
|
|
}
|
|
|
|
memset(cl.model_csqcname, 0, sizeof(cl.model_csqcname));
|
|
memset(cl.model_csqcprecache, 0, sizeof(cl.model_csqcprecache));
|
|
|
|
csqcprogparms.progsversion = PROGSTRUCT_VERSION;
|
|
csqcprogparms.ReadFile = CSQC_PRLoadFile;//char *(*ReadFile) (char *fname, void *buffer, int *len);
|
|
csqcprogparms.FileSize = CSQC_PRFileSize;//int (*FileSize) (char *fname); //-1 if file does not exist
|
|
csqcprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len);
|
|
csqcprogparms.printf = (void *)Con_Printf;//Con_Printf;//void (*printf) (char *, ...);
|
|
csqcprogparms.Sys_Error = Sys_Error;
|
|
csqcprogparms.Abort = CSQC_Abort;
|
|
csqcprogparms.edictsize = sizeof(csqcedict_t);
|
|
|
|
csqcprogparms.entspawn = NULL;//void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set
|
|
csqcprogparms.entcanfree = NULL;//bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed
|
|
csqcprogparms.stateop = NULL;//StateOp;//void (*stateop) (float var, func_t func);
|
|
csqcprogparms.cstateop = NULL;//CStateOp;
|
|
csqcprogparms.cwstateop = NULL;//CWStateOp;
|
|
csqcprogparms.thinktimeop = NULL;//ThinkTimeOp;
|
|
|
|
//used when loading a game
|
|
csqcprogparms.builtinsfor = NULL;//builtin_t *(*builtinsfor) (int num); //must return a pointer to the builtins that were used before the state was saved.
|
|
csqcprogparms.loadcompleate = NULL;//void (*loadcompleate) (int edictsize); //notification to reset any pointers.
|
|
|
|
csqcprogparms.memalloc = PR_CB_Malloc;//void *(*memalloc) (int size); //small string allocation malloced and freed randomly
|
|
csqcprogparms.memfree = PR_CB_Free;//void (*memfree) (void * mem);
|
|
|
|
|
|
csqcprogparms.globalbuiltins = pr_builtin;//builtin_t *globalbuiltins; //these are available to all progs
|
|
csqcprogparms.numglobalbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]);
|
|
|
|
csqcprogparms.autocompile = PR_NOCOMPILE;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;
|
|
|
|
csqcprogparms.gametime = &csqctime;
|
|
|
|
csqcprogparms.sv_edicts = (struct edict_s **)&csqc_edicts;
|
|
csqcprogparms.sv_num_edicts = &num_csqc_edicts;
|
|
|
|
csqcprogparms.useeditor = QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms);
|
|
|
|
csqctime = Sys_DoubleTime();
|
|
if (!csqcprogs)
|
|
{
|
|
in_sensitivityscale = 1;
|
|
csqcmapentitydataloaded = true;
|
|
csqcprogs = InitProgs(&csqcprogparms);
|
|
PR_Configure(csqcprogs, -1, 16);
|
|
|
|
CSQC_InitFields(); //let the qclib know the field order that the engine needs.
|
|
|
|
if (PR_LoadProgs(csqcprogs, "csprogs.dat", 0, NULL, 0) < 0) //no per-progs builtins.
|
|
{
|
|
CSQC_Shutdown();
|
|
//failed to load or something
|
|
return false;
|
|
}
|
|
if (setjmp(csqc_abort))
|
|
{
|
|
CSQC_Shutdown();
|
|
return false;
|
|
}
|
|
|
|
num_csqc_edicts = 0;
|
|
CS_ClearWorld();
|
|
|
|
PF_InitTempStrings(csqcprogs);
|
|
|
|
csqc_fakereadbyte = -1;
|
|
memset(csqcent, 0, sizeof(*csqcent)*maxcsqcentities); //clear the server->csqc entity translations.
|
|
|
|
csqcentsize = PR_InitEnts(csqcprogs, pr_csmaxedicts.value);
|
|
|
|
CSQC_FindGlobals();
|
|
|
|
ED_Alloc(csqcprogs); //we need a word entity.
|
|
//world edict becomes readonly
|
|
worldent = (csqcedict_t *)EDICT_NUM(csqcprogs, 0);
|
|
|
|
worldent->readonly = true;
|
|
worldent->isfree = false;
|
|
worldent->v->model = PR_SetString(csqcprogs, cl.model_name[1]);
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
loadedcsqcpack[i].numents = 0;
|
|
loadedcsqcpack[i].maxents = 0;
|
|
Z_Free(loadedcsqcpack[i].entptr);
|
|
loadedcsqcpack[i].entptr = NULL;
|
|
Z_Free(loadedcsqcpack[i].entnum);
|
|
loadedcsqcpack[i].entnum = NULL;
|
|
}
|
|
|
|
memset(deltaedplayerents, 0, sizeof(deltaedplayerents));
|
|
csqcmapentitydata = NULL;
|
|
csqcmapentitydataloaded = false;
|
|
|
|
if (csqcg.init_function)
|
|
{
|
|
void *pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
G_FLOAT(OFS_PARM0) = 1.0; //api version
|
|
(((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, FULLENGINENAME));
|
|
G_FLOAT(OFS_PARM2) = build_number();
|
|
PR_ExecuteProgram(csqcprogs, csqcg.init_function);
|
|
}
|
|
|
|
Con_Printf("Loaded csqc\n");
|
|
}
|
|
|
|
return true; //success!
|
|
}
|
|
|
|
void CSQC_WorldLoaded(void)
|
|
{
|
|
if (!csqcprogs)
|
|
return;
|
|
if (csqcmapentitydataloaded)
|
|
return;
|
|
csqcmapentitydataloaded = true;
|
|
csqcmapentitydata = cl.worldmodel->entities;
|
|
if (csqcg.worldloaded)
|
|
PR_ExecuteProgram(csqcprogs, csqcg.worldloaded);
|
|
csqcmapentitydata = NULL;
|
|
}
|
|
|
|
void CSQC_CoreDump(void)
|
|
{
|
|
if (!csqcprogs)
|
|
{
|
|
Con_Printf("Can't core dump, you need to be running the CSQC progs first.");
|
|
return;
|
|
}
|
|
|
|
{
|
|
int size = 1024*1024*8;
|
|
char *buffer = BZ_Malloc(size);
|
|
csqcprogs->save_ents(csqcprogs, buffer, &size, 3);
|
|
COM_WriteFile("csqccore.txt", buffer, size);
|
|
BZ_Free(buffer);
|
|
}
|
|
|
|
}
|
|
|
|
void PR_CSExtensionList_f(void)
|
|
{
|
|
int i;
|
|
int ebi;
|
|
int bi;
|
|
lh_extension_t *extlist;
|
|
|
|
#define SHOW_ACTIVEEXT 1
|
|
#define SHOW_ACTIVEBI 2
|
|
#define SHOW_NOTSUPPORTEDEXT 4
|
|
#define SHOW_NOTACTIVEEXT 8
|
|
#define SHOW_NOTACTIVEBI 16
|
|
|
|
int showflags = atoi(Cmd_Argv(1));
|
|
if (!showflags)
|
|
showflags = SHOW_ACTIVEEXT|SHOW_NOTACTIVEEXT;
|
|
|
|
//make sure the info is valid
|
|
if (!pr_builtin[0])
|
|
{
|
|
for (i = 0; i < sizeof(pr_builtin)/sizeof(pr_builtin[0]); i++)
|
|
pr_builtin[i] = PF_Fixme;
|
|
for (i = 0; BuiltinList[i].bifunc; i++)
|
|
{
|
|
if (BuiltinList[i].ebfsnum)
|
|
pr_builtin[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc;
|
|
}
|
|
}
|
|
|
|
|
|
if (showflags & (SHOW_ACTIVEBI|SHOW_NOTACTIVEBI))
|
|
for (i = 0; BuiltinList[i].name; i++)
|
|
{
|
|
if (!BuiltinList[i].ebfsnum)
|
|
continue; //a reserved builtin.
|
|
if (BuiltinList[i].bifunc == PF_Fixme)
|
|
Con_Printf("^1%s:%i needs to be added\n", BuiltinList[i].name, BuiltinList[i].ebfsnum);
|
|
else if (pr_builtin[BuiltinList[i].ebfsnum] == BuiltinList[i].bifunc)
|
|
{
|
|
if (showflags & SHOW_ACTIVEBI)
|
|
Con_Printf("%s is active on %i\n", BuiltinList[i].name, BuiltinList[i].ebfsnum);
|
|
}
|
|
else
|
|
{
|
|
if (showflags & SHOW_NOTACTIVEBI)
|
|
Con_Printf("^4%s is NOT active (%i)\n", BuiltinList[i].name, BuiltinList[i].ebfsnum);
|
|
}
|
|
}
|
|
|
|
if (showflags & (SHOW_NOTSUPPORTEDEXT|SHOW_NOTACTIVEEXT|SHOW_ACTIVEEXT))
|
|
{
|
|
extlist = QSG_Extensions;
|
|
|
|
for (i = 0; i < QSG_Extensions_count; i++)
|
|
{
|
|
if (!extlist[i].name)
|
|
continue;
|
|
|
|
if (i < 32)
|
|
{
|
|
if (!(cls.fteprotocolextensions & (1<<i)))
|
|
{
|
|
if (showflags & SHOW_NOTSUPPORTEDEXT)
|
|
Con_Printf("^4protocol %s is not supported\n", extlist[i].name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for (ebi = 0; ebi < extlist[i].numbuiltins; ebi++)
|
|
{
|
|
for (bi = 0; BuiltinList[bi].name; bi++)
|
|
{
|
|
if (!strcmp(BuiltinList[bi].name, extlist[i].builtinnames[ebi]))
|
|
break;
|
|
}
|
|
|
|
if (!BuiltinList[bi].name)
|
|
{
|
|
if (showflags & SHOW_NOTSUPPORTEDEXT)
|
|
Con_Printf("^4%s is not supported\n", extlist[i].name);
|
|
break;
|
|
}
|
|
if (pr_builtin[BuiltinList[bi].ebfsnum] != BuiltinList[bi].bifunc)
|
|
{
|
|
if (pr_builtin[BuiltinList[bi].ebfsnum] == PF_Fixme)
|
|
{
|
|
if (showflags & SHOW_NOTACTIVEEXT)
|
|
Con_Printf("^4%s is not currently active (builtin: %s#%i)\n", extlist[i].name, BuiltinList[bi].name, BuiltinList[bi].ebfsnum);
|
|
}
|
|
else
|
|
{
|
|
if (showflags & SHOW_NOTACTIVEEXT)
|
|
Con_Printf("^4%s was overridden (builtin: %s#%i)\n", extlist[i].name, BuiltinList[bi].name, BuiltinList[bi].ebfsnum);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (ebi == extlist[i].numbuiltins)
|
|
{
|
|
if (showflags & SHOW_ACTIVEEXT)
|
|
{
|
|
if (!extlist[i].numbuiltins)
|
|
Con_Printf("%s is supported\n", extlist[i].name);
|
|
else
|
|
Con_Printf("%s is currently active\n", extlist[i].name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSQC_RegisterCvarsAndThings(void)
|
|
{
|
|
PF_Common_RegisterCvars();
|
|
|
|
Cmd_AddCommand("coredump_csqc", CSQC_CoreDump);
|
|
Cmd_AddCommand ("extensionlist_csqc", PR_CSExtensionList_f);
|
|
|
|
|
|
Cvar_Register(&pr_csmaxedicts, CSQCPROGSGROUP);
|
|
Cvar_Register(&cl_csqcdebug, CSQCPROGSGROUP);
|
|
Cvar_Register(&cl_nocsqc, CSQCPROGSGROUP);
|
|
Cvar_Register(&pr_csqc_coreonerror, CSQCPROGSGROUP);
|
|
}
|
|
|
|
qboolean CSQC_DrawView(void)
|
|
{
|
|
if (!csqcg.draw_function || !csqcprogs || !cl.worldmodel)
|
|
return false;
|
|
|
|
r_secondaryview = 0;
|
|
|
|
CL_CalcClientTime();
|
|
|
|
DropPunchAngle (0);
|
|
if (cl.worldmodel)
|
|
R_LessenStains();
|
|
|
|
csqc_resortfrags = true;
|
|
|
|
if (csqcg.clientcommandframe)
|
|
*csqcg.clientcommandframe = cls.netchan.outgoing_sequence;
|
|
if (csqcg.servercommandframe)
|
|
*csqcg.servercommandframe = cl.ackedinputsequence;
|
|
if (csqcg.intermission)
|
|
*csqcg.intermission = cl.intermission;
|
|
|
|
CSQC_ChangeLocalPlayer(0);
|
|
|
|
/* if (csqcg.dpcompat_sbshowscores)
|
|
{
|
|
extern qboolean sb_showscores;
|
|
*csqcg.dpcompat_sbshowscores = sb_showscores;
|
|
}
|
|
*/
|
|
if (csqcg.cltime)
|
|
*csqcg.cltime = cl.time;
|
|
if (csqcg.cltime)
|
|
*csqcg.svtime = cl.servertime;
|
|
|
|
CSQC_RunThreads(); //wake up any qc threads
|
|
|
|
//EXT_CSQC_1
|
|
{
|
|
void *pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
G_FLOAT(OFS_PARM0) = vid.width;
|
|
G_FLOAT(OFS_PARM1) = vid.height;
|
|
G_FLOAT(OFS_PARM2) = !m_state;
|
|
}
|
|
//end EXT_CSQC_1
|
|
PR_ExecuteProgram(csqcprogs, csqcg.draw_function);
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean CSQC_KeyPress(int key, qboolean down)
|
|
{
|
|
void *pr_globals;
|
|
|
|
if (!csqcprogs || !csqcg.input_event)
|
|
return false;
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
G_FLOAT(OFS_PARM0) = !down;
|
|
G_FLOAT(OFS_PARM1) = MP_TranslateFTEtoDPCodes(key);
|
|
G_FLOAT(OFS_PARM2) = 0;
|
|
|
|
PR_ExecuteProgram (csqcprogs, csqcg.input_event);
|
|
|
|
return G_FLOAT(OFS_RETURN);
|
|
}
|
|
qboolean CSQC_MouseMove(float xdelta, float ydelta)
|
|
{
|
|
void *pr_globals;
|
|
|
|
if (!csqcprogs || !csqcg.input_event)
|
|
return false;
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
G_FLOAT(OFS_PARM0) = 2;
|
|
G_FLOAT(OFS_PARM1) = xdelta;
|
|
G_FLOAT(OFS_PARM2) = ydelta;
|
|
|
|
PR_ExecuteProgram (csqcprogs, csqcg.input_event);
|
|
|
|
return G_FLOAT(OFS_RETURN);
|
|
}
|
|
|
|
qboolean CSQC_ConsoleCommand(char *cmd)
|
|
{
|
|
void *pr_globals;
|
|
if (!csqcprogs || !csqcg.console_command)
|
|
return false;
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, cmd));
|
|
|
|
PR_ExecuteProgram (csqcprogs, csqcg.console_command);
|
|
return G_FLOAT(OFS_RETURN);
|
|
}
|
|
|
|
#pragma message("do we really need the firstbyte parameter here?")
|
|
qboolean CSQC_ParseTempEntity(unsigned char firstbyte)
|
|
{
|
|
void *pr_globals;
|
|
if (!csqcprogs || !csqcg.parse_tempentity)
|
|
return false;
|
|
|
|
csqc_fakereadbyte = firstbyte;
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
PR_ExecuteProgram (csqcprogs, csqcg.parse_tempentity);
|
|
csqc_fakereadbyte = -1;
|
|
return !!G_FLOAT(OFS_RETURN);
|
|
}
|
|
|
|
qboolean CSQC_LoadResource(char *resname, char *restype)
|
|
{
|
|
void *pr_globals;
|
|
if (!csqcprogs || !csqcg.loadresource)
|
|
return true;
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, resname));
|
|
(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, restype));
|
|
|
|
PR_ExecuteProgram (csqcprogs, csqcg.loadresource);
|
|
|
|
return !!G_FLOAT(OFS_RETURN);
|
|
}
|
|
|
|
qboolean CSQC_StuffCmd(int lplayernum, char *cmd)
|
|
{
|
|
void *pr_globals;
|
|
if (!csqcprogs || !csqcg.parse_stuffcmd)
|
|
return false;
|
|
|
|
CSQC_ChangeLocalPlayer(lplayernum);
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, cmd));
|
|
|
|
PR_ExecuteProgram (csqcprogs, csqcg.parse_stuffcmd);
|
|
return true;
|
|
}
|
|
qboolean CSQC_CenterPrint(int lplayernum, char *cmd)
|
|
{
|
|
void *pr_globals;
|
|
if (!csqcprogs || !csqcg.parse_centerprint)
|
|
return false;
|
|
|
|
CSQC_ChangeLocalPlayer(lplayernum);
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, cmd));
|
|
|
|
PR_ExecuteProgram (csqcprogs, csqcg.parse_centerprint);
|
|
return G_FLOAT(OFS_RETURN);
|
|
}
|
|
|
|
void CSQC_Input_Frame(int lplayernum, usercmd_t *cmd)
|
|
{
|
|
if (!csqcprogs || !csqcg.input_frame)
|
|
return;
|
|
|
|
CSQC_ChangeLocalPlayer(lplayernum);
|
|
|
|
CL_CalcClientTime();
|
|
if (csqcg.svtime)
|
|
*csqcg.svtime = cl.servertime;
|
|
if (csqcg.cltime)
|
|
*csqcg.cltime = cl.time;
|
|
|
|
if (csqcg.clientcommandframe)
|
|
*csqcg.clientcommandframe = cls.netchan.outgoing_sequence;
|
|
|
|
cs_set_input_state(cmd);
|
|
PR_ExecuteProgram (csqcprogs, csqcg.input_frame);
|
|
cs_get_input_state(cmd);
|
|
}
|
|
|
|
//this protocol allows up to 32767 edicts.
|
|
#ifdef PEXT_CSQC
|
|
static void CSQC_EntityCheck(int entnum)
|
|
{
|
|
int newmax;
|
|
|
|
if (entnum >= maxcsqcentities)
|
|
{
|
|
newmax = entnum+64;
|
|
csqcent = BZ_Realloc(csqcent, sizeof(*csqcent)*newmax);
|
|
memset(csqcent + maxcsqcentities, 0, (newmax - maxcsqcentities)*sizeof(csqcent));
|
|
maxcsqcentities = newmax;
|
|
}
|
|
}
|
|
|
|
int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation)
|
|
{
|
|
void *pr_globals;
|
|
csqcedict_t *ent;
|
|
|
|
if (!csqcprogs)
|
|
return false;
|
|
if (csqcg.event_sound)
|
|
{
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
|
|
G_FLOAT(OFS_PARM0) = entnum;
|
|
G_FLOAT(OFS_PARM1) = channel;
|
|
G_INT(OFS_PARM2) = PR_TempString(csqcprogs, soundname);
|
|
G_FLOAT(OFS_PARM3) = vol;
|
|
G_FLOAT(OFS_PARM4) = attenuation;
|
|
VectorCopy(pos, G_VECTOR(OFS_PARM5));
|
|
|
|
PR_ExecuteProgram(csqcprogs, csqcg.event_sound);
|
|
|
|
return G_FLOAT(OFS_RETURN);
|
|
}
|
|
else if (csqcg.serversound)
|
|
{
|
|
CSQC_EntityCheck(entnum);
|
|
ent = csqcent[entnum];
|
|
if (!ent)
|
|
return false;
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
|
|
*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);
|
|
G_FLOAT(OFS_PARM0) = channel;
|
|
G_INT(OFS_PARM1) = PR_TempString(csqcprogs, soundname);
|
|
VectorCopy(pos, G_VECTOR(OFS_PARM2));
|
|
G_FLOAT(OFS_PARM3) = vol;
|
|
G_FLOAT(OFS_PARM4) = attenuation;
|
|
|
|
PR_ExecuteProgram(csqcprogs, csqcg.serversound);
|
|
|
|
return G_FLOAT(OFS_RETURN);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CSQC_ParseEntities(void)
|
|
{
|
|
csqcedict_t *ent;
|
|
unsigned short entnum;
|
|
void *pr_globals;
|
|
int packetsize;
|
|
int packetstart;
|
|
|
|
if (!csqcprogs)
|
|
Host_EndGame("CSQC needs to be initialized for this server.\n");
|
|
|
|
if (!csqcg.ent_update || !csqcg.self)
|
|
Host_EndGame("CSQC is unable to parse entities\n");
|
|
|
|
pr_globals = PR_globals(csqcprogs, PR_CURRENT);
|
|
|
|
CL_CalcClientTime();
|
|
if (csqcg.svtime) //estimated server time
|
|
*csqcg.svtime = cl.servertime;
|
|
if (csqcg.cltime) //smooth client time.
|
|
*csqcg.cltime = cl.time;
|
|
|
|
if (csqcg.clientcommandframe)
|
|
*csqcg.clientcommandframe = cls.netchan.outgoing_sequence;
|
|
if (csqcg.servercommandframe)
|
|
*csqcg.servercommandframe = cl.ackedinputsequence;
|
|
|
|
for(;;)
|
|
{
|
|
entnum = MSG_ReadShort();
|
|
if (!entnum || msg_badread)
|
|
break;
|
|
if (entnum & 0x8000)
|
|
{ //remove
|
|
entnum &= ~0x8000;
|
|
|
|
if (!entnum)
|
|
Host_EndGame("CSQC cannot remove world!\n");
|
|
|
|
CSQC_EntityCheck(entnum);
|
|
|
|
if (cl_csqcdebug.value)
|
|
Con_Printf("Remove %i\n", entnum);
|
|
|
|
ent = csqcent[entnum];
|
|
csqcent[entnum] = NULL;
|
|
|
|
if (!ent) //hrm.
|
|
continue;
|
|
|
|
*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);
|
|
PR_ExecuteProgram(csqcprogs, csqcg.ent_remove);
|
|
//the csqc is expected to call the remove builtin.
|
|
}
|
|
else
|
|
{
|
|
CSQC_EntityCheck(entnum);
|
|
|
|
if (cl.csqcdebug)
|
|
{
|
|
packetsize = MSG_ReadShort();
|
|
packetstart = msg_readcount;
|
|
}
|
|
else
|
|
{
|
|
packetsize = 0;
|
|
packetstart = 0;
|
|
}
|
|
|
|
ent = csqcent[entnum];
|
|
if (!ent)
|
|
{
|
|
ent = (csqcedict_t*)ED_Alloc(csqcprogs);
|
|
csqcent[entnum] = ent;
|
|
ent->v->entnum = entnum;
|
|
G_FLOAT(OFS_PARM0) = true;
|
|
|
|
if (cl_csqcdebug.value)
|
|
Con_Printf("Add %i\n", entnum);
|
|
}
|
|
else
|
|
{
|
|
G_FLOAT(OFS_PARM0) = false;
|
|
if (cl_csqcdebug.value)
|
|
Con_Printf("Update %i\n", entnum);
|
|
}
|
|
|
|
*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);
|
|
PR_ExecuteProgram(csqcprogs, csqcg.ent_update);
|
|
|
|
if (cl.csqcdebug)
|
|
{
|
|
if (msg_readcount != packetstart+packetsize)
|
|
{
|
|
if (msg_readcount > packetstart+packetsize)
|
|
Con_Printf("CSQC overread entity %i. Size %i, read %i\n", entnum, packetsize, msg_readcount - packetsize);
|
|
else
|
|
Con_Printf("CSQC underread entity %i. Size %i, read %i\n", entnum, packetsize, msg_readcount - packetsize);
|
|
Con_Printf("First byte is %i\n", net_message.data[msg_readcount]);
|
|
#ifndef CLIENTONLY
|
|
if (sv.state)
|
|
{
|
|
Con_Printf("Server classname: \"%s\"\n", PR_GetString(svprogfuncs, EDICT_NUM(svprogfuncs, entnum)->v->classname));
|
|
}
|
|
#endif
|
|
}
|
|
msg_readcount = packetstart+packetsize; //leetism.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif
|