]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
csqc patch from [515], seems to work with [515]'s dpcsqc test mod, needs a lot of...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 16 Jan 2006 03:52:42 +0000 (03:52 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 16 Jan 2006 03:52:42 +0000 (03:52 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@5910 d7cf8633-e32d-0410-b094-e92efae38249

32 files changed:
cl_collision.c
cl_collision.h
cl_input.c
cl_main.c
cl_parse.c
cl_screen.c
cl_screen.h
client.h
clprogdefs.h
clvm_cmds.c [new file with mode: 0644]
cmd.c
console.c
csprogs.c [new file with mode: 0644]
csprogs.h [new file with mode: 0644]
gl_backend.c
gl_rmain.c
host.c
keys.c
makefile.inc
mvm_cmds.c
progs.h
progsvm.h
protocol.c
protocol.h
prvm_cmds.c
prvm_cmds.h
sbar.c
sv_main.c
svvm_cmds.c
vid_wgl.c
view.c
world_cs.c [new file with mode: 0644]

index 6a138a8e89e873d46510a7c365bf8eeba91a5006..fdaed38b4c7a6ca2f2815c32255e5b7579448f5e 100644 (file)
@@ -181,10 +181,12 @@ trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
        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;
@@ -213,12 +215,25 @@ float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, ve
        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)
@@ -226,7 +241,7 @@ float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, ve
                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;
index 532ad2503e3f37b492c28840a7c77b458ade0c71..a01940896251276cf81e9c6c61e8187195c33d6b 100644 (file)
@@ -3,7 +3,7 @@
 #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);
index 1ea050da84d00ad1021b987f4277261235f7cc4e..729e306d073173f8df96b68c861d7c8343ab32f5 100644 (file)
@@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // rights reserved.
 
 #include "quakedef.h"
+#include "csprogs.h"
 
 /*
 ===============================================================================
@@ -415,7 +416,7 @@ void CL_Move (void)
        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)
                {
@@ -506,7 +507,7 @@ void CL_UpdatePrydonCursor(void)
        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);
 }
@@ -869,6 +870,8 @@ void CL_SendMove(void)
        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)
        {
index 0606a8d60653c490c55ec7383c0b14559f1e9c04..c35aec1d5508a29c90b0835ff5e5950fa0c89c29 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -23,11 +23,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #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"};
 
@@ -77,6 +81,7 @@ client_static_t       cls;
 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;
@@ -87,7 +92,9 @@ int cl_max_brushmodel_entities;
 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;
@@ -97,6 +104,7 @@ lightstyle_t *cl_lightstyle;
 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;
@@ -116,7 +124,9 @@ void CL_ClearState(void)
        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;
@@ -139,12 +149,14 @@ void CL_ClearState(void)
        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;
@@ -155,7 +167,9 @@ void CL_ClearState(void)
        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));
@@ -175,6 +189,15 @@ void CL_ClearState(void)
                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);
@@ -220,6 +243,34 @@ void CL_ExpandEntities(int num)
        }
 }
 
+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
@@ -235,6 +286,7 @@ void CL_Disconnect(void)
 
        Con_DPrintf("CL_Disconnect\n");
 
+       CL_VM_ShutDown();
 // stop sounds (especially looping!)
        S_StopAllSounds ();
 
@@ -643,7 +695,7 @@ void CL_LinkNetworkEntity(entity_t *e)
        {
                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?
@@ -691,23 +743,35 @@ void CL_LinkNetworkEntity(entity_t *e)
                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;
@@ -741,7 +805,7 @@ void CL_LinkNetworkEntity(entity_t *e)
 
                // 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);
@@ -766,7 +830,10 @@ void CL_LinkNetworkEntity(entity_t *e)
                }
 
                // 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
@@ -1025,6 +1092,7 @@ void CL_LinkNetworkEntity(entity_t *e)
                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
@@ -1043,6 +1111,7 @@ void CL_LinkNetworkEntity(entity_t *e)
                 && (!(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)
@@ -1073,6 +1142,25 @@ void CL_RelinkWorld(void)
        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;
@@ -1100,52 +1188,82 @@ static void CL_RelinkStaticEntities(void)
 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)
@@ -1188,7 +1306,10 @@ 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;
@@ -1318,6 +1439,27 @@ void CL_LerpPlayer(float frac)
        }
 }
 
+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
@@ -1326,6 +1468,7 @@ Read all incoming data from the server
 ===============
 */
 extern void CL_ClientMovement_Replay();
+
 int CL_ReadFromServer(void)
 {
        CL_ReadDemoMessage();
@@ -1347,17 +1490,23 @@ int CL_ReadFromServer(void)
 
                // 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();
@@ -1496,6 +1645,9 @@ void CL_Init (void)
 //
 // register our commands
 //
+       Cvar_RegisterVariable (&csqc_progname);
+       Cvar_RegisterVariable (&csqc_progcrc);
+
        Cvar_RegisterVariable (&cl_upspeed);
        Cvar_RegisterVariable (&cl_forwardspeed);
        Cvar_RegisterVariable (&cl_backspeed);
index 8ca0b33688022bf7b946c05b70ea1d868b3cd425..9021405788fe0a29bddcd2917ed4023489a4103c 100644 (file)
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "cdaudio.h"
 #include "cl_collision.h"
+#include "csprogs.h"
 
 char *svc_strings[128] =
 {
@@ -90,7 +91,7 @@ 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
 };
 
@@ -390,6 +391,7 @@ void CL_ParseServerInfo (void)
        // 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));
 
@@ -500,8 +502,8 @@ void CL_ValidateState(entity_state_t *s)
        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)
@@ -695,6 +697,7 @@ void CL_ParseClientdata (void)
                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)
@@ -1331,6 +1334,14 @@ void CL_ParseTempEntity(void)
 
 #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];
 
 /*
@@ -1475,15 +1486,15 @@ void CL_ParseServerMessage(void)
                        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:
@@ -1492,6 +1503,7 @@ void CL_ParseServerMessage(void)
 
                case svc_serverinfo:
                        CL_ParseServerInfo ();
+                       CL_VM_Init();   //[515]: init csqc
                        break;
 
                case svc_setangle:
@@ -1622,7 +1634,8 @@ void CL_ParseServerMessage(void)
                        CL_ParseStatic (true);
                        break;
                case svc_temp_entity:
-                       CL_ParseTempEntity ();
+                       if(!CL_VM_Parse_TempEntity())
+                               CL_ParseTempEntity ();
                        break;
 
                case svc_setpause:
@@ -1761,6 +1774,9 @@ void CL_ParseServerMessage(void)
                        else
                                EntityFrame5_CL_ReadFrame();
                        break;
+               case svc_csqcentities:
+                       CSQC_ReadEntities();
+                       break;
                }
        }
 
index 3e181fbdd2fdfdb1d0f6c8fb4415fdcefd39a405..20f4ac79ee6d5960d137b083827f0ec8b35b58a6 100644 (file)
@@ -3,6 +3,7 @@
 #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
@@ -758,7 +759,7 @@ void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
        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;
@@ -1520,7 +1521,8 @@ void CL_UpdateScreen(void)
        }
        R_Shadow_EditLights_DrawSelectedLightProperties();
 
-       SCR_DrawConsole();
+       if(!csqc_loaded)
+               SCR_DrawConsole();
 
        SCR_DrawBrand();
 
index d58d7642319c0a90a5ee85f3ca56d22a0f3c5aae..b1663a15e98dfe19d328493c06e9fef0596adc20 100644 (file)
@@ -68,6 +68,8 @@ void DrawQ_SetClipArea(float x, float y, float width, float height);
 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);
index 1f815d72dec306b2fb1d7846ae60495456a9d566..aa323fcd9520cdeba82d24158909bc2ac43d14e4 100644 (file)
--- a/client.h
+++ b/client.h
@@ -323,6 +323,7 @@ entity_persistent_t;
 
 typedef struct entity_s
 {
+       qboolean csqc;
        // baseline state (default values)
        entity_state_t state_baseline;
        // previous state (interpolating from this)
@@ -476,6 +477,14 @@ typedef struct client_movementqueue_s
 }
 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
@@ -553,6 +562,12 @@ typedef struct client_state_s
        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;
 
@@ -663,6 +678,9 @@ extern cvar_t cl_anglespeedkey;
 
 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;
 
@@ -697,13 +715,16 @@ extern vec3_t cl_playercrouchmaxs;
 
 // 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;
index 900831d3efbdcbf0599da62d1fa44831a770b222..44c9ba22cce00a0a80a3e984bd3b6588c4adb95d 100644 (file)
@@ -1,12 +1,96 @@
 /* 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
diff --git a/clvm_cmds.c b/clvm_cmds.c
new file mode 100644 (file)
index 0000000..f6b8ea3
--- /dev/null
@@ -0,0 +1,2994 @@
+#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)
+{
+}
+
diff --git a/cmd.c b/cmd.c
index 9a8a56da40776296ba4a3333e31774917bf0d9b2..de0a77209a6e0e85d4dd55b17e01dc57b6415ace 100644 (file)
--- a/cmd.c
+++ b/cmd.c
@@ -387,6 +387,7 @@ typedef struct cmd_function_s
        struct cmd_function_s *next;
        const char *name;
        xcommand_t function;
+       qboolean csqcfunc;
 } cmd_function_t;
 
 static int cmd_argc;
@@ -696,14 +697,24 @@ void Cmd_AddCommand (const char *cmd_name, xcommand_t function)
        {
                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
@@ -899,6 +910,14 @@ const char **Cmd_CompleteAliasBuildList (const char *partial)
        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
@@ -930,7 +949,14 @@ void Cmd_ExecuteString (const char *text, cmd_source_t src)
        {
                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;
                }
index 0ba430c3e42ecf432b5e43571d727f1a81671982..641bc965c2e7cd0fe08baf3f9ee055eaf7bb898f 100644 (file)
--- a/console.c
+++ b/console.c
@@ -904,6 +904,7 @@ qboolean GetMapList (const char *s, char *completedname, int completednamebuffer
                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)
                {
diff --git a/csprogs.c b/csprogs.c
new file mode 100644 (file)
index 0000000..1de0cf9
--- /dev/null
+++ b/csprogs.c
@@ -0,0 +1,614 @@
+#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);
+}
diff --git a/csprogs.h b/csprogs.h
new file mode 100644 (file)
index 0000000..8d6f2cc
--- /dev/null
+++ b/csprogs.h
@@ -0,0 +1,64 @@
+#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
index fa00725844de3c26611f7f71026fd38d165ecc6a..9e12d566f48fc7716a29ed22d0709813aa6c65fd 100644 (file)
@@ -1733,6 +1733,9 @@ void R_ClearScreen(void)
        }
 }
 
+qboolean CL_VM_UpdateView (void);
+void SCR_DrawConsole (void);
+
 int r_stereo_side;
 
 void SCR_DrawScreen (void)
@@ -1793,7 +1796,10 @@ 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)
                        {
@@ -1810,7 +1816,8 @@ 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();
                        }
                }
 
index d8970209ef4725c999e12ea3e2ba284eeca5faf2..c8216106349d0a32a4b717f80dc3e793ceb92853 100644 (file)
@@ -1097,7 +1097,58 @@ void R_RenderView(void)
        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
@@ -1136,21 +1187,24 @@ void R_RenderScene(void)
        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
@@ -1176,23 +1230,33 @@ void R_RenderScene(void)
 
        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();
diff --git a/host.c b/host.c
index 10b964c17a9e192f4cb39f2927c19445c5331074..1064e9c0897ae7dec9b861466b0a417f954ac1ef 100644 (file)
--- a/host.c
+++ b/host.c
@@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "cdaudio.h"
 #include "cl_video.h"
 #include "progsvm.h"
+#include "csprogs.h"
 
 /*
 
@@ -801,7 +802,13 @@ void _Host_Frame (float time)
                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();
 
diff --git a/keys.c b/keys.c
index 7277a99f09cbd1cf5d9efa63739c2c1b4ebe9d56..97070dd684fac8a5b9eef7c112d398910f8241b6 100644 (file)
--- a/keys.c
+++ b/keys.c
@@ -820,6 +820,16 @@ Key_Init (void)
        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);
 
 /*
 ===================
@@ -831,12 +841,26 @@ void
 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
index 3996f0705555b0b54adf42218b022f301aabc536..7354784eb730b8e07b8bb9f36414b07c9b679130 100644 (file)
@@ -65,10 +65,12 @@ OBJ_COMMON= \
        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 \
@@ -83,8 +85,8 @@ OBJ_COMMON= \
        host.o \
        host_cmd.o \
        image.o \
-       jpeg.o \
        image_png.o \
+       jpeg.o \
        keys.o \
        lhnet.o \
        mathlib.o \
@@ -125,6 +127,7 @@ OBJ_COMMON= \
        view.o \
        wad.o \
        world.o \
+       world_cs.o \
        zone.o
 
 # note that builddate.c is very intentionally not compiled to a .o before
index 47976cfbfe4afa6e0f923178afc3d32f430a4137..92a227c1310f7f12dbead08d74bc4541a0bdb15f 100644 (file)
@@ -282,45 +282,6 @@ void VM_M_getresolution(void)
        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
@@ -940,7 +901,8 @@ prvm_builtin_t vm_m_builtins[] = {
        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
@@ -954,12 +916,12 @@ prvm_builtin_t vm_m_builtins[] = {
        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,
diff --git a/progs.h b/progs.h
index 9afca77606e5d72b8ba9a636d7cca50028e4bbfd..6aeb4f15f10dc72ce4559face912575af4c29b72 100644 (file)
--- a/progs.h
+++ b/progs.h
@@ -123,6 +123,8 @@ extern int eval_cursor_trace_ent;
 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;
index daa1b5b3c6bbcb26d618d7105cdc03706b6fb74c..0ef7dff3fc161e6fd32102bc8d1cc08d21f767e9 100644 (file)
--- a/progsvm.h
+++ b/progsvm.h
@@ -31,6 +31,7 @@ The code uses void pointers instead.
 
 #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
@@ -203,7 +204,8 @@ typedef struct prvm_edict_s
        union
        {
                void *vp;
-               entvars_t *server;
+               entvars_t               *server;
+               cl_entvars_t    *client;
        } fields;
 } prvm_edict_t;
 
@@ -266,6 +268,7 @@ typedef struct prvm_prog_s
        union {
                float *generic;
                globalvars_t *server;
+               cl_globalvars_t *client;
        } globals;
 
        int                                     maxknownstrings;
index 034b11fe57f543a0aefb913ca1df7c4ccf0c1888..ba7cb8122b1d04e9101cefa4f600a1fffc0df05a 100644 (file)
@@ -239,6 +239,137 @@ void EntityFrameQuake_ISeeDeadEntities(void)
        }
 }
 
+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;
@@ -246,6 +377,7 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_sta
        int i, bits;
        sizebuf_t buf;
        unsigned char data[128];
+       prvm_eval_t *val;
 
        // prepare the buffer
        memset(&buf, 0, sizeof(buf));
@@ -254,6 +386,10 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_sta
 
        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);
 
@@ -877,6 +1013,7 @@ void EntityFrame_WriteFrame(sizebuf_t *msg, entityframe_database_t *d, int numst
        entity_frame_t *o = &deltaframe;
        const entity_state_t *ent, *delta;
        vec3_t eye;
+       prvm_eval_t *val;
 
        d->latestframenum++;
 
@@ -906,6 +1043,10 @@ void EntityFrame_WriteFrame(sizebuf_t *msg, entityframe_database_t *d, int numst
        {
                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
@@ -1348,6 +1489,7 @@ void EntityFrame4_WriteFrame(sizebuf_t *msg, entityframe4_database_t *d, int num
        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)
@@ -1392,6 +1534,9 @@ void EntityFrame4_WriteFrame(sizebuf_t *msg, entityframe4_database_t *d, int num
        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
@@ -1558,6 +1703,12 @@ int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
 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
index 2855e0f8ecb03e4559e102d1d40ae1cdd1e6ea4c..1054a27b604f0768ead3bee486fd623c69960ae8 100644 (file)
@@ -232,7 +232,7 @@ void Protocol_Names(char *buffer, size_t buffersize);
 #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
 
 //
index 53b90484f9b2eedf2b1e73cc97ce7993db29f020..aadcabc8c18a9846faf0eb9c657d0dfe1d92ad74 100644 (file)
@@ -2618,6 +2618,45 @@ void VM_getimagesize(void)
        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
 
 /*
@@ -2735,6 +2774,314 @@ void VM_cin_restart( void )
                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
 ////////////////////////////////////////
@@ -2942,11 +3289,494 @@ void VM_altstr_ins(void)
        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)
@@ -2954,6 +3784,11 @@ 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;
+       }
 }
 
-
index 51ccd3e32e9e2f0bb8a91180172158556e3d77dd..5c9673f0ca0b0d394f717746eef382521b0d6174 100644 (file)
@@ -322,17 +322,40 @@ void VM_drawsetcliparea(void);
 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);
diff --git a/sbar.c b/sbar.c
index 16513a97b9d54e5e5432ce03639a69f25aeb7fbc..9501a26aee9b7aa680c354ce8c4d3ed59e346d83 100644 (file)
--- a/sbar.c
+++ b/sbar.c
@@ -485,6 +485,22 @@ Sbar_SortFrags
 */
 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;
@@ -1024,327 +1040,331 @@ extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
 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);
index 7296d9d9387af308539771f7dee5ef900063036c..c9d8e3016fe53cb89422793622e23761a22f59b0 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -24,6 +24,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 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"};
@@ -275,6 +282,7 @@ CLIENT SPAWNING
 ==============================================================================
 */
 
+static const char *SV_InitCmd; //[515]: svprogs able to send cmd to client on connect
 /*
 ================
 SV_SendServerinfo
@@ -320,6 +328,18 @@ void SV_SendServerinfo (client_t *client)
        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);
@@ -371,6 +391,9 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
 
        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));
@@ -855,6 +878,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
 {
        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)
@@ -874,7 +898,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        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++;
 
@@ -896,6 +920,8 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        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)
@@ -1196,6 +1222,7 @@ qboolean SV_SendClientDatagram (client_t *client)
 
        // 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
@@ -1797,6 +1824,10 @@ void SV_SpawnServer (const char *server)
                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;
@@ -2184,6 +2215,8 @@ int eval_cursor_trace_ent;
 int eval_colormod;
 int eval_playermodel;
 int eval_playerskin;
+int eval_SendEntity;
+int eval_Version;
 int eval_customizeentityforclient;
 
 mfunction_t *SV_PlayerPhysicsQC;
@@ -2191,6 +2224,8 @@ mfunction_t *EndFrameQC;
 //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");
@@ -2252,6 +2287,8 @@ void SV_VM_FindEdictFieldOffsets(void)
        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
@@ -2260,6 +2297,12 @@ void SV_VM_FindEdictFieldOffsets(void)
        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))
@@ -2315,6 +2358,7 @@ prvm_required_field_t reqfields[] =
        {ev_float, "scale"},
        {ev_float, "style"},
        {ev_float, "tag_index"},
+       {ev_float, "Version"},
        {ev_float, "viewzoom"},
        {ev_vector, "color"},
        {ev_vector, "colormod"},
@@ -2325,6 +2369,7 @@ prvm_required_field_t reqfields[] =
        {ev_vector, "punchvector"},
        {ev_string, "playermodel"},
        {ev_string, "playerskin"},
+       {ev_function, "SendEntity"},
        {ev_function, "customizeentityforclient"},
 };
 
@@ -2360,6 +2405,9 @@ void SV_VM_Setup(void)
        PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields );
        SV_VM_FindEdictFieldOffsets();
 
+       VM_AutoSentStats_Clear();//[515]: csqc
+       EntityFrameCSQC_ClearVersions();//[515]: csqc
+
        PRVM_End;
 }
 
index 9d1f803d7c980d9c45fdb92d4ecd996d3f379615..94db32b8d974c1d303219422d8467b83405b9795 100644 (file)
@@ -65,6 +65,7 @@ char *vm_sv_extensions =
 "DP_QC_MULTIPLETEMPSTRINGS "
 "DP_QC_RANDOMVEC "
 "DP_QC_SINCOSSQRTPOW "
+"DP_QC_STRINGBUFFERS "
 "DP_QC_TRACEBOX "
 "DP_QC_TRACETOSS "
 "DP_QC_TRACE_MOVETYPE_HITMODEL "
@@ -111,6 +112,8 @@ char *vm_sv_extensions =
 "DP_TE_SPARK "
 "DP_TE_STANDARDEFFECTBUILTINS "
 "DP_VIEWZOOM "
+"EXT_BITSHIFT "
+"EXT_CSQC "
 "FRIK_FILE "
 "KRIMZON_SV_PARSECLIENTCOMMAND "
 "NEH_CMD_PLAY2 "
@@ -135,20 +138,6 @@ void PF_makevectors (void)
        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
@@ -1119,12 +1108,14 @@ MESSAGE WRITING
 #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)
@@ -1150,6 +1141,9 @@ sizebuf_t *WriteDest (void)
 
        case MSG_INIT:
                return &sv.signon;
+
+       case MSG_ENTITY:
+               return sv2csqcbuf;
        }
 
        return NULL;
@@ -1318,6 +1312,160 @@ void PF_registercvar (void)
        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
@@ -1815,7 +1963,7 @@ void PF_te_flamejet (void)
        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;
@@ -2302,9 +2450,14 @@ void PF_clienttype (void)
                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
@@ -2424,7 +2577,35 @@ 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, 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)
@@ -2458,7 +2639,7 @@ PF_te_lightning1,                 // #428 void(entity own, vector start, vector end) te_lightn
 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)
@@ -2485,8 +2666,28 @@ PF_clienttype,                           // #455 float(entity clent) clienttype (DP_SV_BOTCLIENT)
 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);
index 8cce143a75cf04f63b6b0f1f81ae8f2df3ceb8bf..6f189c460a5bc62c04477577a84a422fb73dad92 100644 (file)
--- a/vid_wgl.c
+++ b/vid_wgl.c
@@ -1306,6 +1306,7 @@ static void IN_MouseMove (void)
                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));
        }
diff --git a/view.c b/view.c
index 3c4d5314821cceac1a411a025ac19c2464e7afbf..6f202b1039cc712e9d75d868e44c67676c79c83c 100644 (file)
--- a/view.c
+++ b/view.c
@@ -308,6 +308,7 @@ static void V_BonusFlash_f (void)
 extern matrix4x4_t viewmodelmatrix;
 
 #include "cl_collision.h"
+#include "csprogs.h"
 
 /*
 ==================
@@ -321,6 +322,8 @@ void V_CalcRefdef (void)
        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);
@@ -490,6 +493,8 @@ void V_CalcRefdef (void)
                                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);
                }
        }
 }
diff --git a/world_cs.c b/world_cs.c
new file mode 100644 (file)
index 0000000..11e212f
--- /dev/null
@@ -0,0 +1,732 @@
+/*
+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));
+}
+
+