return cliptrace;
}
-float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent)
+float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent, qboolean csqcents)
{
float maxfrac, maxrealfrac;
- int n;
+ int n, entsnum;
+ entity_t *entlist;
+ unsigned char *entactivelist;
entity_render_t *ent;
float tracemins[3], tracemaxs[3];
trace_t trace;
tracemins[2] = min(start[2], end[2]);
tracemaxs[2] = max(start[2], end[2]);
+ if(csqcents)
+ {
+ entlist = cl_csqcentities;
+ entactivelist = cl_csqcentities_active;
+ entsnum = cl_num_csqcentities;
+ }
+ else
+ {
+ entlist = cl_entities;
+ entactivelist = cl_entities_active;
+ entsnum = cl_num_entities;
+ }
+
// look for embedded bmodels
- for (n = 0;n < cl_num_entities;n++)
+ for (n = 0;n < entsnum;n++)
{
- if (!cl_entities_active[n])
+ if (!entactivelist[n])
continue;
- ent = &cl_entities[n].render;
+ ent = &entlist[n].render;
if (!BoxesOverlap(ent->mins, ent->maxs, tracemins, tracemaxs))
continue;
if (!ent->model || !ent->model->TraceBox)
if ((ent->flags & RENDER_EXTERIORMODEL) && !chase_active.integer)
continue;
// if transparent and not selectable, skip entity
- if (!(cl_entities[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST))))
+ if (!(entlist[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST))))
continue;
if (ent == ignoreent)
continue;
#define CL_COLLISION_H
trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers);
-float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent);
+float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent, qboolean csqcents);
void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius);
int CL_PointQ1Contents(const vec3_t p);
int CL_PointSuperContents(const vec3_t p);
// rights reserved.
#include "quakedef.h"
+#include "csprogs.h"
/*
===============================================================================
old_mouse_y = my;
// if not in menu, apply mouse move to viewangles/movement
- if (in_client_mouse)
+ if (!cl.csqc_wantsmousemove && in_client_mouse)
{
if (cl_prydoncursor.integer)
{
VectorSet(temp, cl.cmd.cursor_screen[2] * scale[2], cl.cmd.cursor_screen[0] * scale[0], cl.cmd.cursor_screen[1] * scale[1]);
Matrix4x4_Transform(&r_refdef.viewentitymatrix, temp, cl.cmd.cursor_end);
// trace from view origin to the cursor
- cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl_entities[cl.playerentity].render : NULL);
+ cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl_entities[cl.playerentity].render : NULL, false);
// makes sparks where cursor is
//CL_SparkShower(cl.cmd.cursor_impact, cl.cmd.cursor_normal, 5, 0);
}
if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
if (cl.cmd.cursor_screen[1] >= 1) bits |= 64;
+ csqc_buttons = bits;
+
// always dump the first two messages, because they may contain leftover inputs from the last level
if (++cl.movemessages >= 2)
{
#include "cl_collision.h"
#include "cl_video.h"
#include "image.h"
+#include "csprogs.h"
#include "r_shadow.h"
// we need to declare some mouse variables here, because the menu system
// references them even when on a unix system.
+cvar_t csqc_progname = {0, "csqc_progname","csprogs.dat"}; //[515]: csqc crc check and right csprogs name according to progs.dat
+cvar_t csqc_progcrc = {CVAR_READONLY, "csqc_progcrc","0"};
+
cvar_t cl_shownet = {0, "cl_shownet","0"};
cvar_t cl_nolerp = {0, "cl_nolerp", "0"};
client_state_t cl;
int cl_max_entities;
+int cl_max_csqcentities;
int cl_max_static_entities;
int cl_max_temp_entities;
int cl_max_effects;
int cl_activedlights;
entity_t *cl_entities;
+entity_t *cl_csqcentities; //[515]: csqc
unsigned char *cl_entities_active;
+unsigned char *cl_csqcentities_active; //[515]: csqc
entity_t *cl_static_entities;
entity_t *cl_temp_entities;
cl_effect_t *cl_effects;
int *cl_brushmodel_entities;
int cl_num_entities;
+int cl_num_csqcentities; //[515]: csqc
int cl_num_static_entities;
int cl_num_temp_entities;
int cl_num_brushmodel_entities;
int i;
if (cl_entities) Mem_Free(cl_entities);cl_entities = NULL;
+ if (cl_csqcentities) Mem_Free(cl_csqcentities);cl_csqcentities = NULL; //[515]: csqc
if (cl_entities_active) Mem_Free(cl_entities_active);cl_entities_active = NULL;
+ if (cl_csqcentities_active) Mem_Free(cl_csqcentities_active);cl_csqcentities_active = NULL; //[515]: csqc
if (cl_static_entities) Mem_Free(cl_static_entities);cl_static_entities = NULL;
if (cl_temp_entities) Mem_Free(cl_temp_entities);cl_temp_entities = NULL;
if (cl_effects) Mem_Free(cl_effects);cl_effects = NULL;
SZ_Clear (&cls.message);
cl_num_entities = 0;
+ cl_num_csqcentities = 0; //[515]: csqc
cl_num_static_entities = 0;
cl_num_temp_entities = 0;
cl_num_brushmodel_entities = 0;
// tweak these if the game runs out
cl_max_entities = 256;
+ cl_max_csqcentities = 256; //[515]: csqc
cl_max_static_entities = 256;
cl_max_temp_entities = 512;
cl_max_effects = 256;
cl_activedlights = 0;
cl_entities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_entities * sizeof(entity_t));
+ cl_csqcentities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_csqcentities * sizeof(entity_t)); //[515]: csqc
cl_entities_active = (unsigned char *)Mem_Alloc(cl_mempool, cl_max_brushmodel_entities * sizeof(unsigned char));
+ cl_csqcentities_active = (unsigned char *)Mem_Alloc(cl_mempool, cl_max_brushmodel_entities * sizeof(unsigned char)); //[515]: csqc
cl_static_entities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_static_entities * sizeof(entity_t));
cl_temp_entities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_temp_entities * sizeof(entity_t));
cl_effects = (cl_effect_t *)Mem_Alloc(cl_mempool, cl_max_effects * sizeof(cl_effect_t));
cl_entities[i].state_current = defaultstate;
}
+ for (i = 0;i < cl_max_csqcentities;i++)
+ {
+ cl_csqcentities[i].state_baseline = defaultstate; //[515]: csqc
+ cl_csqcentities[i].state_previous = defaultstate; //[515]: csqc
+ cl_csqcentities[i].state_current = defaultstate; //[515]: csqc
+ cl_csqcentities[i].csqc = true;
+ cl_csqcentities[i].state_current.number = -i;
+ }
+
if (gamemode == GAME_NEXUIZ)
{
VectorSet(cl_playerstandmins, -16, -16, -24);
}
}
+void CL_ExpandCSQCEntities(int num)
+{
+ int i, oldmaxentities;
+ entity_t *oldentities;
+ if (num >= cl_max_csqcentities)
+ {
+ if (!cl_csqcentities)
+ Sys_Error("CL_ExpandCSQCEntities: cl_csqcentities not initialized\n");
+ if (num >= MAX_EDICTS)
+ Host_Error("CL_ExpandCSQCEntities: num %i >= %i\n", num, MAX_EDICTS);
+ oldmaxentities = cl_max_csqcentities;
+ oldentities = cl_csqcentities;
+ cl_max_csqcentities = (num & ~255) + 256;
+ cl_csqcentities = Mem_Alloc(cl_mempool, cl_max_csqcentities * sizeof(entity_t));
+ memcpy(cl_csqcentities, oldentities, oldmaxentities * sizeof(entity_t));
+ Mem_Free(oldentities);
+ for (i = oldmaxentities;i < cl_max_csqcentities;i++)
+ {
+ cl_csqcentities[i].state_baseline = defaultstate;
+ cl_csqcentities[i].state_previous = defaultstate;
+ cl_csqcentities[i].state_current = defaultstate;
+ cl_csqcentities[i].csqc = true;
+ cl_csqcentities[i].state_current.number = -i;
+ }
+ }
+}
+
+void CL_VM_ShutDown (void);
/*
=====================
CL_Disconnect
Con_DPrintf("CL_Disconnect\n");
+ CL_VM_ShutDown();
// stop sounds (especially looping!)
S_StopAllSounds ();
{
e->persistent.linkframe = entitylinkframenumber;
// skip inactive entities and world
- if (!e->state_current.active || e == cl_entities)
+ if (!e->state_current.active || e == cl_entities || e == cl_csqcentities)
return;
e->render.alpha = e->state_current.alpha * (1.0f / 255.0f); // FIXME: interpolate?
e->render.scale = e->state_current.scale * (1.0f / 16.0f); // FIXME: interpolate?
e->render.skinnum = e->state_current.skin;
if (e->render.flags & RENDER_VIEWMODEL && !e->state_current.tagentity)
{
- if (!r_drawviewmodel.integer || chase_active.integer || envmap)
+ if (!r_drawviewmodel.integer || chase_active.integer || envmap)// || csqc_loaded)
return;
- if (cl.viewentity)
- CL_LinkNetworkEntity(cl_entities + cl.viewentity);
- matrix = &viewmodelmatrix;
- if (e == &cl.viewent && cl_entities[cl.viewentity].state_current.active)
+ if (!e->csqc)
{
- e->state_current.alpha = cl_entities[cl.viewentity].state_current.alpha;
- e->state_current.effects = EF_NOSHADOW | (cl_entities[cl.viewentity].state_current.effects & (EF_ADDITIVE | EF_REFLECTIVE | EF_FULLBRIGHT | EF_NODEPTHTEST));
+ if (cl.viewentity)
+ CL_LinkNetworkEntity(cl_entities + cl.viewentity);
+ if (e == &cl.viewent && cl_entities[cl.viewentity].state_current.active)
+ {
+ e->state_current.alpha = cl_entities[cl.viewentity].state_current.alpha;
+ e->state_current.effects = EF_NOSHADOW | (cl_entities[cl.viewentity].state_current.effects & (EF_ADDITIVE | EF_REFLECTIVE | EF_FULLBRIGHT | EF_NODEPTHTEST));
+ }
}
+ matrix = &viewmodelmatrix;
}
else
{
// if the tag entity is currently impossible, skip it
- if (e->state_current.tagentity >= cl_num_entities)
- return;
- t = cl_entities + e->state_current.tagentity;
+ if (!e->csqc)
+ {
+ if (e->state_current.tagentity >= cl_num_entities)
+ return;
+ t = cl_entities + e->state_current.tagentity;
+ }
+ else
+ {
+ if (e->state_current.tagentity >= cl_num_csqcentities)
+ return;
+ t = cl_csqcentities + e->state_current.tagentity;
+ }
// if the tag entity is inactive, skip it
if (!t->state_current.active)
return;
// movement lerp
// if it's the player entity, update according to client movement
- if (e == cl_entities + cl.playerentity && cl.movement)
+ if (e == cl_entities + cl.playerentity && cl.movement)// && !e->csqc)
{
lerp = (cl.time - cl.mtime[1]) / (cl.mtime[0] - cl.mtime[1]);
lerp = bound(0, lerp, 1);
}
// model setup and some modelflags
- e->render.model = cl.model_precache[e->state_current.modelindex];
+ if(e->state_current.modelindex < MAX_MODELS)
+ e->render.model = cl.model_precache[e->state_current.modelindex];
+ else
+ e->render.model = cl.csqc_model_precache[65536-e->state_current.modelindex];
if (e->render.model)
{
// if model is alias or this is a tenebrae-like dlight, reverse pitch direction
if (gamemode == GAME_TENEBRAE && e->render.model && e->render.model->type == mod_sprite)
e->render.effects |= EF_ADDITIVE;
// player model is only shown with chase_active on
+ if (!e->csqc)
if (e->state_current.number == cl.viewentity)
e->render.flags |= RENDER_EXTERIORMODEL;
// transparent stuff can't be lit during the opaque stage
&& (!(e->render.flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cl.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
e->render.flags |= RENDER_SHADOW;
// as soon as player is known we can call V_CalcRefDef
+ if (!csqc_loaded)
if (e->state_current.number == cl.viewentity)
V_CalcRefdef();
if (e->render.model && e->render.model->name[0] == '*' && e->render.model->TraceBox)
r_refdef.worldmodel = cl.worldmodel;
}
+void CL_RelinkCSQCWorld(void) //[515]: csqc
+{
+ entity_t *ent = &cl_csqcentities[0];
+ if(!csqc_loaded)
+ return;
+// cl_brushmodel_entities[cl_num_brushmodel_entities++] = 0;
+ // FIXME: this should be done at load
+ Matrix4x4_CreateIdentity(&ent->render.matrix);
+ Matrix4x4_CreateIdentity(&ent->render.inversematrix);
+ R_LerpAnimation(&ent->render);
+ CL_BoundingBoxForEntity(&ent->render);
+ ent->render.flags = RENDER_SHADOW;
+ if (!r_fullbright.integer)
+ ent->render.flags |= RENDER_LIGHT;
+ VectorSet(ent->render.colormod, 1, 1, 1);
+// r_refdef.worldentity = &ent->render;
+// r_refdef.worldmodel = cl.worldmodel;
+}
+
static void CL_RelinkStaticEntities(void)
{
int i;
CL_RelinkEntities
===============
*/
-static void CL_RelinkNetworkEntities(void)
+static void CL_RelinkNetworkEntities(int drawmask)
{
entity_t *ent;
- int i;
+ int i, k;
- ent = &cl.viewent;
- ent->state_previous = ent->state_current;
- ent->state_current = defaultstate;
- ent->state_current.time = cl.time;
- ent->state_current.number = -1;
- ent->state_current.active = true;
- ent->state_current.modelindex = cl.stats[STAT_WEAPON];
- ent->state_current.frame = cl.stats[STAT_WEAPONFRAME];
- ent->state_current.flags = RENDER_VIEWMODEL;
- if ((cl.stats[STAT_HEALTH] <= 0 && cl_deathnoviewmodel.integer) || cl.intermission)
- ent->state_current.modelindex = 0;
- else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
+ if(!csqc_loaded)
{
- if (gamemode == GAME_TRANSFUSION)
- ent->state_current.alpha = 128;
- else
+ ent = &cl.viewent;
+ ent->state_previous = ent->state_current;
+ ent->state_current = defaultstate;
+ ent->state_current.time = cl.time;
+ ent->state_current.number = -1;
+ ent->state_current.active = true;
+ ent->state_current.modelindex = cl.stats[STAT_WEAPON];
+ ent->state_current.frame = cl.stats[STAT_WEAPONFRAME];
+ ent->state_current.flags = RENDER_VIEWMODEL;
+ if ((cl.stats[STAT_HEALTH] <= 0 && cl_deathnoviewmodel.integer) || cl.intermission)
ent->state_current.modelindex = 0;
- }
+ else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
+ {
+ if (gamemode == GAME_TRANSFUSION)
+ ent->state_current.alpha = 128;
+ else
+ ent->state_current.modelindex = 0;
+ }
- // reset animation interpolation on weaponmodel if model changed
- if (ent->state_previous.modelindex != ent->state_current.modelindex)
- {
- ent->render.frame = ent->render.frame1 = ent->render.frame2 = ent->state_current.frame;
- ent->render.frame1time = ent->render.frame2time = cl.time;
- ent->render.framelerp = 1;
+ // reset animation interpolation on weaponmodel if model changed
+ if (ent->state_previous.modelindex != ent->state_current.modelindex)
+ {
+ ent->render.frame = ent->render.frame1 = ent->render.frame2 = ent->state_current.frame;
+ ent->render.frame1time = ent->render.frame2time = cl.time;
+ ent->render.framelerp = 1;
+ }
}
// start on the entity after the world
entitylinkframenumber++;
- for (i = 1;i < cl_num_entities;i++)
+ if(drawmask & ENTMASK_ENGINE || !csqc_loaded)
{
- if (cl_entities_active[i])
+ for (i = 1;i < cl_num_entities;i++)
{
- ent = cl_entities + i;
- if (ent->state_current.active)
- CL_LinkNetworkEntity(ent);
- else
- cl_entities_active[i] = false;
+ if (cl_entities_active[i])
+ {
+ ent = cl_entities + i;
+ if (!(drawmask & ENTMASK_ENGINEVIEWMODELS))
+ if (ent->state_current.flags & RENDER_VIEWMODEL) //[515]: csqc drawmask
+ {
+ cl_entities_active[i] = false;
+ continue;
+ }
+ if (ent->state_current.active)
+ CL_LinkNetworkEntity(ent);
+ else
+ cl_entities_active[i] = false;
+ }
}
}
- CL_LinkNetworkEntity(&cl.viewent);
+
+ //[515]: csqc
+ if(csqc_loaded)
+ {
+ for (i=1,k=cl_num_csqcentities;k;i++)
+ {
+ if (cl_csqcentities_active[i])
+ {
+ --k;
+ ent = cl_csqcentities + i;
+ if (ent->state_current.active)
+ CL_LinkNetworkEntity(ent);
+ else
+ cl_csqcentities_active[i] = false;
+ }
+ }
+ }
+ else
+ CL_LinkNetworkEntity(&cl.viewent);
}
static void CL_RelinkEffects(void)
ent->render.frame2time = e->frame2time;
// normal stuff
- ent->render.model = cl.model_precache[e->modelindex];
+ if(e->modelindex < MAX_MODELS)
+ ent->render.model = cl.model_precache[e->modelindex];
+ else
+ ent->render.model = cl.csqc_model_precache[65536-e->modelindex];
ent->render.frame = ent->render.frame2;
ent->render.colormap = -1; // no special coloring
ent->render.alpha = 1;
}
}
+void CSQC_RelinkAllEntities (int drawmask)
+{
+ CL_RelinkNetworkEntities(drawmask);
+ if(drawmask & ENTMASK_ENGINE)
+ {
+ // move particles
+ CL_MoveParticles();
+ R_MoveExplosions();
+ }
+
+ // link stuff
+ CL_RelinkWorld();
+ CL_RelinkCSQCWorld(); //[515]: csqc
+ if(drawmask & ENTMASK_ENGINE)
+ {
+ CL_RelinkStaticEntities();
+ CL_RelinkBeams();
+ CL_RelinkEffects();
+ }
+}
+
/*
===============
CL_ReadFromServer
===============
*/
extern void CL_ClientMovement_Replay();
+
int CL_ReadFromServer(void)
{
CL_ReadDemoMessage();
// relink network entities (note: this sets up the view!)
CL_ClientMovement_Replay();
- CL_RelinkNetworkEntities();
-
- // move particles
- CL_MoveParticles();
- R_MoveExplosions();
-
- // link stuff
- CL_RelinkWorld();
- CL_RelinkStaticEntities();
- CL_RelinkBeams();
- CL_RelinkEffects();
+ if(!csqc_loaded) //[515]: csqc
+ {
+ CL_RelinkNetworkEntities(65536);
+
+ // move particles
+ CL_MoveParticles();
+ R_MoveExplosions();
+
+ // link stuff
+ CL_RelinkWorld();
+ CL_RelinkCSQCWorld(); //[515]: csqc
+ CL_RelinkStaticEntities();
+ CL_RelinkBeams();
+ CL_RelinkEffects();
+ }
+ else
+ csqc_frame = true;
// run cgame code (which can add more entities)
CL_CGVM_Frame();
//
// register our commands
//
+ Cvar_RegisterVariable (&csqc_progname);
+ Cvar_RegisterVariable (&csqc_progcrc);
+
Cvar_RegisterVariable (&cl_upspeed);
Cvar_RegisterVariable (&cl_forwardspeed);
Cvar_RegisterVariable (&cl_backspeed);
#include "quakedef.h"
#include "cdaudio.h"
#include "cl_collision.h"
+#include "csprogs.h"
char *svc_strings[128] =
{
"svc_spawnbaseline2", // 55 // short modelindex instead of byte
"svc_spawnstatic2", // 56 // short modelindex instead of byte
"svc_entities", // 57 // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata
- "svc_unusedlh3", // 58
+ "svc_csqcentities", // 58 // [short] entnum [variable length] entitydata ... [short] 0x0000
"svc_spawnstaticsound2", // 59 // [coord3] [short] samp [byte] vol [byte] aten
};
// disable until we get textures for it
R_ResetSkyBox();
+ memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache)); //[515]: csqc
memset(cl.model_precache, 0, sizeof(cl.model_precache));
memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
if (!s->active)
return;
- if (s->modelindex >= MAX_MODELS)
- Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)", s->modelindex, MAX_MODELS);
+ if (s->modelindex >= MAX_MODELS && (65536-s->modelindex) >= MAX_MODELS)
+ Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)\n", s->modelindex, MAX_MODELS);
// colormap is client index + 1
if ((!s->flags & RENDER_COLORMAPPED) && s->colormap > cl.maxclients)
cl.stats[STAT_ITEMS] = MSG_ReadLong ();
cl.onground = (bits & SU_ONGROUND) != 0;
+ csqc_onground = cl.onground; //[515]: cause without this csqc will receive not right value on svc_print =/
cl.inwater = (bits & SU_INWATER) != 0;
if (cl.protocol == PROTOCOL_DARKPLACES5)
#define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf("%3i:%s\n", msg_readcount-1, x);
+//[515]: csqc
+void CL_VM_Init (void);
+qboolean CL_VM_Parse_TempEntity (void);
+void CL_VM_Parse_StuffCmd (const char *msg);
+void CL_VM_Parse_CenterPrint (const char *msg);
+void CSQC_AddPrintText (const char *msg);
+void CSQC_ReadEntities (void);
+//
static unsigned char cgamenetbuffer[65536];
/*
break;
case svc_print:
- Con_Print(MSG_ReadString());
+ CSQC_AddPrintText(MSG_ReadString()); //[515]: csqc
break;
case svc_centerprint:
- SCR_CenterPrint(MSG_ReadString ());
+ CL_VM_Parse_CenterPrint(MSG_ReadString ()); //[515]: csqc
break;
case svc_stufftext:
- Cbuf_AddText (MSG_ReadString ());
+ CL_VM_Parse_StuffCmd(MSG_ReadString ()); //[515]: csqc
break;
case svc_damage:
case svc_serverinfo:
CL_ParseServerInfo ();
+ CL_VM_Init(); //[515]: init csqc
break;
case svc_setangle:
CL_ParseStatic (true);
break;
case svc_temp_entity:
- CL_ParseTempEntity ();
+ if(!CL_VM_Parse_TempEntity())
+ CL_ParseTempEntity ();
break;
case svc_setpause:
else
EntityFrame5_CL_ReadFrame();
break;
+ case svc_csqcentities:
+ CSQC_ReadEntities();
+ break;
}
}
#include "cl_video.h"
#include "jpeg.h"
#include "cl_collision.h"
+#include "csprogs.h"
cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
cvar_t scr_fov = {CVAR_SAVE, "fov","90"}; // 1 - 170
r_refdef.drawqueuesize += dq->size;
}
-void DrawQ_Lines (drawqueuemesh_t *mesh, int flags)
+void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
{
int size;
void *p;
}
R_Shadow_EditLights_DrawSelectedLightProperties();
- SCR_DrawConsole();
+ if(!csqc_loaded)
+ SCR_DrawConsole();
SCR_DrawBrand();
void DrawQ_ResetClipArea(void);
// draw a line
void DrawQ_Line(float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags);
+// draw a line loop
+void DrawQ_LineLoop(drawqueuemesh_t *mesh, int flags);
void SHOWLMP_decodehide(void);
void SHOWLMP_decodeshow(void);
typedef struct entity_s
{
+ qboolean csqc;
// baseline state (default values)
entity_state_t state_baseline;
// previous state (interpolating from this)
}
client_movementqueue_t;
+//[515]: csqc
+typedef struct
+{
+ qboolean drawworld;
+ qboolean drawenginesbar;
+ qboolean drawcrosshair;
+}csqc_vidvars_t;
+
//
// the client_state_t structure is wiped completely at every
// server signon
float driftmove;
double laststop;
+//[515]: added for csqc purposes
+ float sensitivityscale;
+ csqc_vidvars_t csqc_vidvars; //[515]: these parms must be set to true by default
+ qboolean csqc_wantsmousemove;
+ struct model_s *csqc_model_precache[MAX_MODELS];
+
// local amount for smoothing stepups
//float crouch;
extern cvar_t cl_autofire;
+extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
+extern cvar_t csqc_progcrc;
+
extern cvar_t cl_shownet;
extern cvar_t cl_nolerp;
// these are updated by CL_ClearState
extern int cl_num_entities;
+extern int cl_num_csqcentities;
extern int cl_num_static_entities;
extern int cl_num_temp_entities;
extern int cl_num_brushmodel_entities;
extern mempool_t *cl_mempool;
extern entity_t *cl_entities;
+extern entity_t *cl_csqcentities;
extern unsigned char *cl_entities_active;
+extern unsigned char *cl_csqcentities_active;
extern entity_t *cl_static_entities;
extern entity_t *cl_temp_entities;
extern int *cl_brushmodel_entities;
/* file generated by qcc, do not modify */
+
+#ifndef CLPROGDEFS_H
+#define CLPROGDEFS_H
+
typedef struct cl_globalvars_s
{
- int pad[28];
+ int pad[28];
+ int self;
+ int other;
+ int world;
+ float time;
+ float frametime;
+ float player_localentnum;
+ float player_localnum;
+ float maxclients;
+ float clientcommandframe;
+ float servercommandframe;
+ string_t mapname;
+ vec3_t v_forward;
+ vec3_t v_up;
+ vec3_t v_right;
+ float trace_allsolid;
+ float trace_startsolid;
+ float trace_fraction;
+ vec3_t trace_endpos;
+ vec3_t trace_plane_normal;
+ float trace_plane_dist;
+ int trace_ent;
+ float trace_inopen;
+ float trace_inwater;
+ func_t CSQC_Init;
+ func_t CSQC_Shutdown;
+ func_t CSQC_InputEvent;
+ func_t CSQC_UpdateView;
+ func_t CSQC_ConsoleCommand;
+ vec3_t pmove_org;
+ vec3_t pmove_vel;
+ vec3_t pmove_mins;
+ vec3_t pmove_maxs;
+ float input_timelength;
+ vec3_t input_angles;
+ vec3_t input_movevalues;
+ float input_buttons;
+ float movevar_gravity;
+ float movevar_stopspeed;
+ float movevar_maxspeed;
+ float movevar_spectatormaxspeed;
+ float movevar_accelerate;
+ float movevar_airaccelerate;
+ float movevar_wateraccelerate;
+ float movevar_friction;
+ float movevar_waterfriction;
+ float movevar_entgravity;
} cl_globalvars_t;
-/*typedef struct cl_entvars_s
+typedef struct cl_entvars_s
{
-} cl_entvars_t;*/
+ float modelindex;
+ vec3_t absmin;
+ vec3_t absmax;
+ float entnum;
+ float drawmask;
+ func_t predraw;
+ float movetype;
+ float solid;
+ vec3_t origin;
+ vec3_t oldorigin;
+ vec3_t velocity;
+ vec3_t angles;
+ vec3_t avelocity;
+ string_t classname;
+ string_t model;
+ float frame;
+ float skin;
+ float effects;
+ vec3_t mins;
+ vec3_t maxs;
+ vec3_t size;
+ func_t touch;
+ func_t use;
+ func_t think;
+ func_t blocked;
+ float nextthink;
+ int chain;
+ string_t netname;
+ int enemy;
+ float flags;
+ float colormap;
+ int owner;
+} cl_entvars_t;
+
+#define CL_PROGHEADER_CRC 52195
-#define CL_PROGHEADER_CRC 12923
+#endif
--- /dev/null
+#include "prvm_cmds.h"
+#include "csprogs.h"
+#include "cl_collision.h"
+
+//============================================================================
+// Client
+//[515]: unsolved PROBLEMS
+//- finish player physics code (cs_runplayerphysics)
+//- fix R_AddDynamicLight
+//- EntWasFreed ?
+//- RF_DEPTHHACK is not like it should be
+//- add builtin that sets cl.viewangles instead of reading "input_angles" global
+//- finish lines support for R_Polygon***
+//- insert selecttraceline into traceline somehow
+
+//4 feature darkplaces csqc: add builtin to clientside qc for reading triangles of model meshes (useful to orient a ui along a triangle of a model mesh)
+//4 feature darkplaces csqc: add builtins to clientside qc for gl calls
+
+#ifndef PF_WARNING
+#define PF_WARNING(s) do{Con_Printf(s);PRVM_PrintState();return;}while(0)
+#endif
+
+//[515]: really need new list ?
+char *vm_cl_extensions =
+"DP_CON_SET "
+"DP_CON_SETA "
+"DP_CON_STARTMAP "
+"DP_EF_ADDITIVE "
+"DP_EF_BLUE "
+"DP_EF_FLAME "
+"DP_EF_FULLBRIGHT "
+"DP_EF_NODEPTHTEST "
+"DP_EF_NODRAW "
+"DP_EF_NOSHADOW "
+"DP_EF_RED "
+"DP_EF_STARDUST "
+"DP_ENT_ALPHA "
+"DP_ENT_CUSTOMCOLORMAP "
+"DP_ENT_GLOW "
+"DP_ENT_SCALE "
+"DP_GFX_EXTERNALTEXTURES "
+"DP_GFX_FOG "
+"DP_GFX_QUAKE3MODELTAGS "
+"DP_GFX_SKINFILES "
+"DP_GFX_SKYBOX "
+"DP_HALFLIFE_MAP "
+"DP_HALFLIFE_MAP_CVAR "
+"DP_HALFLIFE_SPRITE "
+"DP_INPUTBUTTONS "
+"DP_LITSPRITES "
+"DP_LITSUPPORT "
+"DP_MONSTERWALK "
+"DP_MOVETYPEBOUNCEMISSILE "
+"DP_MOVETYPEFOLLOW "
+"DP_QC_CHANGEPITCH "
+"DP_QC_COPYENTITY "
+"DP_QC_CVAR_STRING "
+"DP_QC_ETOS "
+"DP_QC_FINDCHAIN "
+"DP_QC_FINDCHAINFLAGS "
+"DP_QC_FINDCHAINFLOAT "
+"DP_QC_FINDFLAGS "
+"DP_QC_FINDFLOAT "
+"DP_QC_FS_SEARCH " // Black: same as in the menu qc
+"DP_QC_GETLIGHT "
+"DP_QC_GETSURFACE "
+"DP_QC_GETTAGINFO "
+"DP_QC_MINMAXBOUND "
+"DP_QC_MULTIPLETEMPSTRINGS "
+"DP_QC_RANDOMVEC "
+"DP_QC_SINCOSSQRTPOW "
+//"DP_QC_STRINGBUFFERS " //[515]: not needed ?
+"DP_QC_TRACEBOX "
+//"DP_QC_TRACETOSS "
+"DP_QC_TRACE_MOVETYPE_HITMODEL "
+"DP_QC_TRACE_MOVETYPE_WORLDONLY "
+"DP_QC_VECTORVECTORS "
+"DP_QUAKE2_MODEL "
+"DP_QUAKE2_SPRITE "
+"DP_QUAKE3_MAP "
+"DP_QUAKE3_MODEL "
+"DP_REGISTERCVAR "
+"DP_SND_DIRECTIONLESSATTNNONE "
+"DP_SND_FAKETRACKS "
+"DP_SND_OGGVORBIS "
+"DP_SND_STEREOWAV "
+"DP_SOLIDCORPSE "
+"DP_SPRITE32 "
+"DP_SV_EFFECT "
+"DP_SV_ROTATINGBMODEL "
+"DP_SV_SLOWMO "
+"DP_TE_BLOOD "
+"DP_TE_BLOODSHOWER "
+"DP_TE_CUSTOMFLASH "
+"DP_TE_EXPLOSIONRGB "
+"DP_TE_FLAMEJET "
+"DP_TE_PARTICLECUBE "
+"DP_TE_PARTICLERAIN "
+"DP_TE_PARTICLESNOW "
+"DP_TE_PLASMABURN "
+"DP_TE_QUADEFFECTS1 "
+"DP_TE_SMALLFLASH "
+"DP_TE_SPARK "
+"DP_TE_STANDARDEFFECTBUILTINS "
+"EXT_BITSHIFT "
+"EXT_CSQC "
+"FRIK_FILE "
+"KRIMZON_SV_PARSECLIENTCOMMAND "
+"NEH_CMD_PLAY2 "
+"NXQ_GFX_LETTERBOX "
+"PRYDON_CLIENTCURSOR "
+"TENEBRAE_GFX_DLIGHTS "
+"TW_SV_STEPCONTROL "
+"NEXUIZ_PLAYERMODEL "
+"NEXUIZ_PLAYERSKIN "
+;
+
+sfx_t *S_FindName(const char *name);
+int CL_PointQ1Contents(const vec3_t p);
+void PF_registercvar (void);
+int Sbar_GetPlayer (int index);
+void Sbar_SortFrags (void);
+void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius);
+void CL_ExpandCSQCEntities(int num);
+void CSQC_RelinkAllEntities (int drawmask);
+void CSQC_RelinkCSQCEntities (void);
+char *Key_GetBind (int key);
+
+
+
+
+
+// #1 void(vector ang) makevectors
+void VM_CL_makevectors (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_CL_makevectors);
+ AngleVectors (PRVM_G_VECTOR(OFS_PARM0), prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up);
+}
+
+// #2 void(entity e, vector o) setorigin
+void VM_CL_setorigin (void)
+{
+ prvm_edict_t *e;
+ float *org;
+
+ e = PRVM_G_EDICT(OFS_PARM0);
+ if (e == prog->edicts)
+ PF_WARNING("setorigin: can not modify world entity\n");
+ if (e->priv.required->free)
+ PF_WARNING("setorigin: can not modify free entity\n");
+ org = PRVM_G_VECTOR(OFS_PARM1);
+ VectorCopy (org, e->fields.client->origin);
+}
+
+// #3 void(entity e, string m) setmodel
+void VM_CL_setmodel (void)
+{
+ prvm_edict_t *e;
+ const char *m;
+ struct model_s *mod;
+ int i;
+
+ VM_SAFEPARMCOUNT(2, VM_CL_setmodel);
+
+ e = PRVM_G_EDICT(OFS_PARM0);
+ m = PRVM_G_STRING(OFS_PARM1);
+ for(i=0;i<MAX_MODELS;i++)
+ if(!cl.csqc_model_precache[i])
+ break;
+ else
+ if(!strcmp(cl.csqc_model_precache[i]->name, m))
+ {
+ e->fields.client->model = PRVM_SetEngineString(cl.csqc_model_precache[i]->name);
+ e->fields.client->modelindex = -(i+1);
+ return;
+ }
+
+ for (i=0, mod = cl.model_precache[0] ; i < MAX_MODELS ; i++, mod = cl.model_precache[i])
+ if(mod)
+ if(!strcmp(mod->name, m))
+ {
+ e->fields.client->model = PRVM_SetEngineString(mod->name);
+ e->fields.client->modelindex = i;
+ return;
+ }
+ e->fields.client->modelindex = 0;
+ e->fields.client->model = 0;
+}
+
+// #4 void(entity e, vector min, vector max) setsize
+void VM_CL_setsize (void)
+{
+ prvm_edict_t *e;
+ float *min, *max;
+ VM_SAFEPARMCOUNT(3, VM_CL_setsize);
+
+ e = PRVM_G_EDICT(OFS_PARM0);
+ if (e == prog->edicts)
+ PF_WARNING("setsize: can not modify world entity\n");
+ if (e->priv.server->free)
+ PF_WARNING("setsize: can not modify free entity\n");
+ min = PRVM_G_VECTOR(OFS_PARM1);
+ max = PRVM_G_VECTOR(OFS_PARM2);
+
+ VectorCopy (min, e->fields.client->mins);
+ VectorCopy (max, e->fields.client->maxs);
+ VectorSubtract (max, min, e->fields.client->size);
+}
+
+// #8 void(entity e, float chan, string samp) sound
+void VM_CL_sound (void)
+{
+ const char *sample;
+ int channel;
+ prvm_edict_t *entity;
+ int volume;
+ float attenuation;
+
+ VM_SAFEPARMCOUNT(5, VM_CL_sound);
+
+ entity = PRVM_G_EDICT(OFS_PARM0);
+ channel = PRVM_G_FLOAT(OFS_PARM1);
+ sample = PRVM_G_STRING(OFS_PARM2);
+ volume = PRVM_G_FLOAT(OFS_PARM3)*255;
+ attenuation = PRVM_G_FLOAT(OFS_PARM4);
+
+ if (volume < 0 || volume > 255)
+ PF_WARNING("VM_CL_sound: volume must be in range 0-1\n");
+
+ if (attenuation < 0 || attenuation > 4)
+ PF_WARNING("VM_CL_sound: attenuation must be in range 0-4\n");
+
+ if (channel < 0 || channel > 7)
+ PF_WARNING("VM_CL_sound: channel must be in range 0-7\n");
+
+ S_StartSound(32768 + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), entity->fields.client->origin, volume, attenuation);
+}
+
+// #14 entity() spawn
+void VM_CL_spawn (void)
+{
+ prvm_edict_t *ed;
+ ed = PRVM_ED_Alloc();
+ ed->fields.client->entnum = PRVM_NUM_FOR_EDICT(ed); //[515]: not needed any more ?
+ VM_RETURN_EDICT(ed);
+}
+
+// #16 float(vector v1, vector v2, float tryents) traceline
+void VM_CL_traceline (void)
+{
+ float *v1, *v2;
+ trace_t trace;
+ int ent;
+
+ v1 = PRVM_G_VECTOR(OFS_PARM0);
+ v2 = PRVM_G_VECTOR(OFS_PARM1);
+
+ trace = CL_TraceBox(v1, vec3_origin, vec3_origin, v2, 1, &ent, 1, false);
+
+ prog->globals.client->trace_allsolid = trace.allsolid;
+ prog->globals.client->trace_startsolid = trace.startsolid;
+ prog->globals.client->trace_fraction = trace.fraction;
+ prog->globals.client->trace_inwater = trace.inwater;
+ prog->globals.client->trace_inopen = trace.inopen;
+ VectorCopy (trace.endpos, prog->globals.client->trace_endpos);
+ VectorCopy (trace.plane.normal, prog->globals.client->trace_plane_normal);
+ prog->globals.client->trace_plane_dist = trace.plane.dist;
+ if (ent)
+ prog->globals.client->trace_ent = ent;
+ else
+ prog->globals.client->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
+}
+
+// #19 void(string s) precache_sound
+void VM_CL_precache_sound (void)
+{
+ const char *n;
+ VM_SAFEPARMCOUNT(1, VM_CL_precache_sound);
+ n = PRVM_G_STRING(OFS_PARM0);
+ S_PrecacheSound(n, true, false);
+}
+
+// #20 void(string s) precache_model
+void VM_CL_precache_model (void)
+{
+ const char *name;
+ int i;
+ model_t *m;
+
+ VM_SAFEPARMCOUNT(1, VM_CL_precache_model);
+
+ name = PRVM_G_STRING(OFS_PARM0);
+ for(i=1;i<MAX_MODELS;i++)
+ if(!cl.csqc_model_precache[i])
+ {
+ i = 0;
+ break;
+ }
+ else
+ if(!strcmp(cl.csqc_model_precache[i]->name, name))
+ {
+ i = -(i+1);
+ break;
+ }
+ if(i)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = i;
+ return;
+ }
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ m = Mod_ForName(name, false, false, false);
+ if(m && m->loaded)
+ {
+ for(i=1;i<MAX_MODELS;i++)
+ if(!cl.csqc_model_precache[i])
+ break;
+ if(i == MAX_MODELS)
+ PF_WARNING("VM_CL_precache_model: no free models\n");
+ cl.csqc_model_precache[i] = (model_t*)m;
+ PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
+ return;
+ }
+ Con_Printf("VM_CL_precache_model: model \"%s\" not found\n", name);
+}
+
+int CSQC_EntitiesInBox (vec3_t mins, vec3_t maxs, int maxlist, prvm_edict_t **list)
+{
+ prvm_edict_t *ent;
+ int i, k;
+
+ ent = PRVM_NEXT_EDICT(prog->edicts);
+ for(k=0,i=1; i<prog->num_edicts ;i++, ent = PRVM_NEXT_EDICT(ent))
+ {
+ if (ent->priv.required->free)
+ continue;
+// VectorAdd(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->absmin);
+// VectorAdd(ent->fields.client->origin, ent->fields.client->maxs, ent->fields.client->absmax);
+ if(BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax))
+ list[k++] = ent;
+ }
+ return k;
+}
+
+// #22 entity(vector org, float rad) findradius
+void VM_CL_findradius (void)
+{
+ prvm_edict_t *ent, *chain;
+ vec_t radius, radius2;
+ vec3_t org, eorg, mins, maxs;
+ int i, numtouchedicts;
+ prvm_edict_t *touchedicts[MAX_EDICTS];
+
+ chain = (prvm_edict_t *)prog->edicts;
+
+ VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
+ radius = PRVM_G_FLOAT(OFS_PARM1);
+ radius2 = radius * radius;
+
+ mins[0] = org[0] - (radius + 1);
+ mins[1] = org[1] - (radius + 1);
+ mins[2] = org[2] - (radius + 1);
+ maxs[0] = org[0] + (radius + 1);
+ maxs[1] = org[1] + (radius + 1);
+ maxs[2] = org[2] + (radius + 1);
+ numtouchedicts = CSQC_EntitiesInBox(mins, maxs, MAX_EDICTS, touchedicts);
+ if (numtouchedicts > MAX_EDICTS)
+ {
+ // this never happens //[515]: for what then ?
+ Con_Printf("CSQC_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
+ numtouchedicts = MAX_EDICTS;
+ }
+ for (i = 0;i < numtouchedicts;i++)
+ {
+ ent = touchedicts[i];
+ // Quake did not return non-solid entities but darkplaces does
+ // (note: this is the reason you can't blow up fallen zombies)
+ if (ent->fields.client->solid == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.integer)
+ continue;
+ // LordHavoc: compare against bounding box rather than center so it
+ // doesn't miss large objects, and use DotProduct instead of Length
+ // for a major speedup
+ VectorSubtract(org, ent->fields.client->origin, eorg);
+ if (sv_gameplayfix_findradiusdistancetobox.integer)
+ {
+ eorg[0] -= bound(ent->fields.client->mins[0], eorg[0], ent->fields.client->maxs[0]);
+ eorg[1] -= bound(ent->fields.client->mins[1], eorg[1], ent->fields.client->maxs[1]);
+ eorg[2] -= bound(ent->fields.client->mins[2], eorg[2], ent->fields.client->maxs[2]);
+ }
+ else
+ VectorMAMAM(1, eorg, 0.5f, ent->fields.client->mins, 0.5f, ent->fields.client->maxs, eorg);
+ if (DotProduct(eorg, eorg) < radius2)
+ {
+ ent->fields.client->chain = PRVM_EDICT_TO_PROG(chain);
+ chain = ent;
+ }
+ }
+
+ VM_RETURN_EDICT(chain);
+}
+
+// #34 float() droptofloor
+void VM_CL_droptofloor (void)
+{
+ prvm_edict_t *ent;
+ vec3_t end;
+ trace_t trace;
+ int i;
+
+ // assume failure if it returns early
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+
+ ent = PRVM_PROG_TO_EDICT(prog->globals.client->self);
+ if (ent == prog->edicts)
+ PF_WARNING("droptofloor: can not modify world entity\n");
+ if (ent->priv.server->free)
+ PF_WARNING("droptofloor: can not modify free entity\n");
+
+ VectorCopy (ent->fields.client->origin, end);
+ end[2] -= 256;
+
+ trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, 1, &i, 1, false);
+
+ if (trace.fraction != 1)
+ {
+ VectorCopy (trace.endpos, ent->fields.client->origin);
+ ent->fields.client->flags = (int)ent->fields.client->flags | FL_ONGROUND;
+// ent->fields.client->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+ // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+// ent->priv.server->suspendedinairflag = true;
+ }
+}
+
+// #35 void(float style, string value) lightstyle
+void VM_CL_lightstyle (void)
+{
+ int i;
+ const char *c;
+
+ VM_SAFEPARMCOUNT(2, VM_CL_lightstyle);
+
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ c = PRVM_G_STRING(OFS_PARM1);
+ if (i >= MAX_LIGHTSTYLES)
+ PF_WARNING("VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
+ strlcpy (cl_lightstyle[i].map, MSG_ReadString(), sizeof (cl_lightstyle[i].map));
+ cl_lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
+ cl_lightstyle[i].length = (int)strlen(cl_lightstyle[i].map);
+}
+
+// #40 float(entity e) checkbottom
+void VM_CL_checkbottom (void)
+{
+ static int cs_yes, cs_no;
+ prvm_edict_t *ent;
+ vec3_t mins, maxs, start, stop;
+ trace_t trace;
+ int x, y, hit;
+ float mid, bottom;
+
+ VM_SAFEPARMCOUNT(1, VM_CL_checkbottom);
+ ent = PRVM_G_EDICT(OFS_PARM0);
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+
+ VectorAdd (ent->fields.client->origin, ent->fields.client->mins, mins);
+ VectorAdd (ent->fields.client->origin, ent->fields.client->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 (!(CL_PointSuperContents(start) & SUPERCONTENTS_SOLID))
+ goto realcheck;
+ }
+
+ cs_yes++;
+ PRVM_G_FLOAT(OFS_RETURN) = true;
+ return; // we got out easy
+
+realcheck:
+ cs_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*sv_stepheight.value;
+ trace = CL_TraceBox (start, vec3_origin, vec3_origin, stop, 1, &hit, 1, true);
+
+ if (trace.fraction == 1.0)
+ return;
+
+ 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];
+
+ trace = CL_TraceBox (start, vec3_origin, vec3_origin, stop, 1, &hit, 1, true);
+
+ if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
+ bottom = trace.endpos[2];
+ if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
+ return;
+ }
+
+ cs_yes++;
+ PRVM_G_FLOAT(OFS_RETURN) = true;
+}
+
+// #41 float(vector v) pointcontents
+void VM_CL_pointcontents (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_CL_pointcontents);
+ PRVM_G_FLOAT(OFS_RETURN) = CL_PointQ1Contents(PRVM_G_VECTOR(OFS_PARM0));
+}
+
+// #48 void(vector o, vector d, float color, float count) particle
+void VM_CL_particle (void)
+{
+ float *org, *dir;
+ int count;
+ unsigned char color;
+ VM_SAFEPARMCOUNT(4, VM_CL_particle);
+
+ org = PRVM_G_VECTOR(OFS_PARM0);
+ dir = PRVM_G_VECTOR(OFS_PARM1);
+ color = PRVM_G_FLOAT(OFS_PARM2);
+ count = PRVM_G_FLOAT(OFS_PARM3);
+ if (cl_particles_blood_bloodhack.integer)
+ {
+ if (color == 73)
+ {
+ // regular blood
+ CL_BloodPuff(org, dir, count / 2);
+ return;
+ }
+ if (color == 225)
+ {
+ // lightning blood
+ CL_BloodPuff(org, dir, count / 2);
+ return;
+ }
+ }
+ CL_RunParticleEffect (org, dir, color, count);
+}
+
+// #49 void(entity ent, float ideal_yaw, float speed_yaw) ChangeYaw
+void VM_CL_changeyaw (void)
+{
+ prvm_edict_t *ent;
+ float ideal, current, move, speed;
+ VM_SAFEPARMCOUNT(3, VM_CL_changeyaw);
+
+ ent = PRVM_G_EDICT(OFS_PARM0);
+ if (ent == prog->edicts)
+ PF_WARNING("changeyaw: can not modify world entity\n");
+ if (ent->priv.server->free)
+ PF_WARNING("changeyaw: can not modify free entity\n");
+ current = ANGLEMOD(ent->fields.client->angles[1]);
+ ideal = PRVM_G_FLOAT(OFS_PARM1);
+ speed = PRVM_G_FLOAT(OFS_PARM2);
+
+ 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->fields.client->angles[1] = ANGLEMOD (current + move);
+}
+
+// #63 void(entity ent, float ideal_pitch, float speed_pitch) changepitch (DP_QC_CHANGEPITCH)
+void VM_CL_changepitch (void)
+{
+ prvm_edict_t *ent;
+ float ideal, current, move, speed;
+ VM_SAFEPARMCOUNT(3, VM_CL_changepitch);
+
+ ent = PRVM_G_EDICT(OFS_PARM0);
+ if (ent == prog->edicts)
+ PF_WARNING("changepitch: can not modify world entity\n");
+ if (ent->priv.server->free)
+ PF_WARNING("changepitch: can not modify free entity\n");
+ current = ANGLEMOD( ent->fields.client->angles[0] );
+ ideal = PRVM_G_FLOAT(OFS_PARM1);
+ speed = PRVM_G_FLOAT(OFS_PARM2);
+
+ 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->fields.client->angles[0] = ANGLEMOD (current + move);
+}
+
+// #64 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS)
+void VM_CL_tracetoss (void)
+{
+/* trace_t trace;
+ prvm_edict_t *ent;
+ prvm_edict_t *ignore;
+
+ ent = PRVM_G_EDICT(OFS_PARM0);
+ if (ent == prog->edicts)
+ PF_WARNING("tracetoss: can not use world entity\n");
+ ignore = PRVM_G_EDICT(OFS_PARM1);
+
+//FIXME
+ trace = SV_Trace_Toss (ent, ignore);
+
+ prog->globals.server->trace_allsolid = trace.allsolid;
+ prog->globals.server->trace_startsolid = trace.startsolid;
+ prog->globals.server->trace_fraction = trace.fraction;
+ prog->globals.server->trace_inwater = trace.inwater;
+ prog->globals.server->trace_inopen = trace.inopen;
+ VectorCopy (trace.endpos, prog->globals.server->trace_endpos);
+ VectorCopy (trace.plane.normal, prog->globals.server->trace_plane_normal);
+ prog->globals.server->trace_plane_dist = trace.plane.dist;
+ if (trace.ent)
+ prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent);
+ else
+ prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
+*/
+}
+
+// #74 void(vector pos, string samp, float vol, float atten) ambientsound
+void VM_CL_ambientsound (void)
+{
+ float *f;
+ sfx_t *s;
+ VM_SAFEPARMCOUNT(4, VM_CL_ambientsound);
+ s = S_FindName(PRVM_G_STRING(OFS_PARM0));
+ f = PRVM_G_VECTOR(OFS_PARM1);
+ S_StaticSound (s, f, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3)*64);
+}
+
+// #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX)
+void VM_CL_tracebox (void)
+{
+ float *v1, *v2, *m1, *m2;
+ trace_t trace;
+ int ent;
+
+ v1 = PRVM_G_VECTOR(OFS_PARM0);
+ m1 = PRVM_G_VECTOR(OFS_PARM1);
+ m2 = PRVM_G_VECTOR(OFS_PARM2);
+ v2 = PRVM_G_VECTOR(OFS_PARM3);
+
+ trace = CL_TraceBox(v1, m1, m2, v2, 1, &ent, 1, false);
+
+ prog->globals.client->trace_allsolid = trace.allsolid;
+ prog->globals.client->trace_startsolid = trace.startsolid;
+ prog->globals.client->trace_fraction = trace.fraction;
+ prog->globals.client->trace_inwater = trace.inwater;
+ prog->globals.client->trace_inopen = trace.inopen;
+ VectorCopy (trace.endpos, prog->globals.client->trace_endpos);
+ VectorCopy (trace.plane.normal, prog->globals.client->trace_plane_normal);
+ prog->globals.client->trace_plane_dist = trace.plane.dist;
+ if (ent)
+ prog->globals.client->trace_ent = ent;
+ else
+ prog->globals.client->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
+}
+
+// #92 vector(vector org) getlight (DP_QC_GETLIGHT)
+void VM_CL_getlight (void)
+{
+ vec3_t ambientcolor, diffusecolor, diffusenormal;
+ vec_t *p;
+
+ VM_SAFEPARMCOUNT(1, VM_CL_getlight);
+
+ p = PRVM_G_VECTOR(OFS_PARM0);
+ VectorClear(ambientcolor);
+ VectorClear(diffusecolor);
+ VectorClear(diffusenormal);
+ if (cl.worldmodel && cl.worldmodel->brush.LightPoint)
+ cl.worldmodel->brush.LightPoint(cl.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
+ VectorMA(ambientcolor, 0.5, diffusecolor, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+
+//============================================================================
+//[515]: SCENE MANAGER builtins
+void V_CalcRefdef (void);//view.c
+void CSQC_R_ClearScreen (void);//gl_rmain.c
+void CSQC_R_RenderScene (void);//gl_rmain.c
+void CSQC_AddEntity (int n);//csprogs.c
+void CSQC_ClearCSQCEntities (void);
+
+matrix4x4_t csqc_listenermatrix;
+qboolean csqc_usecsqclistener = false, csqc_frame = false;//[515]: per-frame
+qboolean csqc_onground;
+
+static char *particleeffect_names[] =
+{
+ "TE_SPIKE",
+ "TE_SUPERSPIKE",
+ "TE_GUNSHOT",
+ "TE_EXPLOSION",
+ "TE_TAREXPLOSION",
+ "TE_LIGHTNING1",//trail
+ "TE_LIGHTNING2",//trail
+ "TE_WIZSPIKE",
+ "TE_KNIGHTSPIKE",
+ "TE_LIGHTNING3",//trail
+ "TE_LAVASPLASH",
+ "TE_TELEPORT",
+ "TE_EXPLOSION2",
+ "TE_BEAM",//trail
+ "TE_EXPLOSION3",
+ "",//TE_LIGHTNING4NEH
+ "TE_BLOOD",
+ "TE_SPARK",
+ "",//TE_BLOODSHOWER
+ "TE_EXPLOSIONRGB",
+ "",//unused
+ "",
+ "",
+ "TE_GUNSHOTQUAD",
+ "TE_SPIKEQUAD",
+ "TE_SUPERSPIKEQUAD",
+ "TE_EXPLOSIONQUAD",
+ "",
+ "",
+ "",
+ "TE_FLAMEJET",
+ "TE_PLASMABURN",
+ "TE_TEI_G3",
+ "TE_TEI_SMOKE",
+ "TE_TEI_BIGEXPLOSION",
+ "TE_TEI_PLASMAHIT",
+
+
+ //trail effects (as modelflags)
+ "EF_ROCKET",
+ "EF_GRENADE",
+ "EF_GIB",
+ "EF_TRACER",
+ "EF_ZOMGIB",
+ "EF_TRACER2",
+ "EF_TRACER3",
+ "EF_NEH_CIGAR",
+ "EF_NEXUIZ_PLASMA",
+ "EF_GLOWTRAIL",
+};
+
+#define CSQC_TRAILSTART 36
+static const int particleeffects_num = sizeof(particleeffect_names)/sizeof(char*);
+
+static void CSQC_R_RecalcView (void)
+{
+ extern matrix4x4_t viewmodelmatrix;
+ Matrix4x4_CreateIdentity(&viewmodelmatrix);
+ Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix);
+ Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, csqc_origin[0], csqc_origin[1], csqc_origin[2], csqc_angles[0], csqc_angles[1], csqc_angles[2], 1);
+ Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, csqc_origin[0], csqc_origin[1], csqc_origin[2], csqc_angles[0], csqc_angles[1], csqc_angles[2], 0.3);
+}
+
+//#300 void() clearscene (EXT_CSQC)
+void VM_R_ClearScene (void)
+{
+ VM_SAFEPARMCOUNT(0, VM_R_ClearScene);
+// CSQC_R_RecalcView();
+ if(csqc_frame)
+ CSQC_ClearCSQCEntities();
+ CSQC_R_ClearScreen();
+}
+
+//#301 void(float mask) addentities (EXT_CSQC)
+void VM_R_AddEntities (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_R_AddEntities);
+ csqc_drawmask = PRVM_G_FLOAT(OFS_PARM0);
+}
+
+//#302 void(entity ent) addentity (EXT_CSQC)
+void VM_R_AddEntity (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_R_AddEntity);
+ CSQC_AddEntity(PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0)));
+}
+
+//#303 float(float property, ...) setproperty (EXT_CSQC)
+void VM_R_SetView (void)
+{
+ int c;
+ float *f;
+ float k;
+
+ if(prog->argc < 2)
+ VM_SAFEPARMCOUNT(2, VM_R_SetView);
+
+ c = PRVM_G_FLOAT(OFS_PARM0);
+ f = PRVM_G_VECTOR(OFS_PARM1);
+ k = PRVM_G_FLOAT(OFS_PARM1);
+
+ switch(c)
+ {
+ case VF_MIN: r_refdef.x = f[0];
+ r_refdef.y = f[1];
+ break;
+ case VF_MIN_X: r_refdef.x = k;
+ break;
+ case VF_MIN_Y: r_refdef.y = k;
+ break;
+ case VF_SIZE: r_refdef.width = f[0];
+ r_refdef.height = f[1];
+ break;
+ case VF_SIZE_Y: r_refdef.width = k;
+ break;
+ case VF_SIZE_X: r_refdef.height = k;
+ break;
+ case VF_VIEWPORT: r_refdef.x = f[0];
+ r_refdef.y = f[1];
+ f = PRVM_G_VECTOR(OFS_PARM2);
+ r_refdef.width = f[0];
+ r_refdef.height = f[1];
+ break;
+ case VF_FOV: //r_refdef.fov_x = f[0]; // FIXME!
+ //r_refdef.fov_y = f[1]; // FIXME!
+ break;
+ case VF_FOVX: //r_refdef.fov_x = k; // FIXME!
+ break;
+ case VF_FOVY: //r_refdef.fov_y = k; // FIXME!
+ break;
+ case VF_ORIGIN: VectorCopy(f, csqc_origin);
+ CSQC_R_RecalcView();
+ break;
+ case VF_ORIGIN_X: csqc_origin[0] = k;
+ CSQC_R_RecalcView();
+ break;
+ case VF_ORIGIN_Y: csqc_origin[1] = k;
+ CSQC_R_RecalcView();
+ break;
+ case VF_ORIGIN_Z: csqc_origin[2] = k;
+ CSQC_R_RecalcView();
+ break;
+ case VF_ANGLES: VectorCopy(f, csqc_angles);
+ CSQC_R_RecalcView();
+ break;
+ case VF_ANGLES_X: csqc_angles[0] = k;
+ CSQC_R_RecalcView();
+ break;
+ case VF_ANGLES_Y: csqc_angles[1] = k;
+ CSQC_R_RecalcView();
+ break;
+ case VF_ANGLES_Z: csqc_angles[2] = k;
+ CSQC_R_RecalcView();
+ break;
+ case VF_DRAWWORLD: cl.csqc_vidvars.drawworld = k;
+ break;
+ case VF_DRAWENGINESBAR: cl.csqc_vidvars.drawenginesbar = k;
+ break;
+ case VF_DRAWCROSSHAIR: cl.csqc_vidvars.drawcrosshair = k;
+ break;
+
+ case VF_CL_VIEWANGLES: VectorCopy(f, cl.viewangles);
+ break;
+ case VF_CL_VIEWANGLES_X:cl.viewangles[0] = k;
+ break;
+ case VF_CL_VIEWANGLES_Y:cl.viewangles[1] = k;
+ break;
+ case VF_CL_VIEWANGLES_Z:cl.viewangles[2] = k;
+ break;
+
+ default: Con_Printf("VM_R_SetView : unknown parm %i\n", c);
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+
+//#304 void() renderscene (EXT_CSQC)
+void VM_R_RenderScene (void) //#134
+{
+ VM_SAFEPARMCOUNT(0, VM_R_RenderScene);
+
+ if(csqc_frame)
+ {
+ CSQC_RelinkCSQCEntities();
+ CSQC_RelinkAllEntities(csqc_drawmask);
+ }
+
+ CSQC_R_RenderScene();
+}
+
+//#305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC)
+void VM_R_AddDynamicLight (void)
+{
+ float *pos, *col;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(3, VM_R_AddDynamicLight);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ col = PRVM_G_VECTOR(OFS_PARM2);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+ CL_AllocDlight(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), col[0], col[1], col[2], 500, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ //CL_AllocDlight(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), col[0], col[1], col[2], 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+}
+
+//============================================================================
+
+//#310 vector (vector v) cs_unproject (EXT_CSQC)
+void VM_CL_unproject (void)
+{
+ float *f;
+ vec3_t temp;
+
+ VM_SAFEPARMCOUNT(1, VM_CL_unproject);
+ f = PRVM_G_VECTOR(OFS_PARM0);
+ VectorSet(temp, f[2], f[0] * f[2] * -r_refdef.frustum_x * 2.0 / r_refdef.width, f[1] * f[2] * -r_refdef.frustum_y * 2.0 / r_refdef.height);
+ Matrix4x4_Transform(&r_refdef.viewentitymatrix, temp, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+//#311 vector (vector v) cs_project (EXT_CSQC)
+void VM_CL_project (void)
+{
+ float *f;
+ vec3_t v;
+ matrix4x4_t m;
+
+ VM_SAFEPARMCOUNT(1, VM_CL_project);
+ f = PRVM_G_VECTOR(OFS_PARM0);
+ Matrix4x4_Invert_Simple(&m, &r_refdef.viewentitymatrix);
+ Matrix4x4_Transform(&m, f, v);
+ VectorSet(PRVM_G_VECTOR(OFS_RETURN), v[1]/v[0]/-r_refdef.frustum_x*0.5*r_refdef.width, v[2]/v[0]/-r_refdef.frustum_y*r_refdef.height*0.5, v[0]);
+}
+
+//#330 float(float stnum) getstatf (EXT_CSQC)
+void VM_CL_getstatf (void)
+{
+ int i;
+ union
+ {
+ float f;
+ int l;
+ }dat;
+ VM_SAFEPARMCOUNT(1, VM_CL_getstatf);
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ if(i < 0 || i >= MAX_CL_STATS)
+ {
+ Con_Printf("VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n");
+ return;
+ }
+ dat.l = cl.stats[i];
+ PRVM_G_FLOAT(OFS_RETURN) = dat.f;
+}
+
+//#331 float(float stnum) getstati (EXT_CSQC)
+void VM_CL_getstati (void)
+{
+ int i, index;
+ VM_SAFEPARMCOUNT(1, VM_CL_getstati);
+ index = PRVM_G_FLOAT(OFS_PARM0);
+
+ if(index < 0 || index >= MAX_CL_STATS)
+ {
+ Con_Printf("VM_CL_getstati: index>=MAX_CL_STATS or index<0\n");
+ return;
+ }
+ i = cl.stats[index];
+ PRVM_G_FLOAT(OFS_RETURN) = i;
+}
+
+//#332 string(float firststnum) getstats (EXT_CSQC)
+void VM_CL_getstats (void)
+{
+ int i;
+ char *t;
+ VM_SAFEPARMCOUNT(1, VM_CL_getstats);
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ if(i < 0 || i > MAX_CL_STATS-4)
+ {
+ Con_Printf("VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
+ return;
+ }
+ t = VM_GetTempString();
+ strlcpy(t, (char*)&cl.stats[i], 16);
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t);
+}
+
+//#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
+void VM_CL_setmodelindex (void)
+{
+ int i;
+ prvm_edict_t *t;
+ struct model_s *m;
+
+ VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex);
+
+ t = PRVM_G_EDICT(OFS_PARM0);
+ i = (int)PRVM_G_FLOAT(OFS_PARM1);
+
+ t->fields.client->model = 0;
+ t->fields.client->modelindex = 0;
+
+ if(!i)
+ return;
+ if(i<0)
+ {
+ i = -(i+1);
+ if(i >= MAX_MODELS)
+ PF_WARNING("VM_CL_setmodelindex >= MAX_MODELS\n");
+ m = cl.csqc_model_precache[i];
+ }
+ else
+ if(i >= MAX_MODELS)
+ PF_WARNING("VM_CL_setmodelindex >= MAX_MODELS\n");
+ else
+ m = cl.model_precache[i];
+ if(!m)
+ PF_WARNING("VM_CL_setmodelindex: null model\n");
+ t->fields.client->model = PRVM_SetEngineString(m->name);
+ t->fields.client->modelindex = i;
+}
+
+//#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
+void VM_CL_modelnameforindex (void)
+{
+ int i;
+
+ VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
+
+ PRVM_G_INT(OFS_RETURN) = 0;
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ if(i<0)
+ {
+ i = -(i+1);
+ if(i >= MAX_MODELS)
+ PF_WARNING("VM_CL_modelnameforindex >= MAX_MODELS\n");
+ if(cl.csqc_model_precache[i])
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(cl.csqc_model_precache[i]->name);
+ return;
+ }
+ if(i >= MAX_MODELS)
+ PF_WARNING("VM_CL_modelnameforindex >= MAX_MODELS\n");
+ if(cl.model_precache[i])
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(cl.model_precache[i]->name);
+}
+
+//#335 float(string effectname) particleeffectnum (EXT_CSQC)
+void VM_CL_particleeffectnum (void)
+{
+ const char *n;
+ int i;
+ VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
+ n = PRVM_G_STRING(OFS_PARM0);
+ for(i=0;i<particleeffects_num;i++)
+ if(!strcasecmp(particleeffect_names[i], n))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = i;
+ return;
+ }
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+}
+
+void CSQC_ParseBeam (int ent, vec3_t start, vec3_t end, model_t *m, int lightning)
+{
+ int i;
+ beam_t *b;
+
+ // override any beam with the same entity
+ for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++)
+ {
+ if (b->entity == ent && ent)
+ {
+ //b->entity = ent;
+ b->lightning = lightning;
+ b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0;
+ b->model = m;
+ b->endtime = cl.time + 0.2;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ return;
+ }
+ }
+
+ // find a free beam
+ for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->lightning = lightning;
+ b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0;
+ b->model = m;
+ b->endtime = cl.time + 0.2;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ return;
+ }
+ }
+ Con_Print("beam list overflow!\n");
+}
+
+// #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC)
+void VM_CL_trailparticles (void)
+{
+ int i, entnum, col;
+ float *start, *end;
+ entity_t *ent;
+ VM_SAFEPARMCOUNT(4, VM_CL_trailparticles);
+
+ entnum = PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0));
+ i = PRVM_G_FLOAT(OFS_PARM1);
+ start = PRVM_G_VECTOR(OFS_PARM2);
+ end = PRVM_G_VECTOR(OFS_PARM3);
+
+ if(i >= particleeffects_num)
+ return;
+ if (entnum >= MAX_EDICTS)
+ {
+ Con_Printf("CSQC_ParseBeam: invalid entity number %i\n", entnum);
+ return;
+ }
+ if (entnum >= cl_max_csqcentities)
+ CL_ExpandCSQCEntities(entnum);
+
+ ent = &cl_csqcentities[entnum];
+
+ if(prog->argc > 4)
+ col = PRVM_G_FLOAT(OFS_PARM4);
+ else
+ col = ent->state_current.glowcolor;
+
+ switch(i)
+ {
+ case TE_LIGHTNING1:
+ CSQC_ParseBeam(entnum, start, end, cl.model_bolt, true);
+ break;
+ case TE_LIGHTNING2:
+ CSQC_ParseBeam(entnum, start, end, cl.model_bolt2, true);
+ break;
+ case TE_LIGHTNING3:
+ CSQC_ParseBeam(entnum, start, end, cl.model_bolt3, false);
+ break;
+ case TE_BEAM:
+ CSQC_ParseBeam(entnum, start, end, cl.model_beam, false);
+ break;
+ default:
+ CL_RocketTrail(start, end, i-CSQC_TRAILSTART, col, ent);
+ break;
+ }
+}
+
+//#337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC)
+void VM_CL_pointparticles (void)
+{
+ int i, n;
+ float *f, *v;
+ if(prog->argc < 2)
+ VM_SAFEPARMCOUNT(2, VM_CL_pointparticles);
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ f = PRVM_G_VECTOR(OFS_PARM1);
+ if(prog->argc >= 4)
+ {
+ v = PRVM_G_VECTOR(OFS_PARM2);
+ n = PRVM_G_FLOAT(OFS_PARM3);
+ }
+ else
+ {
+ v = vec3_origin;
+ n = 15;
+ }
+
+ if(i >= particleeffects_num)
+ return;
+
+ switch(i)
+ {
+ case TE_SPIKE:
+ case TE_SPIKEQUAD:
+ case TE_GUNSHOT:
+ case TE_GUNSHOTQUAD:
+ CL_SparkShower(f, v, 15, 1);
+ CL_Smoke(f, v, 15);
+ if (cl_particles_bulletimpacts.integer)
+ CL_BulletMark(f);
+ break;
+ case TE_SUPERSPIKE:
+ case TE_SUPERSPIKEQUAD:
+ CL_SparkShower(f, v, 30, 1);
+ CL_Smoke(f, v, 30);
+ if (cl_particles_bulletimpacts.integer)
+ CL_BulletMark(f);
+ break;
+ case TE_EXPLOSION:
+ case TE_EXPLOSIONQUAD:
+ case TE_TEI_BIGEXPLOSION:
+ CL_ParticleExplosion(f);
+ break;
+ case TE_TAREXPLOSION:
+ CL_BlobExplosion(f);
+ break;
+ case TE_WIZSPIKE:
+ CL_RunParticleEffect(f, v, 20, 30);
+ break;
+ case TE_KNIGHTSPIKE:
+ CL_RunParticleEffect(f, v, 226, 20);
+ break;
+ case TE_LAVASPLASH:
+ CL_LavaSplash(f);
+ break;
+ case TE_TELEPORT:
+ CL_TeleportSplash(f);
+ break;
+ case TE_EXPLOSION2:
+ case TE_EXPLOSION3:
+ case TE_EXPLOSIONRGB:
+ CL_ParticleExplosion2(f, v[0], v[1]);
+ break;
+ case TE_BLOOD:
+ CL_BloodPuff(f, v, n);
+ break;
+ case TE_SPARK:
+ CL_SparkShower(f, v, n, 1);
+ break;
+ case TE_FLAMEJET:
+ CL_Flames(f, v, n);
+ break;
+ case TE_PLASMABURN:
+ CL_PlasmaBurn(f);
+ break;
+ case TE_TEI_G3:
+ CL_BeamParticle(f, v, 8, 1, 1, 1, 1, 1);
+ break;
+ case TE_TEI_SMOKE:
+ CL_Tei_Smoke(f, v, n);
+ break;
+ case TE_TEI_PLASMAHIT:
+ CL_Tei_PlasmaHit(f, v, n);
+ break;
+ default:break;
+ }
+}
+
+//#338 void(string s) cprint (EXT_CSQC)
+void VM_CL_centerprint (void)
+{
+ char s[VM_STRINGTEMP_LENGTH];
+ if(prog->argc < 1)
+ VM_SAFEPARMCOUNT(1, VM_CL_centerprint);
+ VM_VarString(0, s, sizeof(s));
+ SCR_CenterPrint(s);
+}
+
+//#342 string(float keynum) getkeybind (EXT_CSQC)
+void VM_CL_getkeybind (void)
+{
+ int i;
+
+ VM_SAFEPARMCOUNT(1, VM_CL_getkeybind);
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(Key_GetBind(i));
+}
+
+//#343 void(float usecursor) setcursormode (EXT_CSQC)
+void VM_CL_setcursormode (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
+ cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0);
+ cl_ignoremousemove = true;
+}
+
+//#345 float(float framenum) getinputstate (EXT_CSQC)
+void VM_CL_getinputstate (void)
+{
+ int i, frame;
+ VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
+ frame = PRVM_G_FLOAT(OFS_PARM0);
+ for (i = 0;i < cl.movement_numqueue;i++)
+ if (cl.movement_queue[i].sequence == frame)
+ {
+ VectorCopy(cl.movement_queue[i].viewangles, prog->globals.client->input_angles);
+ //prog->globals.client->input_buttons = cl.movement_queue[i].//FIXME
+ VectorCopy(cl.movement_queue[i].move, prog->globals.client->input_movevalues);
+ prog->globals.client->input_timelength = cl.movement_queue[i].frametime;
+ if(cl.movement_queue[i].crouch)
+ {
+ VectorCopy(cl_playercrouchmins, prog->globals.client->pmove_mins);
+ VectorCopy(cl_playercrouchmaxs, prog->globals.client->pmove_maxs);
+ }
+ else
+ {
+ VectorCopy(cl_playerstandmins, prog->globals.client->pmove_mins);
+ VectorCopy(cl_playerstandmaxs, prog->globals.client->pmove_maxs);
+ }
+ }
+}
+
+//#346 void(float sens) setsensitivityscaler (EXT_CSQC)
+void VM_CL_setsensitivityscale (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale);
+ cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0);
+}
+
+//#347 void() runstandardplayerphysics (EXT_CSQC)
+void VM_CL_runplayerphysics (void)
+{
+}
+
+//#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
+void VM_CL_getplayerkey (void)
+{
+ int i;
+ char t[128];
+ const char *c;
+ char *temp;
+
+ VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
+
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ c = PRVM_G_STRING(OFS_PARM1);
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ Sbar_SortFrags();
+
+ i = Sbar_GetPlayer(i);
+ if(i < 0)
+ return;
+
+ t[0] = 0;
+
+ if(!strcasecmp(c, "name"))
+ strcpy(t, cl.scores[i].name);
+ else
+ if(!strcasecmp(c, "frags"))
+ sprintf(t, "%i", cl.scores[i].frags);
+// else
+// if(!strcasecmp(c, "ping"))
+// sprintf(t, "%i", cl.scores[i].ping);
+// else
+// if(!strcasecmp(c, "entertime"))
+// sprintf(t, "%f", cl.scores[i].entertime);
+ else
+ if(!strcasecmp(c, "colors"))
+ sprintf(t, "%i", cl.scores[i].colors);
+ else
+ if(!strcasecmp(c, "topcolor"))
+ sprintf(t, "%i", cl.scores[i].colors & 0xf0);
+ else
+ if(!strcasecmp(c, "bottomcolor"))
+ sprintf(t, "%i", (cl.scores[i].colors &15)<<4);
+ else
+ if(!strcasecmp(c, "viewentity"))
+ sprintf(t, "%i", i+1);
+ if(!t[0])
+ return;
+ temp = VM_GetTempString();
+ strcpy(temp, t);
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(temp);
+}
+
+//#349 float() isdemo (EXT_CSQC)
+void VM_CL_isdemo (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
+}
+
+//#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
+void VM_CL_setlistener (void)
+{
+ VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
+ Matrix4x4_FromVectors(&csqc_listenermatrix, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_VECTOR(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0));
+ csqc_usecsqclistener = true; //use csqc listener at this frame
+}
+
+//#352 void(string cmdname) registercommand (EXT_CSQC)
+void VM_CL_registercmd (void)
+{
+ char *t;
+ VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
+ if(!Cmd_Exists(PRVM_G_STRING(OFS_PARM0)))
+ {
+ t = Z_Malloc(strlen(PRVM_G_STRING(OFS_PARM0))+1);
+ strcpy(t, PRVM_G_STRING(OFS_PARM0));
+ Cmd_AddCommand(t, NULL);
+ }
+ else
+ Cmd_AddCommand(PRVM_G_STRING(OFS_PARM0), NULL);
+
+}
+
+//#354 float() playernum (EXT_CSQC)
+void VM_CL_playernum (void)
+{
+ int i, k;
+
+ VM_SAFEPARMCOUNT(0, VM_CL_playernum);
+
+ for(i=k=0 ; i<cl.maxclients ; i++)
+ if(cl.scores[i].name[0])
+ k++;
+ PRVM_G_FLOAT(OFS_RETURN) = k;
+}
+
+//#355 float() cl_onground (EXT_CSQC)
+void VM_CL_onground (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = csqc_onground;
+}
+
+//#360 float() readbyte (EXT_CSQC)
+void VM_CL_ReadByte (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte();
+}
+
+//#361 float() readchar (EXT_CSQC)
+void VM_CL_ReadChar (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar();
+}
+
+//#362 float() readshort (EXT_CSQC)
+void VM_CL_ReadShort (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort();
+}
+
+//#363 float() readlong (EXT_CSQC)
+void VM_CL_ReadLong (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong();
+}
+
+//#364 float() readcoord (EXT_CSQC)
+void VM_CL_ReadCoord (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(cl.protocol);
+}
+
+//#365 float() readangle (EXT_CSQC)
+void VM_CL_ReadAngle (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(cl.protocol);
+}
+
+//#366 string() readstring (EXT_CSQC)
+void VM_CL_ReadString (void)
+{
+ char *t, *s;
+ t = VM_GetTempString();
+ s = MSG_ReadString();
+ PRVM_G_INT(OFS_RETURN) = 0;
+ if(s)
+ {
+ strcpy(t, s);
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t);
+ }
+}
+
+//#367 float() readfloat (EXT_CSQC)
+void VM_CL_ReadFloat (void)
+{
+ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat();
+}
+
+//=================================================================//
+
+// #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
+void VM_CL_effect (void)
+{
+ VM_SAFEPARMCOUNT(5, VM_CL_effect);
+ CL_Effect(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4));
+}
+
+// #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
+void VM_CL_te_blood (void)
+{
+ float *pos;
+ vec3_t pos2;
+ VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
+ if (PRVM_G_FLOAT(OFS_PARM2) < 1)
+ return;
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ CL_BloodPuff(pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
+}
+
+// #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
+void VM_CL_te_bloodshower (void)
+{
+ VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
+ if (PRVM_G_FLOAT(OFS_PARM3) < 1)
+ return;
+ CL_BloodShower(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3));
+}
+
+// #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
+void VM_CL_te_explosionrgb (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 10);
+ CL_ParticleExplosion(pos2);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 350, PRVM_G_VECTOR(OFS_PARM1)[0], PRVM_G_VECTOR(OFS_PARM1)[1], PRVM_G_VECTOR(OFS_PARM1)[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+}
+
+// #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
+void VM_CL_te_particlecube (void)
+{
+ VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
+ if (PRVM_G_FLOAT(OFS_PARM3) < 1)
+ return;
+ CL_ParticleCube(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4), PRVM_G_FLOAT(OFS_PARM5), PRVM_G_FLOAT(OFS_PARM6));
+}
+
+// #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
+void VM_CL_te_particlerain (void)
+{
+ VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
+ if (PRVM_G_FLOAT(OFS_PARM3) < 1)
+ return;
+ CL_ParticleRain(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4), 0);
+}
+
+// #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
+void VM_CL_te_particlesnow (void)
+{
+ VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
+ if (PRVM_G_FLOAT(OFS_PARM3) < 1)
+ return;
+ CL_ParticleRain(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4), 1);
+}
+
+// #411 void(vector org, vector vel, float howmany) te_spark
+void VM_CL_te_spark (void)
+{
+ float *pos;
+ vec3_t pos2;
+ VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
+
+ if (PRVM_G_FLOAT(OFS_PARM2) < 1)
+ return;
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ CL_SparkShower(pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2), 1);
+}
+
+// #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
+void VM_CL_te_gunshotquad (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ CL_SparkShower(pos2, vec3_origin, 15, 1);
+ CL_Smoke(pos2, vec3_origin, 15);
+ CL_BulletMark(pos2);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+}
+
+// #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
+void VM_CL_te_spikequad (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ int rnd;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ if (cl_particles_bulletimpacts.integer)
+ {
+ CL_SparkShower(pos2, vec3_origin, 15, 1);
+ CL_Smoke(pos2, vec3_origin, 15);
+ CL_BulletMark(pos2);
+ }
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
+ else
+ {
+ rnd = rand() & 3;
+ if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
+ else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
+ else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
+ }
+}
+
+// #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
+void VM_CL_te_superspikequad (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ int rnd;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ if (cl_particles_bulletimpacts.integer)
+ {
+ CL_SparkShower(pos2, vec3_origin, 30, 1);
+ CL_Smoke(pos2, vec3_origin, 30);
+ CL_BulletMark(pos2);
+ }
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
+ else
+ {
+ rnd = rand() & 3;
+ if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
+ else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
+ else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
+ }
+}
+
+// #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
+void VM_CL_te_explosionquad (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 10);
+ CL_ParticleExplosion(pos2);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ if (gamemode != GAME_NEXUIZ)
+ S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
+}
+
+// #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
+void VM_CL_te_smallflash (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 10);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+}
+
+// #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
+void VM_CL_te_customflash (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM3)[0], PRVM_G_VECTOR(OFS_PARM3)[1], PRVM_G_VECTOR(OFS_PARM3)[2], PRVM_G_FLOAT(OFS_PARM1) / PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM2), 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+}
+
+// #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_gunshot (void)
+{
+ float *pos;
+ vec3_t pos2;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ CL_SparkShower(pos2, vec3_origin, 15, 1);
+ CL_Smoke(pos2, vec3_origin, 15);
+ CL_BulletMark(pos2);
+}
+
+// #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_spike (void)
+{
+ float *pos;
+ vec3_t pos2;
+ int rnd;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ if (cl_particles_bulletimpacts.integer)
+ {
+ CL_SparkShower(pos2, vec3_origin, 15, 1);
+ CL_Smoke(pos2, vec3_origin, 15);
+ CL_BulletMark(pos2);
+ }
+ if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
+ else
+ {
+ rnd = rand() & 3;
+ if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
+ else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
+ else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
+ }
+}
+
+// #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_superspike (void)
+{
+ float *pos;
+ vec3_t pos2;
+ int rnd;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ if (cl_particles_bulletimpacts.integer)
+ {
+ CL_SparkShower(pos2, vec3_origin, 30, 1);
+ CL_Smoke(pos2, vec3_origin, 30);
+ CL_BulletMark(pos2);
+ }
+ if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
+ else
+ {
+ rnd = rand() & 3;
+ if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
+ else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
+ else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
+ }
+}
+
+// #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_explosion (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 10);
+ CL_ParticleExplosion(pos2);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ if (gamemode != GAME_NEXUIZ)
+ S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
+}
+
+// #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_tarexplosion (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 10);
+ CL_BlobExplosion(pos2);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ if (gamemode != GAME_NEXUIZ)
+ S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
+}
+
+// #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_wizspike (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 100, 0.12f, 0.50f, 0.12f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ CL_RunParticleEffect(pos2, vec3_origin, 20, 30);
+ S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
+}
+
+// #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_knightspike (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 100, 0.50f, 0.30f, 0.10f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ CL_RunParticleEffect(pos2, vec3_origin, 226, 20);
+ S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
+}
+
+// #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_lavasplash (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
+ CL_LavaSplash(PRVM_G_VECTOR(OFS_PARM0));
+}
+
+// #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_teleport (void)
+{
+ float *pos;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 200, 1.0f, 1.0f, 1.0f, 600, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ CL_TeleportSplash(pos);
+}
+
+// #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_explosion2 (void)
+{
+ float *pos;
+ vec3_t pos2, color;
+ matrix4x4_t tempmatrix;
+ int colorStart, colorLength;
+ unsigned char *tempcolor;
+ VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ colorStart = PRVM_G_FLOAT(OFS_PARM1);
+ colorLength = PRVM_G_FLOAT(OFS_PARM2);
+ CL_FindNonSolidLocation(pos, pos2, 10);
+ CL_ParticleExplosion2(pos2, colorStart, colorLength);
+ tempcolor = (unsigned char *)&palette_complete[(rand()%colorLength) + colorStart];
+ color[0] = tempcolor[0] * (2.0f / 255.0f);
+ color[1] = tempcolor[1] * (2.0f / 255.0f);
+ color[2] = tempcolor[2] * (2.0f / 255.0f);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ if (gamemode != GAME_NEXUIZ)
+ S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
+}
+
+
+static void VM_CL_NewBeam (int ent, float *start, float *end, model_t *m, qboolean lightning)
+{
+ beam_t *b;
+ int i;
+ extern entity_t *cl_csqcentities;
+ extern int cl_max_csqcentities;
+
+ if (ent >= cl_max_csqcentities)
+ CL_ExpandCSQCEntities(ent);
+
+ // override any beam with the same entity
+ for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++)
+ {
+ if (b->entity == ent && ent)
+ {
+ //b->entity = ent;
+ b->lightning = lightning;
+ b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0;
+ b->model = m;
+ b->endtime = cl.time + 0.2;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ return;
+ }
+ }
+
+ // find a free beam
+ for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->lightning = lightning;
+ b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0;
+ b->model = m;
+ b->endtime = cl.time + 0.2;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ return;
+ }
+ }
+ Con_Print("beam list overflow!\n");
+}
+
+// #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_lightning1 (void)
+{
+ VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
+ VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt, true);
+}
+
+// #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_lightning2 (void)
+{
+ VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
+ VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt2, true);
+}
+
+// #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_lightning3 (void)
+{
+ VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
+ VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt3, false);
+}
+
+// #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
+void VM_CL_te_beam (void)
+{
+ VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
+ VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_beam, false);
+}
+
+// #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
+void VM_CL_te_plasmaburn (void)
+{
+ float *pos;
+ vec3_t pos2;
+ matrix4x4_t tempmatrix;
+ VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ CL_FindNonSolidLocation(pos, pos2, 4);
+ Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
+ CL_AllocDlight(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ CL_PlasmaBurn(pos2);
+}
+
+
+//====================================================================
+//DP_QC_GETSURFACE
+
+void clippointtosurface(msurface_t *surface, vec3_t p, vec3_t out);
+static msurface_t *cl_getsurface(prvm_edict_t *ed, int surfacenum)
+{
+ int modelindex;
+ model_t *model = NULL;
+ if (!ed || ed->priv.server->free)
+ return NULL;
+ modelindex = ed->fields.client->modelindex;
+ if(!modelindex)
+ return NULL;
+ if(modelindex<0)
+ {
+ modelindex = -(modelindex+1);
+ if(modelindex < MAX_MODELS)
+ model = cl.csqc_model_precache[modelindex];
+ }
+ else
+ {
+ if(modelindex < MAX_MODELS)
+ model = cl.model_precache[modelindex];
+ }
+ if(!model)
+ return NULL;
+ if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces)
+ return NULL;
+ return model->data_surfaces + surfacenum + model->firstmodelsurface;
+}
+
+// #434 float(entity e, float s) getsurfacenumpoints
+void VM_CL_getsurfacenumpoints(void)
+{
+ msurface_t *surface;
+ // return 0 if no such surface
+ if (!(surface = cl_getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1))))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+
+ // note: this (incorrectly) assumes it is a simple polygon
+ PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices;
+}
+
+// #435 vector(entity e, float s, float n) getsurfacepoint
+void VM_CL_getsurfacepoint(void)
+{
+ prvm_edict_t *ed;
+ msurface_t *surface;
+ int pointnum;
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ if (!ed || ed->priv.server->free)
+ return;
+ if (!(surface = cl_getsurface(ed, PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ // note: this (incorrectly) assumes it is a simple polygon
+ pointnum = PRVM_G_FLOAT(OFS_PARM2);
+ if (pointnum < 0 || pointnum >= surface->num_vertices)
+ return;
+ // FIXME: implement rotation/scaling
+ VectorAdd(&(surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #436 vector(entity e, float s) getsurfacenormal
+void VM_CL_getsurfacenormal(void)
+{
+ msurface_t *surface;
+ vec3_t normal;
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ if (!(surface = cl_getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ // FIXME: implement rotation/scaling
+ // note: this (incorrectly) assumes it is a simple polygon
+ // note: this only returns the first triangle, so it doesn't work very
+ // well for curved surfaces or arbitrary meshes
+ TriangleNormal((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + 3, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + 6, normal);
+ VectorNormalize(normal);
+ VectorCopy(normal, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #437 string(entity e, float s) getsurfacetexture
+void VM_CL_getsurfacetexture(void)
+{
+ msurface_t *surface;
+ PRVM_G_INT(OFS_RETURN) = 0;
+ if (!(surface = cl_getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(surface->texture->name);
+}
+
+// #438 float(entity e, vector p) getsurfacenearpoint
+void VM_CL_getsurfacenearpoint(void)
+{
+ int surfacenum, best, modelindex;
+ vec3_t clipped, p;
+ vec_t dist, bestdist;
+ prvm_edict_t *ed;
+ model_t *model = NULL;
+ msurface_t *surface;
+ vec_t *point;
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ point = PRVM_G_VECTOR(OFS_PARM1);
+
+ if (!ed || ed->priv.server->free)
+ return;
+ modelindex = ed->fields.client->modelindex;
+ if(!modelindex)
+ return;
+ if(modelindex<0)
+ {
+ modelindex = -(modelindex+1);
+ if(modelindex < MAX_MODELS)
+ model = cl.csqc_model_precache[modelindex];
+ }
+ else
+ if(modelindex < MAX_MODELS)
+ model = cl.model_precache[modelindex];
+ if(!model)
+ return;
+ if (!model->num_surfaces)
+ return;
+
+ // FIXME: implement rotation/scaling
+ VectorSubtract(point, ed->fields.client->origin, p);
+ best = -1;
+ bestdist = 1000000000;
+ for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++)
+ {
+ surface = model->data_surfaces + surfacenum + model->firstmodelsurface;
+ // first see if the nearest point on the surface's box is closer than the previous match
+ clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0];
+ clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1];
+ clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2];
+ dist = VectorLength2(clipped);
+ if (dist < bestdist)
+ {
+ // it is, check the nearest point on the actual geometry
+ clippointtosurface(surface, p, clipped);
+ VectorSubtract(clipped, p, clipped);
+ dist += VectorLength2(clipped);
+ if (dist < bestdist)
+ {
+ // that's closer too, store it as the best match
+ best = surfacenum;
+ bestdist = dist;
+ }
+ }
+ }
+ PRVM_G_FLOAT(OFS_RETURN) = best;
+}
+
+// #439 vector(entity e, float s, vector p) getsurfaceclippedpoint
+void VM_CL_getsurfaceclippedpoint(void)
+{
+ prvm_edict_t *ed;
+ msurface_t *surface;
+ vec3_t p, out;
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ if (!ed || ed->priv.server->free)
+ return;
+ if (!(surface = cl_getsurface(ed, PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ // FIXME: implement rotation/scaling
+ VectorSubtract(PRVM_G_VECTOR(OFS_PARM2), ed->fields.client->origin, p);
+ clippointtosurface(surface, p, out);
+ // FIXME: implement rotation/scaling
+ VectorAdd(out, ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #443 void(entity e, entity tagentity, string tagname) setattachment
+void VM_CL_setattachment (void)
+{
+ prvm_edict_t *e = PRVM_G_EDICT(OFS_PARM0);
+ prvm_edict_t *tagentity = PRVM_G_EDICT(OFS_PARM1);
+ const char *tagname = PRVM_G_STRING(OFS_PARM2);
+ prvm_eval_t *v;
+ int modelindex;
+ model_t *model;
+
+ if (e == prog->edicts)
+ PF_WARNING("setattachment: can not modify world entity\n");
+ if (e->priv.server->free)
+ PF_WARNING("setattachment: can not modify free entity\n");
+
+ if (tagentity == NULL)
+ tagentity = prog->edicts;
+
+ v = PRVM_GETEDICTFIELDVALUE(e, csqc_fieldoff_tag_entity);
+ if (v)
+ v->edict = PRVM_EDICT_TO_PROG(tagentity);
+
+ v = PRVM_GETEDICTFIELDVALUE(e, csqc_fieldoff_tag_index);
+ if (v)
+ v->_float = 0;
+ if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
+ {
+ modelindex = (int)tagentity->fields.client->modelindex;
+ model = NULL;
+
+ if(modelindex)
+ {
+ if(modelindex<0)
+ {
+ modelindex = -(modelindex+1);
+ if(modelindex < MAX_MODELS)
+ model = cl.csqc_model_precache[modelindex];
+ }
+ else
+ if(modelindex < MAX_MODELS)
+ model = cl.model_precache[modelindex];
+ }
+
+ if (model)
+ {
+ v->_float = Mod_Alias_GetTagIndexForName(model, tagentity->fields.client->skin, tagname);
+ if (v->_float == 0)
+ Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity), model->name);
+ }
+ else
+ Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i but it has no model\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity));
+ }
+}
+
+/////////////////////////////////////////
+// DP_MD3_TAGINFO extension coded by VorteX
+
+int CL_GetTagIndex (prvm_edict_t *e, const char *tagname)
+{
+ int i;
+ model_t *m;
+
+ i = e->fields.client->modelindex;
+
+ if(!i)
+ return -1;
+ if(i<0)
+ {
+ i = -(i+1);
+ if(i >= MAX_MODELS)
+ return -1;
+ m = cl.csqc_model_precache[i];
+ }
+ else
+ if(i >= MAX_MODELS)
+ return -1;
+ else
+ m = cl.model_precache[i];
+
+ return Mod_Alias_GetTagIndexForName(m, e->fields.client->skin, tagname);
+};
+
+// Warnings/errors code:
+// 0 - normal (everything all-right)
+// 1 - world entity
+// 2 - free entity
+// 3 - null or non-precached model
+// 4 - no tags with requested index
+// 5 - runaway loop at attachment chain
+extern cvar_t cl_bob;
+extern cvar_t cl_bobcycle;
+extern cvar_t cl_bobup;
+int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
+{
+ prvm_eval_t *val;
+ int modelindex, reqframe, attachloop, i;
+ matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
+ prvm_edict_t *attachent;
+ model_t *model;
+
+ Matrix4x4_CreateIdentity(out); // warnings and errors return identical matrix
+
+ if (ent == prog->edicts)
+ return 1;
+ if (ent->priv.server->free)
+ return 2;
+
+ modelindex = (int)ent->fields.client->modelindex;
+
+ if(!modelindex)
+ return 3;
+ if(modelindex<0)
+ {
+ modelindex = -(modelindex+1);
+ if(modelindex >= MAX_MODELS)
+ return 3;
+ model = cl.csqc_model_precache[modelindex];
+ }
+ else
+ if(modelindex >= MAX_MODELS)
+ return 3;
+ else
+ model = cl.model_precache[modelindex];
+
+ if (ent->fields.client->frame >= 0 && ent->fields.client->frame < model->numframes && model->animscenes)
+ reqframe = model->animscenes[(int)ent->fields.client->frame].firstframe;
+ else
+ reqframe = 0; // if model has wrong frame, engine automatically switches to model first frame
+
+ // get initial tag matrix
+ if (tagindex)
+ {
+ int ret = Mod_Alias_GetTagMatrix(model, reqframe, tagindex - 1, &tagmatrix);
+ if (ret)
+ return ret;
+ }
+ else
+ Matrix4x4_CreateIdentity(&tagmatrix);
+
+ if ((val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_tag_entity)) && val->edict)
+ { // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity
+ attachloop = 0;
+ do
+ {
+ attachent = PRVM_EDICT_NUM(val->edict); // to this it entity our entity is attached
+ val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_tag_index);
+
+ model = NULL;
+ i = attachent->fields.client->modelindex;
+ if(i<0)
+ {
+ i = -(i+1);
+ if(i < MAX_MODELS)
+ model = cl.csqc_model_precache[i];
+ }
+ else
+ if(i < MAX_MODELS)
+ model = cl.model_precache[i];
+
+ if (model && val->_float >= 1 && model->animscenes && attachent->fields.client->frame >= 0 && attachent->fields.client->frame < model->numframes)
+ Mod_Alias_GetTagMatrix(model, model->animscenes[(int)attachent->fields.client->frame].firstframe, val->_float - 1, &attachmatrix);
+ else
+ Matrix4x4_CreateIdentity(&attachmatrix);
+
+ // apply transformation by child entity matrix
+ val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_scale);
+ if (val->_float == 0)
+ val->_float = 1;
+ Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], val->_float);
+ Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
+ out->m[0][3] = entitymatrix.m[0][3] + val->_float*(entitymatrix.m[0][0]*tagmatrix.m[0][3] + entitymatrix.m[0][1]*tagmatrix.m[1][3] + entitymatrix.m[0][2]*tagmatrix.m[2][3]);
+ out->m[1][3] = entitymatrix.m[1][3] + val->_float*(entitymatrix.m[1][0]*tagmatrix.m[0][3] + entitymatrix.m[1][1]*tagmatrix.m[1][3] + entitymatrix.m[1][2]*tagmatrix.m[2][3]);
+ out->m[2][3] = entitymatrix.m[2][3] + val->_float*(entitymatrix.m[2][0]*tagmatrix.m[0][3] + entitymatrix.m[2][1]*tagmatrix.m[1][3] + entitymatrix.m[2][2]*tagmatrix.m[2][3]);
+ Matrix4x4_Copy(&tagmatrix, out);
+
+ // finally transformate by matrix of tag on parent entity
+ Matrix4x4_Concat(out, &attachmatrix, &tagmatrix);
+ out->m[0][3] = attachmatrix.m[0][3] + attachmatrix.m[0][0]*tagmatrix.m[0][3] + attachmatrix.m[0][1]*tagmatrix.m[1][3] + attachmatrix.m[0][2]*tagmatrix.m[2][3];
+ out->m[1][3] = attachmatrix.m[1][3] + attachmatrix.m[1][0]*tagmatrix.m[0][3] + attachmatrix.m[1][1]*tagmatrix.m[1][3] + attachmatrix.m[1][2]*tagmatrix.m[2][3];
+ out->m[2][3] = attachmatrix.m[2][3] + attachmatrix.m[2][0]*tagmatrix.m[0][3] + attachmatrix.m[2][1]*tagmatrix.m[1][3] + attachmatrix.m[2][2]*tagmatrix.m[2][3];
+ Matrix4x4_Copy(&tagmatrix, out);
+
+ ent = attachent;
+ attachloop += 1;
+ if (attachloop > 255) // prevent runaway looping
+ return 5;
+ }
+ while ((val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_tag_entity)) && val->edict);
+ }
+
+ // normal or RENDER_VIEWMODEL entity (or main parent entity on attach chain)
+ val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_scale);
+ if (val->_float == 0)
+ val->_float = 1;
+ // Alias models have inverse pitch, bmodels can't have tags, so don't check for modeltype...
+ Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], val->_float);
+ Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
+ out->m[0][3] = entitymatrix.m[0][3] + val->_float*(entitymatrix.m[0][0]*tagmatrix.m[0][3] + entitymatrix.m[0][1]*tagmatrix.m[1][3] + entitymatrix.m[0][2]*tagmatrix.m[2][3]);
+ out->m[1][3] = entitymatrix.m[1][3] + val->_float*(entitymatrix.m[1][0]*tagmatrix.m[0][3] + entitymatrix.m[1][1]*tagmatrix.m[1][3] + entitymatrix.m[1][2]*tagmatrix.m[2][3]);
+ out->m[2][3] = entitymatrix.m[2][3] + val->_float*(entitymatrix.m[2][0]*tagmatrix.m[0][3] + entitymatrix.m[2][1]*tagmatrix.m[1][3] + entitymatrix.m[2][2]*tagmatrix.m[2][3]);
+
+ if ((val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_renderflags)) && (RF_VIEWMODEL & (int)val->_float))
+ {// RENDER_VIEWMODEL magic
+ Matrix4x4_Copy(&tagmatrix, out);
+
+ val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_scale);
+ if (val->_float == 0)
+ val->_float = 1;
+
+ Matrix4x4_CreateFromQuakeEntity(&entitymatrix, csqc_origin[0], csqc_origin[1], csqc_origin[2], csqc_angles[0], csqc_angles[1], csqc_angles[2], val->_float);
+ Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
+ out->m[0][3] = entitymatrix.m[0][3] + val->_float*(entitymatrix.m[0][0]*tagmatrix.m[0][3] + entitymatrix.m[0][1]*tagmatrix.m[1][3] + entitymatrix.m[0][2]*tagmatrix.m[2][3]);
+ out->m[1][3] = entitymatrix.m[1][3] + val->_float*(entitymatrix.m[1][0]*tagmatrix.m[0][3] + entitymatrix.m[1][1]*tagmatrix.m[1][3] + entitymatrix.m[1][2]*tagmatrix.m[2][3]);
+ out->m[2][3] = entitymatrix.m[2][3] + val->_float*(entitymatrix.m[2][0]*tagmatrix.m[0][3] + entitymatrix.m[2][1]*tagmatrix.m[1][3] + entitymatrix.m[2][2]*tagmatrix.m[2][3]);
+
+ /*
+ // Cl_bob, ported from rendering code
+ if (ent->fields.client->health > 0 && cl_bob.value && cl_bobcycle.value)
+ {
+ double bob, cycle;
+ // LordHavoc: this code is *weird*, but not replacable (I think it
+ // should be done in QC on the server, but oh well, quake is quake)
+ // LordHavoc: figured out bobup: the time at which the sin is at 180
+ // degrees (which allows lengthening or squishing the peak or valley)
+ cycle = sv.time/cl_bobcycle.value;
+ cycle -= (int)cycle;
+ if (cycle < cl_bobup.value)
+ cycle = sin(M_PI * cycle / cl_bobup.value);
+ else
+ cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
+ // bob is proportional to velocity in the xy plane
+ // (don't count Z, or jumping messes it up)
+ bob = sqrt(ent->fields.client->velocity[0]*ent->fields.client->velocity[0] + ent->fields.client->velocity[1]*ent->fields.client->velocity[1])*cl_bob.value;
+ bob = bob*0.3 + bob*0.7*cycle;
+ out->m[2][3] += bound(-7, bob, 4);
+ }
+ */
+ }
+ return 0;
+}
+
+// #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
+void VM_CL_gettagindex (void)
+{
+ prvm_edict_t *ent = PRVM_G_EDICT(OFS_PARM0);
+ const char *tag_name = PRVM_G_STRING(OFS_PARM1);
+ int modelindex, tag_index;
+
+ if (ent == prog->edicts)
+ PF_WARNING("gettagindex: can't affect world entity\n");
+ if (ent->priv.server->free)
+ PF_WARNING("gettagindex: can't affect free entity\n");
+
+ modelindex = (int)ent->fields.client->modelindex;
+ if(modelindex < 0)
+ modelindex = -(modelindex+1);
+ tag_index = 0;
+ if (modelindex <= 0 || modelindex >= MAX_MODELS)
+ Con_DPrintf("gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
+ else
+ {
+ tag_index = CL_GetTagIndex(ent, tag_name);
+ if (tag_index == 0)
+ Con_DPrintf("gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
+ }
+ PRVM_G_FLOAT(OFS_RETURN) = tag_index;
+}
+
+// #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
+void VM_CL_gettaginfo (void)
+{
+ prvm_edict_t *e = PRVM_G_EDICT(OFS_PARM0);
+ int tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
+ matrix4x4_t tag_matrix;
+ int returncode;
+
+ returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex);
+ Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN));
+
+ switch(returncode)
+ {
+ case 1:
+ PF_WARNING("gettagindex: can't affect world entity\n");
+ break;
+ case 2:
+ PF_WARNING("gettagindex: can't affect free entity\n");
+ break;
+ case 3:
+ Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
+ break;
+ case 4:
+ Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
+ break;
+ case 5:
+ Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
+ break;
+ }
+}
+
+//=================================================
+//[515]: here goes test/unfinished/etc.
+
+//[515]: check if it is what it should be
+void VM_WasFreed (void)
+{
+ prvm_edict_t *e;
+ VM_SAFEPARMCOUNT(1, VM_WasFreed);
+
+ e = PRVM_G_EDICT(OFS_PARM0);
+ if (!e->priv.required->free || (e->priv.required->free && (e->priv.required->freetime < 2 || (*prog->time - e->priv.required->freetime) > 0.5 )))
+ PRVM_G_FLOAT(OFS_RETURN) = false;
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = true;
+}
+
+void VM_CL_select_cube (void)
+{
+ int i;
+ int chain_of;
+ float *mins2, *maxs2;
+ prvm_edict_t *ent, *chain;
+ vec3_t mins1, maxs1;
+
+ VM_SAFEPARMCOUNT(2, VM_CL_select_cube);
+
+ // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another
+ if(!prog->flag & PRVM_FE_CHAIN)
+ PRVM_ERROR("VM_findchain: %s doesnt have a chain field !\n", PRVM_NAME);
+
+ chain_of = PRVM_ED_FindField("chain")->ofs;
+ chain = prog->edicts;
+
+ mins2 = PRVM_G_VECTOR(OFS_PARM0);
+ maxs2 = PRVM_G_VECTOR(OFS_PARM1);
+
+ ent = PRVM_NEXT_EDICT(prog->edicts);
+ for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
+ {
+ if (ent->priv.required->free)
+ continue;
+ VectorCopy(ent->fields.client->origin, mins1);
+ VectorAdd(mins1, ent->fields.client->maxs, maxs1);
+ VectorAdd(mins1, ent->fields.client->mins, mins1);
+ if (mins1[0] > maxs2[0] || mins1[1] > maxs2[1] || mins1[2] > maxs2[2])
+ continue;
+ if (maxs1[0] < mins2[0] || maxs1[1] < mins2[1] || maxs1[2] < mins2[2])
+ continue;
+ PRVM_E_INT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
+ chain = ent;
+ }
+
+ VM_RETURN_EDICT(chain);
+}
+
+void VM_CL_select_super (void)
+{
+/* int i;
+ int chain_of;
+ float *v[8];
+ prvm_edict_t *ent, *chain;
+ vec3_t mins1, maxs1;
+
+ VM_SAFEPARMCOUNT(8, VM_findchain);
+ for(i=0;i<8;i++)
+ v[i] = PRVM_G_VECTOR(OFS_PARM0+i*3);
+
+ // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another
+ if(!prog->flag & PRVM_FE_CHAIN)
+ PRVM_ERROR("VM_findchain: %s doesnt have a chain field !\n", PRVM_NAME);
+
+ chain_of = PRVM_ED_FindField("chain")->ofs;
+ chain = prog->edicts;
+
+ mins2 = PRVM_G_VECTOR(OFS_PARM0);
+ maxs2 = PRVM_G_VECTOR(OFS_PARM1);
+
+ ent = PRVM_NEXT_EDICT(prog->edicts);
+ for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
+ {
+ if (ent->priv.required->free)
+ continue;
+ VectorCopy(ent->fields.client->origin, mins1);
+ VectorAdd(mins1, ent->fields.client->maxs, maxs1);
+ VectorAdd(mins1, ent->fields.client->mins, mins1);
+ if (mins1[0] > maxs2[0] || mins1[1] > maxs2[1] || mins1[2] > maxs2[2])
+ continue;
+ if (maxs1[0] < mins2[0] || maxs1[1] < mins2[1] || maxs1[2] < mins2[2])
+ continue;
+ PRVM_E_INT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
+ chain = ent;
+ }
+
+ VM_RETURN_EDICT(chain);*/
+}
+
+static int Is_Text_Color (char c, char t)
+{
+ int a = 0;
+ char c2 = c - (c & 128);
+ char t2 = t - (t & 128);
+
+ if(c != '^' && c2 != '^') return 0;
+ if(t >= '0' && t <= '9') a = 1;
+ if(t2 >= '0' && t2 <= '9') a = 1;
+/* if(t >= 'A' && t <= 'Z') a = 2;
+ if(t2 >= 'A' && t2 <= 'Z') a = 2;
+
+ if(a == 1 && scr_colortext.integer > 0)
+ return 1;
+ if(a == 2 && scr_multifonts.integer > 0)
+ return 2;
+*/
+ return a;
+}
+
+void VM_uncolorstring (void) //#170
+{
+ const char *in;
+ char *out;
+ int k = 0, i = 0;
+
+ VM_SAFEPARMCOUNT(1, VM_uncolorstring);
+ in = PRVM_G_STRING(OFS_PARM0);
+ if(!in)
+ PRVM_ERROR ("VM_uncolorstring: %s: NULL\n", PRVM_NAME);
+ VM_CheckEmptyString (in);
+ out = VM_GetTempString();
+
+ while (in[k])
+ {
+ if(in[k+1])
+ if(Is_Text_Color(in[k], in[k+1]) == 1/* || (in[k] == '&' && in[k+1] == 'r')*/)
+ {
+ k += 2;
+ continue;
+ }
+ out[i] = in[k];
+ ++k;
+ ++i;
+ }
+}
+
+void VM_CL_selecttraceline (void)
+{
+ float *v1, *v2;
+ int ent, ignore, csqcents;
+
+ v1 = PRVM_G_VECTOR(OFS_PARM0);
+ v2 = PRVM_G_VECTOR(OFS_PARM1);
+ ignore = PRVM_G_FLOAT(OFS_PARM2);
+ csqcents = PRVM_G_FLOAT(OFS_PARM3);
+ ent = 0;
+
+ if((csqcents && ignore > cl_num_csqcentities) || (!csqcents && ignore > cl_num_entities))
+ {
+ Con_Printf("VM_CL_selecttraceline: out of entities\n");
+ return;
+ }
+ else
+ if(csqcents)
+ prog->globals.client->trace_fraction = CL_SelectTraceLine(v1, v2, prog->globals.client->trace_endpos, prog->globals.client->trace_plane_normal, &prog->globals.client->trace_ent, &cl_csqcentities[ignore].render, csqcents);
+ else
+ prog->globals.client->trace_fraction = CL_SelectTraceLine(v1, v2, prog->globals.client->trace_endpos, prog->globals.client->trace_plane_normal, &ent, &cl_entities[ignore].render, csqcents);
+ PRVM_G_FLOAT(OFS_RETURN) = ent;
+}
+
+void VM_charindex (void)
+{
+ const char *s;
+ s = PRVM_G_STRING(OFS_PARM0);
+ if(!s)
+ return;
+ if((unsigned)PRVM_G_FLOAT(OFS_PARM1) > strlen(s))
+ return;
+ PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(int)PRVM_G_FLOAT(OFS_PARM1)];
+}
+
+//#223 string(float c, ...) chr2str (FTE_STRINGS)
+void VM_chr2str (void)
+{
+ char *t;
+ int i;
+ t = VM_GetTempString();
+ for(i=0;i<prog->argc;i++)
+ t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
+ t[i] = 0;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t);
+}
+
+//#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
+void VM_strncmp (void)
+{
+ const char *s1, *s2;
+ VM_SAFEPARMCOUNT(1, VM_strncmp);
+ s1 = PRVM_G_STRING(OFS_PARM0);
+ s2 = PRVM_G_STRING(OFS_PARM1);
+ PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
+}
+
+//============================================================================
+//============================================================================
+
+prvm_builtin_t vm_cl_builtins[] = {
+0, // to be consistent with the old vm
+VM_CL_makevectors, // #1 void(vector ang) makevectors
+VM_CL_setorigin, // #2 void(entity e, vector o) setorigin
+VM_CL_setmodel, // #3 void(entity e, string m) setmodel
+VM_CL_setsize, // #4 void(entity e, vector min, vector max) setsize
+0,
+VM_break, // #6 void() break
+VM_random, // #7 float() random
+VM_CL_sound, // #8 void(entity e, float chan, string samp) sound
+VM_normalize, // #9 vector(vector v) normalize
+VM_error, // #10 void(string e) error
+VM_objerror, // #11 void(string e) objerror
+VM_vlen, // #12 float(vector v) vlen
+VM_vectoyaw, // #13 float(vector v) vectoyaw
+VM_CL_spawn, // #14 entity() spawn
+VM_remove, // #15 void(entity e) remove
+VM_CL_traceline, // #16 float(vector v1, vector v2, float tryents) traceline
+0,
+VM_find, // #18 entity(entity start, .string fld, string match) find
+VM_CL_precache_sound, // #19 void(string s) precache_sound
+VM_CL_precache_model, // #20 void(string s) precache_model
+0,
+VM_CL_findradius, // #22 entity(vector org, float rad) findradius
+0,
+0,
+VM_dprint, // #25 void(string s) dprint
+VM_ftos, // #26 void(string s) ftos
+VM_vtos, // #27 void(string s) vtos
+VM_coredump, // #28 void() coredump
+VM_traceon, // #29 void() traceon
+VM_traceoff, // #30 void() traceoff
+VM_eprint, // #31 void(entity e) eprint
+0,
+NULL, // #33
+VM_CL_droptofloor, // #34 float() droptofloor
+VM_CL_lightstyle, // #35 void(float style, string value) lightstyle
+VM_rint, // #36 float(float v) rint
+VM_floor, // #37 float(float v) floor
+VM_ceil, // #38 float(float v) ceil
+NULL, // #39
+VM_CL_checkbottom, // #40 float(entity e) checkbottom
+VM_CL_pointcontents, // #41 float(vector v) pointcontents
+NULL, // #42
+VM_fabs, // #43 float(float f) fabs
+0,
+VM_cvar, // #45 float(string s) cvar
+VM_localcmd, // #46 void(string s) localcmd
+VM_nextent, // #47 entity(entity e) nextent
+VM_CL_particle, // #48 void(vector o, vector d, float color, float count) particle
+VM_CL_changeyaw, // #49 void(entity ent, float ideal_yaw, float speed_yaw) ChangeYaw
+NULL, // #50
+VM_vectoangles, // #51 vector(vector v) vectoangles
+0, // #52 void(float to, float f) WriteByte
+0, // #53 void(float to, float f) WriteChar
+0, // #54 void(float to, float f) WriteShort
+0, // #55 void(float to, float f) WriteLong
+0, // #56 void(float to, float f) WriteCoord
+0, // #57 void(float to, float f) WriteAngle
+0, // #58 void(float to, string s) WriteString
+0,
+VM_sin, // #60 float(float f) sin (DP_QC_SINCOSSQRTPOW)
+VM_cos, // #61 float(float f) cos (DP_QC_SINCOSSQRTPOW)
+VM_sqrt, // #62 float(float f) sqrt (DP_QC_SINCOSSQRTPOW)
+VM_CL_changepitch, // #63 void(entity ent, float ideal_pitch, float speed_pitch) changepitch (DP_QC_CHANGEPITCH)
+VM_CL_tracetoss, // #64 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS)
+VM_etos, // #65 string(entity ent) etos (DP_QC_ETOS)
+NULL, // #66
+0, // #67
+0, // #68
+0, // #69
+0, // #70
+NULL, // #71
+VM_cvar_set, // #72 void(string var, string val) cvar_set
+0, // #73
+VM_CL_ambientsound, // #74 void(vector pos, string samp, float vol, float atten) ambientsound
+VM_CL_precache_model, // #75 string(string s) precache_model2
+VM_CL_precache_sound, // #76 string(string s) precache_sound2
+0, // #77
+VM_chr, // #78
+NULL, // #79
+NULL, // #80
+VM_stof, // #81 float(string s) stof (FRIK_FILE)
+NULL, // #82
+NULL, // #83
+NULL, // #84
+NULL, // #85
+NULL, // #86
+NULL, // #87
+NULL, // #88
+NULL, // #89
+VM_CL_tracebox, // #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX)
+VM_randomvec, // #91 vector() randomvec (DP_QC_RANDOMVEC)
+VM_CL_getlight, // #92 vector(vector org) getlight (DP_QC_GETLIGHT)
+PF_registercvar, // #93 float(string name, string value) registercvar (DP_REGISTERCVAR)
+VM_min, // #94 float(float a, floats) min (DP_QC_MINMAXBOUND)
+VM_max, // #95 float(float a, floats) max (DP_QC_MINMAXBOUND)
+VM_bound, // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND)
+VM_pow, // #97 float(float f, float f) pow (DP_QC_SINCOSSQRTPOW)
+VM_findfloat, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)
+VM_checkextension, // #99 float(string s) checkextension (the basis of the extension system)
+NULL, // #100
+NULL, // #101
+NULL, // #102
+NULL, // #103
+NULL, // #104
+NULL, // #105
+NULL, // #106
+NULL, // #107
+NULL, // #108
+NULL, // #109
+VM_fopen, // #110 float(string filename, float mode) fopen (FRIK_FILE)
+VM_fclose, // #111 void(float fhandle) fclose (FRIK_FILE)
+VM_fgets, // #112 string(float fhandle) fgets (FRIK_FILE)
+VM_fputs, // #113 void(float fhandle, string s) fputs (FRIK_FILE)
+VM_strlen, // #114 float(string s) strlen (FRIK_FILE)
+VM_strcat, // #115 string(string s1, string s2) strcat (FRIK_FILE)
+VM_substring, // #116 string(string s, float start, float length) substring (FRIK_FILE)
+VM_stov, // #117 vector(string) stov (FRIK_FILE)
+VM_strzone, // #118 string(string s) strzone (FRIK_FILE)
+VM_strunzone, // #119 void(string s) strunzone (FRIK_FILE)
+
+e10, e10, e10, e10, e10, e10, e10, e10, // #120-199
+e10, //#200-209
+0, //#210
+0, //#211
+0, //#212
+0, //#213
+0, //#214
+0, //#215
+0, //#216
+0, //#217
+VM_bitshift, //#218 float(float number, float quantity) bitshift (EXT_BITSHIFT)
+0, //#219
+0, //#220
+0, //#221
+VM_charindex, //#222 float(string str, float ofs) str2chr (FTE_STRINGS)
+VM_chr2str, //#223 string(float c, ...) chr2str (FTE_STRINGS)
+0, //#224
+0, //#225
+0, //#226
+0, //#227
+VM_strncmp, //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
+0,
+e10, e10, e10, e10, e10, e10, e10, // #230-299
+
+//======CSQC start=======//
+//3d world (buffer/buffering) operations
+VM_R_ClearScene, //#300 void() clearscene (EXT_CSQC)
+VM_R_AddEntities, //#301 void(float mask) addentities (EXT_CSQC)
+VM_R_AddEntity, //#302 void(entity ent) addentity (EXT_CSQC)
+VM_R_SetView, //#303 float(float property, ...) setproperty (EXT_CSQC)
+VM_R_RenderScene, //#304 void() renderscene (EXT_CSQC)
+VM_R_AddDynamicLight, //#305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC)
+VM_R_PolygonBegin, //#306 void(string texturename, float flag[, float is2d, float lines]) R_BeginPolygon
+VM_R_PolygonVertex, //#307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
+VM_R_PolygonEnd, //#308 void() R_EndPolygon
+0, //#309
+
+//maths stuff that uses the current view settings
+VM_CL_unproject, //#310 vector (vector v) cs_unproject (EXT_CSQC)
+VM_CL_project, //#311 vector (vector v) cs_project (EXT_CSQC)
+0, //#312
+0, //#313
+0, //#314
+
+//2d (immediate) operations
+VM_drawline, //#315 void(float width, vector pos1, vector pos2, float flag) drawline (EXT_CSQC)
+VM_iscachedpic, //#316 float(string name) iscachedpic (EXT_CSQC)
+VM_precache_pic, //#317 string(string name, float trywad) precache_pic (EXT_CSQC)
+VM_getimagesize, //#318 vector(string picname) draw_getimagesize (EXT_CSQC)
+VM_freepic, //#319 void(string name) freepic (EXT_CSQC)
+VM_drawcharacter, //#320 float(vector position, float character, vector scale, vector rgb, float alpha, float flag) drawcharacter (EXT_CSQC)
+VM_drawstring, //#321 float(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring (EXT_CSQC)
+VM_drawpic, //#322 float(vector position, string pic, vector size, vector rgb, float alpha, float flag) drawpic (EXT_CSQC)
+VM_drawfill, //#323 float(vector position, vector size, vector rgb, float alpha, float flag) drawfill (EXT_CSQC)
+VM_drawsetcliparea, //#324 void(float x, float y, float width, float height) drawsetcliparea
+VM_drawresetcliparea, //#325 void(void) drawresetcliparea
+0, //#326
+0, //#327
+0, //#328
+0, //#329
+
+VM_CL_getstatf, //#330 float(float stnum) getstatf (EXT_CSQC)
+VM_CL_getstati, //#331 float(float stnum) getstati (EXT_CSQC)
+VM_CL_getstats, //#332 string(float firststnum) getstats (EXT_CSQC)
+VM_CL_setmodelindex, //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
+VM_CL_modelnameforindex, //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
+VM_CL_particleeffectnum, //#335 float(string effectname) particleeffectnum (EXT_CSQC)
+VM_CL_trailparticles, //#336 void(entity ent, float effectnum, vector start, vector end) trailparticles (EXT_CSQC)
+VM_CL_pointparticles, //#337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC)
+VM_CL_centerprint, //#338 void(string s) cprint (EXT_CSQC)
+VM_print, //#339 void(string s) print (EXT_CSQC)
+VM_keynumtostring, //#340 string(float keynum) keynumtostring (EXT_CSQC)
+VM_stringtokeynum, //#341 float(string keyname) stringtokeynum (EXT_CSQC)
+VM_CL_getkeybind, //#342 string(float keynum) getkeybind (EXT_CSQC)
+VM_CL_setcursormode, //#343 void(float usecursor) setcursormode (EXT_CSQC)
+VM_getmousepos, //#344 vector() getmousepos (EXT_CSQC)
+VM_CL_getinputstate, //#345 float(float framenum) getinputstate (EXT_CSQC)
+VM_CL_setsensitivityscale, //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
+VM_CL_runplayerphysics, //#347 void() runstandardplayerphysics (EXT_CSQC)
+VM_CL_getplayerkey, //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
+VM_CL_isdemo, //#349 float() isdemo (EXT_CSQC)
+VM_isserver, //#350 float() isserver (EXT_CSQC)
+VM_CL_setlistener, //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
+VM_CL_registercmd, //#352 void(string cmdname) registercommand (EXT_CSQC)
+VM_WasFreed, //#353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too)
+VM_CL_playernum, //#354 float() playernum
+VM_CL_onground, //#355 float() cl_onground (EXT_CSQC)
+VM_charindex, //#356 float(string s, float num) charindex
+VM_CL_selecttraceline, //#357 float(vector start, vector end, float ignore, float csqcents) selecttraceline
+0, //#358
+0, //#359
+VM_CL_ReadByte, //#360 float() readbyte (EXT_CSQC)
+VM_CL_ReadChar, //#361 float() readchar (EXT_CSQC)
+VM_CL_ReadShort, //#362 float() readshort (EXT_CSQC)
+VM_CL_ReadLong, //#363 float() readlong (EXT_CSQC)
+VM_CL_ReadCoord, //#364 float() readcoord (EXT_CSQC)
+VM_CL_ReadAngle, //#365 float() readangle (EXT_CSQC)
+VM_CL_ReadString, //#366 string() readstring (EXT_CSQC)
+VM_CL_ReadFloat, //#367 float() readfloat (EXT_CSQC)
+0, //#368
+0, //#369
+0, //#370
+0, //#371
+0, //#372
+0, //#373
+0, //#374
+0, //#375
+0, //#376
+0, //#377
+0, //#378
+0, //#379
+0, //#380
+0, //#381
+0, //#382
+0, //#383
+0, //#384
+0, //#385
+0, //#386
+0, //#387
+0, //#388
+0, //#389
+0, //#390
+0, //#391
+0, //#392
+0, //#393
+0, //#394
+0, //#395
+0, //#396
+0, //#397
+0, //#398
+0, //#399
+//=========CSQC end========//
+
+VM_copyentity, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY)
+0,
+VM_findchain, // #402 entity(.string fld, string match) findchain (DP_QC_FINDCHAIN)
+VM_findchainfloat, // #403 entity(.float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT)
+VM_CL_effect, // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
+VM_CL_te_blood, // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
+VM_CL_te_bloodshower, // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
+VM_CL_te_explosionrgb, // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
+VM_CL_te_particlecube, // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
+VM_CL_te_particlerain, // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
+VM_CL_te_particlesnow, // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
+VM_CL_te_spark, // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK)
+VM_CL_te_gunshotquad, // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
+VM_CL_te_spikequad, // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
+VM_CL_te_superspikequad, // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
+VM_CL_te_explosionquad, // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
+VM_CL_te_smallflash, // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
+VM_CL_te_customflash, // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
+VM_CL_te_gunshot, // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_spike, // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_superspike, // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_explosion, // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_tarexplosion, // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_wizspike, // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_knightspike, // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_lavasplash, // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_teleport, // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_explosion2, // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_lightning1, // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_lightning2, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_lightning3, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
+VM_CL_te_beam, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
+VM_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS)
+VM_CL_te_plasmaburn, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
+VM_CL_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE)
+VM_CL_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE)
+VM_CL_getsurfacenormal, // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE)
+VM_CL_getsurfacetexture, // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE)
+VM_CL_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)
+VM_CL_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)
+0, // #440
+VM_tokenize, // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND)
+VM_argv, // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND)
+VM_CL_setattachment, // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS)
+VM_search_begin, // #444 float(string pattern, float caseinsensitive, float quiet) search_begin (DP_FS_SEARCH)
+VM_search_end, // #445 void(float handle) search_end (DP_FS_SEARCH)
+VM_search_getsize, // #446 float(float handle) search_getsize (DP_FS_SEARCH)
+VM_search_getfilename, // #447 string(float handle, float num) search_getfilename (DP_FS_SEARCH)
+VM_cvar_string, // #448 string(string s) cvar_string (DP_QC_CVAR_STRING)
+VM_findflags, // #449 entity(entity start, .float fld, float match) findflags (DP_QC_FINDFLAGS)
+VM_findchainflags, // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS)
+VM_CL_gettagindex, // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
+VM_CL_gettaginfo, // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
+0, // #453
+0, // #454
+0, // #455
+NULL, // #456
+NULL, // #457
+NULL, // #458
+NULL, // #459
+VM_buf_create, // #460 float() buf_create (DP_QC_STRINGBUFFERS)
+VM_buf_del, // #461 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS)
+VM_buf_getsize, // #462 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS)
+VM_buf_copy, // #463 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS)
+VM_buf_sort, // #464 void(float bufhandle, float sortpower, float backward) buf_sort (DP_QC_STRINGBUFFERS)
+VM_buf_implode, // #465 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS)
+VM_bufstr_get, // #466 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS)
+VM_bufstr_set, // #467 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS)
+VM_bufstr_add, // #468 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS)
+VM_bufstr_free, // #469 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS)
+e10, e10, e10 // #470-499 (LordHavoc)
+};
+
+const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
+
+void VM_CL_Cmd_Init(void)
+{
+}
+
+void VM_CL_Cmd_Reset(void)
+{
+}
+
struct cmd_function_s *next;
const char *name;
xcommand_t function;
+ qboolean csqcfunc;
} cmd_function_t;
static int cmd_argc;
{
if (!strcmp (cmd_name, cmd->name))
{
- Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
- return;
+ if (function)
+ {
+ Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
+ return;
+ }
+ else //[515]: csqc
+ {
+ cmd->csqcfunc = true;
+ return;
+ }
}
}
cmd = (cmd_function_t *)Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
cmd->name = cmd_name;
cmd->function = function;
+ if(!function) //[515]: csqc
+ cmd->csqcfunc = true;
cmd->next = cmd_functions;
// insert it at the right alphanumeric position
return buf;
}
+void Cmd_ClearCsqcFuncs (void)
+{
+ cmd_function_t *cmd;
+ for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+ cmd->csqcfunc = false;
+}
+
+qboolean CL_VM_ConsoleCommand (const char *cmd);
/*
============
Cmd_ExecuteString
{
if (!strcasecmp (cmd_argv[0],cmd->name))
{
- cmd->function ();
+ if(cmd->function && !cmd->csqcfunc)
+ cmd->function ();
+ else
+ if(CL_VM_ConsoleCommand (text)) //[515]: csqc
+ return;
+ else
+ if(cmd->function)
+ cmd->function ();
cmd_tokenizebufferpos = oldpos;
return;
}
char keyname[64];
char entfilename[MAX_QPATH];
strcpy(message, "^1**ERROR**^7");
+ p = 0;
f = FS_Open(t->filenames[i], "rb", true, false);
if(f)
{
--- /dev/null
+#include "quakedef.h"
+#include "progsvm.h"
+#include "clprogdefs.h"
+#include "csprogs.h"
+
+//============================================================================
+// Client prog handling
+//[515]: omg !!! optimize it ! a lot of hacks here and there also :P
+
+#define CSQC_RETURNVAL prog->globals.generic[OFS_RETURN]
+#define CSQC_BEGIN csqc_tmpprog=prog;prog=0;PRVM_SetProg(PRVM_CLIENTPROG);
+#define CSQC_END prog=csqc_tmpprog;
+static prvm_prog_t *csqc_tmpprog;
+
+//[515]: these are required funcs
+#define CL_F_INIT "CSQC_Init"
+#define CL_F_INPUTEVENT "CSQC_InputEvent"
+#define CL_F_UPDATEVIEW "CSQC_UpdateView"
+#define CL_F_CONSOLECOMMAND "CSQC_ConsoleCommand"
+#define CL_F_SHUTDOWN "CSQC_Shutdown"
+
+//[515]: these are optional
+#define CL_F_PARSE_TEMPENTITY "CSQC_Parse_TempEntity" //[515]: very helpfull when you want to create your own particles/decals/etc for effects that allready exist
+#define CL_F_PARSE_STUFFCMD "CSQC_Parse_StuffCmd"
+#define CL_F_PARSE_PRINT "CSQC_Parse_Print"
+#define CL_F_PARSE_CENTERPRINT "CSQC_Parse_CenterPrint"
+#define CL_F_ENT_UPDATE "CSQC_Ent_Update"
+#define CL_F_ENT_REMOVE "CSQC_Ent_Remove"
+#define CL_F_EVENT "CSQC_Event" //[515]: engine call this for its own needs
+ //so csqc can do some things according to what engine it's running on
+ //example: to say about edicts increase, whatever...
+
+#define CSQC_PRINTBUFFERLEN 8192 //[515]: enough ?
+
+static char *cl_required_func[] =
+{
+ CL_F_INIT,
+ CL_F_INPUTEVENT,
+ CL_F_UPDATEVIEW,
+ CL_F_CONSOLECOMMAND,
+ CL_F_SHUTDOWN
+};
+
+static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*);
+
+unsigned int csqc_drawmask = 0;
+static char *csqc_printtextbuf = NULL;
+static unsigned short *csqc_sv2csqcents; //[515]: server entities numbers on client side. FIXME : make pointers instead of numbers ?
+
+static mfunction_t *CSQC_Parse_TempEntity;
+static mfunction_t *CSQC_Parse_StuffCmd;
+static mfunction_t *CSQC_Parse_Print;
+static mfunction_t *CSQC_Parse_CenterPrint;
+static mfunction_t *CSQC_Ent_Update;
+static mfunction_t *CSQC_Ent_Remove;
+static mfunction_t *CSQC_Event;
+
+static int csqc_fieldoff_alpha;
+static int csqc_fieldoff_colormod;
+static int csqc_fieldoff_effects;
+int csqc_fieldoff_scale;
+int csqc_fieldoff_renderflags;
+int csqc_fieldoff_tag_entity;
+int csqc_fieldoff_tag_index;
+
+qboolean csqc_loaded = false;
+
+extern entity_t *cl_csqcentities;
+extern unsigned char *cl_csqcentities_active;
+extern int cl_num_csqcentities;
+
+vec3_t csqc_origin, csqc_angles;
+static double csqc_frametime = 0;
+int csqc_buttons;
+
+static mempool_t *csqc_mempool;
+
+static void CL_VM_FindEdictFieldOffsets (void)
+{
+ csqc_fieldoff_alpha = PRVM_ED_FindFieldOffset("alpha");
+ csqc_fieldoff_scale = PRVM_ED_FindFieldOffset("scale");
+ csqc_fieldoff_colormod = PRVM_ED_FindFieldOffset("colormod");
+ csqc_fieldoff_renderflags = PRVM_ED_FindFieldOffset("renderflags");
+ csqc_fieldoff_effects = PRVM_ED_FindFieldOffset("effects");
+ csqc_fieldoff_tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
+ csqc_fieldoff_tag_index = PRVM_ED_FindFieldOffset("tag_index");
+
+ CSQC_Parse_TempEntity = PRVM_ED_FindFunction (CL_F_PARSE_TEMPENTITY);
+ CSQC_Parse_StuffCmd = PRVM_ED_FindFunction (CL_F_PARSE_STUFFCMD);
+ CSQC_Parse_Print = PRVM_ED_FindFunction (CL_F_PARSE_PRINT);
+ CSQC_Parse_CenterPrint = PRVM_ED_FindFunction (CL_F_PARSE_CENTERPRINT);
+ CSQC_Ent_Update = PRVM_ED_FindFunction (CL_F_ENT_UPDATE);
+ CSQC_Ent_Remove = PRVM_ED_FindFunction (CL_F_ENT_REMOVE);
+ CSQC_Event = PRVM_ED_FindFunction (CL_F_EVENT);
+
+ if(CSQC_Parse_Print)
+ {
+ csqc_printtextbuf = Mem_Alloc(csqc_mempool, CSQC_PRINTBUFFERLEN);
+ csqc_printtextbuf[0] = 0;
+ }
+}
+
+void CL_VM_Error (const char *format, ...) //[515]: hope it will be never executed =)
+{
+ char errorstring[4096];
+ va_list argptr;
+
+ va_start (argptr, format);
+ dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
+ va_end (argptr);
+// Con_Printf( "CL_VM_Error: %s\n", errorstring );
+
+ PRVM_Crash();
+ csqc_loaded = false;
+ Mem_FreePool(&csqc_mempool);
+
+ Cvar_SetValueQuick(&csqc_progcrc, 0);
+
+// Host_AbortCurrentFrame(); //[515]: hmmm... if server says it needs csqc then client MUST disconnect
+ Host_Error(va("CL_VM_Error: %s", errorstring));
+}
+
+//[515]: set globals before calling R_UpdateView, WEIRD CRAP
+static void CSQC_SetGlobals (void)
+{
+ //extern cvar_t sv_accelerate, sv_friction, sv_gravity, sv_stopspeed, sv_maxspeed;
+
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ prog->globals.client->frametime = cl.time - csqc_frametime;
+ csqc_frametime = cl.time;
+ prog->globals.client->servercommandframe = cl.servermovesequence;
+ prog->globals.client->clientcommandframe = cl.movemessages;
+ VectorCopy(cl.viewangles, prog->globals.client->input_angles);
+ VectorCopy(cl.viewangles, csqc_angles);
+ prog->globals.client->input_buttons = csqc_buttons;
+ VectorSet(prog->globals.client->input_movevalues, cl.cmd.forwardmove, cl.cmd.sidemove, cl.cmd.upmove);
+ //VectorCopy(cl.movement_origin, csqc_origin);
+ VectorCopy(cl_entities[cl.viewentity].render.origin, csqc_origin);
+ VectorCopy(csqc_origin, prog->globals.client->pmove_org);
+ prog->globals.client->maxclients = cl.maxclients;
+ //VectorCopy(cl.movement_velocity, prog->globals.client->pmove_vel);
+ VectorCopy(cl.velocity, prog->globals.client->pmove_vel);
+ CSQC_END
+}
+
+static void CSQC_Predraw (prvm_edict_t *ed)
+{
+ int b;
+ if(!ed->fields.client->predraw)
+ return;
+ b = prog->globals.client->self;
+ prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
+ PRVM_ExecuteProgram(ed->fields.client->predraw, "CSQC_Predraw: NULL function\n");
+ prog->globals.client->self = b;
+}
+
+static void CSQC_Think (prvm_edict_t *ed)
+{
+ int b;
+ if(ed->fields.client->think)
+ if(ed->fields.client->nextthink && ed->fields.client->nextthink <= *prog->time)
+ {
+ ed->fields.client->nextthink = 0;
+ b = prog->globals.client->self;
+ prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
+ PRVM_ExecuteProgram(ed->fields.client->think, "CSQC_Think: NULL function\n");
+ prog->globals.client->self = b;
+ }
+}
+
+//[515]: weird too
+static qboolean CSQC_EdictToEntity (prvm_edict_t *ed, entity_t *e)
+{
+ int i;
+ prvm_eval_t *val;
+
+ i = ed->fields.client->modelindex;
+ e->state_current.modelindex = 0;
+ if(i >= MAX_MODELS || i <= -MAX_MODELS) //[515]: make work as error ?
+ {
+ Con_Print("CSQC_EdictToEntity: modelindex >= MAX_MODELS\n");
+ ed->fields.client->modelindex = 0;
+ }
+ else
+ e->state_current.modelindex = i;
+ if(!e->state_current.modelindex)
+ return false;
+
+ e->state_current.time = cl.time;
+
+ i = 0;
+ if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_renderflags)) && val->_float)
+ {
+ i = val->_float;
+ if(i & RF_VIEWMODEL) e->state_current.flags |= RENDER_VIEWMODEL;
+ if(i & RF_EXTERNALMODEL)e->state_current.flags |= RENDER_EXTERIORMODEL;
+ if(i & RF_DEPTHHACK) e->state_current.effects |= EF_NODEPTHTEST;
+ if(i & RF_ADDITIVE) e->state_current.effects |= EF_ADDITIVE;
+ }
+
+ if(i & RF_USEAXIS) //FIXME!!!
+ VectorCopy(ed->fields.client->angles, e->persistent.newangles);
+ else
+ VectorCopy(ed->fields.client->angles, e->persistent.newangles);
+
+ VectorCopy(ed->fields.client->origin, e->persistent.neworigin);
+ VectorCopy(ed->fields.client->origin, e->state_current.origin);
+ e->state_current.colormap = ed->fields.client->colormap;
+ e->state_current.effects = ed->fields.client->effects;
+ e->state_current.frame = ed->fields.client->frame;
+ e->state_current.skin = ed->fields.client->skin;
+
+ if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_alpha)) && val->_float) e->state_current.alpha = val->_float*255;
+ if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_scale)) && val->_float) e->state_current.scale = val->_float*16;
+ if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_colormod)) && VectorLength2(val->vector)) VectorScale(val->vector, 32, e->state_current.colormod);
+ if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_effects)) && val->_float) e->state_current.effects = val->_float;
+ if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_tag_entity)) && val->edict)
+ {
+ e->state_current.tagentity = val->edict;
+ if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_tag_index)) && val->_float)
+ e->state_current.tagindex = val->_float;
+ }
+
+ return true;
+}
+
+void CSQC_ClearCSQCEntities (void)
+{
+ memset(cl_csqcentities_active, 0, sizeof(cl_csqcentities_active));
+ cl_num_csqcentities = 0;
+ csqc_drawmask = 0;
+}
+
+void CL_ExpandCSQCEntities (int num);
+
+void CSQC_RelinkCSQCEntities (void)
+{
+ int i;
+ entity_t *e;
+ prvm_edict_t *ed;
+
+ *prog->time = cl.time;
+ for(i=1;i<prog->num_edicts;i++)
+ {
+ if(i >= cl_max_csqcentities)
+ CL_ExpandCSQCEntities(i);
+
+ e = &cl_csqcentities[i];
+ ed = &prog->edicts[i];
+ if(ed->priv.required->free)
+ {
+ e->state_current.active = false;
+ cl_csqcentities_active[i] = false;
+ continue;
+ }
+ VectorAdd(ed->fields.client->origin, ed->fields.client->mins, ed->fields.client->absmin);
+ VectorAdd(ed->fields.client->origin, ed->fields.client->maxs, ed->fields.client->absmax);
+ CSQC_Think(ed);
+ if(ed->priv.required->free)
+ {
+ e->state_current.active = false;
+ cl_csqcentities_active[i] = false;
+ continue;
+ }
+ CSQC_Predraw(ed);
+ if(ed->priv.required->free)
+ {
+ e->state_current.active = false;
+ cl_csqcentities_active[i] = false;
+ continue;
+ }
+ if(!cl_csqcentities_active[i])
+ if(!((int)ed->fields.client->drawmask & csqc_drawmask))
+ continue;
+
+ e->state_previous = e->state_current;
+ e->state_current = defaultstate;
+ if((cl_csqcentities_active[i] = CSQC_EdictToEntity(ed, e)))
+ {
+ if(!e->state_current.active)
+ {
+ if(!e->state_previous.active)
+ VectorCopy(ed->fields.client->origin, e->persistent.trail_origin);//[515]: hack to make good gibs =/
+ e->state_previous = e->state_current;
+ e->state_current.active = true;
+ }
+ e->persistent.lerpdeltatime = 0;//prog->globals.client->frametime;
+ cl_num_csqcentities++;
+ }
+ }
+}
+
+//[515]: omfg... it's all weird =/
+void CSQC_AddEntity (int n)
+{
+ cl_csqcentities_active[n] = true;
+}
+
+qboolean CL_VM_InputEvent (qboolean pressed, int key)
+{
+ qboolean r;
+ if(!csqc_loaded)
+ return false;
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_G_FLOAT(OFS_PARM0) = pressed;
+ PRVM_G_FLOAT(OFS_PARM1) = key;
+ PRVM_ExecuteProgram (prog->globals.client->CSQC_InputEvent, CL_F_INPUTEVENT);
+ r = CSQC_RETURNVAL;
+ CSQC_END
+ return r;
+}
+
+qboolean CL_VM_UpdateView (void)
+{
+// vec3_t oldangles;
+ if(!csqc_loaded)
+ return false;
+ CSQC_BEGIN
+ //VectorCopy(cl.viewangles, oldangles);
+ *prog->time = cl.time;
+ CSQC_SetGlobals();
+ csqc_drawmask = 0;
+ cl_num_csqcentities = 0;
+ PRVM_ExecuteProgram (prog->globals.client->CSQC_UpdateView, CL_F_UPDATEVIEW);
+ //VectorCopy(oldangles, cl.viewangles);
+ CSQC_END
+ csqc_frame = false;
+ return true;
+}
+
+qboolean CL_VM_ConsoleCommand (const char *cmd)
+{
+ qboolean r;
+ if(!csqc_loaded)
+ return false;
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(cmd);
+ PRVM_ExecuteProgram (prog->globals.client->CSQC_ConsoleCommand, CL_F_CONSOLECOMMAND);
+ r = CSQC_RETURNVAL;
+ CSQC_END
+ return r;
+}
+
+qboolean CL_VM_Parse_TempEntity (void)
+{
+ int t;
+ qboolean r;
+ if(!csqc_loaded || !CSQC_Parse_TempEntity)
+ return false;
+ t = msg_readcount;
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_ExecuteProgram ((func_t)(CSQC_Parse_TempEntity - prog->functions), CL_F_PARSE_TEMPENTITY);
+ r = CSQC_RETURNVAL;
+ CSQC_END
+ if(!r)
+ {
+ msg_readcount = t;
+ msg_badread = false;
+ }
+ return r;
+}
+
+void CL_VM_Parse_StuffCmd (const char *msg)
+{
+ if(!csqc_loaded) //[515]: add more here
+ if(msg[0] == 'c')
+ if(msg[1] == 's')
+ if(msg[2] == 'q')
+ if(msg[3] == 'c')
+ {
+ Cvar_SetQuick(&csqc_progcrc, "0");
+ csqc_progcrc.flags = 0;
+ Cmd_ExecuteString (msg, src_command);
+ csqc_progcrc.flags = CVAR_READONLY;
+ return;
+ }
+ if(!csqc_loaded || !CSQC_Parse_StuffCmd)
+ {
+ Cbuf_AddText(msg);
+ return;
+ }
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg);
+ PRVM_ExecuteProgram ((func_t)(CSQC_Parse_StuffCmd - prog->functions), CL_F_PARSE_STUFFCMD);
+ CSQC_END
+}
+
+static void CL_VM_Parse_Print (const char *msg)
+{
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg);
+ PRVM_ExecuteProgram ((func_t)(CSQC_Parse_Print - prog->functions), CL_F_PARSE_PRINT);
+ CSQC_END
+}
+
+void CSQC_AddPrintText (const char *msg)
+{
+ int i;
+ if(!csqc_loaded || !CSQC_Parse_Print)
+ {
+ Con_Print(msg);
+ return;
+ }
+ i = strlen(msg)-1;
+ if(msg[i] != '\n' && msg[i] != '\r')
+ {
+ if(strlen(csqc_printtextbuf)+i >= CSQC_PRINTBUFFERLEN)
+ {
+ CL_VM_Parse_Print(csqc_printtextbuf);
+ csqc_printtextbuf[0] = 0;
+ }
+ else
+ strcat(csqc_printtextbuf, msg);
+ return;
+ }
+ strcat(csqc_printtextbuf, msg);
+ CL_VM_Parse_Print(csqc_printtextbuf);
+ csqc_printtextbuf[0] = 0;
+}
+
+void CL_VM_Parse_CenterPrint (const char *msg)
+{
+ if(!csqc_loaded || !CSQC_Parse_CenterPrint)
+ {
+ SCR_CenterPrint((char*)msg);
+ return;
+ }
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg);
+ PRVM_ExecuteProgram ((func_t)(CSQC_Parse_CenterPrint - prog->functions), CL_F_PARSE_CENTERPRINT);
+ CSQC_END
+}
+
+float CL_VM_Event (float event) //[515]: needed ? I'd say "YES", but don't know for what :D
+{
+ float r;
+ if(!csqc_loaded || !CSQC_Event)
+ return 0;
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_G_FLOAT(OFS_PARM0) = event;
+ PRVM_ExecuteProgram ((func_t)(CSQC_Event - prog->functions), CL_F_EVENT);
+ r = CSQC_RETURNVAL;
+ CSQC_END
+ return r;
+}
+
+void CSQC_ReadEntities (void)
+{
+ unsigned short entnum, oldself, realentnum;
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ oldself = prog->globals.client->self;
+ while(1)
+ {
+ entnum = MSG_ReadShort();
+ if(!entnum)
+ return;
+ realentnum = entnum & 0x7FFF;
+ prog->globals.client->self = csqc_sv2csqcents[realentnum];
+ if(entnum & 0x8000)
+ {
+ if(prog->globals.client->self)
+ {
+ PRVM_ExecuteProgram((func_t)(CSQC_Ent_Remove - prog->functions), CL_F_ENT_REMOVE);
+ csqc_sv2csqcents[realentnum] = 0;
+ }
+ else
+ Con_Printf("Smth bad happens in csqc...\n"); //[515]: never happens ?
+ }
+ else
+ {
+ if(!prog->globals.client->self)
+ {
+ prvm_edict_t *ed;
+ ed = PRVM_ED_Alloc();
+ ed->fields.client->entnum = realentnum;
+ prog->globals.client->self = csqc_sv2csqcents[realentnum] = PRVM_EDICT_TO_PROG(ed);
+ PRVM_G_FLOAT(OFS_PARM0) = 1;
+ }
+ else
+ PRVM_G_FLOAT(OFS_PARM0) = 0;
+ PRVM_ExecuteProgram((func_t)(CSQC_Ent_Update - prog->functions), CL_F_ENT_UPDATE);
+ }
+ }
+ prog->globals.client->self = oldself;
+ CSQC_END
+}
+
+void Cmd_ClearCsqcFuncs (void);
+
+void CL_VM_Init (void)
+{
+ entity_t *ent;
+
+ csqc_loaded = false;
+ memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache));
+ memset(&cl.csqc_vidvars, true, sizeof(csqc_vidvars_t));
+
+ if(!FS_FileExists(csqc_progname.string))
+ {
+ if(!sv.active && csqc_progcrc.integer)
+ {
+ Con_Printf("CL_VM_Init: server requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
+ CL_Disconnect();
+ }
+ return;
+ }
+ else
+ if(!sv.active && !csqc_progcrc.integer) //[515]: because cheaters may use csqc while server didn't allowed it !
+ {
+ Con_Printf("CL_VM_Init: server didn't sent CSQC crc, so CSQC is disabled\n");
+ return;
+ }
+
+ PRVM_Begin;
+ PRVM_InitProg(PRVM_CLIENTPROG);
+
+ // allocate the mempools
+ prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL);
+ prog->headercrc = CL_PROGHEADER_CRC;
+ prog->edictprivate_size = 0; // no private struct used
+ prog->name = csqc_progname.string;
+ prog->num_edicts = 1;
+ prog->limit_edicts = CL_MAX_EDICTS;
+ prog->extensionstring = vm_cl_extensions;
+ prog->builtins = vm_cl_builtins;
+ prog->numbuiltins = vm_cl_numbuiltins;
+ prog->init_cmd = VM_CL_Cmd_Init;
+ prog->reset_cmd = VM_CL_Cmd_Reset;
+ prog->error_cmd = CL_VM_Error;
+
+ PRVM_LoadProgs(csqc_progname.string, cl_numrequiredfunc, cl_required_func, 0, NULL);
+
+ if(!sv.active && !cls.demoplayback && prog->filecrc != (unsigned short)csqc_progcrc.integer)
+ {
+ Con_Printf("^1Your CSQC version differs from server's one (%i!=%i)\n", prog->filecrc, csqc_progcrc.integer);
+ PRVM_ResetProg();
+ CL_Disconnect();
+ return;
+ }
+
+ if(prog->loaded)
+ {
+ Cvar_SetValueQuick(&csqc_progcrc, prog->filecrc);
+ Con_Printf("CSQC ^5loaded (crc=%i)\n", csqc_progcrc.integer);
+ }
+ else
+ {
+ CL_VM_Error("CSQC ^2failed to load\n");
+ if(!sv.active)
+ CL_Disconnect();
+ return;
+ }
+
+ csqc_mempool = Mem_AllocPool("CSQC", 0, NULL);
+
+ //[515]: optional fields & funcs
+ CL_VM_FindEdictFieldOffsets();
+
+ // set time
+ *prog->time = cl.time;
+ csqc_frametime = 0;
+
+ prog->globals.client->mapname = PRVM_SetEngineString(cl.worldmodel->name);
+ prog->globals.client->player_localentnum = cl.playerentity;
+
+ // call the prog init
+ PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(CL_F_INIT) - prog->functions), CL_F_INIT);
+
+ PRVM_End;
+ csqc_loaded = true;
+
+ csqc_sv2csqcents = Mem_Alloc(csqc_mempool, MAX_EDICTS*sizeof(unsigned short));
+ memset(csqc_sv2csqcents, 0, MAX_EDICTS*sizeof(unsigned short));
+
+ cl.csqc_vidvars.drawcrosshair = false;
+ cl.csqc_vidvars.drawenginesbar = false;
+
+ // local state
+ ent = &cl_csqcentities[0];
+ // entire entity array was cleared, so just fill in a few fields
+ ent->state_current.active = true;
+ ent->render.model = cl.worldmodel = cl.model_precache[1];
+ ent->render.scale = 1; // some of the renderer still relies on scale
+ ent->render.alpha = 1;
+ ent->render.flags = RENDER_SHADOW | RENDER_LIGHT;
+ Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, 0, 0, 0, 0, 0, 0, 1);
+ Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
+ CL_BoundingBoxForEntity(&ent->render);
+}
+
+void CL_VM_ShutDown (void)
+{
+ Cmd_ClearCsqcFuncs();
+ Cvar_SetValueQuick(&csqc_progcrc, 0);
+ if(!csqc_loaded)
+ return;
+ CSQC_BEGIN
+ *prog->time = cl.time;
+ PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(CL_F_SHUTDOWN) - prog->functions), CL_F_SHUTDOWN);
+ PRVM_ResetProg();
+ CSQC_END
+ Con_Print("CSQC ^1unloaded\n");
+ csqc_loaded = false;
+ Mem_FreePool(&csqc_mempool);
+}
--- /dev/null
+#ifndef CSPROGS_H
+#define CSPROGS_H
+
+#define CL_MAX_EDICTS (1<<12)
+
+#define ENTMASK_ENGINE 1
+#define ENTMASK_ENGINEVIEWMODELS 2
+#define ENTMASK_NORMAL 4
+
+#define VF_MIN 1 //(vector)
+#define VF_MIN_X 2 //(float)
+#define VF_MIN_Y 3 //(float)
+#define VF_SIZE 4 //(vector) (viewport size)
+#define VF_SIZE_Y 5 //(float)
+#define VF_SIZE_X 6 //(float)
+#define VF_VIEWPORT 7 //(vector, vector)
+#define VF_FOV 8 //(vector)
+#define VF_FOVX 9 //(float)
+#define VF_FOVY 10 //(float)
+#define VF_ORIGIN 11 //(vector)
+#define VF_ORIGIN_X 12 //(float)
+#define VF_ORIGIN_Y 13 //(float)
+#define VF_ORIGIN_Z 14 //(float)
+#define VF_ANGLES 15 //(vector)
+#define VF_ANGLES_X 16 //(float)
+#define VF_ANGLES_Y 17 //(float)
+#define VF_ANGLES_Z 18 //(float)
+
+#define VF_DRAWWORLD 19 //(float) //actually world model and sky
+#define VF_DRAWENGINESBAR 20 //(float)
+#define VF_DRAWCROSSHAIR 21 //(float)
+
+#define VF_CL_VIEWANGLES 33 //(vector) //sweet thing for RPGs/...
+#define VF_CL_VIEWANGLES_X 34 //(float)
+#define VF_CL_VIEWANGLES_Y 35 //(float)
+#define VF_CL_VIEWANGLES_Z 36 //(float)
+
+//#define VF_PERSPECTIVE 200
+
+#define RF_VIEWMODEL 1 // The entity is never drawn in mirrors. In engines with realtime lighting, it casts no shadows.
+#define RF_EXTERNALMODEL 2 // The entity is appears in mirrors but not in the normal view. It does still cast shadows in engines with realtime lighting.
+#define RF_DEPTHHACK 4 // The entity appears closer to the view than normal, either by scaling it wierdly or by just using a depthrange. This will usually be found in conjunction with RF_VIEWMODEL
+#define RF_ADDITIVE 8 // Add the entity acording to it's alpha values instead of the normal blend
+#define RF_USEAXIS 16 // When set, the entity will use the v_forward, v_right and v_up globals instead of it's angles field for orientation. Angles will be ignored compleatly.
+ // Note that to use this properly, you'll NEED to use the predraw function to set the globals.
+//#define RF_DOUBLESIDED 32
+
+extern unsigned int csqc_drawmask;
+extern int cl_max_csqcentities;
+extern qboolean csqc_frame;
+extern int csqc_buttons;
+extern qboolean csqc_loaded;
+extern qboolean csqc_onground;
+extern vec3_t csqc_origin, csqc_angles;
+extern unsigned int csqc_drawmask;
+extern int csqc_fieldoff_scale;
+extern int csqc_fieldoff_renderflags;
+extern int csqc_fieldoff_tag_entity;
+extern int csqc_fieldoff_tag_index;
+extern cvar_t csqc_progcrc;
+extern qboolean csqc_usecsqclistener;
+extern matrix4x4_t csqc_listenermatrix;
+
+#endif
}
}
+qboolean CL_VM_UpdateView (void);
+void SCR_DrawConsole (void);
+
int r_stereo_side;
void SCR_DrawScreen (void)
r_refdef.frustum_x *= r_refdef.frustumscale_x;
r_refdef.frustum_y *= r_refdef.frustumscale_y;
- R_RenderView();
+ if(!CL_VM_UpdateView())
+ R_RenderView();
+ else
+ SCR_DrawConsole();
if (scr_zoomwindow.integer)
{
r_refdef.frustum_x *= r_refdef.frustumscale_x;
r_refdef.frustum_y *= r_refdef.frustumscale_y;
- R_RenderView();
+ if(!CL_VM_UpdateView())
+ R_RenderView();
}
}
GL_ScissorTest(false);
}
+//[515]: csqc
+void CSQC_R_ClearScreen (void)
+{
+ if (!r_refdef.entities/* || !r_refdef.worldmodel*/)
+ return; //Host_Error ("R_RenderView: NULL worldmodel");
+
+ r_view_width = bound(0, r_refdef.width, vid.width);
+ r_view_height = bound(0, r_refdef.height, vid.height);
+ r_view_depth = 1;
+ r_view_x = bound(0, r_refdef.x, vid.width - r_refdef.width);
+ r_view_y = bound(0, r_refdef.y, vid.height - r_refdef.height);
+ r_view_z = 0;
+ r_view_matrix = r_refdef.viewentitymatrix;
+ GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
+ r_rtworld = r_shadow_realtime_world.integer;
+ r_rtworldshadows = r_shadow_realtime_world_shadows.integer && gl_stencil;
+ r_rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer;
+ r_rtdlightshadows = r_rtdlight && (r_rtworld ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer) && gl_stencil;
+ r_lightmapintensity = r_rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
+
+ // GL is weird because it's bottom to top, r_view_y is top to bottom
+ qglViewport(r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height);
+ GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
+ GL_ScissorTest(true);
+ GL_DepthMask(true);
+ R_ClearScreen();
+ R_Textures_Frame();
+ R_UpdateFog();
+ R_TimeReport("setup");
+}
+
+//[515]: csqc
+void CSQC_R_RenderScene (void)
+{
+ qglDepthFunc(GL_LEQUAL);
+ qglPolygonOffset(0, 0);
+ qglEnable(GL_POLYGON_OFFSET_FILL);
+
+ R_RenderScene();
+
+ qglPolygonOffset(0, 0);
+ qglDisable(GL_POLYGON_OFFSET_FILL);
+
+ R_BlendView();
+ R_TimeReport("blendview");
+
+ GL_Scissor(0, 0, vid.width, vid.height);
+ GL_ScissorTest(false);
+}
+
extern void R_DrawLightningBeams (void);
+extern void VM_AddPolygonsToMeshQueue (void);
void R_RenderScene(void)
{
// don't let sound skip if going slow
if (r_refdef.extraupdate)
S_ExtraUpdate ();
- GL_ShowTrisColor(0.025, 0.025, 0, 1);
- if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky)
+ if (cl.csqc_vidvars.drawworld)
{
- r_refdef.worldmodel->DrawSky(r_refdef.worldentity);
- R_TimeReport("worldsky");
- }
+ GL_ShowTrisColor(0.025, 0.025, 0, 1);
+ if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky)
+ {
+ r_refdef.worldmodel->DrawSky(r_refdef.worldentity);
+ R_TimeReport("worldsky");
+ }
- if (R_DrawBrushModelsSky())
- R_TimeReport("bmodelsky");
+ if (R_DrawBrushModelsSky())
+ R_TimeReport("bmodelsky");
- GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
- if (r_refdef.worldmodel && r_refdef.worldmodel->Draw)
- {
- r_refdef.worldmodel->Draw(r_refdef.worldentity);
- R_TimeReport("world");
+ GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
+ if (r_refdef.worldmodel && r_refdef.worldmodel->Draw)
+ {
+ r_refdef.worldmodel->Draw(r_refdef.worldentity);
+ R_TimeReport("world");
+ }
}
// don't let sound skip if going slow
GL_ShowTrisColor(0.1, 0, 0, 1);
- R_DrawLightningBeams();
- R_TimeReport("lightning");
+ if (cl.csqc_vidvars.drawworld)
+ {
+ R_DrawLightningBeams();
+ R_TimeReport("lightning");
- R_DrawParticles();
- R_TimeReport("particles");
+ R_DrawParticles();
+ R_TimeReport("particles");
- R_DrawExplosions();
- R_TimeReport("explosions");
+ R_DrawExplosions();
+ R_TimeReport("explosions");
+ }
R_MeshQueue_RenderTransparent();
R_TimeReport("drawtrans");
- R_DrawCoronas();
- R_TimeReport("coronas");
+ if (cl.csqc_vidvars.drawworld)
+ {
+ R_DrawCoronas();
+ R_TimeReport("coronas");
+ }
+ if(cl.csqc_vidvars.drawcrosshair)
+ {
+ R_DrawWorldCrosshair();
+ R_TimeReport("crosshair");
+ }
- R_DrawWorldCrosshair();
- R_TimeReport("crosshair");
+ VM_AddPolygonsToMeshQueue();
R_MeshQueue_Render();
R_MeshQueue_EndScene();
#include "cdaudio.h"
#include "cl_video.h"
#include "progsvm.h"
+#include "csprogs.h"
/*
time2 = Sys_DoubleTime();
// update audio
- S_Update(&r_refdef.viewentitymatrix);
+ if(csqc_usecsqclistener)
+ {
+ S_Update(&csqc_listenermatrix);
+ csqc_usecsqclistener = false;
+ }
+ else
+ S_Update(&r_refdef.viewentitymatrix);
CDAudio_Update();
Cmd_AddCommand ("unbindall", Key_Unbindall_f);
}
+const char *Key_GetBind (int key)
+{
+ const char *bind;
+ bind = keybindings[key_bmap][key];
+ if (!bind)
+ bind = keybindings[key_bmap2][key];
+ return bind;
+}
+
+qboolean CL_VM_InputEvent (qboolean pressed, int key);
/*
===================
Key_Event (int key, char ascii, qboolean down)
{
const char *bind;
+ qboolean q;
// get key binding
bind = keybindings[key_bmap][key];
if (!bind)
bind = keybindings[key_bmap2][key];
+ if(key_dest == key_game)
+ {
+ q = CL_VM_InputEvent(!down, key);
+ if(q)
+ {
+ if (down)
+ keydown[key] = min(keydown[key] + 1, 2);
+ else
+ keydown[key] = 0;
+ return;
+ }
+ }
+
if (!down)
{
// clear repeat count now that the key is released
cl_particles.o \
cl_screen.o \
cl_video.o \
+ clvm_cmds.o \
cmd.o \
collision.o \
common.o \
console.o \
+ csprogs.o \
curves.o \
cvar.o \
dpvsimpledecode.o \
host.o \
host_cmd.o \
image.o \
- jpeg.o \
image_png.o \
+ jpeg.o \
keys.o \
lhnet.o \
mathlib.o \
view.o \
wad.o \
world.o \
+ world_cs.o \
zone.o
# note that builddate.c is very intentionally not compiled to a .o before
PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
}
-/*
-=========
-VM_M_keynumtostring
-
-string keynumtostring(float keynum)
-=========
-*/
-void VM_M_keynumtostring(void)
-{
- int keynum;
- char *tmp;
- VM_SAFEPARMCOUNT(1, VM_M_keynumtostring);
-
- keynum = PRVM_G_FLOAT(OFS_PARM0);
-
- tmp = VM_GetTempString();
-
- strcpy(tmp, Key_KeynumToString(keynum));
-
- PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp);
-}
-
-/*
-=========
-VM_M_stringtokeynum
-
-float stringtokeynum(string key)
-=========
-*/
-void VM_M_stringtokeynum( void )
-{
- const char *str;
- VM_SAFEPARMCOUNT( 1, VM_M_keynumtostring );
-
- str = PRVM_G_STRING( OFS_PARM0 );
-
- PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum( str );
-}
-
/*
=========
VM_M_findkeysforcommand
VM_cin_setstate,
VM_cin_getstate,
VM_cin_restart, // 465
- 0,0,0,0,0, // 470
+ VM_drawline, // 466
+ 0,0,0,0, // 470
e10, // 480
e10, // 490
e10, // 500
VM_M_writetofile,
VM_M_isfunction,
VM_M_getresolution,
- VM_M_keynumtostring,
+ VM_keynumtostring,
VM_M_findkeysforcommand,// 610
VM_M_getserverliststat,
VM_M_getserverliststring,
VM_parseentitydata,
- VM_M_stringtokeynum,
+ VM_stringtokeynum,
VM_M_resetserverlistmasks,
VM_M_setserverlistmaskstring,
VM_M_setserverlistmasknumber,
extern int eval_colormod;
extern int eval_playermodel;
extern int eval_playerskin;
+extern int eval_SendEntity;
+extern int eval_Version;
extern int eval_customizeentityforclient;
extern mfunction_t *SV_PlayerPhysicsQC;
#include "pr_comp.h" // defs shared with qcc
#include "progdefs.h" // generated by program cdefs
+#include "clprogdefs.h" // generated by program cdefs
/*
typedef union vm_eval_s
union
{
void *vp;
- entvars_t *server;
+ entvars_t *server;
+ cl_entvars_t *client;
} fields;
} prvm_edict_t;
union {
float *generic;
globalvars_t *server;
+ cl_globalvars_t *client;
} globals;
int maxknownstrings;
}
}
+static mempool_t *sv2csqc = NULL;
+int csqc_clent = 0;
+sizebuf_t *sv2csqcbuf = NULL;
+static unsigned char *sv2csqcents_version[64];
+
+void EntityFrameCSQC_ClearVersions (void)
+{
+ if(sv2csqc)
+ {
+ Mem_FreePool(&sv2csqc);
+ sv2csqc = NULL;
+ }
+ memset(sv2csqcents_version, 0, 64*sizeof(unsigned char *));
+}
+
+void EntityFrameCSQC_InitClientVersions (int client, qboolean clear)
+{
+ if(!sv2csqc)
+ sv2csqc = Mem_AllocPool("SV2CSQC", 0, NULL);
+ if(sv2csqcents_version[client])
+ {
+ Mem_Free(sv2csqcents_version[client]);
+ sv2csqcents_version[client] = NULL;
+ }
+ sv2csqcents_version[client] = Mem_Alloc(sv2csqc, MAX_EDICTS);
+ memset(sv2csqcents_version[client], 0, MAX_EDICTS);
+}
+
+//[515]: we use only one array per-client for SendEntity feature
+void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_state_t *states)
+{
+ sizebuf_t buf;
+ unsigned char data[2048];
+ const entity_state_t *s;
+ unsigned short i, t, t2, t0;
+ prvm_eval_t *val, *val2;
+ int csqcents = 0;
+
+ if(!eval_SendEntity || !eval_Version)
+ return;
+ --csqc_clent;
+ if(!sv2csqcents_version[csqc_clent])
+ EntityFrameCSQC_InitClientVersions(csqc_clent, false);
+
+ for (csqcents = i = 0, s = states;i < numstates;i++, s++)
+ {
+ //[515]: entities remove
+ if(i+1 >= numstates)
+ t2 = prog->num_edicts;
+ else
+ t2 = states[i+1].number;
+ if(!i)
+ {
+ t0 = 1;
+ t2 = s->number;
+ }
+ else
+ t0 = s->number+1;
+ for(t=t0; t<t2 ;t++)
+ if(sv2csqcents_version[csqc_clent][t])
+ {
+ if(!csqcents)
+ {
+ csqcents = 1;
+ memset(&buf, 0, sizeof(buf));
+ buf.data = data;
+ buf.maxsize = sizeof(data);
+ sv2csqcbuf = &buf;
+ SZ_Clear(&buf);
+ MSG_WriteByte(&buf, svc_csqcentities);
+ }
+ sv2csqcents_version[csqc_clent][t] = 0;
+ MSG_WriteShort(&buf, (unsigned short)t | 0x8000);
+ csqcents++;
+ }
+ //[515]: entities remove
+
+// if(!s->active)
+// continue;
+ val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_SendEntity);
+ if(val->function)
+ {
+ val2 = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_Version);
+ if(sv2csqcents_version[csqc_clent][s->number] == (unsigned char)val2->_float)
+ continue;
+ if(!csqcents)
+ {
+ csqcents = 1;
+ memset(&buf, 0, sizeof(buf));
+ buf.data = data;
+ buf.maxsize = sizeof(data);
+ sv2csqcbuf = &buf;
+ SZ_Clear(&buf);
+ MSG_WriteByte(&buf, svc_csqcentities);
+ }
+ if((unsigned char)val2->_float == 0)
+ val2->_float = 1;
+ MSG_WriteShort(&buf, s->number);
+ ((int *)prog->globals.generic)[OFS_PARM0] = csqc_clent+1;
+ prog->globals.server->self = s->number;
+ PRVM_ExecuteProgram(val->function, "Null SendEntity\n");
+ if(!prog->globals.generic[OFS_RETURN])
+ {
+ buf.cursize -= 2;
+ if(sv2csqcents_version[csqc_clent][s->number])
+ {
+ sv2csqcents_version[csqc_clent][s->number] = 0;
+ MSG_WriteShort(&buf, (unsigned short)s->number | 0x8000);
+ csqcents++;
+ }
+ }
+ else
+ {
+ sv2csqcents_version[csqc_clent][s->number] = (unsigned char)val2->_float;
+ csqcents++;
+ }
+ if (msg->cursize + buf.cursize > msg->maxsize)
+ break;
+ }
+ }
+ if(csqcents)
+ {
+ if(csqcents > 1)
+ {
+ MSG_WriteShort(&buf, 0);
+ SZ_Write(msg, buf.data, buf.cursize);
+ }
+ sv2csqcbuf = NULL;
+ }
+}
+
void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_state_t *states)
{
const entity_state_t *s;
int i, bits;
sizebuf_t buf;
unsigned char data[128];
+ prvm_eval_t *val;
// prepare the buffer
memset(&buf, 0, sizeof(buf));
for (i = 0, s = states;i < numstates;i++, s++)
{
+ val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_SendEntity);
+ if(val && val->function)
+ continue;
+
// prepare the buffer
SZ_Clear(&buf);
entity_frame_t *o = &deltaframe;
const entity_state_t *ent, *delta;
vec3_t eye;
+ prvm_eval_t *val;
d->latestframenum++;
{
ent = states + i;
number = ent->number;
+
+ val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[number]), eval_SendEntity);
+ if(val && val->function)
+ continue;
for (;onum < o->numentities && o->entitydata[onum].number < number;onum++)
{
// write remove message
int i, n, startnumber;
sizebuf_t buf;
unsigned char data[128];
+ prvm_eval_t *val;
// if there isn't enough space to accomplish anything, skip it
if (msg->cursize + 24 > msg->maxsize)
d->currententitynumber = 1;
for (i = 0, n = startnumber;n < prog->max_edicts;n++)
{
+ val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[n]), eval_SendEntity);
+ if(val && val->function)
+ continue;
// find the old state to delta from
e = EntityFrame4_GetReferenceEntity(d, n);
// prepare the buffer
void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbits, sizebuf_t *msg)
{
unsigned int bits = 0;
+
+ prvm_eval_t *val;
+ val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_SendEntity);
+ if(val && val->function)
+ return;
+
if (!s->active)
MSG_WriteShort(msg, number | 0x8000);
else
#define svc_spawnbaseline2 55 // short modelindex instead of byte
#define svc_spawnstatic2 56 // short modelindex instead of byte
#define svc_entities 57 // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata
-#define svc_unusedlh3 58
+#define svc_csqcentities 58 // [short] entnum [variable length] entitydata ... [short] 0x0000
#define svc_spawnstaticsound2 59 // [coord3] [short] samp [byte] vol [byte] aten
//
PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
}
+/*
+=========
+VM_keynumtostring
+
+string keynumtostring(float keynum)
+=========
+*/
+void VM_keynumtostring (void)
+{
+ int keynum;
+ char *tmp;
+ VM_SAFEPARMCOUNT(1, VM_keynumtostring);
+
+ keynum = PRVM_G_FLOAT(OFS_PARM0);
+
+ tmp = VM_GetTempString();
+
+ strcpy(tmp, Key_KeynumToString(keynum));
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp);
+}
+
+/*
+=========
+VM_stringtokeynum
+
+float stringtokeynum(string key)
+=========
+*/
+void VM_stringtokeynum (void)
+{
+ const char *str;
+ VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
+
+ str = PRVM_G_STRING( OFS_PARM0 );
+
+ PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum( str );
+}
+
// CL_Video interface functions
/*
CL_RestartVideo( video );
}
+/*
+==============
+VM_vectorvectors
+
+Writes new values for v_forward, v_up, and v_right based on the given forward vector
+vectorvectors(vector, vector)
+==============
+*/
+void VM_vectorvectors (void)
+{
+ VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward);
+ VectorVectors(prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up);
+}
+
+/*
+========================
+VM_drawline
+
+void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
+========================
+*/
+void VM_drawline (void)
+{
+ float *c1, *c2, *rgb;
+ float alpha, width;
+ unsigned char flags;
+
+ VM_SAFEPARMCOUNT(6, VM_drawline);
+ width = PRVM_G_FLOAT(OFS_PARM0);
+ c1 = PRVM_G_VECTOR(OFS_PARM1);
+ c2 = PRVM_G_VECTOR(OFS_PARM2);
+ rgb = PRVM_G_VECTOR(OFS_PARM3);
+ alpha = PRVM_G_FLOAT(OFS_PARM4);
+ flags = PRVM_G_FLOAT(OFS_PARM5);
+ DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
+}
+
+//====================
+//QC POLYGON functions
+//====================
+
+typedef struct
+{
+ rtexture_t *tex;
+ float data[36]; //[515]: enough for polygons
+ unsigned char flags; //[515]: + VM_POLYGON_2D and VM_POLYGON_FL4V flags
+}vm_polygon_t;
+
+//static float vm_polygon_linewidth = 1;
+static mempool_t *vm_polygons_pool = NULL;
+static unsigned char vm_current_vertices = 0;
+static qboolean vm_polygons_initialized = false;
+static vm_polygon_t *vm_polygons = NULL;
+static unsigned long vm_polygons_num = 0, vm_drawpolygons_num = 0; //[515]: ok long on 64bit ?
+static qboolean vm_polygonbegin = false; //[515]: for "no-crap-on-the-screen" check
+#define VM_DEFPOLYNUM 64 //[515]: enough for default ?
+
+#define VM_POLYGON_FL3V 16 //more than 2 vertices (used only for lines)
+#define VM_POLYGON_FLLINES 32
+#define VM_POLYGON_FL2D 64
+#define VM_POLYGON_FL4V 128 //4 vertices
+
+void VM_InitPolygons (void)
+{
+ vm_polygons_pool = Mem_AllocPool("VMPOLY", 0, NULL);
+ vm_polygons = Mem_Alloc(vm_polygons_pool, VM_DEFPOLYNUM*sizeof(vm_polygon_t));
+ memset(vm_polygons, 0, VM_DEFPOLYNUM*sizeof(vm_polygon_t));
+ vm_polygons_num = VM_DEFPOLYNUM;
+ vm_polygonbegin = vm_drawpolygons_num = 0;
+ vm_polygons_initialized = true;
+}
+
+void VM_DrawPolygonCallback (const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
+{
+ const vm_polygon_t *p = &vm_polygons[surfacenumber];
+ int flags = p->flags & 0x0f;
+
+ if(flags == DRAWFLAG_ADDITIVE)
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+ else if(flags == DRAWFLAG_MODULATE)
+ GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
+ else if(flags == DRAWFLAG_2XMODULATE)
+ GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
+ else
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ R_Mesh_TexBind(0, R_GetTexture(p->tex));
+
+ //[515]: is speed is max ?
+ if(p->flags & VM_POLYGON_FLLINES) //[515]: lines
+ {
+ qglLineWidth(p->data[13]);
+ qglBegin(GL_LINE_LOOP);
+ qglTexCoord1f (p->data[12]);
+ qglColor4f (p->data[20], p->data[21], p->data[22], p->data[23]);
+ qglVertex3f (p->data[0] , p->data[1], p->data[2]);
+
+ qglTexCoord1f (p->data[14]);
+ qglColor4f (p->data[24], p->data[25], p->data[26], p->data[27]);
+ qglVertex3f (p->data[3] , p->data[4], p->data[5]);
+
+ if(p->flags & VM_POLYGON_FL3V)
+ {
+ qglTexCoord1f (p->data[16]);
+ qglColor4f (p->data[28], p->data[29], p->data[30], p->data[31]);
+ qglVertex3f (p->data[6] , p->data[7], p->data[8]);
+
+ if(p->flags & VM_POLYGON_FL4V)
+ {
+ qglTexCoord1f (p->data[18]);
+ qglColor4f (p->data[32], p->data[33], p->data[34], p->data[35]);
+ qglVertex3f (p->data[9] , p->data[10], p->data[11]);
+ }
+ }
+ qglEnd();
+ }
+ else
+ {
+ qglBegin(GL_POLYGON);
+ qglTexCoord2f (p->data[12], p->data[13]);
+ qglColor4f (p->data[20], p->data[21], p->data[22], p->data[23]);
+ qglVertex3f (p->data[0] , p->data[1], p->data[2]);
+
+ qglTexCoord2f (p->data[14], p->data[15]);
+ qglColor4f (p->data[24], p->data[25], p->data[26], p->data[27]);
+ qglVertex3f (p->data[3] , p->data[4], p->data[5]);
+
+ qglTexCoord2f (p->data[16], p->data[17]);
+ qglColor4f (p->data[28], p->data[29], p->data[30], p->data[31]);
+ qglVertex3f (p->data[6] , p->data[7], p->data[8]);
+
+ if(p->flags & VM_POLYGON_FL4V)
+ {
+ qglTexCoord2f (p->data[18], p->data[19]);
+ qglColor4f (p->data[32], p->data[33], p->data[34], p->data[35]);
+ qglVertex3f (p->data[9] , p->data[10], p->data[11]);
+ }
+ qglEnd();
+ }
+}
+
+void VM_AddPolygonTo2DScene (vm_polygon_t *p)
+{
+ drawqueuemesh_t mesh;
+ static int picelements[6] = {0, 1, 2, 0, 2, 3};
+
+ mesh.texture = p->tex;
+ mesh.data_element3i = picelements;
+ mesh.data_vertex3f = p->data;
+ mesh.data_texcoord2f = p->data + 12;
+ mesh.data_color4f = p->data + 20;
+ if(p->flags & VM_POLYGON_FL4V)
+ {
+ mesh.num_vertices = 4;
+ mesh.num_triangles = 2;
+ }
+ else
+ {
+ mesh.num_vertices = 3;
+ mesh.num_triangles = 1;
+ }
+ if(p->flags & VM_POLYGON_FLLINES) //[515]: lines
+ DrawQ_LineLoop (&mesh, (p->flags&0x0f));
+ else
+ DrawQ_Mesh (&mesh, (p->flags&0x0f));
+}
+
+//void(string texturename, float flag, float 2d, float lines) R_BeginPolygon
+void VM_R_PolygonBegin (void)
+{
+ vm_polygon_t *p;
+ const char *picname;
+ if(prog->argc < 2)
+ VM_SAFEPARMCOUNT(2, VM_R_PolygonBegin);
+
+ if(!vm_polygons_initialized)
+ VM_InitPolygons();
+ if(vm_polygonbegin)
+ {
+ Con_Printf("VM_R_PolygonBegin: called twice without VM_R_PolygonEnd after first\n");
+ return;
+ }
+ if(vm_drawpolygons_num >= vm_polygons_num)
+ {
+ p = Mem_Alloc(vm_polygons_pool, 2 * vm_polygons_num * sizeof(vm_polygon_t));
+ memset(p, 0, 2 * vm_polygons_num * sizeof(vm_polygon_t));
+ memcpy(p, vm_polygons, vm_polygons_num * sizeof(vm_polygon_t));
+ Mem_Free(vm_polygons);
+ vm_polygons = p;
+ vm_polygons_num *= 2;
+ }
+ p = &vm_polygons[vm_drawpolygons_num];
+ picname = PRVM_G_STRING(OFS_PARM0);
+ if(picname[0])
+ p->tex = Draw_CachePic(picname, false)->tex;
+ else
+ p->tex = r_texture_notexture;
+ p->flags = (unsigned char)PRVM_G_FLOAT(OFS_PARM1);
+ vm_current_vertices = 0;
+ vm_polygonbegin = true;
+ if(prog->argc >= 3)
+ {
+ if(PRVM_G_FLOAT(OFS_PARM2))
+ p->flags |= VM_POLYGON_FL2D;
+ if(prog->argc >= 4 && PRVM_G_FLOAT(OFS_PARM3))
+ {
+ p->data[13] = PRVM_G_FLOAT(OFS_PARM3); //[515]: linewidth
+ p->flags |= VM_POLYGON_FLLINES;
+ }
+ }
+}
+
+//void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
+void VM_R_PolygonVertex (void)
+{
+ float *coords, *tx, *rgb, alpha;
+ vm_polygon_t *p;
+ VM_SAFEPARMCOUNT(4, VM_R_PolygonVertex);
+
+ if(!vm_polygonbegin)
+ {
+ Con_Printf("VM_R_PolygonVertex: VM_R_PolygonBegin wasn't called\n");
+ return;
+ }
+ coords = PRVM_G_VECTOR(OFS_PARM0);
+ tx = PRVM_G_VECTOR(OFS_PARM1);
+ rgb = PRVM_G_VECTOR(OFS_PARM2);
+ alpha = PRVM_G_FLOAT(OFS_PARM3);
+
+ p = &vm_polygons[vm_drawpolygons_num];
+ if(vm_current_vertices > 4)
+ {
+ Con_Printf("VM_R_PolygonVertex: may have 4 vertices max\n");
+ return;
+ }
+
+ p->data[vm_current_vertices*3] = coords[0];
+ p->data[1+vm_current_vertices*3] = coords[1];
+ if(!(p->flags & VM_POLYGON_FL2D))
+ p->data[2+vm_current_vertices*3] = coords[2];
+
+ p->data[12+vm_current_vertices*2] = tx[0];
+ if(!(p->flags & VM_POLYGON_FLLINES))
+ p->data[13+vm_current_vertices*2] = tx[1];
+
+ p->data[20+vm_current_vertices*4] = rgb[0];
+ p->data[21+vm_current_vertices*4] = rgb[1];
+ p->data[22+vm_current_vertices*4] = rgb[2];
+ p->data[23+vm_current_vertices*4] = alpha;
+
+ vm_current_vertices++;
+ if(vm_current_vertices == 4)
+ p->flags |= VM_POLYGON_FL4V;
+ else
+ if(vm_current_vertices == 3)
+ p->flags |= VM_POLYGON_FL3V;
+}
+
+//void() R_EndPolygon
+void VM_R_PolygonEnd (void)
+{
+ if(!vm_polygonbegin)
+ {
+ Con_Printf("VM_R_PolygonEnd: VM_R_PolygonBegin wasn't called\n");
+ return;
+ }
+ if(vm_current_vertices > 2 || (vm_current_vertices >= 2 && vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FLLINES))
+ {
+ if(vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FL2D) //[515]: don't use qcpolygons memory if 2D
+ VM_AddPolygonTo2DScene(&vm_polygons[vm_drawpolygons_num]);
+ else
+ vm_drawpolygons_num++;
+ }
+ else
+ Con_Printf("VM_R_PolygonEnd: %i vertices isn't a good choice\n", vm_current_vertices);
+ vm_polygonbegin = false;
+}
+
+void VM_AddPolygonsToMeshQueue (void)
+{
+ unsigned int i;
+ if(!vm_drawpolygons_num)
+ return;
+ for(i = 0;i < vm_drawpolygons_num;i++)
+ R_MeshQueue_Add(VM_DrawPolygonCallback, NULL, i, NULL);
+ vm_drawpolygons_num = 0;
+}
+
+
+
+
+// float(float number, float quantity) bitshift (EXT_BITSHIFT)
+void VM_bitshift (void)
+{
+ int n1, n2;
+ VM_SAFEPARMCOUNT(2, VM_bitshift);
+
+ n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0));
+ n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(!n1)
+ PRVM_G_FLOAT(OFS_RETURN) = n1;
+ else
+ if(n2 < 0)
+ PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
+}
+
////////////////////////////////////////
// AltString functions
////////////////////////////////////////
PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr );
}
+
+////////////////////////////////////////
+// BufString functions
+////////////////////////////////////////
+//[515]: string buffers support
+#define MAX_QCSTR_BUFFERS 128
+#define MAX_QCSTR_STRINGS 1024
+
+typedef struct
+{
+ int num_strings;
+ char *strings[MAX_QCSTR_STRINGS];
+}qcstrbuffer_t;
+
+static qcstrbuffer_t *qcstringbuffers[MAX_QCSTR_BUFFERS];
+static int num_qcstringbuffers;
+static int buf_sortpower;
+
+#define BUFSTR_BUFFER(a) (a>=MAX_QCSTR_BUFFERS) ? NULL : (qcstringbuffers[a])
+#define BUFSTR_ISFREE(a) (a<MAX_QCSTR_BUFFERS&&qcstringbuffers[a]&&qcstringbuffers[a]->num_strings<=0) ? 1 : 0
+
+static int BufStr_FindFreeBuffer (void)
+{
+ int i;
+ if(num_qcstringbuffers == MAX_QCSTR_BUFFERS)
+ return -1;
+ for(i=0;i<MAX_QCSTR_BUFFERS;i++)
+ if(!qcstringbuffers[i])
+ {
+ qcstringbuffers[i] = malloc(sizeof(qcstrbuffer_t));
+ memset(qcstringbuffers[i], 0, sizeof(qcstrbuffer_t));
+ return i;
+ }
+ return -1;
+}
+
+static void BufStr_ClearBuffer (int index)
+{
+ qcstrbuffer_t *b = qcstringbuffers[index];
+ int i;
+
+ if(b)
+ {
+ if(b->num_strings > 0)
+ {
+ for(i=0;i<b->num_strings;i++)
+ if(b->strings[i])
+ free(b->strings[i]);
+ num_qcstringbuffers--;
+ }
+ free(qcstringbuffers[index]);
+ qcstringbuffers[index] = NULL;
+ }
+}
+
+static int BufStr_FindFreeString (qcstrbuffer_t *b)
+{
+ int i;
+ for(i=0;i<b->num_strings;i++)
+ if(!b->strings[i] || !b->strings[i][0])
+ return i;
+ if(i == MAX_QCSTR_STRINGS) return -1;
+ else return i;
+}
+
+static int BufStr_SortStringsUP (const void *in1, const void *in2)
+{
+ const char *a, *b;
+ a = *((const char **) in1);
+ b = *((const char **) in2);
+ if(!a[0]) return 1;
+ if(!b[0]) return -1;
+ return strncmp(a, b, buf_sortpower);
+}
+
+static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
+{
+ const char *a, *b;
+ a = *((const char **) in1);
+ b = *((const char **) in2);
+ if(!a[0]) return 1;
+ if(!b[0]) return -1;
+ return strncmp(b, a, buf_sortpower);
+}
+
+#ifdef REMOVETHIS
+static void VM_BufStr_Init (void)
+{
+ memset(qcstringbuffers, 0, sizeof(qcstringbuffers));
+ num_qcstringbuffers = 0;
+}
+
+static void VM_BufStr_ShutDown (void)
+{
+ int i;
+ for(i=0;i<MAX_QCSTR_BUFFERS && num_qcstringbuffers;i++)
+ BufStr_ClearBuffer(i);
+}
+#endif
+
+/*
+========================
+VM_buf_create
+creates new buffer, and returns it's index, returns -1 if failed
+float buf_create(void) = #460;
+========================
+*/
+void VM_buf_create (void)
+{
+ int i;
+ VM_SAFEPARMCOUNT(0, VM_buf_create);
+ i = BufStr_FindFreeBuffer();
+ if(i >= 0)
+ num_qcstringbuffers++;
+ //else
+ //Con_Printf("VM_buf_create: buffers overflow in %s\n", PRVM_NAME);
+ PRVM_G_FLOAT(OFS_RETURN) = i;
+}
+
+/*
+========================
+VM_buf_del
+deletes buffer and all strings in it
+void buf_del(float bufhandle) = #461;
+========================
+*/
+void VM_buf_del (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_buf_del);
+ if(BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)))
+ BufStr_ClearBuffer((int)PRVM_G_FLOAT(OFS_PARM0));
+ else
+ {
+ Con_Printf("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+}
+
+/*
+========================
+VM_buf_getsize
+how many strings are stored in buffer
+float buf_getsize(float bufhandle) = #462;
+========================
+*/
+void VM_buf_getsize (void)
+{
+ qcstrbuffer_t *b;
+ VM_SAFEPARMCOUNT(1, VM_buf_getsize);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ Con_Printf("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = b->num_strings;
+}
+
+/*
+========================
+VM_buf_copy
+copy all content from one buffer to another, make sure it exists
+void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
+========================
+*/
+void VM_buf_copy (void)
+{
+ qcstrbuffer_t *b1, *b2;
+ int i;
+ VM_SAFEPARMCOUNT(2, VM_buf_copy);
+
+ b1 = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b1)
+ {
+ Con_Printf("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ i = PRVM_G_FLOAT(OFS_PARM1);
+ if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
+ {
+ Con_Printf("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME);
+ return;
+ }
+ b2 = BUFSTR_BUFFER(i);
+ if(!b2)
+ {
+ Con_Printf("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
+ return;
+ }
+
+ BufStr_ClearBuffer(i);
+ qcstringbuffers[i] = malloc(sizeof(qcstrbuffer_t));
+ memset(qcstringbuffers[i], 0, sizeof(qcstrbuffer_t));
+ b2->num_strings = b1->num_strings;
+
+ for(i=0;i<b1->num_strings;i++)
+ if(b1->strings[i] && b1->strings[i][0])
+ {
+ b2->strings[i] = malloc(strlen(b1->strings[i])+1);
+ if(!b2->strings[i])
+ {
+ Con_Printf("VM_buf_copy: not enough memory for buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
+ break;
+ }
+ strcpy(b2->strings[i], b1->strings[i]);
+ }
+}
+
+/*
+========================
+VM_buf_sort
+sort buffer by beginnings of strings (sortpower defaults it's lenght)
+"backward == TRUE" means that sorting goes upside-down
+void buf_sort(float bufhandle, float sortpower, float backward) = #464;
+========================
+*/
+void VM_buf_sort (void)
+{
+ qcstrbuffer_t *b;
+ int i;
+ VM_SAFEPARMCOUNT(3, VM_buf_sort);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ Con_Printf("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ if(b->num_strings <= 0)
+ {
+ Con_Printf("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ buf_sortpower = PRVM_G_FLOAT(OFS_PARM1);
+ if(buf_sortpower <= 0)
+ buf_sortpower = 99999999;
+
+ if(!PRVM_G_FLOAT(OFS_PARM2))
+ qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsUP);
+ else
+ qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
+
+ for(i=b->num_strings-1;i>=0;i--) //[515]: delete empty lines
+ if(b->strings)
+ {
+ if(b->strings[i][0])
+ break;
+ else
+ {
+ free(b->strings[i]);
+ --b->num_strings;
+ b->strings[i] = NULL;
+ }
+ }
+ else
+ --b->num_strings;
+}
+
+/*
+========================
+VM_buf_implode
+concantenates all buffer string into one with "glue" separator and returns it as tempstring
+string buf_implode(float bufhandle, string glue) = #465;
+========================
+*/
+void VM_buf_implode (void)
+{
+ qcstrbuffer_t *b;
+ char *k;
+ const char *sep;
+ int i, l;
+ VM_SAFEPARMCOUNT(2, VM_buf_implode);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ PRVM_G_INT(OFS_RETURN) = 0;
+ if(!b)
+ {
+ Con_Printf("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ if(!b->num_strings)
+ return;
+ sep = PRVM_G_STRING(OFS_PARM1);
+ k = VM_GetTempString();
+ k[0] = 0;
+ for(l=i=0;i<b->num_strings;i++)
+ if(b->strings[i])
+ {
+ l += strlen(b->strings[i]);
+ if(l>=4095)
+ break;
+ k = strcat(k, b->strings[i]);
+ if(!k)
+ break;
+ if(sep && (i != b->num_strings-1))
+ {
+ l += strlen(sep);
+ if(l>=4095)
+ break;
+ k = strcat(k, sep);
+ if(!k)
+ break;
+ }
+ }
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(k);
+}
+
+/*
+========================
+VM_bufstr_get
+get a string from buffer, returns direct pointer, dont str_unzone it!
+string bufstr_get(float bufhandle, float string_index) = #465;
+========================
+*/
+void VM_bufstr_get (void)
+{
+ qcstrbuffer_t *b;
+ int strindex;
+ VM_SAFEPARMCOUNT(2, VM_bufstr_get);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ Con_Printf("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(strindex < 0 || strindex > MAX_QCSTR_STRINGS)
+ {
+ Con_Printf("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
+ return;
+ }
+ PRVM_G_INT(OFS_RETURN) = 0;
+ if(b->num_strings <= strindex)
+ return;
+ if(b->strings[strindex])
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(b->strings[strindex]);
+}
+
+/*
+========================
+VM_bufstr_set
+copies a string into selected slot of buffer
+void bufstr_set(float bufhandle, float string_index, string str) = #466;
+========================
+*/
+void VM_bufstr_set (void)
+{
+ int bufindex, strindex;
+ qcstrbuffer_t *b;
+ const char *news;
+
+ VM_SAFEPARMCOUNT(3, VM_bufstr_set);
+
+ bufindex = PRVM_G_FLOAT(OFS_PARM0);
+ b = BUFSTR_BUFFER(bufindex);
+ if(!b)
+ {
+ Con_Printf("VM_bufstr_set: invalid buffer %i used in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ strindex = PRVM_G_FLOAT(OFS_PARM1);
+ if(strindex < 0 || strindex > MAX_QCSTR_STRINGS)
+ {
+ Con_Printf("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME);
+ return;
+ }
+ news = PRVM_G_STRING(OFS_PARM2);
+ if(!news)
+ {
+ Con_Printf("VM_bufstr_set: null string used in %s\n", PRVM_NAME);
+ return;
+ }
+ if(b->strings[strindex])
+ free(b->strings[strindex]);
+ b->strings[strindex] = malloc(strlen(news)+1);
+ strcpy(b->strings[strindex], news);
+}
+
+/*
+========================
+VM_bufstr_add
+adds string to buffer in nearest free slot and returns it
+"order == TRUE" means that string will be added after last "full" slot
+float bufstr_add(float bufhandle, string str, float order) = #467;
+========================
+*/
+void VM_bufstr_add (void)
+{
+ int bufindex, order, strindex;
+ qcstrbuffer_t *b;
+ const char *string;
+
+ VM_SAFEPARMCOUNT(3, VM_bufstr_add);
+
+ bufindex = PRVM_G_FLOAT(OFS_PARM0);
+ b = BUFSTR_BUFFER(bufindex);
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ if(!b)
+ {
+ Con_Printf("VM_bufstr_add: invalid buffer %i used in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ string = PRVM_G_STRING(OFS_PARM1);
+ if(!string)
+ {
+ Con_Printf("VM_bufstr_add: null string used in %s\n", PRVM_NAME);
+ return;
+ }
+
+ order = PRVM_G_FLOAT(OFS_PARM2);
+ if(order)
+ strindex = b->num_strings;
+ else
+ {
+ strindex = BufStr_FindFreeString(b);
+ if(strindex < 0)
+ {
+ Con_Printf("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ }
+
+ while(b->num_strings <= strindex)
+ {
+ if(b->num_strings == MAX_QCSTR_STRINGS)
+ {
+ Con_Printf("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ b->strings[b->num_strings] = NULL;
+ b->num_strings++;
+ }
+ if(b->strings[strindex])
+ free(b->strings[strindex]);
+ b->strings[strindex] = malloc(strlen(string)+1);
+ strcpy(b->strings[strindex], string);
+ PRVM_G_FLOAT(OFS_RETURN) = strindex;
+}
+
+/*
+========================
+VM_bufstr_free
+delete string from buffer
+void bufstr_free(float bufhandle, float string_index) = #468;
+========================
+*/
+void VM_bufstr_free (void)
+{
+ int i;
+ qcstrbuffer_t *b;
+ VM_SAFEPARMCOUNT(2, VM_bufstr_free);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ Con_Printf("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ i = PRVM_G_FLOAT(OFS_PARM1);
+ if(i < 0 || i > MAX_QCSTR_STRINGS)
+ {
+ Con_Printf("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME);
+ return;
+ }
+ if(b->strings[i])
+ free(b->strings[i]);
+ b->strings[i] = NULL;
+ if(i+1 == b->num_strings)
+ --b->num_strings;
+}
+
+//=============
+
void VM_Cmd_Init(void)
{
// only init the stuff for the current prog
VM_Files_Init();
VM_Search_Init();
+// VM_BufStr_Init();
+ if(vm_polygons_initialized)
+ {
+ Mem_FreePool(&vm_polygons_pool);
+ vm_polygons_initialized = false;
+ }
}
void VM_Cmd_Reset(void)
CL_PurgeOwner( MENUOWNER );
VM_Search_Reset();
VM_Files_CloseAll();
+// VM_BufStr_ShutDown();
+ if(vm_polygons_initialized)
+ {
+ Mem_FreePool(&vm_polygons_pool);
+ vm_polygons_initialized = false;
+ }
}
-
void VM_drawresetcliparea(void);
void VM_getimagesize(void);
+void VM_vectorvectors (void);
+
+void VM_keynumtostring (void);
+void VM_stringtokeynum (void);
+
void VM_cin_open( void );
void VM_cin_close( void );
void VM_cin_setstate( void );
void VM_cin_getstate( void );
void VM_cin_restart( void );
+void VM_drawline (void);
+void VM_R_PolygonBegin (void);
+void VM_R_PolygonVertex (void);
+void VM_R_PolygonEnd (void);
+
+void VM_bitshift (void);
+
void VM_altstr_count( void );
void VM_altstr_prepare( void );
void VM_altstr_get( void );
void VM_altstr_set( void );
void VM_altstr_ins(void);
+void VM_buf_create(void);
+void VM_buf_del (void);
+void VM_buf_getsize (void);
+void VM_buf_copy (void);
+void VM_buf_sort (void);
+void VM_buf_implode (void);
+void VM_bufstr_get (void);
+void VM_bufstr_set (void);
+void VM_bufstr_add (void);
+void VM_bufstr_free (void);
+
void VM_Cmd_Init(void);
void VM_Cmd_Reset(void);
*/
static int fragsort[MAX_SCOREBOARD];
static int scoreboardlines;
+
+//[515]: Sbar_GetPlayer for csqc "getplayerkey" func
+int Sbar_GetPlayer (int index)
+{
+ if(index < 0)
+ {
+ index = -1-index;
+ if(index >= scoreboardlines)
+ return -1;
+ index = fragsort[index];
+ }
+ if(index >= scoreboardlines)
+ return -1;
+ return index;
+}
+
static scoreboard_t teams[MAX_SCOREBOARD];
static int teamsort[MAX_SCOREBOARD];
static int teamlines;
extern cvar_t v_kicktime;
void Sbar_Draw (void)
{
- if (cl.intermission == 1)
- {
- Sbar_IntermissionOverlay();
- return;
- }
- else if (cl.intermission == 2)
- {
- Sbar_FinaleOverlay();
- return;
- }
-
- if (gamemode == GAME_NETHERWORLD)
+ if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
{
- }
- else if (gamemode == GAME_SOM)
- {
- if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
- Sbar_DrawScoreboard ();
- else if (sb_lines)
+ if (cl.intermission == 1)
{
- // this is the top left of the sbar area
- sbar_x = 0;
- sbar_y = vid_conheight.integer - 24*3;
+ Sbar_IntermissionOverlay();
+ return;
+ }
+ else if (cl.intermission == 2)
+ {
+ Sbar_FinaleOverlay();
+ return;
+ }
- // armor
- if (cl.stats[STAT_ARMOR])
+ if (gamemode == GAME_NETHERWORLD)
+ {
+ }
+ else if (gamemode == GAME_SOM)
+ {
+ if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
+ Sbar_DrawScoreboard ();
+ else if (sb_lines)
{
- if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
- Sbar_DrawPic(0, 0, somsb_armor[2]);
- else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
- Sbar_DrawPic(0, 0, somsb_armor[1]);
- else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
- Sbar_DrawPic(0, 0, somsb_armor[0]);
- Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
- }
+ // this is the top left of the sbar area
+ sbar_x = 0;
+ sbar_y = vid_conheight.integer - 24*3;
- // health
- Sbar_DrawPic(0, 24, somsb_health);
- Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
-
- // ammo icon
- if (cl.stats[STAT_ITEMS] & IT_SHELLS)
- Sbar_DrawPic(0, 48, somsb_ammo[0]);
- else if (cl.stats[STAT_ITEMS] & IT_NAILS)
- Sbar_DrawPic(0, 48, somsb_ammo[1]);
- else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
- Sbar_DrawPic(0, 48, somsb_ammo[2]);
- else if (cl.stats[STAT_ITEMS] & IT_CELLS)
- Sbar_DrawPic(0, 48, somsb_ammo[3]);
- Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
- if (cl.stats[STAT_SHELLS])
- Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
- }
- }
- else if (gamemode == GAME_NEXUIZ)
- {
- sbar_y = vid_conheight.integer - 47;
- sbar_x = (vid_conwidth.integer - 640)/2;
+ // armor
+ if (cl.stats[STAT_ARMOR])
+ {
+ if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
+ Sbar_DrawPic(0, 0, somsb_armor[2]);
+ else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
+ Sbar_DrawPic(0, 0, somsb_armor[1]);
+ else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
+ Sbar_DrawPic(0, 0, somsb_armor[0]);
+ Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
+ }
- if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
- {
- Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
- Sbar_DrawScoreboard ();
+ // health
+ Sbar_DrawPic(0, 24, somsb_health);
+ Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
+
+ // ammo icon
+ if (cl.stats[STAT_ITEMS] & IT_SHELLS)
+ Sbar_DrawPic(0, 48, somsb_ammo[0]);
+ else if (cl.stats[STAT_ITEMS] & IT_NAILS)
+ Sbar_DrawPic(0, 48, somsb_ammo[1]);
+ else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
+ Sbar_DrawPic(0, 48, somsb_ammo[2]);
+ else if (cl.stats[STAT_ITEMS] & IT_CELLS)
+ Sbar_DrawPic(0, 48, somsb_ammo[3]);
+ Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
+ if (cl.stats[STAT_SHELLS])
+ Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
+ }
}
- else if (sb_lines)
+ else if (gamemode == GAME_NEXUIZ)
{
- int i;
- double time;
- float fade;
+ sbar_y = vid_conheight.integer - 47;
+ sbar_x = (vid_conwidth.integer - 640)/2;
- // we have a max time 2s (min time = 0)
- if ((time = cl.time - cl.weapontime) < 2)
+ if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
{
- fade = (1.0 - 0.5 * time);
- fade *= fade;
- for (i = 0; i < 8;i++)
- if (cl.stats[STAT_ITEMS] & (1 << i))
- Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
-
- if((cl.stats[STAT_ITEMS] & (1<<12)))
- Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
+ Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
+ Sbar_DrawScoreboard ();
}
+ else if (sb_lines)
+ {
+ int i;
+ double time;
+ float fade;
- //if (!cl.islocalgame)
- // Sbar_DrawFrags ();
+ // we have a max time 2s (min time = 0)
+ if ((time = cl.time - cl.weapontime) < 2)
+ {
+ fade = (1.0 - 0.5 * time);
+ fade *= fade;
+ for (i = 0; i < 8;i++)
+ if (cl.stats[STAT_ITEMS] & (1 << i))
+ Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
+
+ if((cl.stats[STAT_ITEMS] & (1<<12)))
+ Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
+ }
- if (sb_lines > 24)
- Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
- else
- Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
+ //if (!cl.islocalgame)
+ // Sbar_DrawFrags ();
- // special items
- if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
- {
- // Nexuiz has no anum pics
- //Sbar_DrawNum (36, 0, 666, 3, 1);
- // Nexuiz has no disc pic
- //Sbar_DrawPic (0, 0, sb_disc);
- }
+ if (sb_lines > 24)
+ Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
+ else
+ Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
- // armor
- Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
+ // special items
+ if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
+ {
+ // Nexuiz has no anum pics
+ //Sbar_DrawNum (36, 0, 666, 3, 1);
+ // Nexuiz has no disc pic
+ //Sbar_DrawPic (0, 0, sb_disc);
+ }
- // health
- if(cl.stats[STAT_HEALTH] > 100)
- Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
- else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
- Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
- else
- Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
+ // armor
+ Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
- // AK dont draw ammo for the laser
- if(cl.stats[STAT_ACTIVEWEAPON] != 12)
- {
- if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
- Sbar_DrawPic (519, 0, sb_ammo[0]);
- else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
- Sbar_DrawPic (519, 0, sb_ammo[1]);
- else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
- Sbar_DrawPic (519, 0, sb_ammo[2]);
- else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
- Sbar_DrawPic (519, 0, sb_ammo[3]);
-
- if(cl.stats[STAT_AMMO] <= 10)
- Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
+ // health
+ if(cl.stats[STAT_HEALTH] > 100)
+ Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
+ else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
+ Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
else
- Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
+ Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
- }
+ // AK dont draw ammo for the laser
+ if(cl.stats[STAT_ACTIVEWEAPON] != 12)
+ {
+ if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
+ Sbar_DrawPic (519, 0, sb_ammo[0]);
+ else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
+ Sbar_DrawPic (519, 0, sb_ammo[1]);
+ else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
+ Sbar_DrawPic (519, 0, sb_ammo[2]);
+ else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
+ Sbar_DrawPic (519, 0, sb_ammo[3]);
+
+ if(cl.stats[STAT_AMMO] <= 10)
+ Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
+ else
+ Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
- if (sb_lines > 24)
- DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay->name,0,0,1,1,1,1,DRAWFLAG_MODULATE);
- }
+ }
- //if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH)
- // Sbar_MiniDeathmatchOverlay (0, 17);
- }
- else if (gamemode == GAME_ZYMOTIC)
- {
-#if 1
- float scale = 64.0f / 256.0f;
- float kickoffset[3];
- VectorClear(kickoffset);
- if (v_dmg_time > 0)
- {
- kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
- kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
- }
- sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
- sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
- // left1 16, 48 : 126 -66
- // left2 16, 128 : 196 -66
- // right 176, 48 : 196 -136
- Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 48 * scale, zymsb_crosshair_left1->name, 64*scale, 80*scale, 78*scale, -66*scale, cl.stats[STAT_AMMO] * (1.0 / 200.0), cl.stats[STAT_SHELLS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
- Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 128 * scale, zymsb_crosshair_left2->name, 64*scale, 80*scale, 68*scale, -66*scale, cl.stats[STAT_NAILS] * (1.0 / 200.0), cl.stats[STAT_ROCKETS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
- Sbar_DrawGauge(sbar_x + 176 * scale, sbar_y + 48 * scale, zymsb_crosshair_right->name, 64*scale, 160*scale, 148*scale, -136*scale, cl.stats[STAT_ARMOR] * (1.0 / 300.0), cl.stats[STAT_HEALTH] * (1.0 / 300.0), 0.0f,0.5f,1.0f,1.0f, 1.0f,0.0f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
- DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
-#else
- float scale = 128.0f / 256.0f;
- float healthstart, healthheight, healthstarttc, healthendtc;
- float shieldstart, shieldheight, shieldstarttc, shieldendtc;
- float ammostart, ammoheight, ammostarttc, ammoendtc;
- float clipstart, clipheight, clipstarttc, clipendtc;
- float kickoffset[3], offset;
- VectorClear(kickoffset);
- if (v_dmg_time > 0)
- {
- kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
- kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
- }
- sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
- sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
- offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
- DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + ( 88 - offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 0,0, 1,1,1,1, 1,0, 1,1,1,1, 0,1, 1,1,1,1, 1,1, 1,1,1,1, 0);
- DrawQ_SuperPic(sbar_x + (132 + offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 0,1, 1,1,1,1, 0,0, 1,1,1,1, 1,1, 1,1,1,1, 1,0, 1,1,1,1, 0);
- DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + (132 + offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 1,1, 1,1,1,1, 0,1, 1,1,1,1, 1,0, 1,1,1,1, 0,0, 1,1,1,1, 0);
- DrawQ_SuperPic(sbar_x + ( 88 - offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 1,0, 1,1,1,1, 1,1, 1,1,1,1, 0,0, 1,1,1,1, 0,1, 1,1,1,1, 0);
- healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
- shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
- healthstart = 204 - healthheight;
- shieldstart = healthstart - shieldheight;
- healthstarttc = healthstart * (1.0f / 256.0f);
- healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
- shieldstarttc = shieldstart * (1.0f / 256.0f);
- shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
- ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
- ammostart = 114 - ammoheight;
- ammostarttc = ammostart * (1.0f / 256.0f);
- ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
- clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
- clipstart = 190 - clipheight;
- clipstarttc = clipstart * (1.0f / 256.0f);
- clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
- if (healthheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + healthstart * scale, zymsb_crosshair_health->name, 256 * scale, healthheight * scale, 0,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 1,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 0,healthendtc, 1.0f,0.0f,0.0f,1.0f, 1,healthendtc, 1.0f,0.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
- if (shieldheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + shieldstart * scale, zymsb_crosshair_health->name, 256 * scale, shieldheight * scale, 0,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 1,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 0,shieldendtc, 0.0f,0.5f,1.0f,1.0f, 1,shieldendtc, 0.0f,0.5f,1.0f,1.0f, DRAWFLAG_NORMAL);
- if (ammoheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + ammostart * scale, zymsb_crosshair_ammo->name, 256 * scale, ammoheight * scale, 0,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 1,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 0,ammoendtc, 0.8f,0.8f,0.0f,1.0f, 1,ammoendtc, 0.8f,0.8f,0.0f,1.0f, DRAWFLAG_NORMAL);
- if (clipheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + clipstart * scale, zymsb_crosshair_clip->name, 256 * scale, clipheight * scale, 0,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 1,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 0,clipendtc, 1.0f,1.0f,0.0f,1.0f, 1,clipendtc, 1.0f,1.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
- DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background->name, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
- DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
-#endif
- }
- else // Quake and others
- {
- sbar_y = vid_conheight.integer - SBAR_HEIGHT;
- if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
- sbar_x = 0;
- else
- sbar_x = (vid_conwidth.integer - 320)/2;
+ if (sb_lines > 24)
+ DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay->name,0,0,1,1,1,1,DRAWFLAG_MODULATE);
+ }
- if (sb_lines > 24)
- {
- if (gamemode != GAME_GOODVSBAD2)
- Sbar_DrawInventory ();
- if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
- Sbar_DrawFrags ();
+ //if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH)
+ // Sbar_MiniDeathmatchOverlay (0, 17);
}
-
- if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
+ else if (gamemode == GAME_ZYMOTIC)
{
- if (gamemode != GAME_GOODVSBAD2)
- Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
- Sbar_DrawScoreboard ();
+ #if 1
+ float scale = 64.0f / 256.0f;
+ float kickoffset[3];
+ VectorClear(kickoffset);
+ if (v_dmg_time > 0)
+ {
+ kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
+ kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
+ }
+ sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
+ sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
+ // left1 16, 48 : 126 -66
+ // left2 16, 128 : 196 -66
+ // right 176, 48 : 196 -136
+ Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 48 * scale, zymsb_crosshair_left1->name, 64*scale, 80*scale, 78*scale, -66*scale, cl.stats[STAT_AMMO] * (1.0 / 200.0), cl.stats[STAT_SHELLS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
+ Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 128 * scale, zymsb_crosshair_left2->name, 64*scale, 80*scale, 68*scale, -66*scale, cl.stats[STAT_NAILS] * (1.0 / 200.0), cl.stats[STAT_ROCKETS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
+ Sbar_DrawGauge(sbar_x + 176 * scale, sbar_y + 48 * scale, zymsb_crosshair_right->name, 64*scale, 160*scale, 148*scale, -136*scale, cl.stats[STAT_ARMOR] * (1.0 / 300.0), cl.stats[STAT_HEALTH] * (1.0 / 300.0), 0.0f,0.5f,1.0f,1.0f, 1.0f,0.0f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
+ DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
+ #else
+ float scale = 128.0f / 256.0f;
+ float healthstart, healthheight, healthstarttc, healthendtc;
+ float shieldstart, shieldheight, shieldstarttc, shieldendtc;
+ float ammostart, ammoheight, ammostarttc, ammoendtc;
+ float clipstart, clipheight, clipstarttc, clipendtc;
+ float kickoffset[3], offset;
+ VectorClear(kickoffset);
+ if (v_dmg_time > 0)
+ {
+ kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
+ kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
+ }
+ sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
+ sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
+ offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
+ DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + ( 88 - offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 0,0, 1,1,1,1, 1,0, 1,1,1,1, 0,1, 1,1,1,1, 1,1, 1,1,1,1, 0);
+ DrawQ_SuperPic(sbar_x + (132 + offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 0,1, 1,1,1,1, 0,0, 1,1,1,1, 1,1, 1,1,1,1, 1,0, 1,1,1,1, 0);
+ DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + (132 + offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 1,1, 1,1,1,1, 0,1, 1,1,1,1, 1,0, 1,1,1,1, 0,0, 1,1,1,1, 0);
+ DrawQ_SuperPic(sbar_x + ( 88 - offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 1,0, 1,1,1,1, 1,1, 1,1,1,1, 0,0, 1,1,1,1, 0,1, 1,1,1,1, 0);
+ healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
+ shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
+ healthstart = 204 - healthheight;
+ shieldstart = healthstart - shieldheight;
+ healthstarttc = healthstart * (1.0f / 256.0f);
+ healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
+ shieldstarttc = shieldstart * (1.0f / 256.0f);
+ shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
+ ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
+ ammostart = 114 - ammoheight;
+ ammostarttc = ammostart * (1.0f / 256.0f);
+ ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
+ clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
+ clipstart = 190 - clipheight;
+ clipstarttc = clipstart * (1.0f / 256.0f);
+ clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
+ if (healthheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + healthstart * scale, zymsb_crosshair_health->name, 256 * scale, healthheight * scale, 0,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 1,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 0,healthendtc, 1.0f,0.0f,0.0f,1.0f, 1,healthendtc, 1.0f,0.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
+ if (shieldheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + shieldstart * scale, zymsb_crosshair_health->name, 256 * scale, shieldheight * scale, 0,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 1,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 0,shieldendtc, 0.0f,0.5f,1.0f,1.0f, 1,shieldendtc, 0.0f,0.5f,1.0f,1.0f, DRAWFLAG_NORMAL);
+ if (ammoheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + ammostart * scale, zymsb_crosshair_ammo->name, 256 * scale, ammoheight * scale, 0,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 1,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 0,ammoendtc, 0.8f,0.8f,0.0f,1.0f, 1,ammoendtc, 0.8f,0.8f,0.0f,1.0f, DRAWFLAG_NORMAL);
+ if (clipheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + clipstart * scale, zymsb_crosshair_clip->name, 256 * scale, clipheight * scale, 0,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 1,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 0,clipendtc, 1.0f,1.0f,0.0f,1.0f, 1,clipendtc, 1.0f,1.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
+ DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background->name, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
+ DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
+ #endif
}
- else if (sb_lines)
+ else // Quake and others
{
- Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
+ sbar_y = vid_conheight.integer - SBAR_HEIGHT;
+ if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
+ sbar_x = 0;
+ else
+ sbar_x = (vid_conwidth.integer - 320)/2;
- // keys (hipnotic only)
- //MED 01/04/97 moved keys here so they would not be overwritten
- if (gamemode == GAME_HIPNOTIC)
+ if (sb_lines > 24)
{
- if (cl.stats[STAT_ITEMS] & IT_KEY1)
- Sbar_DrawPic (209, 3, sb_items[0]);
- if (cl.stats[STAT_ITEMS] & IT_KEY2)
- Sbar_DrawPic (209, 12, sb_items[1]);
+ if (gamemode != GAME_GOODVSBAD2)
+ Sbar_DrawInventory ();
+ if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
+ Sbar_DrawFrags ();
}
- // armor
- if (gamemode != GAME_GOODVSBAD2)
+
+ if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
{
- if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
+ if (gamemode != GAME_GOODVSBAD2)
+ Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
+ Sbar_DrawScoreboard ();
+ }
+ else if (sb_lines)
+ {
+ Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
+
+ // keys (hipnotic only)
+ //MED 01/04/97 moved keys here so they would not be overwritten
+ if (gamemode == GAME_HIPNOTIC)
{
- Sbar_DrawNum (24, 0, 666, 3, 1);
- Sbar_DrawPic (0, 0, sb_disc);
+ if (cl.stats[STAT_ITEMS] & IT_KEY1)
+ Sbar_DrawPic (209, 3, sb_items[0]);
+ if (cl.stats[STAT_ITEMS] & IT_KEY2)
+ Sbar_DrawPic (209, 12, sb_items[1]);
}
- else
+ // armor
+ if (gamemode != GAME_GOODVSBAD2)
{
- if (gamemode == GAME_ROGUE)
+ if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
{
- Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
- if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
- Sbar_DrawPic (0, 0, sb_armor[2]);
- else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
- Sbar_DrawPic (0, 0, sb_armor[1]);
- else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
- Sbar_DrawPic (0, 0, sb_armor[0]);
+ Sbar_DrawNum (24, 0, 666, 3, 1);
+ Sbar_DrawPic (0, 0, sb_disc);
}
else
{
- Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
- if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
- Sbar_DrawPic (0, 0, sb_armor[2]);
- else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
- Sbar_DrawPic (0, 0, sb_armor[1]);
- else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
- Sbar_DrawPic (0, 0, sb_armor[0]);
+ if (gamemode == GAME_ROGUE)
+ {
+ Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
+ if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
+ Sbar_DrawPic (0, 0, sb_armor[2]);
+ else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
+ Sbar_DrawPic (0, 0, sb_armor[1]);
+ else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
+ Sbar_DrawPic (0, 0, sb_armor[0]);
+ }
+ else
+ {
+ Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
+ if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
+ Sbar_DrawPic (0, 0, sb_armor[2]);
+ else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
+ Sbar_DrawPic (0, 0, sb_armor[1]);
+ else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
+ Sbar_DrawPic (0, 0, sb_armor[0]);
+ }
}
}
- }
- // face
- Sbar_DrawFace ();
+ // face
+ Sbar_DrawFace ();
- // health
- Sbar_DrawNum (154, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
+ // health
+ Sbar_DrawNum (154, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
- // ammo icon
- if (gamemode == GAME_ROGUE)
- {
- if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
- Sbar_DrawPic (224, 0, sb_ammo[0]);
- else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
- Sbar_DrawPic (224, 0, sb_ammo[1]);
- else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
- Sbar_DrawPic (224, 0, sb_ammo[2]);
- else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
- Sbar_DrawPic (224, 0, sb_ammo[3]);
- else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
- Sbar_DrawPic (224, 0, rsb_ammo[0]);
- else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
- Sbar_DrawPic (224, 0, rsb_ammo[1]);
- else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
- Sbar_DrawPic (224, 0, rsb_ammo[2]);
- }
- else
- {
- if (cl.stats[STAT_ITEMS] & IT_SHELLS)
- Sbar_DrawPic (224, 0, sb_ammo[0]);
- else if (cl.stats[STAT_ITEMS] & IT_NAILS)
- Sbar_DrawPic (224, 0, sb_ammo[1]);
- else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
- Sbar_DrawPic (224, 0, sb_ammo[2]);
- else if (cl.stats[STAT_ITEMS] & IT_CELLS)
- Sbar_DrawPic (224, 0, sb_ammo[3]);
- }
+ // ammo icon
+ if (gamemode == GAME_ROGUE)
+ {
+ if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
+ Sbar_DrawPic (224, 0, sb_ammo[0]);
+ else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
+ Sbar_DrawPic (224, 0, sb_ammo[1]);
+ else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
+ Sbar_DrawPic (224, 0, sb_ammo[2]);
+ else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
+ Sbar_DrawPic (224, 0, sb_ammo[3]);
+ else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
+ Sbar_DrawPic (224, 0, rsb_ammo[0]);
+ else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
+ Sbar_DrawPic (224, 0, rsb_ammo[1]);
+ else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
+ Sbar_DrawPic (224, 0, rsb_ammo[2]);
+ }
+ else
+ {
+ if (cl.stats[STAT_ITEMS] & IT_SHELLS)
+ Sbar_DrawPic (224, 0, sb_ammo[0]);
+ else if (cl.stats[STAT_ITEMS] & IT_NAILS)
+ Sbar_DrawPic (224, 0, sb_ammo[1]);
+ else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
+ Sbar_DrawPic (224, 0, sb_ammo[2]);
+ else if (cl.stats[STAT_ITEMS] & IT_CELLS)
+ Sbar_DrawPic (224, 0, sb_ammo[3]);
+ }
- Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
+ Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
- }
+ }
- if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH)
- {
- if (gamemode == GAME_TRANSFUSION)
- Sbar_MiniDeathmatchOverlay (0, 0);
- else
- Sbar_MiniDeathmatchOverlay (324, vid_conheight.integer - sb_lines);
+ if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH)
+ {
+ if (gamemode == GAME_TRANSFUSION)
+ Sbar_MiniDeathmatchOverlay (0, 0);
+ else
+ Sbar_MiniDeathmatchOverlay (324, vid_conheight.integer - sb_lines);
+ }
}
}
Sbar_ShowFPS();
- R_Draw2DCrosshair();
+ if(cl.csqc_vidvars.drawcrosshair)
+ R_Draw2DCrosshair();
if (cl_prydoncursor.integer)
DrawQ_Pic((cl.cmd.cursor_screen[0] + 1) * 0.5 * vid_conwidth.integer, (cl.cmd.cursor_screen[1] + 1) * 0.5 * vid_conheight.integer, va("gfx/prydoncursor%03i", cl_prydoncursor.integer), 0, 0, 1, 1, 1, 1, 0);
void SV_VM_Init();
void SV_VM_Setup();
+void VM_AutoSentStats_Clear (void);
+void EntityFrameCSQC_ClearVersions (void);
+void EntityFrameCSQC_InitClientVersions (int client, qboolean clear);
+void VM_SV_WriteAutoSentStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
+void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_state_t *states);
+
+
// select which protocol to host, this is fed to Protocol_EnumForName
cvar_t sv_protocolname = {0, "sv_protocolname", "DP7"};
cvar_t sv_ratelimitlocalplayer = {0, "sv_ratelimitlocalplayer", "0"};
==============================================================================
*/
+static const char *SV_InitCmd; //[515]: svprogs able to send cmd to client on connect
/*
================
SV_SendServerinfo
dpsnprintf (message, sizeof (message), "\002\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
MSG_WriteString (&client->message,message);
+ // LordHavoc: this does not work on dedicated servers, needs fixing.
+extern qboolean csqc_loaded;
+//[515]: init csprogs according to version of svprogs, check the crc, etc.
+ if(csqc_loaded && (cls.state == ca_dedicated || PRVM_NUM_FOR_EDICT(client->edict) != 1))
+ {
+ MSG_WriteByte (&client->message, svc_stufftext);
+ if(SV_InitCmd)
+ MSG_WriteString (&client->message, va("csqc_progcrc %i;%s\n", csqc_progcrc.integer, SV_InitCmd));
+ else
+ MSG_WriteString (&client->message, va("csqc_progcrc %i\n", csqc_progcrc.integer));
+ }
+
MSG_WriteByte (&client->message, svc_serverinfo);
MSG_WriteLong (&client->message, Protocol_NumberForEnum(sv.protocol));
MSG_WriteByte (&client->message, svs.maxclients);
client = svs.clients + clientnum;
+ if(netconnection)//[515]: bots don't play with csqc =)
+ EntityFrameCSQC_InitClientVersions(clientnum, false);
+
// set up the client_t
if (sv.loadgame)
memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
{
int i, numsendstates;
entity_state_t *s;
+ extern int csqc_clent;
// if there isn't enough space to accomplish anything, skip it
if (msg->cursize + 25 > msg->maxsize)
if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
- sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
+ csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
sententitiesmark++;
if (sv_cullentities_stats.integer)
Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv_writeentitiestoclient_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_trace);
+ EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
+
if (client->entitydatabase5)
EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
else if (client->entitydatabase4)
// add the client specific data to the datagram
SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
+ VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
// expand packet size to allow effects to go over the rate limit
ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
}*/
+ // reset client csqc entity versions right away.
+ for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ EntityFrameCSQC_InitClientVersions(i, true);
+
sv.datagram.maxsize = sizeof(sv.datagram_buf);
sv.datagram.cursize = 0;
sv.datagram.data = sv.datagram_buf;
int eval_colormod;
int eval_playermodel;
int eval_playerskin;
+int eval_SendEntity;
+int eval_Version;
int eval_customizeentityforclient;
mfunction_t *SV_PlayerPhysicsQC;
//KrimZon - SERVER COMMANDS IN QUAKEC
mfunction_t *SV_ParseClientCommandQC;
+ddef_t *PRVM_ED_FindGlobal(const char *name);
+
void SV_VM_FindEdictFieldOffsets(void)
{
eval_gravity = PRVM_ED_FindFieldOffset("gravity");
eval_colormod = PRVM_ED_FindFieldOffset("colormod");
eval_playermodel = PRVM_ED_FindFieldOffset("playermodel");
eval_playerskin = PRVM_ED_FindFieldOffset("playerskin");
+ eval_SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
+ eval_Version = PRVM_ED_FindFieldOffset("Version");
eval_customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
// LordHavoc: allowing QuakeC to override the player movement code
EndFrameQC = PRVM_ED_FindFunction ("EndFrame");
//KrimZon - SERVER COMMANDS IN QUAKEC
SV_ParseClientCommandQC = PRVM_ED_FindFunction ("SV_ParseClientCommand");
+
+ //[515]: init stufftext string (it is sent before svc_serverinfo)
+ if(PRVM_ED_FindGlobal("SV_InitCmd") && PRVM_ED_FindGlobal("SV_InitCmd")->type & ev_string)
+ SV_InitCmd = PRVM_G_STRING(PRVM_ED_FindGlobal("SV_InitCmd")->ofs);
+ else
+ SV_InitCmd = NULL;
}
#define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
{ev_float, "scale"},
{ev_float, "style"},
{ev_float, "tag_index"},
+ {ev_float, "Version"},
{ev_float, "viewzoom"},
{ev_vector, "color"},
{ev_vector, "colormod"},
{ev_vector, "punchvector"},
{ev_string, "playermodel"},
{ev_string, "playerskin"},
+ {ev_function, "SendEntity"},
{ev_function, "customizeentityforclient"},
};
PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields );
SV_VM_FindEdictFieldOffsets();
+ VM_AutoSentStats_Clear();//[515]: csqc
+ EntityFrameCSQC_ClearVersions();//[515]: csqc
+
PRVM_End;
}
"DP_QC_MULTIPLETEMPSTRINGS "
"DP_QC_RANDOMVEC "
"DP_QC_SINCOSSQRTPOW "
+"DP_QC_STRINGBUFFERS "
"DP_QC_TRACEBOX "
"DP_QC_TRACETOSS "
"DP_QC_TRACE_MOVETYPE_HITMODEL "
"DP_TE_SPARK "
"DP_TE_STANDARDEFFECTBUILTINS "
"DP_VIEWZOOM "
+"EXT_BITSHIFT "
+"EXT_CSQC "
"FRIK_FILE "
"KRIMZON_SV_PARSECLIENTCOMMAND "
"NEH_CMD_PLAY2 "
AngleVectors (PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up);
}
-/*
-==============
-PF_vectorvectors
-
-Writes new values for v_forward, v_up, and v_right based on the given forward vector
-vectorvectors(vector, vector)
-==============
-*/
-void PF_vectorvectors (void)
-{
- VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward);
- VectorVectors(prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up);
-}
-
/*
=================
PF_setorigin
#define MSG_ONE 1 // reliable to one (msg_entity)
#define MSG_ALL 2 // reliable to all
#define MSG_INIT 3 // write to the init string
+#define MSG_ENTITY 5
sizebuf_t *WriteDest (void)
{
int entnum;
int dest;
prvm_edict_t *ent;
+ extern sizebuf_t *sv2csqcbuf;
dest = PRVM_G_FLOAT(OFS_PARM0);
switch (dest)
case MSG_INIT:
return &sv.signon;
+
+ case MSG_ENTITY:
+ return sv2csqcbuf;
}
return NULL;
PRVM_G_FLOAT(OFS_RETURN) = 1; // success
}
+typedef struct
+{
+ unsigned char type; // 1/2/8 or other value if isn't used
+ int fieldoffset;
+}autosentstat_t;
+
+static autosentstat_t *vm_autosentstats = NULL; //[515]: it starts from 0, not 32
+static int vm_autosentstats_last;
+
+void VM_AutoSentStats_Clear (void)
+{
+ if(vm_autosentstats)
+ {
+ free(vm_autosentstats);
+ vm_autosentstats = NULL;
+ vm_autosentstats_last = -1;
+ }
+}
+
+//[515]: add check if even bigger ? "try to use two stats, cause it's too big" ?
+#define VM_SENDSTAT(a,b,c)\
+{\
+/* if((c))*/\
+ if((c)==(unsigned char)(c))\
+ {\
+ MSG_WriteByte((a), svc_updatestatubyte);\
+ MSG_WriteByte((a), (b));\
+ MSG_WriteByte((a), (c));\
+ }\
+ else\
+ {\
+ MSG_WriteByte((a), svc_updatestat);\
+ MSG_WriteByte((a), (b));\
+ MSG_WriteLong((a), (c));\
+ }\
+}\
+
+void VM_SV_WriteAutoSentStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
+{
+ int i, v, *si;
+ char s[17];
+ const char *t;
+ qboolean send;
+ union
+ {
+ float f;
+ int i;
+ }k;
+
+ if(!vm_autosentstats)
+ return;
+
+ send = (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5);
+
+ for(i=0; i<vm_autosentstats_last+1 ;i++)
+ {
+ if(!vm_autosentstats[i].type)
+ continue;
+ switch(vm_autosentstats[i].type)
+ {
+ //string
+ case 1:
+ t = PRVM_E_STRING(ent, vm_autosentstats[i].fieldoffset);
+ if(t && t[0])
+ {
+ memset(s, 0, 17);
+ strlcpy(s, t, 16);
+ si = (int*)s;
+ if (!send)
+ {
+ stats[i+32] = si[0];
+ stats[i+33] = si[1];
+ stats[i+34] = si[2];
+ stats[i+35] = si[3];
+ }
+ else
+ {
+ VM_SENDSTAT(msg, i+32, si[0]);
+ VM_SENDSTAT(msg, i+33, si[1]);
+ VM_SENDSTAT(msg, i+34, si[2]);
+ VM_SENDSTAT(msg, i+35, si[3]);
+ }
+ }
+ break;
+ //float
+ case 2:
+ k.f = PRVM_E_FLOAT(ent, vm_autosentstats[i].fieldoffset); //[515]: use PRVM_E_INT ?
+ k.i = LittleLong (k.i);
+ if (!send)
+ stats[i+32] = k.i;
+ else
+ VM_SENDSTAT(msg, i+32, k.i);
+ break;
+ //integer
+ case 8:
+ v = PRVM_E_FLOAT(ent, vm_autosentstats[i].fieldoffset); //[515]: use PRVM_E_INT ?
+ if (!send)
+ stats[i+32] = v;
+ else
+ VM_SENDSTAT(msg, i+32, v);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// void(float index, float type, .void field) SV_AddStat = #470;
+// Set up an auto-sent player stat.
+// Client's get thier own fields sent to them. Index may not be less than 32.
+// Type is a value equating to the ev_ values found in qcc to dictate types. Valid ones are:
+// 1: string (4 stats carrying a total of 16 charactures)
+// 2: float (one stat, float converted to an integer for transportation)
+// 8: integer (one stat, not converted to an int, so this can be used to transport floats as floats - what a unique idea!)
+void PF_SV_AddStat (void)
+{
+ int off, i;
+ unsigned char type;
+
+ if(!vm_autosentstats)
+ {
+ vm_autosentstats = malloc((MAX_CL_STATS-32) * sizeof(autosentstat_t));
+ if(!vm_autosentstats)
+ {
+ Con_Printf("PF_SV_AddStat: not enough memory\n");
+ return;
+ }
+ }
+ i = PRVM_G_FLOAT(OFS_PARM0);
+ type = PRVM_G_FLOAT(OFS_PARM1);
+ off = PRVM_G_INT (OFS_PARM2);
+ i -= 32;
+
+ if(i < 0)
+ {
+ Con_Printf("PF_SV_AddStat: index may not be less than 32\n");
+ return;
+ }
+ if(i >= (MAX_CL_STATS-32))
+ {
+ Con_Printf("PF_SV_AddStat: index >= MAX_CL_STATS\n");
+ return;
+ }
+ if(i > (MAX_CL_STATS-32-4) && type == 1)
+ {
+ Con_Printf("PF_SV_AddStat: index > (MAX_CL_STATS-4) with string\n");
+ return;
+ }
+ vm_autosentstats[i].type = type;
+ vm_autosentstats[i].fieldoffset = off;
+ if(vm_autosentstats_last < i)
+ vm_autosentstats_last = i;
+}
+
/*
=================
PF_copyentity
MSG_WriteByte(&sv.datagram, PRVM_G_FLOAT(OFS_PARM2));
}
-static void clippointtosurface(msurface_t *surface, vec3_t p, vec3_t out)
+void clippointtosurface(msurface_t *surface, vec3_t p, vec3_t out)
{
int i, j, k;
float *v[3], facenormal[3], edgenormal[3], sidenormal[3], temp[3], offsetdist, dist, bestdist;
PRVM_G_FLOAT(OFS_RETURN) = 2;
}
+void PF_edict_num (void)
+{
+ VM_RETURN_EDICT(PRVM_EDICT_NUM((int)PRVM_G_FLOAT(OFS_PARM0)));
+}
+
prvm_builtin_t vm_sv_builtins[] = {
NULL, // #0
-PF_makevectors, // #1 void(entity e) makevectors
+PF_makevectors, // #1 void(vector ang) makevectors
PF_setorigin, // #2 void(entity e, vector o) setorigin
PF_setmodel, // #3 void(entity e, string m) setmodel
PF_setsize, // #4 void(entity e, vector min, vector max) setsize
VM_strzone, // #118 string(string s) strzone (FRIK_FILE)
VM_strunzone, // #119 void(string s) strunzone (FRIK_FILE)
e10, e10, e10, e10, e10, e10, e10, e10, // #120-199
-e10, e10, e10, e10, e10, e10, e10, e10, e10, e10, // #200-299
+// FTEQW range #200-#299
+NULL, // #200
+NULL, // #201
+NULL, // #202
+NULL, // #203
+NULL, // #204
+NULL, // #205
+NULL, // #206
+NULL, // #207
+NULL, // #208
+NULL, // #209
+NULL, // #210
+NULL, // #211
+NULL, // #212
+NULL, // #213
+NULL, // #214
+NULL, // #215
+NULL, // #216
+NULL, // #217
+VM_bitshift, // #218 float(float number, float quantity) bitshift (EXT_BITSHIFT)
+NULL, // #219
+e10, // #220-#229
+e10, // #230-#239
+e10, // #240-#249
+e10, // #250-#259
+e10, // #260-#269
+e10, // #270-#279
+e10, // #280-#289
+e10, // #290-#299
e10, e10, e10, e10, e10, e10, e10, e10, e10, e10, // #300-399
VM_copyentity, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY)
PF_setcolor, // #401 void(entity ent, float colors) setcolor (DP_QC_SETCOLOR)
PF_te_lightning2, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
PF_te_lightning3, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
PF_te_beam, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
-PF_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS)
+VM_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS)
PF_te_plasmaburn, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE)
PF_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE)
PF_WriteUnterminatedString, // #456 void(float to, string s) WriteUnterminatedString (DP_SV_WRITEUNTERMINATEDSTRING)
PF_te_flamejet, // #457 void(vector org, vector vel, float howmany) te_flamejet = #457 (DP_TE_FLAMEJET)
NULL, // #458
-NULL, // #459
-e10, e10, e10, e10 // #460-499 (LordHavoc)
+PF_edict_num, // #459 entity(float num) (??)
+VM_buf_create, // #460 float() buf_create (DP_QC_STRINGBUFFERS)
+VM_buf_del, // #461 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS)
+VM_buf_getsize, // #462 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS)
+VM_buf_copy, // #463 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS)
+VM_buf_sort, // #464 void(float bufhandle, float sortpower, float backward) buf_sort (DP_QC_STRINGBUFFERS)
+VM_buf_implode, // #465 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS)
+VM_bufstr_get, // #466 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS)
+VM_bufstr_set, // #467 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS)
+VM_bufstr_add, // #468 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS)
+VM_bufstr_free, // #469 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS)
+PF_SV_AddStat, // #470 void(float index, float type, .void field) SV_AddStat (EXT_CSQC)
+NULL, // #471
+NULL, // #472
+NULL, // #473
+NULL, // #474
+NULL, // #475
+NULL, // #476
+NULL, // #477
+NULL, // #478
+NULL, // #479
+e10, e10 // #471-499 (LordHavoc)
};
const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t);
in_mouse_y = my;
// if the mouse has moved, force it to the center, so there's room to move
+ if (!cl.csqc_wantsmousemove)
if (mx || my)
SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2));
}
extern matrix4x4_t viewmodelmatrix;
#include "cl_collision.h"
+#include "csprogs.h"
/*
==================
entity_t *ent;
float vieworg[3], gunorg[3], viewangles[3];
trace_t trace;
+ if(csqc_loaded)
+ return;
VectorClear(gunorg);
Matrix4x4_CreateIdentity(&viewmodelmatrix);
Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix);
Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2] + v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 1);
// calculate a viewmodel matrix for use in view-attached entities
Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, gunorg[0], gunorg[1], gunorg[2], viewangles[0], viewangles[1], viewangles[2], 0.3);
+ VectorCopy(vieworg, csqc_origin);
+ VectorCopy(viewangles, csqc_angles);
}
}
}
--- /dev/null
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// world.c -- world query functions
+
+#include "quakedef.h"
+
+/*
+
+entities never clip against themselves, or their owner
+
+line of sight checks trace->inopen and trace->inwater, but bullets don't
+
+*/
+
+extern cvar_t sv_debugmove;
+extern cvar_t sv_areagrid_mingridsize;
+
+trace_t CSSV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict);
+
+void CSSV_AreaStats_f(void);
+
+void CSSV_World_Init(void)
+{
+ Cvar_RegisterVariable(&sv_debugmove);
+ Cvar_RegisterVariable(&sv_areagrid_mingridsize);
+ Cmd_AddCommand("cssv_areastats", CSSV_AreaStats_f);
+ Collision_Init();
+}
+
+//============================================================================
+
+// CSClearLink is used for new headnodes
+static void CSClearLink (link_t *l)
+{
+ l->entitynumber = 0;
+ l->prev = l->next = l;
+}
+
+static void CSRemoveLink (link_t *l)
+{
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+}
+
+static void CSInsertLinkBefore (link_t *l, link_t *before, int entitynumber)
+{
+ l->entitynumber = entitynumber;
+ l->next = before;
+ l->prev = before->prev;
+ l->prev->next = l;
+ l->next->prev = l;
+}
+
+
+/*
+===============================================================================
+
+ENTITY AREA CHECKING
+
+===============================================================================
+*/
+
+int cssv_areagrid_stats_calls = 0;
+int cssv_areagrid_stats_nodechecks = 0;
+int cssv_areagrid_stats_entitychecks = 0;
+
+void CSSV_AreaStats_f(void)
+{
+ Con_Printf("csareagrid check stats: %d calls %d nodes (%f per call) %d entities (%f per call)\n", cssv_areagrid_stats_calls, cssv_areagrid_stats_nodechecks, (double) cssv_areagrid_stats_nodechecks / (double) cssv_areagrid_stats_calls, cssv_areagrid_stats_entitychecks, (double) cssv_areagrid_stats_entitychecks / (double) cssv_areagrid_stats_calls);
+ cssv_areagrid_stats_calls = 0;
+ cssv_areagrid_stats_nodechecks = 0;
+ cssv_areagrid_stats_entitychecks = 0;
+}
+
+typedef struct areagrid_s
+{
+ link_t edicts;
+}
+csareagrid_t;
+
+#define CSAREA_GRID 512
+#define CSAREA_GRIDNODES (CSAREA_GRID * CSAREA_GRID)
+
+static csareagrid_t cssv_areagrid[CSAREA_GRIDNODES], cssv_areagrid_outside;
+static vec3_t cssv_areagrid_bias, cssv_areagrid_scale, cssv_areagrid_mins, cssv_areagrid_maxs, cssv_areagrid_size;
+static int cssv_areagrid_marknumber = 1;
+
+void CSSV_CreateAreaGrid (vec3_t mins, vec3_t maxs)
+{
+ int i;
+ CSClearLink (&cssv_areagrid_outside.edicts);
+ // choose either the world box size, or a larger box to ensure the grid isn't too fine
+ cssv_areagrid_size[0] = max(maxs[0] - mins[0], CSAREA_GRID * sv_areagrid_mingridsize.value);
+ cssv_areagrid_size[1] = max(maxs[1] - mins[1], CSAREA_GRID * sv_areagrid_mingridsize.value);
+ cssv_areagrid_size[2] = max(maxs[2] - mins[2], CSAREA_GRID * sv_areagrid_mingridsize.value);
+ // figure out the corners of such a box, centered at the center of the world box
+ cssv_areagrid_mins[0] = (mins[0] + maxs[0] - cssv_areagrid_size[0]) * 0.5f;
+ cssv_areagrid_mins[1] = (mins[1] + maxs[1] - cssv_areagrid_size[1]) * 0.5f;
+ cssv_areagrid_mins[2] = (mins[2] + maxs[2] - cssv_areagrid_size[2]) * 0.5f;
+ cssv_areagrid_maxs[0] = (mins[0] + maxs[0] + cssv_areagrid_size[0]) * 0.5f;
+ cssv_areagrid_maxs[1] = (mins[1] + maxs[1] + cssv_areagrid_size[1]) * 0.5f;
+ cssv_areagrid_maxs[2] = (mins[2] + maxs[2] + cssv_areagrid_size[2]) * 0.5f;
+ // now calculate the actual useful info from that
+ VectorNegate(cssv_areagrid_mins, cssv_areagrid_bias);
+ cssv_areagrid_scale[0] = CSAREA_GRID / cssv_areagrid_size[0];
+ cssv_areagrid_scale[1] = CSAREA_GRID / cssv_areagrid_size[1];
+ cssv_areagrid_scale[2] = CSAREA_GRID / cssv_areagrid_size[2];
+ for (i = 0;i < CSAREA_GRIDNODES;i++)
+ {
+ CSClearLink (&cssv_areagrid[i].edicts);
+ }
+ Con_DPrintf("cssv_areagrid settings: divisions %ix%ix1 : box %f %f %f : %f %f %f size %f %f %f grid %f %f %f (mingrid %f)\n", CSAREA_GRID, CSAREA_GRID, cssv_areagrid_mins[0], cssv_areagrid_mins[1], cssv_areagrid_mins[2], cssv_areagrid_maxs[0], cssv_areagrid_maxs[1], cssv_areagrid_maxs[2], cssv_areagrid_size[0], cssv_areagrid_size[1], cssv_areagrid_size[2], 1.0f / cssv_areagrid_scale[0], 1.0f / cssv_areagrid_scale[1], 1.0f / cssv_areagrid_scale[2], sv_areagrid_mingridsize.value);
+}
+
+/*
+===============
+CSSV_ClearWorld
+
+===============
+*/
+void CSSV_ClearWorld (void)
+{
+ CSSV_CreateAreaGrid(cl.worldmodel->normalmins, cl.worldmodel->normalmaxs);
+}
+
+
+/*
+===============
+CSSV_UnlinkEdict
+
+===============
+*/
+void CSSV_UnlinkEdict (prvm_edict_t *ent)
+{
+ int i;
+ for (i = 0;i < ENTITYGRIDAREAS;i++)
+ {
+ if (ent->priv.server->areagrid[i].prev)
+ {
+ CSRemoveLink (&ent->priv.server->areagrid[i]);
+ ent->priv.server->areagrid[i].prev = ent->priv.server->areagrid[i].next = NULL;
+ }
+ }
+}
+
+int CSSV_EntitiesInBox(vec3_t mins, vec3_t maxs, int maxlist, prvm_edict_t **list)
+{
+ int numlist;
+ csareagrid_t *grid;
+ link_t *l;
+ prvm_edict_t *ent;
+ int igrid[3], igridmins[3], igridmaxs[3];
+
+ cssv_areagrid_stats_calls++;
+ cssv_areagrid_marknumber++;
+ igridmins[0] = (int) ((mins[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]);
+ igridmins[1] = (int) ((mins[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]);
+ //igridmins[2] = (int) ((mins[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]);
+ igridmaxs[0] = (int) ((maxs[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]) + 1;
+ igridmaxs[1] = (int) ((maxs[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]) + 1;
+ //igridmaxs[2] = (int) ((maxs[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]) + 1;
+ igridmins[0] = max(0, igridmins[0]);
+ igridmins[1] = max(0, igridmins[1]);
+ //igridmins[2] = max(0, igridmins[2]);
+ igridmaxs[0] = min(CSAREA_GRID, igridmaxs[0]);
+ igridmaxs[1] = min(CSAREA_GRID, igridmaxs[1]);
+ //igridmaxs[2] = min(CSAREA_GRID, igridmaxs[2]);
+
+ numlist = 0;
+ // add entities not linked into areagrid because they are too big or
+ // outside the grid bounds
+ if (cssv_areagrid_outside.edicts.next != &cssv_areagrid_outside.edicts)
+ {
+ for (l = cssv_areagrid_outside.edicts.next;l != &cssv_areagrid_outside.edicts;l = l->next)
+ {
+ ent = PRVM_EDICT_NUM_UNSIGNED(l->entitynumber);
+ if (ent->priv.server->areagridmarknumber != cssv_areagrid_marknumber)
+ {
+ ent->priv.server->areagridmarknumber = cssv_areagrid_marknumber;
+ if (!ent->priv.server->free && BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax))
+ {
+ if (numlist < maxlist)
+ list[numlist] = ent;
+ numlist++;
+ }
+ cssv_areagrid_stats_entitychecks++;
+ }
+ }
+ }
+ // add grid linked entities
+ for (igrid[1] = igridmins[1];igrid[1] < igridmaxs[1];igrid[1]++)
+ {
+ grid = cssv_areagrid + igrid[1] * CSAREA_GRID + igridmins[0];
+ for (igrid[0] = igridmins[0];igrid[0] < igridmaxs[0];igrid[0]++, grid++)
+ {
+ if (grid->edicts.next != &grid->edicts)
+ {
+ for (l = grid->edicts.next;l != &grid->edicts;l = l->next)
+ {
+ ent = PRVM_EDICT_NUM_UNSIGNED(l->entitynumber);
+ if (ent->priv.server->areagridmarknumber != cssv_areagrid_marknumber)
+ {
+ ent->priv.server->areagridmarknumber = cssv_areagrid_marknumber;
+ if (!ent->priv.server->free && BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax))
+ {
+ if (numlist < maxlist)
+ list[numlist] = ent;
+ numlist++;
+ }
+ }
+ cssv_areagrid_stats_entitychecks++;
+ }
+ }
+ }
+ }
+ return numlist;
+}
+
+void CSSV_TouchAreaGrid(prvm_edict_t *ent)
+{
+ int i, numtouchedicts, old_self, old_other;
+ prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
+
+ // build a list of edicts to touch, because the link loop can be corrupted
+ // by CSSV_IncreaseEdicts called during touch functions
+ numtouchedicts = CSSV_EntitiesInBox(ent->fields.client->absmin, ent->fields.client->absmax, MAX_EDICTS, touchedicts);
+ if (numtouchedicts > MAX_EDICTS)
+ {
+ // this never happens
+ Con_Printf("CSSV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
+ numtouchedicts = MAX_EDICTS;
+ }
+
+ old_self = prog->globals.client->self;
+ old_other = prog->globals.client->other;
+ for (i = 0;i < numtouchedicts;i++)
+ {
+ touch = touchedicts[i];
+ if (touch != ent && (int)touch->fields.client->solid == SOLID_TRIGGER && touch->fields.client->touch)
+ {
+ prog->globals.client->self = PRVM_EDICT_TO_PROG(touch);
+ prog->globals.client->other = PRVM_EDICT_TO_PROG(ent);
+ prog->globals.client->time = cl.time;
+ PRVM_ExecuteProgram (touch->fields.client->touch, "QC function self.touch is missing");
+ }
+ }
+ prog->globals.client->self = old_self;
+ prog->globals.client->other = old_other;
+}
+
+void CSSV_LinkEdict_AreaGrid(prvm_edict_t *ent)
+{
+ csareagrid_t *grid;
+ int igrid[3], igridmins[3], igridmaxs[3], gridnum, entitynumber = PRVM_NUM_FOR_EDICT(ent);
+
+ if (entitynumber <= 0 || entitynumber >= prog->max_edicts || PRVM_EDICT_NUM(entitynumber) != ent)
+ {
+ Con_Printf ("CSSV_LinkEdict_AreaGrid: invalid edict %p (edicts is %p, edict compared to prog->edicts is %i)\n", ent, prog->edicts, entitynumber);
+ return;
+ }
+
+ igridmins[0] = (int) ((ent->fields.client->absmin[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]);
+ igridmins[1] = (int) ((ent->fields.client->absmin[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]);
+ //igridmins[2] = (int) ((ent->fields.client->absmin[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]);
+ igridmaxs[0] = (int) ((ent->fields.client->absmax[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]) + 1;
+ igridmaxs[1] = (int) ((ent->fields.client->absmax[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]) + 1;
+ //igridmaxs[2] = (int) ((ent->fields.client->absmax[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]) + 1;
+ if (igridmins[0] < 0 || igridmaxs[0] > CSAREA_GRID || igridmins[1] < 0 || igridmaxs[1] > CSAREA_GRID || ((igridmaxs[0] - igridmins[0]) * (igridmaxs[1] - igridmins[1])) > ENTITYGRIDAREAS)
+ {
+ // wow, something outside the grid, store it as such
+ CSInsertLinkBefore (&ent->priv.server->areagrid[0], &cssv_areagrid_outside.edicts, entitynumber);
+ return;
+ }
+
+ gridnum = 0;
+ for (igrid[1] = igridmins[1];igrid[1] < igridmaxs[1];igrid[1]++)
+ {
+ grid = cssv_areagrid + igrid[1] * CSAREA_GRID + igridmins[0];
+ for (igrid[0] = igridmins[0];igrid[0] < igridmaxs[0];igrid[0]++, grid++, gridnum++)
+ CSInsertLinkBefore (&ent->priv.server->areagrid[gridnum], &grid->edicts, entitynumber);
+ }
+}
+
+/*
+===============
+SV_LinkEdict
+
+===============
+*/
+void CSSV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
+{
+ model_t *model;
+
+ if (ent->priv.server->areagrid[0].prev)
+ CSSV_UnlinkEdict (ent); // unlink from old position
+
+ if (ent == prog->edicts)
+ return; // don't add the world
+
+ if (ent->priv.server->free)
+ return;
+
+// set the abs box
+
+ if (ent->fields.client->solid == SOLID_BSP)
+ {
+ int modelindex = ent->fields.client->modelindex;
+ if (modelindex < 0 || modelindex > MAX_MODELS)
+ {
+ Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
+ modelindex = 0;
+ }
+ model = cl.model_precache[modelindex];
+ if (model != NULL)
+ {
+ if (!model->TraceBox)
+ Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
+
+ if (ent->fields.client->angles[0] || ent->fields.client->angles[2] || ent->fields.client->avelocity[0] || ent->fields.client->avelocity[2])
+ {
+ VectorAdd(ent->fields.client->origin, model->rotatedmins, ent->fields.client->absmin);
+ VectorAdd(ent->fields.client->origin, model->rotatedmaxs, ent->fields.client->absmax);
+ }
+ else if (ent->fields.client->angles[1] || ent->fields.client->avelocity[1])
+ {
+ VectorAdd(ent->fields.client->origin, model->yawmins, ent->fields.client->absmin);
+ VectorAdd(ent->fields.client->origin, model->yawmaxs, ent->fields.client->absmax);
+ }
+ else
+ {
+ VectorAdd(ent->fields.client->origin, model->normalmins, ent->fields.client->absmin);
+ VectorAdd(ent->fields.client->origin, model->normalmaxs, ent->fields.client->absmax);
+ }
+ }
+ else
+ {
+ // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
+ VectorAdd(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->absmin);
+ VectorAdd(ent->fields.client->origin, ent->fields.client->maxs, ent->fields.client->absmax);
+ }
+ }
+ else
+ {
+ VectorAdd(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->absmin);
+ VectorAdd(ent->fields.client->origin, ent->fields.client->maxs, ent->fields.client->absmax);
+ }
+
+//
+// to make items easier to pick up and allow them to be grabbed off
+// of shelves, the abs sizes are expanded
+//
+ if ((int)ent->fields.client->flags & FL_ITEM)
+ {
+ ent->fields.client->absmin[0] -= 15;
+ ent->fields.client->absmin[1] -= 15;
+ ent->fields.client->absmin[2] -= 1;
+ ent->fields.client->absmax[0] += 15;
+ ent->fields.client->absmax[1] += 15;
+ ent->fields.client->absmax[2] += 1;
+ }
+ 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->fields.client->absmin[0] -= 1;
+ ent->fields.client->absmin[1] -= 1;
+ ent->fields.client->absmin[2] -= 1;
+ ent->fields.client->absmax[0] += 1;
+ ent->fields.client->absmax[1] += 1;
+ ent->fields.client->absmax[2] += 1;
+ }
+
+ if (ent->fields.client->solid == SOLID_NOT)
+ return;
+
+ CSSV_LinkEdict_AreaGrid(ent);
+
+// if touch_triggers, touch all entities at this node and descend for more
+ if (touch_triggers)
+ CSSV_TouchAreaGrid(ent);
+}
+
+
+
+/*
+===============================================================================
+
+POINT TESTING IN HULLS
+
+===============================================================================
+*/
+
+/*
+============
+SV_TestEntityPosition
+
+This could be a lot more efficient...
+============
+*/
+int CSSV_TestEntityPosition (prvm_edict_t *ent)
+{
+ return CSSV_Move (ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, ent->fields.client->origin, MOVE_NORMAL, ent).startsolid;
+}
+
+
+/*
+===============================================================================
+
+LINE TESTING IN HULLS
+
+===============================================================================
+*/
+
+/*
+==================
+SV_ClipMoveToEntity
+
+Handles selection or creation of a clipping hull, and offseting (and
+eventually rotation) of the end points
+==================
+*/
+trace_t CSSV_ClipMoveToEntity(prvm_edict_t *ent, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int movetype, int hitsupercontents)
+{
+ trace_t trace;
+ model_t *model = NULL;
+ matrix4x4_t matrix, imatrix;
+ float tempnormal[3], starttransformed[3], endtransformed[3];
+ float starttransformedmins[3], starttransformedmaxs[3], endtransformedmins[3], endtransformedmaxs[3];
+
+ memset(&trace, 0, sizeof(trace));
+ trace.fraction = trace.realfraction = 1;
+ VectorCopy(end, trace.endpos);
+
+ if ((int) ent->fields.client->solid == SOLID_BSP || movetype == MOVE_HITMODEL)
+ {
+ unsigned int modelindex = ent->fields.client->modelindex;
+ // if the modelindex is 0, it shouldn't be SOLID_BSP!
+ if (modelindex == 0)
+ {
+ Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with no model\n", PRVM_NUM_FOR_EDICT(ent));
+ return trace;
+ }
+ if (modelindex >= MAX_MODELS)
+ {
+ Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent));
+ return trace;
+ }
+ model = cl.model_precache[modelindex];
+ if (modelindex != 0 && model == NULL)
+ {
+ Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent));
+ return trace;
+ }
+
+ if ((int) ent->fields.client->solid == SOLID_BSP)
+ {
+ if (!model->TraceBox)
+ {
+ Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with a non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
+ return trace;
+ }
+ //if (ent->fields.client->movetype != MOVETYPE_PUSH)
+ //{
+ // Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP without MOVETYPE_PUSH\n", PRVM_NUM_FOR_EDICT(ent));
+ // return trace;
+ //}
+ }
+ Matrix4x4_CreateFromQuakeEntity(&matrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], 1);
+ }
+ else
+ Matrix4x4_CreateTranslate(&matrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2]);
+
+ Matrix4x4_Invert_Simple(&imatrix, &matrix);
+ Matrix4x4_Transform(&imatrix, start, starttransformed);
+ Matrix4x4_Transform(&imatrix, end, endtransformed);
+#if COLLISIONPARANOID >= 3
+ Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2], end[0], end[1], end[2], endtransformed[0], endtransformed[1], endtransformed[2]);
+#endif
+
+ if (model && model->TraceBox)
+ {
+ int frame;
+ frame = (int)ent->fields.client->frame;
+ frame = bound(0, frame, (model->numframes - 1));
+ VectorAdd(starttransformed, maxs, starttransformedmaxs);
+ VectorAdd(endtransformed, maxs, endtransformedmaxs);
+ VectorAdd(starttransformed, mins, starttransformedmins);
+ VectorAdd(endtransformed, mins, endtransformedmins);
+ model->TraceBox(model, frame, &trace, starttransformedmins, starttransformedmaxs, endtransformedmins, endtransformedmaxs, hitsupercontents);
+ }
+ else
+ Collision_ClipTrace_Box(&trace, ent->fields.client->mins, ent->fields.client->maxs, starttransformed, mins, maxs, endtransformed, hitsupercontents, SUPERCONTENTS_SOLID);
+ trace.fraction = bound(0, trace.fraction, 1);
+ trace.realfraction = bound(0, trace.realfraction, 1);
+
+ if (trace.fraction < 1)
+ {
+ VectorLerp(start, trace.fraction, end, trace.endpos);
+ VectorCopy(trace.plane.normal, tempnormal);
+ Matrix4x4_Transform3x3(&matrix, tempnormal, trace.plane.normal);
+ // FIXME: should recalc trace.plane.dist
+ }
+ else
+ VectorCopy(end, trace.endpos);
+
+ return trace;
+}
+
+//===========================================================================
+
+/*
+==================
+SV_Move
+==================
+*/
+#if COLLISIONPARANOID >= 1
+trace_t CSSV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict)
+#else
+trace_t CSSV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict)
+#endif
+{
+ vec3_t hullmins, hullmaxs;
+ int i;
+ int hitsupercontentsmask;
+ int passedictprog;
+ qboolean pointtrace;
+ prvm_edict_t *traceowner, *touch;
+ trace_t trace;
+ // bounding box of entire move area
+ vec3_t clipboxmins, clipboxmaxs;
+ // size of the moving object
+ vec3_t clipmins, clipmaxs;
+ // size when clipping against monsters
+ vec3_t clipmins2, clipmaxs2;
+ // start and end origin of move
+ vec3_t clipstart, clipend;
+ // trace results
+ trace_t cliptrace;
+ int numtouchedicts;
+ prvm_edict_t *touchedicts[MAX_EDICTS];
+
+ VectorCopy(start, clipstart);
+ VectorCopy(end, clipend);
+ VectorCopy(mins, clipmins);
+ VectorCopy(maxs, clipmaxs);
+ VectorCopy(mins, clipmins2);
+ VectorCopy(maxs, clipmaxs2);
+#if COLLISIONPARANOID >= 3
+ Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
+#endif
+
+ hitsupercontentsmask = SUPERCONTENTS_SOLID;
+ if (passedict)
+ {
+ if (passedict->fields.client->solid == SOLID_SLIDEBOX)
+ hitsupercontentsmask |= SUPERCONTENTS_PLAYERCLIP;
+ if ((int)passedict->fields.client->flags & FL_MONSTER)
+ hitsupercontentsmask |= SUPERCONTENTS_MONSTERCLIP;
+ }
+
+ // clip to world
+ cliptrace = CSSV_ClipMoveToEntity(prog->edicts, clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask);
+ if (cliptrace.startsolid || cliptrace.fraction < 1)
+ cliptrace.ent = prog->edicts;
+ if (type == MOVE_WORLDONLY)
+ return cliptrace;
+
+ if (type == MOVE_MISSILE)
+ {
+ // LordHavoc: modified this, was = -15, now -= 15
+ for (i = 0;i < 3;i++)
+ {
+ clipmins2[i] -= 15;
+ clipmaxs2[i] += 15;
+ }
+ }
+
+ // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
+ if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
+ cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
+ else
+ {
+ VectorCopy(clipmins, hullmins);
+ VectorCopy(clipmaxs, hullmaxs);
+ }
+
+ // create the bounding box of the entire move
+ for (i = 0;i < 3;i++)
+ {
+ clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
+ clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
+ }
+
+ // debug override to test against everything
+ if (sv_debugmove.integer)
+ {
+ clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
+ clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
+ }
+
+ // if the passedict is world, make it NULL (to avoid two checks each time)
+ if (passedict == prog->edicts)
+ passedict = NULL;
+ // precalculate prog value for passedict for comparisons
+ passedictprog = PRVM_EDICT_TO_PROG(passedict);
+ // figure out whether this is a point trace for comparisons
+ pointtrace = VectorCompare(clipmins, clipmaxs);
+ // precalculate passedict's owner edict pointer for comparisons
+ traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.client->owner) : 0;
+
+ // clip to enttiies
+ numtouchedicts = CSSV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
+ if (numtouchedicts > MAX_EDICTS)
+ {
+ // this never happens
+ Con_Printf("CSSV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
+ numtouchedicts = MAX_EDICTS;
+ }
+ for (i = 0;i < numtouchedicts;i++)
+ {
+ touch = touchedicts[i];
+
+ if (touch->fields.client->solid < SOLID_BBOX)
+ continue;
+ if (type == MOVE_NOMONSTERS && touch->fields.client->solid != SOLID_BSP)
+ continue;
+
+ if (passedict)
+ {
+ // don't clip against self
+ if (passedict == touch)
+ continue;
+ // don't clip owned entities against owner
+ if (traceowner == touch)
+ continue;
+ // don't clip owner against owned entities
+ if (passedictprog == touch->fields.client->owner)
+ continue;
+ // don't clip points against points (they can't collide)
+ if (pointtrace && VectorCompare(touch->fields.client->mins, touch->fields.client->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.client->flags & FL_MONSTER)))
+ continue;
+ // don't clip corpse against character
+ if (passedict->fields.client->solid == SOLID_CORPSE && (touch->fields.client->solid == SOLID_SLIDEBOX || touch->fields.client->solid == SOLID_CORPSE))
+ continue;
+ // don't clip character against corpse
+ if (passedict->fields.client->solid == SOLID_SLIDEBOX && touch->fields.client->solid == SOLID_CORPSE)
+ continue;
+ }
+
+ // might interact, so do an exact clip
+ if ((int)touch->fields.client->flags & FL_MONSTER)
+ trace = CSSV_ClipMoveToEntity(touch, clipstart, clipmins2, clipmaxs2, clipend, type, hitsupercontentsmask);
+ else
+ trace = CSSV_ClipMoveToEntity(touch, clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask);
+ // LordHavoc: take the 'best' answers from the new trace and combine with existing data
+ if (trace.allsolid)
+ cliptrace.allsolid = true;
+ if (trace.startsolid)
+ {
+ cliptrace.startsolid = true;
+ if (cliptrace.realfraction == 1)
+ cliptrace.ent = touch;
+ }
+ // don't set this except on the world, because it can easily confuse
+ // monsters underwater if there's a bmodel involved in the trace
+ // (inopen && inwater is how they check water visibility)
+ //if (trace.inopen)
+ // cliptrace.inopen = true;
+ if (trace.inwater)
+ cliptrace.inwater = true;
+ if (trace.realfraction < cliptrace.realfraction)
+ {
+ cliptrace.fraction = trace.fraction;
+ cliptrace.realfraction = trace.realfraction;
+ VectorCopy(trace.endpos, cliptrace.endpos);
+ cliptrace.plane = trace.plane;
+ cliptrace.ent = touch;
+ }
+ cliptrace.startsupercontents |= trace.startsupercontents;
+ }
+
+ return cliptrace;
+}
+
+#if COLLISIONPARANOID >= 1
+trace_t CSSV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict)
+{
+ int endstuck;
+ trace_t trace;
+ vec3_t temp;
+ trace = CSSV_Move_(start, mins, maxs, end, type, passedict);
+ if (passedict)
+ {
+ VectorCopy(trace.endpos, temp);
+ endstuck = CSSV_Move_(temp, mins, maxs, temp, type, passedict).startsolid;
+#if COLLISIONPARANOID < 3
+ if (trace.startsolid || endstuck)
+#endif
+ Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "\002" : "", passedict ? passedict - prog->edicts : -1, passedict->fields.client->origin[0], passedict->fields.client->origin[1], passedict->fields.client->origin[2], end[0] - passedict->fields.client->origin[0], end[1] - passedict->fields.client->origin[1], end[2] - passedict->fields.client->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.client->origin[0], trace.endpos[1] - passedict->fields.client->origin[1], trace.endpos[2] - passedict->fields.client->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
+ }
+ return trace;
+}
+#endif
+
+int CSSV_PointSuperContents(const vec3_t point)
+{
+ return CSSV_Move(point, vec3_origin, vec3_origin, point, sv_gameplayfix_swiminbmodels.integer ? MOVE_NOMONSTERS : MOVE_WORLDONLY, NULL).startsupercontents;
+}
+
+int CSSV_PointQ1Contents(const vec3_t point)
+{
+ return Mod_Q1BSP_NativeContentsFromSuperContents(NULL, CSSV_PointSuperContents(point));
+}
+
+