#include "quakedef.h" #ifdef CSQC_DAT #ifdef RGLQUAKE #include "glquake.h" //evil to include this #include "shader.h" #endif #include "pr_common.h" 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; #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"); // 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(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(console_command, "CSQC_ConsoleCommand"); \ \ globalfunction(ent_update, "CSQC_Ent_Update"); \ globalfunction(ent_remove, "CSQC_Ent_Remove"); \ \ globalfunction(serversound, "CSQC_ServerSound"); \ \ /*These are pointers to the csqc's globals.*/ \ globalfloat(time, "time"); /*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 */ \ \ 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*/ \ \ globalfloat(clientcommandframe, "clientcommandframe"); \ globalfloat(servercommandframe, "servercommandframe"); \ \ globalfloat(player_localentnum, "player_localentnum"); /*float the entity number of the local player*/ \ globalfloat(intermission, "intermission"); /*float the entity number of the local player*/ \ \ globalvector(pmove_org, "pmove_org"); \ globalvector(pmove_vel, "pmove_vel"); \ globalvector(pmove_mins, "pmove_mins"); \ globalvector(pmove_maxs, "pmove_maxs"); \ globalfloat(pmove_jump_held, "pmove_jump_held"); \ globalfloat(pmove_waterjumptime, "pmove_waterjumptime"); \ globalfloat(input_timelength, "input_timelength"); \ globalvector(input_angles, "input_angles"); \ globalvector(input_movevalues, "input_movevalues"); \ globalfloat(input_buttons, "input_buttons"); \ \ globalfloat(movevar_gravity, "movevar_gravity"); \ globalfloat(movevar_stopspeed, "movevar_stopspeed"); \ globalfloat(movevar_maxspeed, "movevar_maxspeed"); \ globalfloat(movevar_spectatormaxspeed,"movevar_spectatormaxspeed"); \ globalfloat(movevar_accelerate, "movevar_accelerate"); \ globalfloat(movevar_airaccelerate, "movevar_airaccelerate"); \ globalfloat(movevar_wateraccelerate,"movevar_wateraccelerate"); \ globalfloat(movevar_friction, "movevar_friction"); \ globalfloat(movevar_waterfriction, "movevar_waterfriction"); \ globalfloat(movevar_entgravity, "movevar_entgravity"); \ 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; #define plnum 0 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.time) *csqcg.time = Sys_DoubleTime(); if (csqcg.player_localentnum) *csqcg.player_localentnum = cl.playernum[plnum]+1; 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 thier normals.*/ \ fieldfloat(skin); \ fieldfloat(colormap); \ fieldfloat(flags); \ fieldfloat(frame); \ fieldfloat(frame2); \ fieldfloat(frame1time); \ fieldfloat(frame2time); \ fieldfloat(lerpfrac); \ fieldfloat(renderflags);\ fieldfloat(forceshader);\ fieldfloat(dimension_hit); \ fieldfloat(dimension_solid); \ \ 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 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); } 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; 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*)EDICT_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; } 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; } P_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 compatable.", 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 compatable.", 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(0, str); } 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) 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; 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. if (!model) { Con_Printf("CopyCSQCEdictToEntity: model wasn't precached!\n"); return false; } memset(out, 0, sizeof(*out)); out->model = model; if (in->v->renderflags) { i = in->v->renderflags; if (i & CSQCRF_VIEWMODEL) out->flags |= Q2RF_DEPTHHACK|Q2RF_WEAPONMODEL; if (i & CSQCRF_EXTERNALMODEL) out->flags |= Q2RF_EXTERNALMODEL; if (i & CSQCRF_DEPTHHACK) out->flags |= Q2RF_DEPTHHACK; if (i & CSQCRF_ADDITIVE) out->flags |= Q2RF_ADDATIVE; //CSQCRF_USEAXIS is below if (i & CSQCRF_NOSHADOW) out->flags |= RF_NOSHADOW; } out->frame = in->v->frame; out->oldframe = in->v->frame2; out->lerpfrac = in->v->lerpfrac; VectorCopy(in->v->origin, out->origin); if ((int)in->v->renderflags & 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; } out->frame1time = in->v->frame1time; out->frame2time = in->v->frame2time; 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 (in->v->predraw) { int oldself = *csqcg.self; *csqcg.self = EDICT_TO_PROG(prinst, (void*)in); PR_ExecuteProgram(prinst, in->v->predraw); *csqcg.self = oldself; if (in->isfree) return; //bummer... } 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); } #define MASK_ENGINE 1 static void PF_R_AddEntityMask(progfuncs_t *prinst, struct globalvars_s *pr_globals) { int mask = G_FLOAT(OFS_PARM0); csqcedict_t *ent; int e; 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) { G_INT(OFS_PARM0) = EDICT_TO_PROG(prinst, (void*)ent); PF_R_AddEntity(prinst, pr_globals); } } if (mask & MASK_ENGINE && cl.worldmodel) { CL_LinkViewModel (); 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]; ML_ModelViewMatrix(modelview, r_refdef.viewangles, r_refdef.vieworg); ML_ProjectionMatrix2(proj, r_refdef.fov_x, r_refdef.fov_y); Matrix4_Multiply(proj, modelview, mvp); Matrix4x4_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; 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 = NULL;//&cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; view_message = NULL;//&view_frame->playerstate[cl.playernum[plnum]]; V_CalcRefdef(0); //set up the defaults (for player 0) /* VectorCopy(cl.simangles[0], r_refdef.viewangles); VectorCopy(cl.simorg[0], 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, VF_PERSPECTIVE = 200 } 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_ORIGIN: VectorCopy(p, r_refdef.vieworg); cl.crouch[0] = 0; break; case VF_ORIGIN_Z: cl.crouch[0] = 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_CARTESIAN_ANGLES: Con_Printf(S_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_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 VectorCopy (r_refdef.vieworg, cl.viewent[0].origin); CalcGunAngle(0); 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 Sbar_Draw(); #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[plnum][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[plnum][stnum]; if (*prinst->callargc > 1) { int first, count; first = G_FLOAT(OFS_PARM1); count = G_FLOAT(OFS_PARM2); G_FLOAT(OFS_RETURN) = (((unsigned int)val)&(((1<>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; out = PF_TempStr(prinst); //the network protocol byteswaps ((unsigned int*)out)[0] = LittleLong(cl.stats[0][stnum+0]); ((unsigned int*)out)[1] = LittleLong(cl.stats[0][stnum+1]); ((unsigned int*)out)[2] = LittleLong(cl.stats[0][stnum+2]); ((unsigned int*)out)[3] = LittleLong(cl.stats[0][stnum+3]); ((unsigned int*)out)[4] = 0; //make sure it's null terminated RETURN_SSTRING(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); pr_global_struct->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) { ent->v->model = PR_SetString(prinst, cl.model_csqcname[-modelindex]); model = cl.model_csqcprecache[-modelindex]; } else { 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] = Mod_ForName(cl.model_csqcname[-freei], false); } 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.model_csqcprecache[-freei] = Mod_ForName(cl.model_csqcname[-freei], false); } 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) { 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_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 *str = PF_TempStr(prinst); char *read = MSG_ReadString(); Q_strncpyz(str, read, MAXTEMPBUFFERLEN); } 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; edict_t *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, pr_global_struct->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); if (sv.time > 10) Cbuf_AddText("restart\n", RESTRICT_LOCAL); } } 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. P_ParticleTrail(start, end, efnum, NULL); else P_ParticleTrail(start, end, efnum, &ent->trailstate); } static void PF_cs_particlesloaded (progfuncs_t *prinst, struct globalvars_s *pr_globals) { char *effectname = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = P_DescriptionIsLoaded(effectname); } //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[plnum]; if (csqcg.input_timelength) *csqcg.input_timelength = cmd->msec/1000.0f; if (csqcg.input_angles) { csqcg.input_angles[0] = SHORT2ANGLE(cmd->angles[0]+0.5); csqcg.input_angles[1] = SHORT2ANGLE(cmd->angles[1]+0.5); csqcg.input_angles[2] = SHORT2ANGLE(cmd->angles[2]+0.5); } 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; G_FLOAT(OFS_RETURN) = true; } #define ANGLE2SHORT(x) ((x/360.0)*65535) //read lots of globals, run the default player physics, write lots of globals. 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); } 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(false, "pings"); } } static void PF_cs_serverkey (progfuncs_t *prinst, struct globalvars_s *pr_globals) { char *keyname = PF_VarString(prinst, 0, pr_globals); char *ret; if (!strcmp(keyname, "ip")) ret = NET_AdrToString(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_SSTRING(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 *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 = PF_TempStr(prinst); sprintf(ret, "%i", cl.players[pnum].ping); } else if (!strcmp(keyname, "frags")) { ret = PF_TempStr(prinst); sprintf(ret, "%i", cl.players[pnum].frags); } else if (!strcmp(keyname, "pl")) //packet loss { CheckSendPings(); ret = PF_TempStr(prinst); sprintf(ret, "%i", cl.players[pnum].pl); } else if (!strcmp(keyname, "entertime")) //packet loss { ret = PF_TempStr(prinst); sprintf(ret, "%i", (int)cl.players[pnum].entertime); } else { ret = Info_ValueForKey(cl.players[pnum].userinfo, keyname); } if (*ret) RETURN_SSTRING(ret); else G_INT(OFS_RETURN) = 0; } #define lh_extension_t void lh_extension_t *checkfteextensionsv(char *name); lh_extension_t *checkextension(char *name); static void PF_checkextension (progfuncs_t *prinst, struct globalvars_s *pr_globals) { char *extname = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = checkextension(extname) || checkfteextensionsv(extname); } 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); }; 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); P_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); P_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); P_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); P_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); } 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); } 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 *)sv.edicts; org = G_VECTOR(OFS_PARM0); rad = G_FLOAT(OFS_PARM1); for (i=1 ; iisfree) continue; if (ent->v->solid == SOLID_NOT) 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); } void PF_WasFreed (progfuncs_t *prinst, struct globalvars_s *pr_globals) { edict_t *ent; ent = (edict_t*)G_EDICT(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = ent->isfree; } 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, pt_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, pt_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, pt_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_BlobExplosion (pos); 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_LavaSplash (pos); } 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) { if (sv.worldmodel->fromgame == fg_quake2) CMQ2_SetAreaPortalState(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1)); } 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) { G_FLOAT(OFS_RETURN) = !!sv.active; } 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); } */ 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) 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 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); if (Mod_TagNumForName) G_FLOAT(OFS_RETURN) = Mod_TagNumForName(mod, tagname); else G_FLOAT(OFS_RETURN) = 0; } 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_INT(OFS_RETURN) = shad-r_shaders + 1; else G_INT(OFS_RETURN) = 0; #else G_INT(OFS_RETURN) = 0; #endif } 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*pm_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] > pm_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 } 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] += pm_stepheight; VectorCopy (neworg, end); end[2] -= pm_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] -= pm_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]; sprintf(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); 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); } #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 PF_cs_gettaginfo PF_Fixme #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 builtin_t csqc_builtins[] = { //0 PF_Fixme, // #0 PF_cs_makevectors, // #1 void() makevectors (QUAKE) PF_cs_SetOrigin, // #2 void(entity e, vector org) setorigin (QUAKE) PF_cs_SetModel, // #3 void(entity e, string modl) setmodel (QUAKE) PF_cs_SetSize, // #4 void(entity e, vector mins, vector maxs) setsize (QUAKE) PF_Fixme, // #5 PF_cs_break, // #6 void() debugbreak (QUAKE) PF_random, // #7 float() random (QUAKE) PF_cs_sound, // #8 void(entity e, float chan, string samp, float vol, float atten) sound (QUAKE) PF_normalize, // #9 vector(vector in) normalize (QUAKE) //10 PF_error, // #10 void(string errortext) error (QUAKE) PF_objerror, // #11 void(string errortext) onjerror (QUAKE) PF_vlen, // #12 float(vector v) vlen (QUAKE) PF_vectoyaw, // #13 float(vector v) vectoyaw (QUAKE) PF_Spawn, // #14 entity() spawn (QUAKE) PF_cs_remove, // #15 void(entity e) remove (QUAKE) PF_cs_traceline, // #16 void(vector v1, vector v2, float nomonst, entity forent) traceline (QUAKE) PF_NoCSQC, // #17 entity() checkclient (QUAKE) (don't support) PF_FindString, // #18 entity(entity start, .string fld, string match) findstring (QUAKE) PF_cs_PrecacheSound, // #19 void(string str) precache_sound (QUAKE) //20 PF_cs_PrecacheModel, // #20 void(string str) precache_model (QUAKE) PF_NoCSQC, // #21 void(entity client, string s) stuffcmd (QUAKE) (don't support) PF_cs_findradius, // #22 entity(vector org, float rad) findradius (QUAKE) PF_NoCSQC, // #23 void(string s, ...) bprint (QUAKE) (don't support) PF_NoCSQC, // #24 void(entity e, string s, ...) sprint (QUAKE) (don't support) PF_dprint, // #25 void(string s, ...) dprint (QUAKE) PF_ftos, // #26 string(float f) ftos (QUAKE) PF_vtos, // #27 string(vector f) vtos (QUAKE) PF_coredump, // #28 void(void) coredump (QUAKE) PF_traceon, // #29 void() traceon (QUAKE) //30 PF_traceoff, // #30 void() traceoff (QUAKE) PF_eprint, // #31 void(entity e) eprint (QUAKE) PF_cs_walkmove, // #32 float(float yaw, float dist) walkmove (QUAKE) PF_Fixme, // #33 PF_cs_droptofloor, // #34 PF_cs_lightstyle, // #35 void(float lightstyle, string stylestring) lightstyle (QUAKE) PF_rint, // #36 float(float f) rint (QUAKE) PF_floor, // #37 float(float f) floor (QUAKE) PF_ceil, // #38 float(float f) ceil (QUAKE) PF_Fixme, // #39 //40 PF_cs_checkbottom, // #40 float(entity e) checkbottom (QUAKE) PF_cs_pointcontents, // #41 float(vector org) pointcontents (QUAKE) PF_Fixme, // #42 PF_fabs, // #43 float(float f) fabs (QUAKE) PF_NoCSQC, // #44 vector(entity e, float speed) aim (QUAKE) (don't support) PF_cvar, // #45 float(string cvarname) cvar (QUAKE) PF_localcmd, // #46 void(string str) localcmd (QUAKE) PF_nextent, // #47 entity(entity e) nextent (QUAKE) PF_cs_particle, // #48 void(vector org, vector dir, float colour, float count) particle (QUAKE) PF_cs_changeyaw, // #49 void() changeyaw (QUAKE) //50 PF_Fixme, // #50 PF_vectoangles, // #51 vector(vector v) vectoangles (QUAKE) PF_Fixme, // #52 void(float to, float f) WriteByte (QUAKE) PF_Fixme, // #53 void(float to, float f) WriteChar (QUAKE) PF_Fixme, // #54 void(float to, float f) WriteShort (QUAKE) PF_Fixme, // #55 void(float to, float f) WriteLong (QUAKE) PF_Fixme, // #56 void(float to, float f) WriteCoord (QUAKE) PF_Fixme, // #57 void(float to, float f) WriteAngle (QUAKE) PF_Fixme, // #58 void(float to, float f) WriteString (QUAKE) PF_Fixme, // #59 void(float to, float f) WriteEntity (QUAKE) //60 PF_Sin, // #60 float(float angle) sin (DP_QC_SINCOSSQRTPOW) PF_Cos, // #61 float(float angle) cos (DP_QC_SINCOSSQRTPOW) PF_Sqrt, // #62 float(float value) sqrt (DP_QC_SINCOSSQRTPOW) PF_cs_changepitch, // #63 void(entity ent) changepitch (DP_QC_CHANGEPITCH) PF_cs_tracetoss, // #64 void(entity ent, entity ignore) tracetoss (DP_QC_TRACETOSS) PF_etos, // #65 string(entity ent) etos (DP_QC_ETOS) PF_Fixme, // #66 PF_Fixme, // #67 void(float step) movetogoal (QUAKE) PF_NoCSQC, // #68 void(string s) precache_file (QUAKE) (don't support) PF_cs_makestatic, // #69 void(entity e) makestatic (QUAKE) //70 PF_NoCSQC, // #70 void(string mapname) changelevel (QUAKE) (don't support) PF_Fixme, // #71 PF_cvar_set, // #72 void(string cvarname, string valuetoset) cvar_set (QUAKE) PF_NoCSQC, // #73 void(entity ent, string text) centerprint (QUAKE) (don't support - cprint is supported instead) PF_cl_ambientsound, // #74 void (vector pos, string samp, float vol, float atten) ambientsound (QUAKE) PF_cs_PrecacheModel, // #75 void(string str) precache_model2 (QUAKE) PF_cs_PrecacheSound, // #76 void(string str) precache_sound2 (QUAKE) PF_NoCSQC, // #77 void(string str) precache_file2 (QUAKE) PF_NoCSQC, // #78 void() setspawnparms (QUAKE) (don't support) PF_NoCSQC, // #79 void(entity killer, entity killee) logfrag (QW_ENGINE) (don't support) //80 PF_NoCSQC, // #80 string(entity e, string keyname) infokey (QW_ENGINE) (don't support) PF_stof, // #81 float(string s) stof (FRIK_FILE or QW_ENGINE) PF_NoCSQC, // #82 void(vector where, float set) multicast (QW_ENGINE) (don't support) PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, //90 PF_cs_tracebox, PF_randomvector, // #91 vector() randomvec (DP_QC_RANDOMVEC) PF_cl_getlight, // #92 vector(vector org) getlight (DP_QC_GETLIGHT) PF_registercvar, // #93 void(string cvarname, string defaultvalue) registercvar (DP_QC_REGISTERCVAR) PF_min, // #94 float(float a, floats) min (DP_QC_MINMAXBOUND) PF_max, // #95 float(float a, floats) max (DP_QC_MINMAXBOUND) PF_bound, // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND) PF_pow, // #97 float(float value) pow (DP_QC_SINCOSSQRTPOW) PF_FindFloat, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT) PF_checkextension, // #99 float(string extname) checkextension (EXT_CSQC) //100 PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, //110 PF_fopen, // #110 float(string strname, float accessmode) fopen (FRIK_FILE) PF_fclose, // #111 void(float fnum) fclose (FRIK_FILE) PF_fgets, // #112 string(float fnum) fgets (FRIK_FILE) PF_fputs, // #113 void(float fnum, string str) fputs (FRIK_FILE) PF_strlen, // #114 float(string str) strlen (FRIK_FILE) PF_strcat, // #115 string(string str1, string str2, ...) strcat (FRIK_FILE) PF_substring, // #116 string(string str, float start, float length) substring (FRIK_FILE) PF_stov, // #117 vector(string str) stov (FRIK_FILE) PF_dupstring, // #118 string(string str) dupstring (FRIK_FILE) PF_forgetstring, // #119 void(string str) freestring (FRIK_FILE) //120 PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, //130 PF_FixTen, //140 PF_FixTen, //150 PF_FixTen, //160 PF_FixTen, //170 PF_FixTen, //180 PF_FixTen, //190 PF_FixTen, //200 PF_cs_PrecacheModel, PF_externcall, PF_cs_addprogs, PF_externvalue, PF_externset, PF_externrefcall, PF_instr, PF_cs_OpenPortal, //q2bsps PF_NoCSQC,//{"RegisterTempEnt", PF_RegisterTEnt, 0, 0, 0, 208}, PF_NoCSQC,//{"CustomTempEnt", PF_CustomTEnt, 0, 0, 0, 209}, //210 PF_Fixme,//{"fork", PF_Fork, 0, 0, 0, 210}, PF_Abort, //#211 void() abort (FTE_MULTITHREADED) PF_Fixme,//{"sleep", PF_Sleep, 0, 0, 0, 212}, PF_NoCSQC,//{"forceinfokey", PF_ForceInfoKey, 0, 0, 0, 213}, PF_NoCSQC,//{"chat", PF_chat, 0, 0, 0, 214},// #214 void(string filename, float starttag, entity edict) SV_Chat (FTE_NPCCHAT) PF_cs_particle2, //215 (FTE_PEXT_HEXEN2) PF_cs_particle3, //216 (FTE_PEXT_HEXEN2) PF_cs_particle4, //217 (FTE_PEXT_HEXEN2) //EXT_DIMENSION_PLANES PF_bitshift, //#218 bitshift (EXT_DIMENSION_PLANES) PF_cl_te_lightningblood,// #219 te_lightningblood void(vector org) (FTE_TE_STANDARDEFFECTBUILTINS) //220 PF_Fixme, //{"map_builtin", PF_builtinsupported,0, 0, 0, 220}, //like #100 - takes 2 args. arg0 is builtinname, 1 is number to map to. PF_strstrofs, // #221 float(string s1, string sub) strstrofs (FTE_STRINGS) PF_str2chr, // #222 float(string str, float index) str2chr (FTE_STRINGS) PF_chr2str, // #223 string(float chr, ...) chr2str (FTE_STRINGS) PF_strconv, // #224 string(float ccase, float redalpha, float redchars, string str, ...) strconv (FTE_STRINGS) PF_strpad, // #225 string(float ccase, float redalpha, float redchars, string str, ...) strconv (FTE_STRINGS) PF_infoadd, // #226 string(string old, string key, string value) infoadd PF_infoget, // #227 string(string info, string key) infoget PF_strncmp, // #228 float(string s1, string s2, float len) strncmp (FTE_STRINGS) PF_strcasecmp, // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS) //230 PF_strncasecmp, // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS) PF_Fixme, // #231 clientstat PF_Fixme, // #232 runclientphys PF_Fixme, // #233 float(entity ent) isbackbuffered PF_rotatevectorsbytag, // #234 PF_rotatevectorsbyangles, // #235 PF_rotatevectorsbymatrix, // #236 PF_skinforname, // #237 PF_shaderforname, // #238 PF_cl_te_bloodqw, // #239 void te_bloodqw(vector org[, float count]) (FTE_TE_STANDARDEFFECTBUILTINS) //240 PF_FixTen, //250 PF_FixTen, //260 PF_FixTen, //270 PF_FixTen, //280 PF_FixTen, //290 PF_FixTen, //300 PF_R_ClearScene, // #300 void() clearscene (EXT_CSQC) PF_R_AddEntityMask, // #301 void(float mask) addentities (EXT_CSQC) PF_R_AddEntity, // #302 void(entity ent) addentity (EXT_CSQC) PF_R_SetViewFlag, // #303 float(float property, ...) setproperty (EXT_CSQC) PF_R_RenderScene, // #304 void() renderscene (EXT_CSQC) PF_R_AddDynamicLight, // #305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC) PF_R_PolygonBegin, // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???) PF_R_PolygonVertex, // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???) PF_R_PolygonEnd, // #308 void() R_EndPolygon (EXT_CSQC_???) PF_Fixme, // #309 //310 //maths stuff that uses the current view settings. PF_cs_unproject, // #310 vector (vector v) unproject (EXT_CSQC) PF_cs_project, // #311 vector (vector v) project (EXT_CSQC) PF_Fixme, // #312 PF_Fixme, // #313 PF_Fixme, // #314 //2d (immediate) operations PF_CL_drawline, // #315 void(float width, vector pos1, vector pos2) drawline (EXT_CSQC) PF_CL_is_cached_pic, // #316 float(string name) iscachedpic (EXT_CSQC) PF_CL_precache_pic, // #317 string(string name, float trywad) precache_pic (EXT_CSQC) PF_CL_drawgetimagesize, // #318 vector(string picname) draw_getimagesize (EXT_CSQC) PF_CL_free_pic, // #319 void(string name) freepic (EXT_CSQC) //320 PF_CL_drawcharacter, // #320 float(vector position, float character, vector scale, vector rgb, float alpha [, float flag]) drawcharacter (EXT_CSQC, [EXT_CSQC_???]) PF_CL_drawstring, // #321 float(vector position, string text, vector scale, vector rgb, float alpha [, float flag]) drawstring (EXT_CSQC, [EXT_CSQC_???]) PF_CL_drawpic, // #322 float(vector position, string pic, vector size, vector rgb, float alpha [, float flag]) drawpic (EXT_CSQC, [EXT_CSQC_???]) PF_CL_drawfill, // #323 float(vector position, vector size, vector rgb, float alpha [, float flag]) drawfill (EXT_CSQC, [EXT_CSQC_???]) PF_CL_drawsetcliparea, // #324 void(float x, float y, float width, float height) drawsetcliparea (EXT_CSQC_???) PF_CL_drawresetcliparea, // #325 void(void) drawresetcliparea (EXT_CSQC_???) PF_Fixme, // #326 PF_Fixme, // #327 PF_Fixme, // #328 PF_Fixme, // #329 //330 PF_cs_getstatf, // #330 float(float stnum) getstatf (EXT_CSQC) PF_cs_getstati, // #331 float(float stnum) getstati (EXT_CSQC) PF_cs_getstats, // #332 string(float firststnum) getstats (EXT_CSQC) PF_cs_SetModelIndex, // #333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC) PF_cs_ModelnameForIndex, // #334 string(float mdlindex) modelnameforindex (EXT_CSQC) PF_cs_particlesloaded, // #335 float(string effectname) particleeffectnum (EXT_CSQC) PF_cs_trailparticles, // #336 void(entity ent, float effectnum, vector start, vector end) trailparticles (EXT_CSQC), PF_cs_pointparticles, // #337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC) PF_cl_cprint, // #338 void(string s) cprint (EXT_CSQC) PF_print, // #339 void(string s) print (EXT_CSQC) //340 PF_cl_keynumtostring, // #340 string(float keynum) keynumtostring (EXT_CSQC) PF_cl_stringtokeynum, // #341 float(string keyname) stringtokeynum (EXT_CSQC) PF_cl_getkeybind, // #342 string(float keynum) getkeybind (EXT_CSQC) PF_Fixme, // #343 PF_Fixme, // #344 PF_cs_getinputstate, // #345 float(float framenum) getinputstate (EXT_CSQC) PF_cs_setsensativityscaler, // #346 void(float sens) setsensitivityscaler (EXT_CSQC) PF_cs_runplayerphysics, // #347 void() runstandardplayerphysics (EXT_CSQC) PF_cs_getplayerkey, // #348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC) PF_cl_playingdemo, // #349 float() isdemo (EXT_CSQC) //350 PF_cl_runningserver, // #350 float() isserver (EXT_CSQC) PF_cs_setlistener, // #351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC) PF_cs_registercommand, // #352 void(string cmdname) registercommand (EXT_CSQC) PF_WasFreed, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too) PF_cs_serverkey, // #354 string(string key) serverkey; PF_Fixme, // #355 PF_Fixme, // #356 PF_Fixme, // #357 PF_Fixme, // #358 PF_Fixme, // #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) PF_ReadByte, // #360 float() readbyte (EXT_CSQC) PF_ReadChar, // #361 float() readchar (EXT_CSQC) PF_ReadShort, // #362 float() readshort (EXT_CSQC) PF_ReadLong, // #363 float() readlong (EXT_CSQC) PF_ReadCoord, // #364 float() readcoord (EXT_CSQC) PF_ReadAngle, // #365 float() readangle (EXT_CSQC) PF_ReadString, // #366 string() readstring (EXT_CSQC) PF_ReadFloat, // #367 string() readfloat (EXT_CSQC) PF_Fixme, // #368 PF_Fixme, // #369 //370 PF_FixTen, //380 PF_FixTen, //390 PF_FixTen, //400 PF_cs_copyentity, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY) PF_NoCSQC, // #401 void(entity cl, float colours) setcolors (DP_SV_SETCOLOR) (don't implement) PF_findchain, // #402 entity(string field, string match) findchain (DP_QC_FINDCHAIN) PF_findchainfloat, // #403 entity(float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT) PF_cl_effect, // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT) PF_cl_te_blooddp, // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD) PF_cl_te_bloodshower, // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER) PF_cl_te_explosionrgb, // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB) PF_cl_te_particlecube, // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE) PF_cl_te_particlerain, // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN) PF_cl_te_particlesnow, // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW) PF_cl_te_spark, // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK) PF_cl_te_gunshotquad, // #412 void(vector org) te_gunshotquad (DP_TE_QUADEFFECTS1) PF_cl_te_spikequad, // #413 void(vector org) te_spikequad (DP_TE_QUADEFFECTS1) PF_cl_te_superspikequad,// #414 void(vector org) te_superspikequad (DP_TE_QUADEFFECTS1) PF_cl_te_explosionquad, // #415 void(vector org) te_explosionquad (DP_TE_QUADEFFECTS1) PF_cl_te_smallflash, // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH) PF_cl_te_customflash, // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH) PF_cl_te_gunshot, // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_spike, // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_superspike, // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_explosion, // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_tarexplosion, // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_wizspike, // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_knightspike, // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_lavasplash, // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_teleport, // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_explosion2, // #427 void(vector org, float color, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_lightning1, // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_lightning2, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_lightning3, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS) PF_cl_te_beam, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS) PF_cs_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS) PF_cl_te_plasmaburn, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN) PF_Fixme, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE) PF_Fixme, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE) PF_Fixme, // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE) PF_Fixme, // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE) PF_Fixme, // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE) PF_Fixme, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE) PF_NoCSQC, // #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIENTCOMMAND) (don't implement) PF_Tokenize, // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND) PF_ArgV, // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND) PS_cs_setattachment, // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS) PF_search_begin, // #444 float search_begin(string pattern, float caseinsensitive, float quiet) (DP_QC_FS_SEARCH) PF_search_end, // #445 void search_end(float handle) (DP_QC_FS_SEARCH) PF_search_getsize, // #446 float search_getsize(float handle) (DP_QC_FS_SEARCH) PF_search_getfilename, // #447 string search_getfilename(float handle, float num) (DP_QC_FS_SEARCH) PF_cvar_string, // #448 string(float n) cvar_string (DP_QC_CVAR_STRING) PF_FindFlags, // #449 entity(entity start, .entity fld, float match) findflags (DP_QC_FINDFLAGS) PF_findchainflags, // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS) PF_cs_gettagindex, // #451 float(entity ent, string tagname) gettagindex (DP_MD3_TAGSINFO) PF_cs_gettaginfo, // #452 vector(entity ent, float tagindex) gettaginfo (DP_MD3_TAGSINFO) PF_NoCSQC, // #453 void(entity player) dropclient (DP_QC_BOTCLIENT) (don't implement) PF_NoCSQC, // #454 entity() spawnclient (DP_QC_BOTCLIENT) (don't implement) PF_NoCSQC, // #455 float(entity client) clienttype (DP_QC_BOTCLIENT) (don't implement) PF_Fixme, // #456 PF_Fixme, // #457 PF_Fixme, // #458 PF_Fixme, // #459 //460 PF_FixTen, //470 PF_FixTen, //480 PF_FixTen, //490 PF_FixTen, //500 PF_FixTen, }; static int csqc_numbuiltins = sizeof(csqc_builtins)/sizeof(csqc_builtins[0]); static jmp_buf csqc_abort; static progparms_t csqcprogparms; int COM_FileSize(char *path); pbool QC_WriteFile(char *name, void *data, int len); void *VARGS PR_CB_Malloc(int size); //these functions should be tracked by the library reliably, so there should be no need to track them ourselves. void VARGS PR_CB_Free(void *mem); //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); file = COM_LoadStackFile(newname, buffer, bufsize); if (file) if (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 (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); file = COM_LoadTempFile (newname); if (file) if (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 (Com_BlockChecksum(file, com_filesize) != csqcchecksum) return -1; //not valid } return com_filesize; } return COM_FileSize(path); } double csqctime; qboolean CSQC_Init (unsigned int checksum) { csqcchecksum = checksum; CSQC_Shutdown(); if (!qrenderer) { return false; } if (cl_nocsqc.value) return false; 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 = csqc_builtins;//builtin_t *globalbuiltins; //these are available to all progs csqcprogparms.numglobalbuiltins = csqc_numbuiltins; csqcprogparms.autocompile = PR_NOCOMPILE;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile; csqcprogparms.gametime = &csqctime; csqcprogparms.sv_edicts = (edict_t **)&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; 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); 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 EDICT_NUM(csqcprogs, 0)->readonly = true; EDICT_NUM(csqcprogs, 0)->isfree = false; if (csqcg.init_function) PR_ExecuteProgram(csqcprogs, csqcg.init_function); Con_Printf("Loaded csqc\n"); } return true; //success! } 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 CSQC_RegisterCvarsAndThings(void) { Cmd_AddCommand("coredump_csqc", CSQC_CoreDump); 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 = cls.netchan.incoming_sequence; if (csqcg.intermission) *csqcg.intermission = cl.intermission; if (csqcg.time) *csqcg.time = Sys_DoubleTime(); CSQC_RunThreads(); //wake up any qc threads 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; char *str; if (!csqcprogs || !csqcg.console_command) return false; str = PF_TempStr(csqcprogs); Q_strncpyz(str, cmd, MAXTEMPBUFFERLEN); pr_globals = PR_globals(csqcprogs, PR_CURRENT); (((string_t *)pr_globals)[OFS_PARM0] = PR_SetString(csqcprogs, str)); PR_ExecuteProgram (csqcprogs, csqcg.console_command); return G_FLOAT(OFS_RETURN); } qboolean CSQC_StuffCmd(char *cmd) { void *pr_globals; char *str; if (!csqcprogs || !csqcg.parse_stuffcmd) return false; str = PF_TempStr(csqcprogs); Q_strncpyz(str, cmd, MAXTEMPBUFFERLEN); pr_globals = PR_globals(csqcprogs, PR_CURRENT); (((string_t *)pr_globals)[OFS_PARM0] = PR_SetString(csqcprogs, str)); PR_ExecuteProgram (csqcprogs, csqcg.parse_stuffcmd); return true; } qboolean CSQC_CenterPrint(char *cmd) { void *pr_globals; char *str; if (!csqcprogs || !csqcg.parse_centerprint) return false; str = PF_TempStr(csqcprogs); Q_strncpyz(str, cmd, MAXTEMPBUFFERLEN); pr_globals = PR_globals(csqcprogs, PR_CURRENT); (((string_t *)pr_globals)[OFS_PARM0] = PR_SetString(csqcprogs, str)); PR_ExecuteProgram (csqcprogs, csqcg.parse_centerprint); return G_FLOAT(OFS_RETURN); } //this protocol allows up to 32767 edicts. #ifdef PEXT_CSQC int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation) { void *pr_globals; char *str; csqcedict_t *ent; if (!csqcprogs || !csqcg.serversound) return false; if (entnum >= maxcsqcentities) { maxcsqcentities = entnum+64; csqcent = BZ_Realloc(csqcent, sizeof(*csqcent)*maxcsqcentities); } ent = csqcent[entnum]; if (!ent) return false; pr_globals = PR_globals(csqcprogs, PR_CURRENT); str = PF_TempStr(csqcprogs); Q_strncpyz(str, soundname, MAXTEMPBUFFERLEN); *csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent); G_FLOAT(OFS_PARM0) = channel; G_INT(OFS_PARM1) = PR_SetString(csqcprogs, str); 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); } 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); if (csqcg.time) *csqcg.time = Sys_DoubleTime(); if (csqcg.clientcommandframe) *csqcg.clientcommandframe = cls.netchan.outgoing_sequence; if (csqcg.servercommandframe) *csqcg.servercommandframe = cls.netchan.incoming_sequence; for(;;) { entnum = MSG_ReadShort(); if (!entnum || msg_badread) break; if (entnum & 0x8000) { //remove entnum &= ~0x8000; if (!entnum) Host_EndGame("CSQC cannot remove world!\n"); if (entnum >= maxcsqcentities) { maxcsqcentities = entnum+64; csqcent = BZ_Realloc(csqcent, sizeof(*csqcent)*maxcsqcentities); } 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 { if (entnum >= maxcsqcentities) { maxcsqcentities = entnum+64; csqcent = BZ_Realloc(csqcent, sizeof(*csqcent)*maxcsqcentities); } 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", svprogfuncs->stringtable+EDICT_NUM(svprogfuncs, entnum)->v->classname); } #endif } msg_readcount = packetstart+packetsize; //leetism. } } } } #endif #endif