]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - clvm_cmds.c
remove support for GL_EXT_compiled_vertex_array extension
[xonotic/darkplaces.git] / clvm_cmds.c
index 03f3fe911b626e10c727dcaecd21697307a501b1..c8140474379c4a4d603df21008d7fa9a75dd2c6c 100644 (file)
@@ -1,7 +1,11 @@
+#include "quakedef.h"
+
 #include "prvm_cmds.h"
 #include "csprogs.h"
 #include "cl_collision.h"
 #include "r_shadow.h"
+#include "jpeg.h"
+#include "image.h"
 
 //============================================================================
 // Client
 //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
 
+extern cvar_t v_flipped;
+extern cvar_t r_equalize_entities_fullbright;
+
 sfx_t *S_FindName(const char *name);
-int Sbar_GetPlayer (int index);
+int Sbar_GetSortedPlayerIndex (int index);
 void Sbar_SortFrags (void);
 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius);
 void CSQC_RelinkAllEntities (int drawmask);
 void CSQC_RelinkCSQCEntities (void);
-char *Key_GetBind (int key);
-
-
-
-
-
+const char *Key_GetBind (int key);
 
 // #1 void(vector ang) makevectors
 static void VM_CL_makevectors (void)
@@ -37,7 +39,7 @@ static void VM_CL_makevectors (void)
 }
 
 // #2 void(entity e, vector o) setorigin
-static void VM_CL_setorigin (void)
+void VM_CL_setorigin (void)
 {
        prvm_edict_t    *e;
        float   *org;
@@ -59,41 +61,71 @@ static void VM_CL_setorigin (void)
        CL_LinkEdict(e);
 }
 
+static void SetMinMaxSize (prvm_edict_t *e, float *min, float *max)
+{
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+               if (min[i] > max[i])
+                       PRVM_ERROR("SetMinMaxSize: backwards mins/maxs");
+
+       // set derived values
+       VectorCopy (min, e->fields.client->mins);
+       VectorCopy (max, e->fields.client->maxs);
+       VectorSubtract (max, min, e->fields.client->size);
+
+       CL_LinkEdict (e);
+}
+
 // #3 void(entity e, string m) setmodel
-static void VM_CL_setmodel (void)
+void VM_CL_setmodel (void)
 {
        prvm_edict_t    *e;
        const char              *m;
-       struct model_s  *mod;
+       dp_model_t *mod;
        int                             i;
 
        VM_SAFEPARMCOUNT(2, VM_CL_setmodel);
 
        e = PRVM_G_EDICT(OFS_PARM0);
+       e->fields.client->modelindex = 0;
+       e->fields.client->model = 0;
+
        m = PRVM_G_STRING(OFS_PARM1);
+       mod = NULL;
        for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
        {
                if (!strcmp(cl.csqc_model_precache[i]->name, m))
                {
-                       e->fields.client->model = PRVM_SetEngineString(cl.csqc_model_precache[i]->name);
+                       mod = cl.csqc_model_precache[i];
+                       e->fields.client->model = PRVM_SetEngineString(mod->name);
                        e->fields.client->modelindex = -(i+1);
-                       return;
+                       break;
                }
        }
 
-       for (i = 0;i < MAX_MODELS;i++)
-       {
-               mod = cl.model_precache[i];
-               if (mod && !strcmp(mod->name, m))
+       if( !mod ) {
+               for (i = 0;i < MAX_MODELS;i++)
                {
-                       e->fields.client->model = PRVM_SetEngineString(mod->name);
-                       e->fields.client->modelindex = i;
-                       return;
+                       mod = cl.model_precache[i];
+                       if (mod && !strcmp(mod->name, m))
+                       {
+                               e->fields.client->model = PRVM_SetEngineString(mod->name);
+                               e->fields.client->modelindex = i;
+                               break;
+                       }
                }
        }
 
-       e->fields.client->modelindex = 0;
-       e->fields.client->model = 0;
+       if( mod ) {
+               // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
+               //SetMinMaxSize (e, mod->normalmins, mod->normalmaxs);
+       }
+       else
+       {
+               SetMinMaxSize (e, vec3_origin, vec3_origin);
+               VM_Warning ("setmodel: model '%s' not precached\n", m);
+       }
 }
 
 // #4 void(entity e, vector min, vector max) setsize
@@ -117,9 +149,7 @@ static void VM_CL_setsize (void)
        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);
+       SetMinMaxSize( e, min, max );
 
        CL_LinkEdict(e);
 }
@@ -130,18 +160,19 @@ static void VM_CL_sound (void)
        const char                      *sample;
        int                                     channel;
        prvm_edict_t            *entity;
-       int                             volume;
+       float                           volume;
        float                           attenuation;
+       vec3_t                          org;
 
        VM_SAFEPARMCOUNT(5, VM_CL_sound);
 
        entity = PRVM_G_EDICT(OFS_PARM0);
        channel = (int)PRVM_G_FLOAT(OFS_PARM1);
        sample = PRVM_G_STRING(OFS_PARM2);
-       volume = (int)(PRVM_G_FLOAT(OFS_PARM3)*255.0f);
+       volume = PRVM_G_FLOAT(OFS_PARM3);
        attenuation = PRVM_G_FLOAT(OFS_PARM4);
 
-       if (volume < 0 || volume > 255)
+       if (volume < 0 || volume > 1)
        {
                VM_Warning("VM_CL_sound: volume must be in range 0-1\n");
                return;
@@ -159,7 +190,39 @@ static void VM_CL_sound (void)
                return;
        }
 
-       S_StartSound(32768 + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), entity->fields.client->origin, volume, attenuation);
+       CL_VM_GetEntitySoundOrigin(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), org);
+       S_StartSound(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), org, volume, attenuation);
+}
+
+// #483 void(vector origin, string sample, float volume, float attenuation) pointsound
+static void VM_CL_pointsound(void)
+{
+       const char                      *sample;
+       float                           volume;
+       float                           attenuation;
+       vec3_t                          org;
+
+       VM_SAFEPARMCOUNT(4, VM_CL_pointsound);
+
+       VectorCopy( PRVM_G_VECTOR(OFS_PARM0), org);
+       sample = PRVM_G_STRING(OFS_PARM1);
+       volume = PRVM_G_FLOAT(OFS_PARM2);
+       attenuation = PRVM_G_FLOAT(OFS_PARM3);
+
+       if (volume < 0 || volume > 1)
+       {
+               VM_Warning("VM_CL_pointsound: volume must be in range 0-1\n");
+               return;
+       }
+
+       if (attenuation < 0 || attenuation > 4)
+       {
+               VM_Warning("VM_CL_pointsound: attenuation must be in range 0-4\n");
+               return;
+       }
+
+       // Send World Entity as Entity to Play Sound (for CSQC, that is MAX_EDICTS)
+       S_StartSound(MAX_EDICTS, 0, S_FindName(sample), org, volume, attenuation);
 }
 
 // #14 entity() spawn
@@ -167,19 +230,29 @@ static 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 movetype, entity ignore) traceline
+void CL_VM_SetTraceGlobals(const trace_t *trace, int svent)
+{
+       prvm_eval_t *val;
+       VM_SetTraceGlobals(trace);
+       if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_networkentity)))
+               val->_float = svent;
+}
+
+#define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY)
+#define CL_HitNetworkPlayers(move)     !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS)
+
+// #16 void(vector v1, vector v2, float movetype, entity ignore) traceline
 static void VM_CL_traceline (void)
 {
        float   *v1, *v2;
        trace_t trace;
-       int             move;
+       int             move, svent;
        prvm_edict_t    *ent;
 
-       VM_SAFEPARMCOUNTRANGE(4, 8, VM_CL_traceline); // allow more parameters for future expansion
+       VM_SAFEPARMCOUNTRANGE(4, 4, VM_CL_traceline);
 
        prog->xfunction->builtinsprofile += 30;
 
@@ -188,12 +261,12 @@ static void VM_CL_traceline (void)
        move = (int)PRVM_G_FLOAT(OFS_PARM2);
        ent = PRVM_G_EDICT(OFS_PARM3);
 
-       if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2]))
+       if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
                PRVM_ERROR("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
 
-       trace = CL_Move(v1, vec3_origin, vec3_origin, v2, move, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+       trace = CL_TraceLine(v1, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
 
-       VM_SetTraceGlobals(&trace);
+       CL_VM_SetTraceGlobals(&trace, svent);
 }
 
 /*
@@ -212,7 +285,7 @@ static void VM_CL_tracebox (void)
 {
        float   *v1, *v2, *m1, *m2;
        trace_t trace;
-       int             move;
+       int             move, svent;
        prvm_edict_t    *ent;
 
        VM_SAFEPARMCOUNTRANGE(6, 8, VM_CL_tracebox); // allow more parameters for future expansion
@@ -226,16 +299,15 @@ static void VM_CL_tracebox (void)
        move = (int)PRVM_G_FLOAT(OFS_PARM4);
        ent = PRVM_G_EDICT(OFS_PARM5);
 
-       if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2]))
+       if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
                PRVM_ERROR("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
 
-       trace = CL_Move(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+       trace = CL_TraceBox(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
 
-       VM_SetTraceGlobals(&trace);
+       CL_VM_SetTraceGlobals(&trace, svent);
 }
 
-extern cvar_t cl_gravity;
-trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
+trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore, int *svent)
 {
        int i;
        float gravity;
@@ -257,7 +329,7 @@ trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
                gravity = val->_float;
        else
                gravity = 1.0;
-       gravity *= cl_gravity.value * 0.05;
+       gravity *= cl.movevars_gravity * 0.05;
 
        for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
        {
@@ -265,7 +337,7 @@ trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
                VectorMA (tossent->fields.client->angles, 0.05, tossent->fields.client->avelocity, tossent->fields.client->angles);
                VectorScale (tossent->fields.client->velocity, 0.05, move);
                VectorAdd (tossent->fields.client->origin, move, end);
-               trace = CL_Move (tossent->fields.client->origin, tossent->fields.client->mins, tossent->fields.client->maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), true, true, NULL, true);
+               trace = CL_TraceBox(tossent->fields.client->origin, tossent->fields.client->mins, tossent->fields.client->maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), true, true, NULL, true);
                VectorCopy (trace.endpos, tossent->fields.client->origin);
 
                if (trace.fraction < 1)
@@ -285,6 +357,7 @@ static void VM_CL_tracetoss (void)
        trace_t trace;
        prvm_edict_t    *ent;
        prvm_edict_t    *ignore;
+       int svent = 0;
 
        prog->xfunction->builtinsprofile += 600;
 
@@ -298,23 +371,23 @@ static void VM_CL_tracetoss (void)
        }
        ignore = PRVM_G_EDICT(OFS_PARM1);
 
-       trace = CL_Trace_Toss (ent, ignore);
+       trace = CL_Trace_Toss (ent, ignore, &svent);
 
-       VM_SetTraceGlobals(&trace);
+       CL_VM_SetTraceGlobals(&trace, svent);
 }
 
 
 // #20 void(string s) precache_model
-static void VM_CL_precache_model (void)
+void VM_CL_precache_model (void)
 {
        const char      *name;
        int                     i;
-       model_t         *m;
+       dp_model_t              *m;
 
        VM_SAFEPARMCOUNT(1, VM_CL_precache_model);
 
        name = PRVM_G_STRING(OFS_PARM0);
-       for (i = 1;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
+       for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
        {
                if(!strcmp(cl.csqc_model_precache[i]->name, name))
                {
@@ -323,14 +396,14 @@ static void VM_CL_precache_model (void)
                }
        }
        PRVM_G_FLOAT(OFS_RETURN) = 0;
-       m = Mod_ForName(name, false, false, false);
+       m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL);
        if(m && m->loaded)
        {
-               for (i = 1;i < MAX_MODELS;i++)
+               for (i = 0;i < MAX_MODELS;i++)
                {
                        if (!cl.csqc_model_precache[i])
                        {
-                               cl.csqc_model_precache[i] = (model_t*)m;
+                               cl.csqc_model_precache[i] = (dp_model_t*)m;
                                PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
                                return;
                        }
@@ -351,8 +424,6 @@ int CSQC_EntitiesInBox (vec3_t mins, vec3_t maxs, int maxlist, prvm_edict_t **li
        {
                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;
        }
@@ -366,9 +437,17 @@ static void VM_CL_findradius (void)
        vec_t                   radius, radius2;
        vec3_t                  org, eorg, mins, maxs;
        int                             i, numtouchedicts;
-       prvm_edict_t    *touchedicts[MAX_EDICTS];
+       static prvm_edict_t     *touchedicts[MAX_EDICTS];
+       int             chainfield;
 
-       VM_SAFEPARMCOUNT(2, VM_CL_findradius);
+       VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findradius);
+
+       if(prog->argc == 3)
+               chainfield = PRVM_G_INT(OFS_PARM2);
+       else
+               chainfield = prog->fieldoffsets.chain;
+       if(chainfield < 0)
+               PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
 
        chain = (prvm_edict_t *)prog->edicts;
 
@@ -407,10 +486,10 @@ static void VM_CL_findradius (void)
                        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);
+                       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);
+                       PRVM_EDICTFIELDVALUE(ent, chainfield)->edict = PRVM_EDICT_TO_PROG(chain);
                        chain = ent;
                }
        }
@@ -446,7 +525,7 @@ static void VM_CL_droptofloor (void)
        VectorCopy (ent->fields.client->origin, end);
        end[2] -= 256;
 
-       trace = CL_Move(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+       trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
 
        if (trace.fraction != 1)
        {
@@ -475,7 +554,7 @@ static void VM_CL_lightstyle (void)
                VM_Warning("VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
                return;
        }
-       strlcpy (cl.lightstyle[i].map,  MSG_ReadString(), sizeof (cl.lightstyle[i].map));
+       strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map));
        cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
        cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
 }
@@ -525,7 +604,7 @@ realcheck:
        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_Move (start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+       trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
 
        if (trace.fraction == 1.0)
                return;
@@ -539,7 +618,7 @@ realcheck:
                        start[0] = stop[0] = x ? maxs[0] : mins[0];
                        start[1] = stop[1] = y ? maxs[1] : mins[1];
 
-                       trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+                       trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
 
                        if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
                                bottom = trace.endpos[2];
@@ -604,33 +683,53 @@ static void VM_CL_getlight (void)
 
 //============================================================================
 //[515]: SCENE MANAGER builtins
-extern qboolean CSQC_AddRenderEdict (prvm_edict_t *ed);//csprogs.c
-
-matrix4x4_t csqc_listenermatrix;
-qboolean csqc_usecsqclistener = false;//[515]: per-frame
+extern qboolean CSQC_AddRenderEdict (prvm_edict_t *ed, int edictnum);//csprogs.c
 
 static void CSQC_R_RecalcView (void)
 {
        extern matrix4x4_t viewmodelmatrix;
-       Matrix4x4_CreateFromQuakeEntity(&r_view.matrix, 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], cl_viewmodel_scale.value);
+       Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], 1);
+       Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], cl_viewmodel_scale.value);
 }
 
 void CL_RelinkLightFlashes(void);
 //#300 void() clearscene (EXT_CSQC)
-static void VM_CL_R_ClearScene (void)
+void VM_CL_R_ClearScene (void)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_R_ClearScene);
        // clear renderable entity and light lists
-       r_refdef.numentities = 0;
-       r_refdef.numlights = 0;
+       r_refdef.scene.numentities = 0;
+       r_refdef.scene.numlights = 0;
+       // FIXME: restore these to the values from VM_CL_UpdateView
+       r_refdef.view.x = 0;
+       r_refdef.view.y = 0;
+       r_refdef.view.z = 0;
+       r_refdef.view.width = vid.width;
+       r_refdef.view.height = vid.height;
+       r_refdef.view.depth = 1;
+       // FIXME: restore frustum_x/frustum_y
+       r_refdef.view.useperspective = true;
+       r_refdef.view.frustum_y = tan(scr_fov.value * M_PI / 360.0) * (3.0/4.0) * cl.viewzoom;
+       r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
+       r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
+       r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
+       r_refdef.view.ortho_x = scr_fov.value * (3.0 / 4.0) * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
+       r_refdef.view.ortho_y = scr_fov.value * (3.0 / 4.0);
+       r_refdef.view.clear = true;
+       r_refdef.view.isoverlay = false;
+       // FIXME: restore cl.csqc_origin
+       // FIXME: restore cl.csqc_angles
+       cl.csqc_vidvars.drawworld = true;
+       cl.csqc_vidvars.drawenginesbar = false;
+       cl.csqc_vidvars.drawcrosshair = false;
 }
 
 //#301 void(float mask) addentities (EXT_CSQC)
 extern void CSQC_Predraw (prvm_edict_t *ed);//csprogs.c
 extern void CSQC_Think (prvm_edict_t *ed);//csprogs.c
-static void VM_CL_R_AddEntities (void)
+void VM_CL_R_AddEntities (void)
 {
+       double t = Sys_DoubleTime();
        int                     i, drawmask;
        prvm_edict_t *ed;
        VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntities);
@@ -644,8 +743,6 @@ static void VM_CL_R_AddEntities (void)
                ed = &prog->edicts[i];
                if(ed->priv.required->free)
                        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)
                        continue;
@@ -655,19 +752,24 @@ static void VM_CL_R_AddEntities (void)
                        continue;
                if(!((int)ed->fields.client->drawmask & drawmask))
                        continue;
-               CSQC_AddRenderEdict(ed);
+               CSQC_AddRenderEdict(ed, i);
        }
+
+       // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
+       prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
 }
 
 //#302 void(entity ent) addentity (EXT_CSQC)
-static void VM_CL_R_AddEntity (void)
+void VM_CL_R_AddEntity (void)
 {
+       double t = Sys_DoubleTime();
        VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntity);
-       CSQC_AddRenderEdict(PRVM_G_EDICT(OFS_PARM0));
+       CSQC_AddRenderEdict(PRVM_G_EDICT(OFS_PARM0), 0);
+       prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
 }
 
 //#303 float(float property, ...) setproperty (EXT_CSQC)
-static void VM_CL_R_SetView (void)
+void VM_CL_R_SetView (void)
 {
        int             c;
        float   *f;
@@ -681,109 +783,161 @@ static void VM_CL_R_SetView (void)
 
        switch(c)
        {
-       case VF_MIN:                    r_view.x = (int)f[0];
-                                                       r_view.y = (int)f[1];
-                                                       break;
-       case VF_MIN_X:                  r_view.x = (int)k;
-                                                       break;
-       case VF_MIN_Y:                  r_view.y = (int)k;
-                                                       break;
-       case VF_SIZE:                   r_view.width = (int)f[0];
-                                                       r_view.height = (int)f[1];
-                                                       break;
-       case VF_SIZE_Y:                 r_view.width = (int)k;
-                                                       break;
-       case VF_SIZE_X:                 r_view.height = (int)k;
-                                                       break;
-       case VF_VIEWPORT:               r_view.x = (int)f[0];
-                                                       r_view.y = (int)f[1];
-                                                       r_view.z = 0;
-                                                       // TODO: make sure that view_z and view_depth are set properly even if csqc does not set them!
-                                                       f = PRVM_G_VECTOR(OFS_PARM2);
-                                                       r_view.width = (int)f[0];
-                                                       r_view.height = (int)f[1];
-                                                       r_view.depth = 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:                                PRVM_G_FLOAT(OFS_RETURN) = 0;
-                                                       VM_Warning("VM_CL_R_SetView : unknown parm %i\n", c);
-                                                       return;
+       case VF_MIN:
+               r_refdef.view.x = (int)(f[0]);
+               r_refdef.view.y = (int)(f[1]);
+               break;
+       case VF_MIN_X:
+               r_refdef.view.x = (int)(k);
+               break;
+       case VF_MIN_Y:
+               r_refdef.view.y = (int)(k);
+               break;
+       case VF_SIZE:
+               r_refdef.view.width = (int)(f[0]);
+               r_refdef.view.height = (int)(f[1]);
+               break;
+       case VF_SIZE_X:
+               r_refdef.view.width = (int)(k);
+               break;
+       case VF_SIZE_Y:
+               r_refdef.view.height = (int)(k);
+               break;
+       case VF_VIEWPORT:
+               r_refdef.view.x = (int)(f[0]);
+               r_refdef.view.y = (int)(f[1]);
+               f = PRVM_G_VECTOR(OFS_PARM2);
+               r_refdef.view.width = (int)(f[0]);
+               r_refdef.view.height = (int)(f[1]);
+               break;
+       case VF_FOV:
+               r_refdef.view.frustum_x = tan(f[0] * M_PI / 360.0);r_refdef.view.ortho_x = f[0];
+               r_refdef.view.frustum_y = tan(f[1] * M_PI / 360.0);r_refdef.view.ortho_y = f[1];
+               break;
+       case VF_FOVX:
+               r_refdef.view.frustum_x = tan(k * M_PI / 360.0);r_refdef.view.ortho_x = k;
+               break;
+       case VF_FOVY:
+               r_refdef.view.frustum_y = tan(k * M_PI / 360.0);r_refdef.view.ortho_y = k;
+               break;
+       case VF_ORIGIN:
+               VectorCopy(f, cl.csqc_origin);
+               CSQC_R_RecalcView();
+               break;
+       case VF_ORIGIN_X:
+               cl.csqc_origin[0] = k;
+               CSQC_R_RecalcView();
+               break;
+       case VF_ORIGIN_Y:
+               cl.csqc_origin[1] = k;
+               CSQC_R_RecalcView();
+               break;
+       case VF_ORIGIN_Z:
+               cl.csqc_origin[2] = k;
+               CSQC_R_RecalcView();
+               break;
+       case VF_ANGLES:
+               VectorCopy(f, cl.csqc_angles);
+               CSQC_R_RecalcView();
+               break;
+       case VF_ANGLES_X:
+               cl.csqc_angles[0] = k;
+               CSQC_R_RecalcView();
+               break;
+       case VF_ANGLES_Y:
+               cl.csqc_angles[1] = k;
+               CSQC_R_RecalcView();
+               break;
+       case VF_ANGLES_Z:
+               cl.csqc_angles[2] = k;
+               CSQC_R_RecalcView();
+               break;
+       case VF_DRAWWORLD:
+               cl.csqc_vidvars.drawworld = k != 0;
+               break;
+       case VF_DRAWENGINESBAR:
+               cl.csqc_vidvars.drawenginesbar = k != 0;
+               break;
+       case VF_DRAWCROSSHAIR:
+               cl.csqc_vidvars.drawcrosshair = k != 0;
+               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;
+       case VF_PERSPECTIVE:
+               r_refdef.view.useperspective = k != 0;
+               break;
+       case VF_CLEARSCREEN:
+               r_refdef.view.isoverlay = !k;
+               break;
+       default:
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               VM_Warning("VM_CL_R_SetView : unknown parm %i\n", c);
+               return;
        }
        PRVM_G_FLOAT(OFS_RETURN) = 1;
 }
 
-//#304 void() renderscene (EXT_CSQC)
-static void VM_CL_R_RenderScene (void)
-{
-       VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
-       // we need to update any RENDER_VIEWMODEL entities at this point because
-       // csqc supplies its own view matrix
-       CL_UpdateViewEntities();
-       // now draw stuff!
-       R_RenderView();
-}
-
-//#305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC)
-static void VM_CL_R_AddDynamicLight (void)
+//#305 void(vector org, float radius, vector lightcolours[, float style, string cubemapname, float pflags]) adddynamiclight (EXT_CSQC)
+void VM_CL_R_AddDynamicLight (void)
 {
-       float           *pos, *col;
-       matrix4x4_t     matrix;
-       VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight); // allow more than 3 because we may extend this in the future
+       double t = Sys_DoubleTime();
+       vec_t *org;
+       float radius = 300;
+       vec_t *col;
+       int style = -1;
+       const char *cubemapname = NULL;
+       int pflags = PFLAGS_CORONA | PFLAGS_FULLDYNAMIC;
+       float coronaintensity = 1;
+       float coronasizescale = 0.25;
+       qboolean castshadow = true;
+       float ambientscale = 0;
+       float diffusescale = 1;
+       float specularscale = 1;
+       matrix4x4_t matrix;
+       vec3_t forward, left, up;
+       VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight);
 
        // if we've run out of dlights, just return
-       if (r_refdef.numlights >= MAX_DLIGHTS)
+       if (r_refdef.scene.numlights >= MAX_DLIGHTS)
                return;
 
-       pos = PRVM_G_VECTOR(OFS_PARM0);
+       org = PRVM_G_VECTOR(OFS_PARM0);
+       radius = PRVM_G_FLOAT(OFS_PARM1);
        col = PRVM_G_VECTOR(OFS_PARM2);
-       Matrix4x4_CreateFromQuakeEntity(&matrix, pos[0], pos[1], pos[2], 0, 0, 0, PRVM_G_FLOAT(OFS_PARM1));
-       R_RTLight_Update(&r_refdef.lights[r_refdef.numlights++], false, &matrix, col, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+       if (prog->argc >= 4)
+       {
+               style = (int)PRVM_G_FLOAT(OFS_PARM3);
+               if (style >= MAX_LIGHTSTYLES)
+               {
+                       Con_DPrintf("VM_CL_R_AddDynamicLight: out of bounds lightstyle index %i\n", style);
+                       style = -1;
+               }
+       }
+       if (prog->argc >= 5)
+               cubemapname = PRVM_G_STRING(OFS_PARM4);
+       if (prog->argc >= 6)
+               pflags = (int)PRVM_G_FLOAT(OFS_PARM5);
+       coronaintensity = (pflags & PFLAGS_CORONA) != 0;
+       castshadow = (pflags & PFLAGS_NOSHADOW) == 0;
+
+       VectorScale(prog->globals.client->v_forward, radius, forward);
+       VectorScale(prog->globals.client->v_right, -radius, left);
+       VectorScale(prog->globals.client->v_up, radius, up);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, org);
+
+       R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &matrix, col, style, cubemapname, castshadow, coronaintensity, coronasizescale, ambientscale, diffusescale, specularscale, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+       r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
+       prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
 }
 
 //============================================================================
@@ -796,8 +950,13 @@ static void VM_CL_unproject (void)
 
        VM_SAFEPARMCOUNT(1, VM_CL_unproject);
        f = PRVM_G_VECTOR(OFS_PARM0);
-       VectorSet(temp, f[2], f[0] * f[2] * -r_view.frustum_x * 2.0 / r_view.width, f[1] * f[2] * -r_view.frustum_y * 2.0 / r_view.height);
-       Matrix4x4_Transform(&r_view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN));
+       if(v_flipped.integer)
+               f[0] = (2 * r_refdef.view.x + r_refdef.view.width) * (vid_conwidth.integer / (float) vid.width) - f[0];
+       VectorSet(temp,
+               f[2],
+               (-1.0 + 2.0 * (f[0] / (vid_conwidth.integer / (float) vid.width) - r_refdef.view.x) / r_refdef.view.width) * f[2] * -r_refdef.view.frustum_x,
+               (-1.0 + 2.0 * (f[1] / (vid_conheight.integer / (float) vid.height) - r_refdef.view.y) / r_refdef.view.height) * f[2] * -r_refdef.view.frustum_y);
+       Matrix4x4_Transform(&r_refdef.view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN));
 }
 
 //#311 vector (vector v) cs_project (EXT_CSQC)
@@ -809,9 +968,14 @@ static void VM_CL_project (void)
 
        VM_SAFEPARMCOUNT(1, VM_CL_project);
        f = PRVM_G_VECTOR(OFS_PARM0);
-       Matrix4x4_Invert_Simple(&m, &r_view.matrix);
+       Matrix4x4_Invert_Simple(&m, &r_refdef.view.matrix);
        Matrix4x4_Transform(&m, f, v);
-       VectorSet(PRVM_G_VECTOR(OFS_RETURN), v[1]/v[0]/-r_view.frustum_x*0.5*r_view.width, v[2]/v[0]/-r_view.frustum_y*r_view.height*0.5, v[0]);
+       if(v_flipped.integer)
+               v[1] = -v[1];
+       VectorSet(PRVM_G_VECTOR(OFS_RETURN),
+               (vid_conwidth.integer / (float) vid.width) * (r_refdef.view.x + r_refdef.view.width*0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x)),
+               (vid_conheight.integer / (float) vid.height) * (r_refdef.view.y + r_refdef.view.height*0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y)),
+               v[0]);
 }
 
 //#330 float(float stnum) getstatf (EXT_CSQC)
@@ -838,8 +1002,24 @@ static void VM_CL_getstatf (void)
 static void VM_CL_getstati (void)
 {
        int i, index;
-       VM_SAFEPARMCOUNT(1, VM_CL_getstati);
+       int firstbit, bitcount;
+
+       VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getstati);
+
        index = (int)PRVM_G_FLOAT(OFS_PARM0);
+       if (prog->argc > 1)
+       {
+               firstbit = (int)PRVM_G_FLOAT(OFS_PARM1);
+               if (prog->argc > 2)
+                       bitcount = (int)PRVM_G_FLOAT(OFS_PARM2);
+               else
+                       bitcount = 1;
+       }
+       else
+       {
+               firstbit = 0;
+               bitcount = 32;
+       }
 
        if(index < 0 || index >= MAX_CL_STATS)
        {
@@ -847,6 +1027,8 @@ static void VM_CL_getstati (void)
                return;
        }
        i = cl.stats[index];
+       if (bitcount != 32)     //32 causes the mask to overflow, so there's nothing to subtract from.
+               i = (((unsigned int)i)&(((1<<bitcount)-1)<<firstbit))>>firstbit;
        PRVM_G_FLOAT(OFS_RETURN) = i;
 }
 
@@ -894,12 +1076,20 @@ static void VM_CL_setmodelindex (void)
        }
        t->fields.client->model = PRVM_SetEngineString(model->name);
        t->fields.client->modelindex = i;
+
+       // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
+       if (model)
+       {
+               SetMinMaxSize (t, model->normalmins, model->normalmaxs);
+       }
+       else
+               SetMinMaxSize (t, vec3_origin, vec3_origin);
 }
 
 //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
 static void VM_CL_modelnameforindex (void)
 {
-       model_t *model;
+       dp_model_t *model;
 
        VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
 
@@ -932,6 +1122,8 @@ static void VM_CL_trailparticles (void)
        start   = PRVM_G_VECTOR(OFS_PARM2);
        end             = PRVM_G_VECTOR(OFS_PARM3);
 
+       if (i < 0)
+               return;
        CL_ParticleEffect(i, VectorDistance(start, end), start, end, t->fields.client->velocity, t->fields.client->velocity, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
 }
 
@@ -945,6 +1137,8 @@ static void VM_CL_pointparticles (void)
        f = PRVM_G_VECTOR(OFS_PARM1);
        v = PRVM_G_VECTOR(OFS_PARM2);
        n = (int)PRVM_G_FLOAT(OFS_PARM3);
+       if (i < 0)
+               return;
        CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
 }
 
@@ -959,8 +1153,21 @@ static void VM_CL_getkeybind (void)
 static void VM_CL_setcursormode (void)
 {
        VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
-       cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0);
-       cl_ignoremousemove = true;
+       cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0) != 0;
+       cl_ignoremousemoves = 2;
+}
+
+//#344 vector() getmousepos (EXT_CSQC)
+static void VM_CL_getmousepos(void)
+{
+       VM_SAFEPARMCOUNT(0,VM_CL_getmousepos);
+
+       if (key_consoleactive || key_dest != key_game)
+               VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0);
+       else if (cl.csqc_wantsmousemove)
+               VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0);
+       else
+               VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0);
 }
 
 //#345 float(float framenum) getinputstate (EXT_CSQC)
@@ -969,14 +1176,18 @@ static void VM_CL_getinputstate (void)
        int i, frame;
        VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
        frame = (int)PRVM_G_FLOAT(OFS_PARM0);
-       for (i = 0;i < cl.movement_numqueue;i++)
-               if (cl.movement_queue[i].sequence == frame)
+       PRVM_G_FLOAT(OFS_RETURN) = false;
+       for (i = 0;i < CL_MAX_USERCMDS;i++)
+       {
+               if (cl.movecmd[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.movecmd[i].viewangles, prog->globals.client->input_angles);
+                       prog->globals.client->input_buttons = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?)
+                       prog->globals.client->input_movevalues[0] = cl.movecmd[i].forwardmove;
+                       prog->globals.client->input_movevalues[1] = cl.movecmd[i].sidemove;
+                       prog->globals.client->input_movevalues[2] = cl.movecmd[i].upmove;
+                       prog->globals.client->input_timelength = cl.movecmd[i].frametime;
+                       if(cl.movecmd[i].crouch)
                        {
                                VectorCopy(cl.playercrouchmins, prog->globals.client->pmove_mins);
                                VectorCopy(cl.playercrouchmaxs, prog->globals.client->pmove_maxs);
@@ -986,7 +1197,9 @@ static void VM_CL_getinputstate (void)
                                VectorCopy(cl.playerstandmins, prog->globals.client->pmove_mins);
                                VectorCopy(cl.playerstandmaxs, prog->globals.client->pmove_maxs);
                        }
+                       PRVM_G_FLOAT(OFS_RETURN) = true;
                }
+       }
 }
 
 //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
@@ -1015,8 +1228,9 @@ static void VM_CL_getplayerkey (void)
        PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        Sbar_SortFrags();
 
-       i = Sbar_GetPlayer(i);
-       if(i < 0)
+       if (i < 0)
+               i = Sbar_GetSortedPlayerIndex(-1-i);
+       if(i < 0 || i >= cl.maxclients)
                return;
 
        t[0] = 0;
@@ -1025,25 +1239,31 @@ static void VM_CL_getplayerkey (void)
                strlcpy(t, cl.scores[i].name, sizeof(t));
        else
                if(!strcasecmp(c, "frags"))
-                       sprintf(t, "%i", cl.scores[i].frags);
+                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
        else
                if(!strcasecmp(c, "ping"))
-                       sprintf(t, "%i", cl.scores[i].qw_ping);
+                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
+       else
+               if(!strcasecmp(c, "pl"))
+                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
+       else
+               if(!strcasecmp(c, "movementloss"))
+                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
        else
                if(!strcasecmp(c, "entertime"))
-                       sprintf(t, "%f", cl.scores[i].qw_entertime);
+                       dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
        else
                if(!strcasecmp(c, "colors"))
-                       sprintf(t, "%i", cl.scores[i].colors);
+                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
        else
                if(!strcasecmp(c, "topcolor"))
-                       sprintf(t, "%i", cl.scores[i].colors & 0xf0);
+                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
        else
                if(!strcasecmp(c, "bottomcolor"))
-                       sprintf(t, "%i", (cl.scores[i].colors &15)<<4);
+                       dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
        else
                if(!strcasecmp(c, "viewentity"))
-                       sprintf(t, "%i", i+1);
+                       dpsnprintf(t, sizeof(t), "%i", i+1);
        if(!t[0])
                return;
        PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
@@ -1060,8 +1280,8 @@ static void VM_CL_isdemo (void)
 static 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
+       Matrix4x4_FromVectors(&cl.csqc_listenermatrix, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_VECTOR(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0));
+       cl.csqc_usecsqclistener = true; //use csqc listener at this frame
 }
 
 //#352 void(string cmdname) registercommand (EXT_CSQC)
@@ -1139,6 +1359,55 @@ static void VM_CL_ReadFloat (void)
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat();
 }
 
+//#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
+extern cvar_t cl_readpicture_force;
+static void VM_CL_ReadPicture (void)
+{
+       const char *name;
+       unsigned char *data;
+       unsigned char *buf;
+       int size;
+       int i;
+       cachepic_t *pic;
+
+       VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
+
+       name = MSG_ReadString();
+       size = MSG_ReadShort();
+
+       // check if a texture of that name exists
+       // if yes, it is used and the data is discarded
+       // if not, the (low quality) data is used to build a new texture, whose name will get returned
+
+       pic = Draw_CachePic_Flags (name, CACHEPICFLAG_NOTPERSISTENT);
+
+       if(size)
+       {
+               if(pic->tex == r_texture_notexture)
+                       pic->tex = NULL; // don't overwrite the notexture by Draw_NewPic
+               if(pic->tex && !cl_readpicture_force.integer)
+               {
+                       // texture found and loaded
+                       // skip over the jpeg as we don't need it
+                       for(i = 0; i < size; ++i)
+                               MSG_ReadByte();
+               }
+               else
+               {
+                       // texture not found
+                       // use the attached jpeg as texture
+                       buf = (unsigned char *) Mem_Alloc(tempmempool, size);
+                       MSG_ReadBytes(size, buf);
+                       data = JPEG_LoadImage_BGRA(buf, size);
+                       Mem_Free(buf);
+                       Draw_NewPic(name, image_width, image_height, false, data);
+                       Mem_Free(data);
+               }
+       }
+
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(name);
+}
+
 //////////////////////////////////////////////////////////
 
 static void VM_CL_makestatic (void)
@@ -1166,12 +1435,12 @@ static void VM_CL_makestatic (void)
                entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
 
                // copy it to the current state
+               memset(staticent, 0, sizeof(*staticent));
                staticent->render.model = CL_GetModelByIndex((int)ent->fields.client->modelindex);
-               staticent->render.frame = staticent->render.frame1 = staticent->render.frame2 = (int)ent->fields.client->frame;
-               staticent->render.framelerp = 0;
+               staticent->render.framegroupblend[0].frame = (int)ent->fields.client->frame;
+               staticent->render.framegroupblend[0].lerp = 1;
                // make torchs play out of sync
-               staticent->render.frame1time = staticent->render.frame2time = lhrandom(-10, -1);
-               staticent->render.colormap = (int)ent->fields.client->colormap; // no special coloring
+               staticent->render.framegroupblend[0].start = lhrandom(-10, -1);
                staticent->render.skinnum = (int)ent->fields.client->skin;
                staticent->render.effects = (int)ent->fields.client->effects;
                staticent->render.alpha = 1;
@@ -1179,6 +1448,11 @@ static void VM_CL_makestatic (void)
                staticent->render.scale = 1;
                if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)) && val->_float) staticent->render.scale = val->_float;
                if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.colormod);
+               if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glowmod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.glowmod);
+               if (!VectorLength2(staticent->render.colormod))
+                       VectorSet(staticent->render.colormod, 1, 1, 1);
+               if (!VectorLength2(staticent->render.glowmod))
+                       VectorSet(staticent->render.glowmod, 1, 1, 1);
 
                renderflags = 0;
                if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && val->_float) renderflags = (int)val->_float;
@@ -1191,14 +1465,27 @@ static void VM_CL_makestatic (void)
                }
                else
                        Matrix4x4_CreateFromQuakeEntity(&staticent->render.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], staticent->render.scale);
-               CL_UpdateRenderEntity(&staticent->render);
 
                // either fullbright or lit
-               if (!(staticent->render.effects & EF_FULLBRIGHT) && !r_fullbright.integer)
-                       staticent->render.flags |= RENDER_LIGHT;
+               if(!r_fullbright.integer)
+               {
+                       if (!(staticent->render.effects & EF_FULLBRIGHT))
+                               staticent->render.flags |= RENDER_LIGHT;
+                       else if(r_equalize_entities_fullbright.integer)
+                               staticent->render.flags |= RENDER_LIGHT | RENDER_EQUALIZE;
+               }
                // turn off shadows from transparent objects
                if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
                        staticent->render.flags |= RENDER_SHADOW;
+               if (staticent->render.effects & EF_NODEPTHTEST)
+                       staticent->render.flags |= RENDER_NODEPTHTEST;
+               if (staticent->render.effects & EF_ADDITIVE)
+                       staticent->render.flags |= RENDER_ADDITIVE;
+               if (staticent->render.effects & EF_DOUBLESIDED)
+                       staticent->render.flags |= RENDER_DOUBLESIDED;
+
+               staticent->render.allowdecals = true;
+               CL_UpdateRenderEntity(&staticent->render);
        }
        else
                Con_Printf("Too many static entities");
@@ -1335,16 +1622,29 @@ static void VM_CL_te_spark (void)
        CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
 }
 
+extern cvar_t cl_sound_ric_gunshot;
 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
 static void VM_CL_te_gunshotquad (void)
 {
        float           *pos;
        vec3_t          pos2;
+       int                     rnd;
        VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
 
        pos = PRVM_G_VECTOR(OFS_PARM0);
        CL_FindNonSolidLocation(pos, pos2, 4);
        CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
+       if(cl_sound_ric_gunshot.integer >= 2)
+       {
+               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);
+               }
+       }
 }
 
 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
@@ -1433,11 +1733,23 @@ static void VM_CL_te_gunshot (void)
 {
        float           *pos;
        vec3_t          pos2;
+       int                     rnd;
        VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
 
        pos = PRVM_G_VECTOR(OFS_PARM0);
        CL_FindNonSolidLocation(pos, pos2, 4);
        CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
+       if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
+       {
+               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);
+               }
+       }
 }
 
 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
@@ -1563,7 +1875,7 @@ static void VM_CL_te_explosion2 (void)
        colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
        CL_FindNonSolidLocation(pos, pos2, 10);
        CL_ParticleExplosion2(pos2, colorStart, colorLength);
-       tempcolor = (unsigned char *)&palette_complete[(rand()%colorLength) + colorStart];
+       tempcolor = palette_rgb[(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);
@@ -1630,9 +1942,9 @@ static void VM_CL_te_flamejet (void)
 //====================================================================
 //DP_QC_GETSURFACE
 
-extern void clippointtosurface(model_t *model, msurface_t *surface, vec3_t p, vec3_t out);
+extern void clippointtosurface(dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t out);
 
-static msurface_t *cl_getsurface(model_t *model, int surfacenum)
+static msurface_t *cl_getsurface(dp_model_t *model, int surfacenum)
 {
        if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces)
                return NULL;
@@ -1642,7 +1954,7 @@ static msurface_t *cl_getsurface(model_t *model, int surfacenum)
 // #434 float(entity e, float s) getsurfacenumpoints
 static void VM_CL_getsurfacenumpoints(void)
 {
-       model_t *model;
+       dp_model_t *model;
        msurface_t *surface;
        VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenumpoints);
        // return 0 if no such surface
@@ -1660,7 +1972,7 @@ static void VM_CL_getsurfacenumpoints(void)
 static void VM_CL_getsurfacepoint(void)
 {
        prvm_edict_t *ed;
-       model_t *model;
+       dp_model_t *model;
        msurface_t *surface;
        int pointnum;
        VM_SAFEPARMCOUNT(3, VM_CL_getsurfacenumpoints);
@@ -1675,11 +1987,85 @@ static void VM_CL_getsurfacepoint(void)
        // FIXME: implement rotation/scaling
        VectorAdd(&(model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN));
 }
+//PF_getsurfacepointattribute,     // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
+// float SPA_POSITION = 0;
+// float SPA_S_AXIS = 1;
+// float SPA_T_AXIS = 2;
+// float SPA_R_AXIS = 3; // same as SPA_NORMAL
+// float SPA_TEXCOORDS0 = 4;
+// float SPA_LIGHTMAP0_TEXCOORDS = 5;
+// float SPA_LIGHTMAP0_COLOR = 6;
+// TODO: add some wrapper code and merge VM_CL/SV_getsurface* [12/16/2007 Black]
+static void VM_CL_getsurfacepointattribute(void)
+{
+       prvm_edict_t *ed;
+       dp_model_t *model;
+       msurface_t *surface;
+       int pointnum;
+       int attributetype;
 
+       VM_SAFEPARMCOUNT(4, VM_CL_getsurfacenumpoints);
+       VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+       ed = PRVM_G_EDICT(OFS_PARM0);
+       if (!(model = CL_GetModelFromEdict(ed)) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+               return;
+       // note: this (incorrectly) assumes it is a simple polygon
+       pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
+       if (pointnum < 0 || pointnum >= surface->num_vertices)
+               return;
+
+       // FIXME: implement rotation/scaling
+       attributetype = (int) PRVM_G_FLOAT(OFS_PARM3);
+
+       switch( attributetype ) {
+               // float SPA_POSITION = 0;
+               case 0:
+                       VectorAdd(&(model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN));
+                       break;
+               // float SPA_S_AXIS = 1;
+               case 1:
+                       VectorCopy(&(model->surfmesh.data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], PRVM_G_VECTOR(OFS_RETURN));
+                       break;
+               // float SPA_T_AXIS = 2;
+               case 2:
+                       VectorCopy(&(model->surfmesh.data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], PRVM_G_VECTOR(OFS_RETURN));
+                       break;
+               // float SPA_R_AXIS = 3; // same as SPA_NORMAL
+               case 3:
+                       VectorCopy(&(model->surfmesh.data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], PRVM_G_VECTOR(OFS_RETURN));
+                       break;
+               // float SPA_TEXCOORDS0 = 4;
+               case 4: {
+                       float *ret = PRVM_G_VECTOR(OFS_RETURN);
+                       float *texcoord = &(model->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[pointnum * 2];
+                       ret[0] = texcoord[0];
+                       ret[1] = texcoord[1];
+                       ret[2] = 0.0f;
+                       break;
+               }
+               // float SPA_LIGHTMAP0_TEXCOORDS = 5;
+               case 5: {
+                       float *ret = PRVM_G_VECTOR(OFS_RETURN);
+                       float *texcoord = &(model->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[pointnum * 2];
+                       ret[0] = texcoord[0];
+                       ret[1] = texcoord[1];
+                       ret[2] = 0.0f;
+                       break;
+               }
+               // float SPA_LIGHTMAP0_COLOR = 6;
+               case 6:
+                       // ignore alpha for now..
+                       VectorCopy( &(model->surfmesh.data_lightmapcolor4f + 4 * surface->num_firstvertex)[pointnum * 4], PRVM_G_VECTOR(OFS_RETURN));
+                       break;
+               default:
+                       VectorSet( PRVM_G_VECTOR(OFS_RETURN), 0.0f, 0.0f, 0.0f );
+                       break;
+       }
+}
 // #436 vector(entity e, float s) getsurfacenormal
 static void VM_CL_getsurfacenormal(void)
 {
-       model_t *model;
+       dp_model_t *model;
        msurface_t *surface;
        vec3_t normal;
        VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenormal);
@@ -1698,7 +2084,7 @@ static void VM_CL_getsurfacenormal(void)
 // #437 string(entity e, float s) getsurfacetexture
 static void VM_CL_getsurfacetexture(void)
 {
-       model_t *model;
+       dp_model_t *model;
        msurface_t *surface;
        VM_SAFEPARMCOUNT(2, VM_CL_getsurfacetexture);
        PRVM_G_INT(OFS_RETURN) = OFS_NULL;
@@ -1714,7 +2100,7 @@ static void VM_CL_getsurfacenearpoint(void)
        vec3_t clipped, p;
        vec_t dist, bestdist;
        prvm_edict_t *ed;
-       model_t *model = NULL;
+       dp_model_t *model = NULL;
        msurface_t *surface;
        vec_t *point;
        VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenearpoint);
@@ -1757,7 +2143,7 @@ static void VM_CL_getsurfacenearpoint(void)
 static void VM_CL_getsurfaceclippedpoint(void)
 {
        prvm_edict_t *ed;
-       model_t *model;
+       dp_model_t *model;
        msurface_t *surface;
        vec3_t p, out;
        VM_SAFEPARMCOUNT(3, VM_CL_getsurfaceclippedpoint);
@@ -1773,14 +2159,14 @@ static void VM_CL_getsurfaceclippedpoint(void)
 }
 
 // #443 void(entity e, entity tagentity, string tagname) setattachment
-static void VM_CL_setattachment (void)
+void VM_CL_setattachment (void)
 {
        prvm_edict_t *e;
        prvm_edict_t *tagentity;
        const char *tagname;
        prvm_eval_t *v;
        int modelindex;
-       model_t *model;
+       dp_model_t *model;
        VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
 
        e = PRVM_G_EDICT(OFS_PARM0);
@@ -1828,12 +2214,81 @@ static void VM_CL_setattachment (void)
 
 int CL_GetTagIndex (prvm_edict_t *e, const char *tagname)
 {
-       model_t *model = CL_GetModelFromEdict(e);
+       dp_model_t *model = CL_GetModelFromEdict(e);
        if (model)
                return Mod_Alias_GetTagIndexForName(model, (int)e->fields.client->skin, tagname);
        else
                return -1;
-};
+}
+
+int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
+{
+       int r;
+       dp_model_t *model;
+
+       *tagname = NULL;
+       *parentindex = 0;
+       Matrix4x4_CreateIdentity(tag_localmatrix);
+
+       if (tagindex >= 0
+        && (model = CL_GetModelFromEdict(e))
+        && model->animscenes)
+       {
+               r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.client->skin, e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
+
+               if(!r) // success?
+                       *parentindex += 1;
+
+               return r;
+       }
+
+       return 1;
+}
+
+int CL_GetPitchSign(prvm_edict_t *ent)
+{
+       dp_model_t *model;
+       if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias)
+               return -1;
+       return 1;
+}
+
+void CL_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix)
+{
+       prvm_eval_t *val;
+       float scale;
+       float pitchsign = 1;
+
+       scale = 1;
+       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale);
+       if (val && val->_float != 0)
+               scale = val->_float;
+
+       // TODO do we need the same weird angle inverting logic here as in the server side case?
+       if(viewmatrix)
+               Matrix4x4_CreateFromQuakeEntity(out, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], scale * cl_viewmodel_scale.value);
+       else
+       {
+               pitchsign = CL_GetPitchSign(ent);
+               Matrix4x4_CreateFromQuakeEntity(out, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], pitchsign * ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale);
+       }
+}
+
+int CL_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
+{
+       dp_model_t *model;
+       if (tagindex >= 0
+        && (model = CL_GetModelFromEdict(ent))
+        && model->animscenes)
+       {
+               VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
+               VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
+               return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
+       }
+       *out = identitymatrix;
+       return 0;
+}
 
 // Warnings/errors code:
 // 0 - normal (everything all-right)
@@ -1847,11 +2302,11 @@ extern cvar_t cl_bobcycle;
 extern cvar_t cl_bobup;
 int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
 {
+       int ret;
        prvm_eval_t *val;
-       int reqframe, attachloop;
+       int attachloop;
        matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
-       prvm_edict_t *attachent;
-       model_t *model;
+       dp_model_t *model;
 
        *out = identitymatrix; // warnings and errors return identical matrix
 
@@ -1861,77 +2316,40 @@ int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
                return 2;
 
        model = CL_GetModelFromEdict(ent);
-
        if(!model)
                return 3;
 
-       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)
+       tagmatrix = identitymatrix;
+       attachloop = 0;
+       for(;;)
        {
-               int ret = Mod_Alias_GetTagMatrix(model, reqframe, tagindex - 1, &tagmatrix);
-               if (ret)
+               if(attachloop >= 256)
+                       return 5;
+               // apply transformation by child's tagindex on parent entity and then
+               // by parent entity itself
+               ret = CL_GetEntityLocalTagMatrix(ent, tagindex - 1, &attachmatrix);
+               if(ret && attachloop == 0)
                        return ret;
-       }
-       else
-               tagmatrix = identitymatrix;
-
-       if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict)
-       { // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity
-               attachloop = 0;
-               do
+               CL_GetEntityMatrix(ent, &entitymatrix, false);
+               Matrix4x4_Concat(&tagmatrix, &attachmatrix, out);
+               Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
+               // next iteration we process the parent entity
+               if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict)
                {
-                       attachent = PRVM_EDICT_NUM(val->edict); // to this it entity our entity is attached
-                       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index);
-
-                       model = CL_GetModelFromEdict(attachent);
-
-                       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, (int)val->_float - 1, &attachmatrix);
-                       else
-                               attachmatrix = identitymatrix;
-
-                       // apply transformation by child entity matrix
-                       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.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);
-                       Matrix4x4_Copy(&tagmatrix, out);
-
-                       // finally transformate by matrix of tag on parent entity
-                       Matrix4x4_Concat(out, &attachmatrix, &tagmatrix);
-                       Matrix4x4_Copy(&tagmatrix, out);
-
-                       ent = attachent;
-                       attachloop += 1;
-                       if (attachloop > 255) // prevent runaway looping
-                               return 5;
+                       tagindex = (int)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
+                       ent = PRVM_EDICT_NUM(val->edict);
                }
-               while ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict);
+               else
+                       break;
+               attachloop++;
        }
 
-       // normal or RENDER_VIEWMODEL entity (or main parent entity on attach chain)
-       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.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);
-
+       // RENDER_VIEWMODEL magic
        if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && (RF_VIEWMODEL & (int)val->_float))
-       {// RENDER_VIEWMODEL magic
+       {
                Matrix4x4_Copy(&tagmatrix, out);
 
-               val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.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);
+               CL_GetEntityMatrix(prog->edicts, &entitymatrix, true);
                Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
 
                /*
@@ -1943,7 +2361,7 @@ int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
                        // 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 = cl.time/cl_bobcycle.value;
                        cycle -= (int)cycle;
                        if (cycle < cl_bobup.value)
                                cycle = sin(M_PI * cycle / cl_bobup.value);
@@ -1961,11 +2379,11 @@ int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
 }
 
 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
-static void VM_CL_gettagindex (void)
+void VM_CL_gettagindex (void)
 {
        prvm_edict_t *ent;
        const char *tag_name;
-       int modelindex, tag_index;
+       int tag_index;
 
        VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
 
@@ -1973,44 +2391,67 @@ static void VM_CL_gettagindex (void)
        tag_name = PRVM_G_STRING(OFS_PARM1);
        if (ent == prog->edicts)
        {
-               VM_Warning("gettagindex: can't affect world entity\n");
+               VM_Warning("VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
                return;
        }
        if (ent->priv.server->free)
        {
-               VM_Warning("gettagindex: can't affect free entity\n");
+               VM_Warning("VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
                return;
        }
 
-       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));
+       if (!CL_GetModelFromEdict(ent))
+               Con_DPrintf("VM_CL_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);
+                       Con_Printf("VM_CL_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)
-static void VM_CL_gettaginfo (void)
+void VM_CL_gettaginfo (void)
 {
        prvm_edict_t *e;
        int tagindex;
        matrix4x4_t tag_matrix;
+       matrix4x4_t tag_localmatrix;
+       int parentindex;
+       const char *tagname;
        int returncode;
+       prvm_eval_t *val;
+       vec3_t fo, le, up, trans;
+       const dp_model_t *model;
 
        VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
 
        e = PRVM_G_EDICT(OFS_PARM0);
        tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
        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));
+       Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, le, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN));
+       VectorScale(le, -1, prog->globals.client->v_right);
+       model = CL_GetModelFromEdict(e);
+       VM_GenerateFrameGroupBlend(e->priv.server->framegroupblend, e);
+       VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model);
+       VM_UpdateEdictSkeleton(e, model, e->priv.server->frameblend);
+       CL_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix);
+       Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans);
+
+       if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_parent)))
+               val->_float = parentindex;
+       if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_name)))
+               val->string = tagname ? PRVM_SetTempString(tagname) : 0;
+       if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_offset)))
+               VectorCopy(trans, val->vector);
+       if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_forward)))
+               VectorCopy(fo, val->vector);
+       if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_right)))
+               VectorScale(le, -1, val->vector);
+       if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_up)))
+               VectorCopy(up, val->vector);
 
        switch(returncode)
        {
@@ -2035,361 +2476,833 @@ static void VM_CL_gettaginfo (void)
 //============================================================================
 
 //====================
-//QC POLYGON functions
+// DP_CSQC_SPAWNPARTICLE
+// a QC hook to engine's CL_NewParticle
 //====================
 
-typedef struct
+// particle theme struct
+typedef struct vmparticletheme_s
 {
-       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 ?
+       unsigned short typeindex;
+       qboolean initialized;
+       pblend_t blendmode;
+       porientation_t orientation;
+       int color1;
+       int color2;
+       int tex;
+       float size;
+       float sizeincrease;
+       int alpha;
+       int alphafade;
+       float gravity;
+       float bounce;
+       float airfriction;
+       float liquidfriction;
+       float originjitter;
+       float velocityjitter;
+       qboolean qualityreduction;
+       float lifetime;
+       float stretch;
+       int staincolor1;
+       int staincolor2;
+       int staintex;
+       float delayspawn;
+       float delaycollision;
+}vmparticletheme_t;
+
+// particle spawner
+typedef struct vmparticlespawner_s
+{
+       mempool_t                       *pool;
+       qboolean                        initialized;
+       qboolean                        verified;
+       vmparticletheme_t       *themes;
+       int                                     max_themes;
+       // global addresses
+       float *particle_type;
+       float *particle_blendmode; 
+       float *particle_orientation;
+       float *particle_color1;
+       float *particle_color2;
+       float *particle_tex;
+       float *particle_size;
+       float *particle_sizeincrease;
+       float *particle_alpha;
+       float *particle_alphafade;
+       float *particle_time;
+       float *particle_gravity;
+       float *particle_bounce;
+       float *particle_airfriction;
+       float *particle_liquidfriction;
+       float *particle_originjitter;
+       float *particle_velocityjitter;
+       float *particle_qualityreduction;
+       float *particle_stretch;
+       float *particle_staincolor1;
+       float *particle_staincolor2;
+       float *particle_staintex;
+       float *particle_delayspawn;
+       float *particle_delaycollision;
+}vmparticlespawner_t;
+
+vmparticlespawner_t vmpartspawner;
+
+// TODO: automatic max_themes grow
+static void VM_InitParticleSpawner (int maxthemes)
+{
+       prvm_eval_t *val;
 
-#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
+       // bound max themes to not be an insane value
+       if (maxthemes < 4)
+               maxthemes = 4;
+       if (maxthemes > 2048)
+               maxthemes = 2048;
+       // allocate and set up structure
+       if (vmpartspawner.initialized) // reallocate
+       {
+               Mem_FreePool(&vmpartspawner.pool);
+               memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t));
+       }
+       vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL);
+       vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes);
+       vmpartspawner.max_themes = maxthemes;
+       vmpartspawner.initialized = true;
+       vmpartspawner.verified = true;
+       // get field addresses for fast querying (we can do 1000 calls of spawnparticle in a frame)
+       #define getglobal(v,s) val = PRVM_GLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset(s)); if (val) { vmpartspawner.v = &val->_float; } else { VM_Warning("VM_InitParticleSpawner: missing global '%s', spawner cannot work\n", s); vmpartspawner.verified = false; }
+       #define getglobalvector(v,s) val = PRVM_GLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset(s)); if (val) { vmpartspawner.v = (float *)val->vector; } else { VM_Warning("VM_InitParticleSpawner: missing global '%s', spawner cannot work\n", s); vmpartspawner.verified = false; }
+       getglobal(particle_type, "particle_type");
+       getglobal(particle_blendmode, "particle_blendmode");
+       getglobal(particle_orientation, "particle_orientation");
+       getglobalvector(particle_color1, "particle_color1");
+       getglobalvector(particle_color2, "particle_color2");
+       getglobal(particle_tex, "particle_tex");
+       getglobal(particle_size, "particle_size");
+       getglobal(particle_sizeincrease, "particle_sizeincrease");
+       getglobal(particle_alpha, "particle_alpha");
+       getglobal(particle_alphafade, "particle_alphafade");
+       getglobal(particle_time, "particle_time");
+       getglobal(particle_gravity, "particle_gravity");
+       getglobal(particle_bounce, "particle_bounce");
+       getglobal(particle_airfriction, "particle_airfriction");
+       getglobal(particle_liquidfriction, "particle_liquidfriction");
+       getglobal(particle_originjitter, "particle_originjitter");
+       getglobal(particle_velocityjitter, "particle_velocityjitter");
+       getglobal(particle_qualityreduction, "particle_qualityreduction");
+       getglobal(particle_stretch, "particle_stretch");
+       getglobalvector(particle_staincolor1, "particle_staincolor1");
+       getglobalvector(particle_staincolor2, "particle_staincolor2");
+       getglobal(particle_staintex, "particle_staintex");
+       getglobal(particle_delayspawn, "particle_delayspawn");
+       getglobal(particle_delaycollision, "particle_delaycollision");
+       #undef getglobal
+       #undef getglobalvector
+}
 
-static void VM_InitPolygons (void)
+// reset particle theme to default values
+static void VM_ResetParticleTheme (vmparticletheme_t *theme)
 {
-       vm_polygons_pool = Mem_AllocPool("VMPOLY", 0, NULL);
-       vm_polygons = (vm_polygon_t *)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_drawpolygons_num = 0;
-       vm_polygonbegin = false;
-       vm_polygons_initialized = true;
+       theme->initialized = true;
+       theme->typeindex = pt_static;
+       theme->blendmode = PBLEND_ADD;
+       theme->orientation = PARTICLE_BILLBOARD;
+       theme->color1 = 0x808080;
+       theme->color2 = 0xFFFFFF;
+       theme->tex = 63;
+       theme->size = 2;
+       theme->sizeincrease = 0;
+       theme->alpha = 256;
+       theme->alphafade = 512;
+       theme->gravity = 0.0f;
+       theme->bounce = 0.0f;
+       theme->airfriction = 1.0f;
+       theme->liquidfriction = 4.0f;
+       theme->originjitter = 0.0f;
+       theme->velocityjitter = 0.0f;
+       theme->qualityreduction = false;
+       theme->lifetime = 4;
+       theme->stretch = 1;
+       theme->staincolor1 = -1;
+       theme->staincolor2 = -1;
+       theme->staintex = -1;
+       theme->delayspawn = 0.0f;
+       theme->delaycollision = 0.0f;
 }
 
-static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
+// particle theme -> QC globals
+void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme)
 {
-       int surfacelistindex;
-       // LordHavoc: FIXME: this is stupid code
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
-       {
-               const vm_polygon_t      *p = &vm_polygons[surfacelist[surfacelistindex]];
-               int                                     flags = p->flags & 0x0f;
+       *vmpartspawner.particle_type = theme->typeindex;
+       *vmpartspawner.particle_blendmode = theme->blendmode;
+       *vmpartspawner.particle_orientation = theme->orientation;
+       vmpartspawner.particle_color1[0] = (theme->color1 >> 16) & 0xFF; // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375...
+       vmpartspawner.particle_color1[1] = (theme->color1 >> 8) & 0xFF;
+       vmpartspawner.particle_color1[2] = (theme->color1 >> 0) & 0xFF;
+       vmpartspawner.particle_color2[0] = (theme->color2 >> 16) & 0xFF;
+       vmpartspawner.particle_color2[1] = (theme->color2 >> 8) & 0xFF;
+       vmpartspawner.particle_color2[2] = (theme->color2 >> 0) & 0xFF;
+       *vmpartspawner.particle_tex = (float)theme->tex;
+       *vmpartspawner.particle_size = theme->size;
+       *vmpartspawner.particle_sizeincrease = theme->sizeincrease;
+       *vmpartspawner.particle_alpha = (float)theme->alpha/256;
+       *vmpartspawner.particle_alphafade = (float)theme->alphafade/256;
+       *vmpartspawner.particle_time = theme->lifetime;
+       *vmpartspawner.particle_gravity = theme->gravity;
+       *vmpartspawner.particle_bounce = theme->bounce;
+       *vmpartspawner.particle_airfriction = theme->airfriction;
+       *vmpartspawner.particle_liquidfriction = theme->liquidfriction;
+       *vmpartspawner.particle_originjitter = theme->originjitter;
+       *vmpartspawner.particle_velocityjitter = theme->velocityjitter;
+       *vmpartspawner.particle_qualityreduction = theme->qualityreduction;
+       *vmpartspawner.particle_stretch = theme->stretch;
+       vmpartspawner.particle_staincolor1[0] = (theme->staincolor1 >> 16) & 0xFF;
+       vmpartspawner.particle_staincolor1[1] = (theme->staincolor1 >> 8) & 0xFF;
+       vmpartspawner.particle_staincolor1[2] = (theme->staincolor1 >> 0) & 0xFF;
+       vmpartspawner.particle_staincolor2[0] = (theme->staincolor2 >> 16) & 0xFF;
+       vmpartspawner.particle_staincolor2[1] = (theme->staincolor2 >> 8) & 0xFF;
+       vmpartspawner.particle_staincolor2[2] = (theme->staincolor2 >> 0) & 0xFF;
+       *vmpartspawner.particle_staintex = (float)theme->staintex;
+       *vmpartspawner.particle_delayspawn = theme->delayspawn;
+       *vmpartspawner.particle_delaycollision = theme->delaycollision;
+}
 
-               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);
+// QC globals ->  particle theme
+void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme)
+{
+       theme->typeindex = (unsigned short)*vmpartspawner.particle_type;
+       theme->blendmode = (pblend_t)*vmpartspawner.particle_blendmode;
+       theme->orientation = (porientation_t)*vmpartspawner.particle_orientation;
+       theme->color1 = ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]);
+       theme->color2 = ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]);
+       theme->tex = (int)*vmpartspawner.particle_tex;
+       theme->size = *vmpartspawner.particle_size;
+       theme->sizeincrease = *vmpartspawner.particle_sizeincrease;
+       theme->alpha = (int)(*vmpartspawner.particle_alpha*256);
+       theme->alphafade = (int)(*vmpartspawner.particle_alphafade*256);
+       theme->lifetime = *vmpartspawner.particle_time;
+       theme->gravity = *vmpartspawner.particle_gravity;
+       theme->bounce = *vmpartspawner.particle_bounce;
+       theme->airfriction = *vmpartspawner.particle_airfriction;
+       theme->liquidfriction = *vmpartspawner.particle_liquidfriction;
+       theme->originjitter = *vmpartspawner.particle_originjitter;
+       theme->velocityjitter = *vmpartspawner.particle_velocityjitter;
+       theme->qualityreduction = (*vmpartspawner.particle_qualityreduction) ? true : false;
+       theme->stretch = *vmpartspawner.particle_stretch;
+       theme->staincolor1 = vmpartspawner.particle_staincolor1[0]*65536 + vmpartspawner.particle_staincolor1[1]*256 + vmpartspawner.particle_staincolor1[2];
+       theme->staincolor2 = vmpartspawner.particle_staincolor2[0]*65536 + vmpartspawner.particle_staincolor2[1]*256 + vmpartspawner.particle_staincolor2[2];
+       theme->staintex =(int)*vmpartspawner.particle_staintex;
+       theme->delayspawn = *vmpartspawner.particle_delayspawn;
+       theme->delaycollision = *vmpartspawner.particle_delaycollision;
+}
 
-               R_Mesh_TexBind(0, R_GetTexture(p->tex));
+// init particle spawner interface
+// # float(float max_themes) initparticlespawner
+void VM_CL_InitParticleSpawner (void)
+{
+       VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner);
+       VM_InitParticleSpawner((int)PRVM_G_FLOAT(OFS_PARM0));
+       vmpartspawner.themes[0].initialized = true;
+       VM_ResetParticleTheme(&vmpartspawner.themes[0]);
+       PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0;
+}
 
-               CHECKGLERROR
-               //[515]: is speed is max ?
-               if(p->flags & VM_POLYGON_FLLINES)       //[515]: lines
-               {
-                       qglLineWidth(p->data[13]);CHECKGLERROR
-                       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();
-                       CHECKGLERROR
-               }
-               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();
-                       CHECKGLERROR
-               }
+// void() resetparticle
+void VM_CL_ResetParticle (void)
+{
+       VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle);
+       if (vmpartspawner.verified == false)
+       {
+               VM_Warning("VM_CL_ResetParticle: particle spawner not initialized\n");
+               return;
        }
+       VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
 }
 
-static void VM_CL_AddPolygonTo2DScene (vm_polygon_t *p)
+// void(float themenum) particletheme
+void VM_CL_ParticleTheme (void)
 {
-       drawqueuemesh_t mesh;
-       static int              picelements[6] = {0, 1, 2, 0, 2, 3};
+       int themenum;
 
-       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)
+       VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme);
+       if (vmpartspawner.verified == false)
        {
-               mesh.num_vertices = 4;
-               mesh.num_triangles = 2;
+               VM_Warning("VM_CL_ParticleTheme: particle spawner not initialized\n");
+               return;
        }
-       else
+       themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
+       if (themenum < 0 || themenum >= vmpartspawner.max_themes)
        {
-               mesh.num_vertices = 3;
-               mesh.num_triangles = 1;
+               VM_Warning("VM_CL_ParticleTheme: bad theme number %i\n", themenum);
+               VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
+               return;
        }
-       if(p->flags & VM_POLYGON_FLLINES)       //[515]: lines
-               DrawQ_LineLoop (&mesh, (p->flags&0x0f));
-       else
-               DrawQ_Mesh (&mesh, (p->flags&0x0f));
-}
+       if (vmpartspawner.themes[themenum].initialized == false)
+       {
+               VM_Warning("VM_CL_ParticleTheme: theme #%i not exists\n", themenum);
+               VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
+               return;
+       }
+       // load particle theme into globals
+       VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum]);
+}
 
-void VM_CL_AddPolygonsToMeshQueue (void)
+// float() saveparticletheme
+// void(float themenum) updateparticletheme
+void VM_CL_ParticleThemeSave (void)
 {
-       int i;
-       if(!vm_drawpolygons_num)
+       int themenum;
+
+       VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave);
+       if (vmpartspawner.verified == false)
+       {
+               VM_Warning("VM_CL_ParticleThemeSave: particle spawner not initialized\n");
                return;
-       R_Mesh_Matrix(&identitymatrix);
-       GL_CullFace(GL_NONE);
-       for(i = 0;i < (int)vm_drawpolygons_num;i++)
-               VM_DrawPolygonCallback(NULL, NULL, 1, &i);
-       vm_drawpolygons_num = 0;
+       }
+       // allocate new theme, save it and return
+       if (prog->argc < 1)
+       {
+               for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++)
+                       if (vmpartspawner.themes[themenum].initialized == false)
+                               break;
+               if (themenum >= vmpartspawner.max_themes)
+               {
+                       if (vmpartspawner.max_themes == 2048)
+                               VM_Warning("VM_CL_ParticleThemeSave: no free theme slots\n");
+                       else
+                               VM_Warning("VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n");
+                       PRVM_G_FLOAT(OFS_RETURN) = -1;
+                       return;
+               }
+               vmpartspawner.themes[themenum].initialized = true;
+               VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
+               PRVM_G_FLOAT(OFS_RETURN) = themenum;
+               return;
+       }
+       // update existing theme
+       themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
+       if (themenum < 0 || themenum >= vmpartspawner.max_themes)
+       {
+               VM_Warning("VM_CL_ParticleThemeSave: bad theme number %i\n", themenum);
+               return;
+       }
+       vmpartspawner.themes[themenum].initialized = true;
+       VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
 }
 
-//void(string texturename, float flag[, float 2d[, float lines]]) R_BeginPolygon
-static void VM_CL_R_PolygonBegin (void)
+// void(float themenum) freeparticletheme
+void VM_CL_ParticleThemeFree (void)
 {
-       vm_polygon_t    *p;
-       const char              *picname;
-       VM_SAFEPARMCOUNTRANGE(2, 4, VM_CL_R_PolygonBegin);
+       int themenum;
 
-       if(!vm_polygons_initialized)
-               VM_InitPolygons();
-       if(vm_polygonbegin)
+       VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree);
+       if (vmpartspawner.verified == false)
        {
-               VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonEnd after first\n");
+               VM_Warning("VM_CL_ParticleThemeFree: particle spawner not initialized\n");
                return;
        }
-       if(vm_drawpolygons_num >= vm_polygons_num)
+       themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
+       // check parms
+       if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
        {
-               p = (vm_polygon_t *)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;
+               VM_Warning("VM_CL_ParticleThemeFree: bad theme number %i\n", themenum);
+               return;
        }
-       p = &vm_polygons[vm_drawpolygons_num];
-       picname = PRVM_G_STRING(OFS_PARM0);
-       if(picname[0])
-               p->tex = Draw_CachePic(picname, true)->tex;
-       else
-               p->tex = r_texture_white;
-       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))
+       if (vmpartspawner.themes[themenum].initialized == false)
+       {
+               VM_Warning("VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum);
+               VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
+               return;
+       }
+       // free theme
+       VM_ResetParticleTheme(&vmpartspawner.themes[themenum]);
+       vmpartspawner.themes[themenum].initialized = false;
+}
+
+// float(vector org, vector dir, [float theme]) particle
+// returns 0 if failed, 1 if succesful
+void VM_CL_SpawnParticle (void)
+{
+       float *org, *dir;
+       vmparticletheme_t *theme;
+       particle_t *part;
+       int themenum;
+
+       VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle2);
+       if (vmpartspawner.verified == false)
+       {
+               VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
+               PRVM_G_FLOAT(OFS_RETURN) = 0; 
+               return;
+       }
+       org = PRVM_G_VECTOR(OFS_PARM0);
+       dir = PRVM_G_VECTOR(OFS_PARM1);
+       
+       if (prog->argc < 3) // global-set particle
+       {
+               part = CL_NewParticle((unsigned short)*vmpartspawner.particle_type, ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, (int)(*vmpartspawner.particle_alpha*256), (int)(*vmpartspawner.particle_alphafade*256), *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)*vmpartspawner.particle_blendmode, (porientation_t)*vmpartspawner.particle_orientation, ((int)vmpartspawner.particle_staincolor1[0] << 16) + ((int)vmpartspawner.particle_staincolor1[1] << 8) + ((int)vmpartspawner.particle_staincolor1[2]), ((int)vmpartspawner.particle_staincolor2[0] << 16) + ((int)vmpartspawner.particle_staincolor2[1] << 8) + ((int)vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex);
+               if (!part)
+               {
+                       PRVM_G_FLOAT(OFS_RETURN) = 0; 
+                       return;
+               }
+               if (*vmpartspawner.particle_delayspawn)
+                       part->delayedspawn = cl.time + *vmpartspawner.particle_delayspawn;
+               if (*vmpartspawner.particle_delaycollision)
+                       part->delayedcollisions = cl.time + *vmpartspawner.particle_delaycollision;
+       }
+       else // quick themed particle
+       {
+               themenum = (int)PRVM_G_FLOAT(OFS_PARM2);
+               if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
+               {
+                       VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
+                       PRVM_G_FLOAT(OFS_RETURN) = 0; 
+                       return;
+               }
+               theme = &vmpartspawner.themes[themenum];
+               part = CL_NewParticle(theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex);
+               if (!part)
                {
-                       p->data[13] = PRVM_G_FLOAT(OFS_PARM3);  //[515]: linewidth
-                       p->flags |= VM_POLYGON_FLLINES;
+                       PRVM_G_FLOAT(OFS_RETURN) = 0; 
+                       return;
                }
+               if (theme->delayspawn)
+                       part->delayedspawn = cl.time + theme->delayspawn;
+               if (theme->delaycollision)
+                       part->delayedcollisions = cl.time + theme->delaycollision;
        }
+       PRVM_G_FLOAT(OFS_RETURN) = 1; 
 }
 
-//void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
-static void VM_CL_R_PolygonVertex (void)
+// float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle
+// returns 0 if failed, 1 if success
+void VM_CL_SpawnParticleDelayed (void)
 {
-       float                   *coords, *tx, *rgb, alpha;
-       vm_polygon_t    *p;
-       VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
+       float *org, *dir;
+       vmparticletheme_t *theme;
+       particle_t *part;
+       int themenum;
 
-       if(!vm_polygonbegin)
+       VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticle2);
+       if (vmpartspawner.verified == false)
        {
-               VM_Warning("VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
+               VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
+               PRVM_G_FLOAT(OFS_RETURN) = 0; 
                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);
+       org = PRVM_G_VECTOR(OFS_PARM0);
+       dir = PRVM_G_VECTOR(OFS_PARM1);
+       if (prog->argc < 5) // global-set particle
+               part = CL_NewParticle((unsigned short)*vmpartspawner.particle_type, ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, (int)(*vmpartspawner.particle_alpha*256), (int)(*vmpartspawner.particle_alphafade*256), *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)*vmpartspawner.particle_blendmode, (porientation_t)*vmpartspawner.particle_orientation, ((int)vmpartspawner.particle_staincolor1[0] << 16) + ((int)vmpartspawner.particle_staincolor1[1] << 8) + ((int)vmpartspawner.particle_staincolor1[2]), ((int)vmpartspawner.particle_staincolor2[0] << 16) + ((int)vmpartspawner.particle_staincolor2[1] << 8) + ((int)vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex);
+       else // themed particle
+       {
+               themenum = (int)PRVM_G_FLOAT(OFS_PARM4);
+               if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
+               {
+                       VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
+                       PRVM_G_FLOAT(OFS_RETURN) = 0;  
+                       return;
+               }
+               theme = &vmpartspawner.themes[themenum];
+               part = CL_NewParticle(theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex);
+       }
+       if (!part) 
+       { 
+               PRVM_G_FLOAT(OFS_RETURN) = 0; 
+               return; 
+       }
+       part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2);
+       part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3);
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+}
+
+//
+//====================
+//QC POLYGON functions
+//====================
+
+#define VMPOLYGONS_MAXPOINTS 64
+
+typedef struct vmpolygons_triangle_s
+{
+       rtexture_t              *texture;
+       int                             drawflag;
+       unsigned short  elements[3];
+}vmpolygons_triangle_t;
+
+typedef struct vmpolygons_s
+{
+       mempool_t               *pool;
+       qboolean                initialized;
+       double          progstarttime;
+
+       int                             max_vertices;
+       int                             num_vertices;
+       float                   *data_vertex3f;
+       float                   *data_color4f;
+       float                   *data_texcoord2f;
+
+       int                             max_triangles;
+       int                             num_triangles;
+       vmpolygons_triangle_t *data_triangles;
+       unsigned short  *data_sortedelement3s;
+
+       qboolean                begin_active;
+       rtexture_t              *begin_texture;
+       int                             begin_drawflag;
+       int                             begin_vertices;
+       float                   begin_vertex[VMPOLYGONS_MAXPOINTS][3];
+       float                   begin_color[VMPOLYGONS_MAXPOINTS][4];
+       float                   begin_texcoord[VMPOLYGONS_MAXPOINTS][2];
+} vmpolygons_t;
+
+// FIXME: make VM_CL_R_Polygon functions use Debug_Polygon functions?
+vmpolygons_t vmpolygons[PRVM_MAXPROGS];
+
+//#304 void() renderscene (EXT_CSQC)
+// moved that here to reset the polygons,
+// resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0
+// --blub
+void VM_CL_R_RenderScene (void)
+{
+       double t = Sys_DoubleTime();
+       vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
+       VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
+
+       // we need to update any RENDER_VIEWMODEL entities at this point because
+       // csqc supplies its own view matrix
+       CL_UpdateViewEntities();
+       // now draw stuff!
+       R_RenderView();
+
+       polys->num_vertices = polys->num_triangles = 0;
+       polys->progstarttime = prog->starttime;
+
+       // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
+       prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
+}
 
-       p = &vm_polygons[vm_drawpolygons_num];
-       if(vm_current_vertices > 4)
+static void VM_ResizePolygons(vmpolygons_t *polys)
+{
+       float *oldvertex3f = polys->data_vertex3f;
+       float *oldcolor4f = polys->data_color4f;
+       float *oldtexcoord2f = polys->data_texcoord2f;
+       vmpolygons_triangle_t *oldtriangles = polys->data_triangles;
+       unsigned short *oldsortedelement3s = polys->data_sortedelement3s;
+       polys->max_vertices = min(polys->max_triangles*3, 65536);
+       polys->data_vertex3f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[3]));
+       polys->data_color4f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[4]));
+       polys->data_texcoord2f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[2]));
+       polys->data_triangles = (vmpolygons_triangle_t *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(vmpolygons_triangle_t));
+       polys->data_sortedelement3s = (unsigned short *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(unsigned short[3]));
+       if (polys->num_vertices)
        {
-               VM_Warning("VM_CL_R_PolygonVertex: may have 4 vertices max\n");
-               return;
+               memcpy(polys->data_vertex3f, oldvertex3f, polys->num_vertices*sizeof(float[3]));
+               memcpy(polys->data_color4f, oldcolor4f, polys->num_vertices*sizeof(float[4]));
+               memcpy(polys->data_texcoord2f, oldtexcoord2f, polys->num_vertices*sizeof(float[2]));
        }
+       if (polys->num_triangles)
+       {
+               memcpy(polys->data_triangles, oldtriangles, polys->num_triangles*sizeof(vmpolygons_triangle_t));
+               memcpy(polys->data_sortedelement3s, oldsortedelement3s, polys->num_triangles*sizeof(unsigned short[3]));
+       }
+       if (oldvertex3f)
+               Mem_Free(oldvertex3f);
+       if (oldcolor4f)
+               Mem_Free(oldcolor4f);
+       if (oldtexcoord2f)
+               Mem_Free(oldtexcoord2f);
+       if (oldtriangles)
+               Mem_Free(oldtriangles);
+       if (oldsortedelement3s)
+               Mem_Free(oldsortedelement3s);
+}
 
-       p->data[vm_current_vertices*3]          = coords[0];
-       p->data[1+vm_current_vertices*3]        = coords[1];
-       p->data[2+vm_current_vertices*3]        = coords[2];
+static void VM_InitPolygons (vmpolygons_t* polys)
+{
+       memset(polys, 0, sizeof(*polys));
+       polys->pool = Mem_AllocPool("VMPOLY", 0, NULL);
+       polys->max_triangles = 1024;
+       VM_ResizePolygons(polys);
+       polys->initialized = true;
+}
 
-       p->data[12+vm_current_vertices*2]       = tx[0];
-       if(!(p->flags & VM_POLYGON_FLLINES))
-               p->data[13+vm_current_vertices*2]       = tx[1];
+static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
+{
+       int surfacelistindex;
+       vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
+       if(polys->progstarttime != prog->starttime) // from other progs? won't draw these (this can cause crashes!)
+               return;
+       R_Mesh_ResetTextureState();
+       R_EntityMatrix(&identitymatrix);
+       GL_CullFace(GL_NONE);
+       R_Mesh_VertexPointer(polys->data_vertex3f, 0, 0);
+       R_Mesh_ColorPointer(polys->data_color4f, 0, 0);
+       R_Mesh_TexCoordPointer(0, 2, polys->data_texcoord2f, 0, 0);
 
-       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;
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
+       {
+               int numtriangles = 0;
+               rtexture_t *tex = polys->data_triangles[surfacelist[surfacelistindex]].texture;
+               int drawflag = polys->data_triangles[surfacelist[surfacelistindex]].drawflag;
+               // this can't call _DrawQ_ProcessDrawFlag, but should be in sync with it
+               // FIXME factor this out
+               if(drawflag == DRAWFLAG_ADDITIVE)
+                       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+               else if(drawflag == DRAWFLAG_MODULATE)
+                       GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
+               else if(drawflag == DRAWFLAG_2XMODULATE)
+                       GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
+               else if(drawflag == DRAWFLAG_SCREEN)
+                       GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
+               else
+                       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               R_SetupShader_Generic(tex, NULL, GL_MODULATE, 1);
+               numtriangles = 0;
+               for (;surfacelistindex < numsurfaces;surfacelistindex++)
+               {
+                       if (polys->data_triangles[surfacelist[surfacelistindex]].texture != tex || polys->data_triangles[surfacelist[surfacelistindex]].drawflag != drawflag)
+                               break;
+                       VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles);
+                       numtriangles++;
+               }
+               R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, polys->data_sortedelement3s, 0, 0);
+       }
+}
 
-       vm_current_vertices++;
-       if(vm_current_vertices == 4)
-               p->flags |= VM_POLYGON_FL4V;
+void VMPolygons_Store(vmpolygons_t *polys)
+{
+       if (r_refdef.draw2dstage)
+       {
+               // draw the polygon as 2D immediately
+               drawqueuemesh_t mesh;
+               mesh.texture = polys->begin_texture;
+               mesh.num_vertices = polys->begin_vertices;
+               mesh.num_triangles = polys->begin_vertices-2;
+               mesh.data_element3i = polygonelement3i;
+               mesh.data_element3s = polygonelement3s;
+               mesh.data_vertex3f = polys->begin_vertex[0];
+               mesh.data_color4f = polys->begin_color[0];
+               mesh.data_texcoord2f = polys->begin_texcoord[0];
+               DrawQ_Mesh(&mesh, polys->begin_drawflag);
+       }
        else
-               if(vm_current_vertices == 3)
-                       p->flags |= VM_POLYGON_FL3V;
+       {
+               // queue the polygon as 3D for sorted transparent rendering later
+               int i;
+               if (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
+               {
+                       polys->max_triangles *= 2;
+                       VM_ResizePolygons(polys);
+               }
+               if (polys->num_vertices + polys->begin_vertices <= polys->max_vertices)
+               {
+                       // needle in a haystack!
+                       // polys->num_vertices was used for copying where we actually want to copy begin_vertices
+                       // that also caused it to not render the first polygon that is added
+                       // --blub
+                       memcpy(polys->data_vertex3f + polys->num_vertices * 3, polys->begin_vertex[0], polys->begin_vertices * sizeof(float[3]));
+                       memcpy(polys->data_color4f + polys->num_vertices * 4, polys->begin_color[0], polys->begin_vertices * sizeof(float[4]));
+                       memcpy(polys->data_texcoord2f + polys->num_vertices * 2, polys->begin_texcoord[0], polys->begin_vertices * sizeof(float[2]));
+                       for (i = 0;i < polys->begin_vertices-2;i++)
+                       {
+                               polys->data_triangles[polys->num_triangles].texture = polys->begin_texture;
+                               polys->data_triangles[polys->num_triangles].drawflag = polys->begin_drawflag;
+                               polys->data_triangles[polys->num_triangles].elements[0] = polys->num_vertices;
+                               polys->data_triangles[polys->num_triangles].elements[1] = polys->num_vertices + i+1;
+                               polys->data_triangles[polys->num_triangles].elements[2] = polys->num_vertices + i+2;
+                               polys->num_triangles++;
+                       }
+                       polys->num_vertices += polys->begin_vertices;
+               }
+       }
+       polys->begin_active = false;
 }
 
-//void() R_EndPolygon
-static void VM_CL_R_PolygonEnd (void)
+// TODO: move this into the client code and clean-up everything else, too! [1/6/2008 Black]
+// LordHavoc: agreed, this is a mess
+void VM_CL_AddPolygonsToMeshQueue (void)
 {
-       VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
-       if(!vm_polygonbegin)
+       int i;
+       vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
+       vec3_t center;
+
+       // only add polygons of the currently active prog to the queue - if there is none, we're done
+       if( !prog )
+               return;
+
+       if (!polys->num_triangles)
+               return;
+
+       for (i = 0;i < polys->num_triangles;i++)
        {
-               VM_Warning("VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
+               VectorMAMAM(1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[0], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[1], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[2], center);
+               R_MeshQueue_AddTransparent(center, VM_DrawPolygonCallback, NULL, i, NULL);
+       }
+
+       /*polys->num_triangles = 0; // now done after rendering the scene,
+         polys->num_vertices = 0;  // otherwise it's not rendered at all and prints an error message --blub */
+}
+
+//void(string texturename, float flag) R_BeginPolygon
+void VM_CL_R_PolygonBegin (void)
+{
+       const char              *picname;
+       skinframe_t     *sf;
+       vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
+       int tf;
+
+       // TODO instead of using skinframes here (which provides the benefit of
+       // better management of flags, and is more suited for 3D rendering), what
+       // about supporting Q3 shaders?
+
+       VM_SAFEPARMCOUNT(2, VM_CL_R_PolygonBegin);
+
+       if (!polys->initialized)
+               VM_InitPolygons(polys);
+       if(polys->progstarttime != prog->starttime)
+       {
+               // from another progs? then reset the polys first (fixes crashes on map change, because that can make skinframe textures invalid)
+               polys->num_vertices = polys->num_triangles = 0;
+               polys->progstarttime = prog->starttime;
+       }
+       if (polys->begin_active)
+       {
+               VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonBegin after first\n");
                return;
        }
-       vm_polygonbegin = false;
-       if(vm_current_vertices > 2 || (vm_current_vertices >= 2 && vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FLLINES))
+       picname = PRVM_G_STRING(OFS_PARM0);
+
+       sf = NULL;
+       if(*picname)
        {
-               if(vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FL2D)    //[515]: don't use qcpolygons memory if 2D
-                       VM_CL_AddPolygonTo2DScene(&vm_polygons[vm_drawpolygons_num]);
-               else
-                       vm_drawpolygons_num++;
+               tf = TEXF_ALPHA;
+               if((int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MIPMAP)
+                       tf |= TEXF_MIPMAP;
+
+               do
+               {
+                       sf = R_SkinFrame_FindNextByName(sf, picname);
+               }
+               while(sf && sf->textureflags != tf);
+
+               if(!sf || !sf->base)
+                       sf = R_SkinFrame_LoadExternal(picname, tf, true);
+
+               if(sf)
+                       R_SkinFrame_MarkUsed(sf);
        }
-       else
-               VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", vm_current_vertices);
+
+       polys->begin_texture = (sf && sf->base) ? sf->base : r_texture_white;
+       polys->begin_drawflag = (int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MASK;
+       polys->begin_vertices = 0;
+       polys->begin_active = true;
 }
 
-void Debug_PolygonBegin(const char *picname, int flags, qboolean draw2d, float linewidth)
+//void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
+void VM_CL_R_PolygonVertex (void)
 {
-       vm_polygon_t    *p;
+       vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
 
-       if(!vm_polygons_initialized)
-               VM_InitPolygons();
-       if(vm_polygonbegin)
+       VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
+
+       if (!polys->begin_active)
        {
-               Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
+               VM_Warning("VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
+               return;
+       }
+
+       if (polys->begin_vertices >= VMPOLYGONS_MAXPOINTS)
+       {
+               VM_Warning("VM_CL_R_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
                return;
        }
-       // limit polygons to a vaguely sane amount, beyond this each one just
-       // replaces the last one
-       vm_drawpolygons_num = min(vm_drawpolygons_num, (1<<20)-1);
-       if(vm_drawpolygons_num >= vm_polygons_num)
+
+       polys->begin_vertex[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM0)[0];
+       polys->begin_vertex[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM0)[1];
+       polys->begin_vertex[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM0)[2];
+       polys->begin_texcoord[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM1)[0];
+       polys->begin_texcoord[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM1)[1];
+       polys->begin_color[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM2)[0];
+       polys->begin_color[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM2)[1];
+       polys->begin_color[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM2)[2];
+       polys->begin_color[polys->begin_vertices][3] = PRVM_G_FLOAT(OFS_PARM3);
+       polys->begin_vertices++;
+}
+
+//void() R_EndPolygon
+void VM_CL_R_PolygonEnd (void)
+{
+       vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
+
+       VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
+       if (!polys->begin_active)
        {
-               p = (vm_polygon_t *)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;
+               VM_Warning("VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
+               return;
        }
-       p = &vm_polygons[vm_drawpolygons_num];
-       if(picname && picname[0])
-               p->tex = Draw_CachePic(picname, true)->tex;
+       polys->begin_active = false;
+       if (polys->begin_vertices >= 3)
+               VMPolygons_Store(polys);
        else
-               p->tex = r_texture_white;
-       p->flags = flags;
-       vm_current_vertices = 0;
-       vm_polygonbegin = true;
-       if(draw2d)
-               p->flags |= VM_POLYGON_FL2D;
-       if(linewidth)
+               VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->begin_vertices);
+}
+
+static vmpolygons_t debugPolys;
+
+void Debug_PolygonBegin(const char *picname, int drawflag)
+{
+       if(!debugPolys.initialized)
+               VM_InitPolygons(&debugPolys);
+       if(debugPolys.begin_active)
        {
-               p->data[13] = linewidth;        //[515]: linewidth
-               p->flags |= VM_POLYGON_FLLINES;
+               Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
+               return;
        }
+       debugPolys.begin_texture = picname[0] ? Draw_CachePic (picname)->tex : r_texture_white;
+       debugPolys.begin_drawflag = drawflag;
+       debugPolys.begin_vertices = 0;
+       debugPolys.begin_active = true;
 }
 
 void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a)
 {
-       vm_polygon_t    *p;
-
-       if(!vm_polygonbegin)
+       if(!debugPolys.begin_active)
        {
                Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n");
                return;
        }
 
-       p = &vm_polygons[vm_drawpolygons_num];
-       if(vm_current_vertices > 4)
+       if(debugPolys.begin_vertices > VMPOLYGONS_MAXPOINTS)
        {
-               Con_Printf("Debug_PolygonVertex: may have 4 vertices max\n");
+               Con_Printf("Debug_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
                return;
        }
 
-       p->data[vm_current_vertices*3]          = x;
-       p->data[1+vm_current_vertices*3]        = y;
-       p->data[2+vm_current_vertices*3]        = z;
-
-       p->data[12+vm_current_vertices*2]       = s;
-       if(!(p->flags & VM_POLYGON_FLLINES))
-               p->data[13+vm_current_vertices*2]       = t;
-
-       p->data[20+vm_current_vertices*4]       = r;
-       p->data[21+vm_current_vertices*4]       = g;
-       p->data[22+vm_current_vertices*4]       = b;
-       p->data[23+vm_current_vertices*4]       = a;
-
-       vm_current_vertices++;
-       if(vm_current_vertices == 4)
-               p->flags |= VM_POLYGON_FL4V;
-       else
-               if(vm_current_vertices == 3)
-                       p->flags |= VM_POLYGON_FL3V;
+       debugPolys.begin_vertex[debugPolys.begin_vertices][0] = x;
+       debugPolys.begin_vertex[debugPolys.begin_vertices][1] = y;
+       debugPolys.begin_vertex[debugPolys.begin_vertices][2] = z;
+       debugPolys.begin_texcoord[debugPolys.begin_vertices][0] = s;
+       debugPolys.begin_texcoord[debugPolys.begin_vertices][1] = t;
+       debugPolys.begin_color[debugPolys.begin_vertices][0] = r;
+       debugPolys.begin_color[debugPolys.begin_vertices][1] = g;
+       debugPolys.begin_color[debugPolys.begin_vertices][2] = b;
+       debugPolys.begin_color[debugPolys.begin_vertices][3] = a;
+       debugPolys.begin_vertices++;
 }
 
 void Debug_PolygonEnd(void)
 {
-       if(!vm_polygonbegin)
+       if (!debugPolys.begin_active)
        {
                Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n");
                return;
        }
-       vm_polygonbegin = false;
-       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_CL_AddPolygonTo2DScene(&vm_polygons[vm_drawpolygons_num]);
-               else
-                       vm_drawpolygons_num++;
-       }
+       debugPolys.begin_active = false;
+       if (debugPolys.begin_vertices >= 3)
+               VMPolygons_Store(&debugPolys);
        else
-               Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", vm_current_vertices);
+               Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.begin_vertices);
 }
 
 /*
@@ -2436,7 +3349,7 @@ realcheck:
        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_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+       trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
 
        if (trace.fraction == 1.0)
                return false;
@@ -2449,7 +3362,7 @@ realcheck:
                        start[0] = stop[0] = x ? maxs[0] : mins[0];
                        start[1] = stop[1] = y ? maxs[1] : mins[1];
 
-                       trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+                       trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
 
                        if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
                                bottom = trace.endpos[2];
@@ -2474,7 +3387,7 @@ qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean
        float           dz;
        vec3_t          oldorg, neworg, end, traceendpos;
        trace_t         trace;
-       int                     i;
+       int                     i, svent;
        prvm_edict_t            *enemy;
        prvm_eval_t     *val;
 
@@ -2498,9 +3411,9 @@ qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean
                                if (dz < 30)
                                        neworg[2] += 8;
                        }
-                       trace = CL_Move (ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+                       trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
                        if (settrace)
-                               VM_SetTraceGlobals(&trace);
+                               CL_VM_SetTraceGlobals(&trace, svent);
 
                        if (trace.fraction == 1)
                        {
@@ -2526,16 +3439,16 @@ qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean
        VectorCopy (neworg, end);
        end[2] -= sv_stepheight.value*2;
 
-       trace = CL_Move (neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+       trace = CL_TraceBox(neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
        if (settrace)
-               VM_SetTraceGlobals(&trace);
+               CL_VM_SetTraceGlobals(&trace, svent);
 
        if (trace.startsolid)
        {
                neworg[2] -= sv_stepheight.value;
-               trace = CL_Move (neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
+               trace = CL_TraceBox(neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
                if (settrace)
-                       VM_SetTraceGlobals(&trace);
+                       CL_VM_SetTraceGlobals(&trace, svent);
                if (trace.startsolid)
                        return false;
        }
@@ -2654,8 +3567,393 @@ void VM_CL_serverkey(void)
        PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
 }
 
+/*
+=================
+VM_CL_checkpvs
+
+Checks if an entity is in a point's PVS.
+Should be fast but can be inexact.
+
+float checkpvs(vector viewpos, entity viewee) = #240;
+=================
+*/
+static void VM_CL_checkpvs (void)
+{
+       vec3_t viewpos;
+       prvm_edict_t *viewee;
+       vec3_t mi, ma;
+#if 1
+       unsigned char *pvs;
+#else
+       int fatpvsbytes;
+       unsigned char fatpvs[MAX_MAP_LEAFS/8];
+#endif
+
+       VM_SAFEPARMCOUNT(2, VM_SV_checkpvs);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos);
+       viewee = PRVM_G_EDICT(OFS_PARM1);
+
+       if(viewee->priv.required->free)
+       {
+               VM_Warning("checkpvs: can not check free entity\n");
+               PRVM_G_FLOAT(OFS_RETURN) = 4;
+               return;
+       }
+
+       VectorAdd(viewee->fields.server->origin, viewee->fields.server->mins, mi);
+       VectorAdd(viewee->fields.server->origin, viewee->fields.server->maxs, ma);
+
+#if 1
+       if(!sv.worldmodel->brush.GetPVS || !sv.worldmodel->brush.BoxTouchingPVS)
+       {
+               // no PVS support on this worldmodel... darn
+               PRVM_G_FLOAT(OFS_RETURN) = 3;
+               return;
+       }
+       pvs = sv.worldmodel->brush.GetPVS(sv.worldmodel, viewpos);
+       if(!pvs)
+       {
+               // viewpos isn't in any PVS... darn
+               PRVM_G_FLOAT(OFS_RETURN) = 2;
+               return;
+       }
+       PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, pvs, mi, ma);
+#else
+       // using fat PVS like FTEQW does (slow)
+       if(!sv.worldmodel->brush.FatPVS || !sv.worldmodel->brush.BoxTouchingPVS)
+       {
+               // no PVS support on this worldmodel... darn
+               PRVM_G_FLOAT(OFS_RETURN) = 3;
+               return;
+       }
+       fatpvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false);
+       if(!fatpvsbytes)
+       {
+               // viewpos isn't in any PVS... darn
+               PRVM_G_FLOAT(OFS_RETURN) = 2;
+               return;
+       }
+       PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma);
+#endif
+}
+
+// #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure  (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex.
+static void VM_CL_skel_create(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       skeleton_t *skeleton;
+       int i;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (!model || !model->num_bones)
+               return;
+       for (i = 0;i < MAX_EDICTS;i++)
+               if (!prog->skeletons[i])
+                       break;
+       if (i == MAX_EDICTS)
+               return;
+       prog->skeletons[i] = skeleton = Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
+       skeleton->model = model;
+       skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
+       // initialize to identity matrices
+       for (i = 0;i < skeleton->model->num_bones;i++)
+               skeleton->relativetransforms[i] = identitymatrix;
+       PRVM_G_FLOAT(OFS_RETURN) = i + 1;
+}
+
+// #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
+static void VM_CL_skel_build(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
+       float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
+       int firstbone = PRVM_G_FLOAT(OFS_PARM4);
+       int lastbone = PRVM_G_FLOAT(OFS_PARM5);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       float blendfrac;
+       int numblends;
+       int bonenum;
+       int blendindex;
+       framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
+       matrix4x4_t blendedmatrix;
+       matrix4x4_t matrix;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, model->num_bones - 1);
+       lastbone = min(lastbone, skeleton->model->num_bones - 1);
+       VM_GenerateFrameGroupBlend(framegroupblend, ed);
+       VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model);
+       blendfrac = 1.0f - retainfrac;
+       for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
+               frameblend[numblends].lerp *= blendfrac;
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+       {
+               memset(&blendedmatrix, 0, sizeof(blendedmatrix));
+               Matrix4x4_Accumulate(&blendedmatrix, &skeleton->relativetransforms[bonenum], retainfrac);
+               for (blendindex = 0;blendindex < numblends;blendindex++)
+               {
+                       Matrix4x4_FromArray12FloatD3D(&matrix, model->data_poses + 12 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
+                       Matrix4x4_Accumulate(&blendedmatrix, &matrix, frameblend[blendindex].lerp);
+               }
+               skeleton->relativetransforms[bonenum] = blendedmatrix;
+       }
+       PRVM_G_FLOAT(OFS_RETURN) = skeletonindex;
+}
+
+// #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
+static void VM_CL_skel_get_numbones(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
+}
+
+// #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
+static void VM_CL_skel_get_bonename(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_INT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(skeleton->model->data_bones[bonenum].name);
+}
+
+// #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
+static void VM_CL_skel_get_boneparent(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
+}
+
+// #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
+static void VM_CL_skel_find_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       const char *tagname = PRVM_G_STRING(OFS_PARM1);
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname) + 1;
+}
+
+// #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
+static void VM_CL_skel_get_bonerel(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       vec3_t forward, left, up, origin;
+       VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+       VectorClear(prog->globals.client->v_forward);
+       VectorClear(prog->globals.client->v_right);
+       VectorClear(prog->globals.client->v_up);
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       matrix = skeleton->relativetransforms[bonenum];
+       Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
+       VectorCopy(forward, prog->globals.client->v_forward);
+       VectorNegate(left, prog->globals.client->v_right);
+       VectorCopy(up, prog->globals.client->v_up);
+       VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
+static void VM_CL_skel_get_boneabs(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       vec3_t forward, left, up, origin;
+       VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+       VectorClear(prog->globals.client->v_forward);
+       VectorClear(prog->globals.client->v_right);
+       VectorClear(prog->globals.client->v_up);
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       matrix = skeleton->relativetransforms[bonenum];
+       // convert to absolute
+       while ((bonenum = skeleton->model->data_bones[bonenum].parent) >= 0)
+       {
+               temp = matrix;
+               Matrix4x4_Concat(&matrix, &skeleton->relativetransforms[bonenum], &temp);
+       }
+       Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
+       VectorCopy(forward, prog->globals.client->v_forward);
+       VectorNegate(left, prog->globals.client->v_right);
+       VectorCopy(up, prog->globals.client->v_up);
+       VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+static void VM_CL_skel_set_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       skeleton->relativetransforms[bonenum] = matrix;
+}
+
+// #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+static void VM_CL_skel_mul_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       temp = skeleton->relativetransforms[bonenum];
+       Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
+}
+
+// #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)
+static void VM_CL_skel_mul_bones(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int firstbone = PRVM_G_FLOAT(OFS_PARM1) - 1;
+       int lastbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
+       int bonenum;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin);
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, skeleton->model->num_bones - 1);
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+       {
+               temp = skeleton->relativetransforms[bonenum];
+               Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
+       }
+}
+
+// #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
+static void VM_CL_skel_copybones(void)
+{
+       int skeletonindexdst = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int skeletonindexsrc = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       int firstbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
+       int lastbone = PRVM_G_FLOAT(OFS_PARM3) - 1;
+       int bonenum;
+       skeleton_t *skeletondst;
+       skeleton_t *skeletonsrc;
+       if (skeletonindexdst < 0 || skeletonindexdst >= MAX_EDICTS || !(skeletondst = prog->skeletons[skeletonindexdst]))
+               return;
+       if (skeletonindexsrc < 0 || skeletonindexsrc >= MAX_EDICTS || !(skeletonsrc = prog->skeletons[skeletonindexsrc]))
+               return;
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, skeletondst->model->num_bones - 1);
+       lastbone = min(lastbone, skeletonsrc->model->num_bones - 1);
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+               skeletondst->relativetransforms[bonenum] = skeletonsrc->relativetransforms[bonenum];
+}
+
+// #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
+static void VM_CL_skel_delete(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       Mem_Free(skeleton);
+       prog->skeletons[skeletonindex] = NULL;
+}
+
+// #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
+static void VM_CL_frameforname(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       const char *name = PRVM_G_STRING(OFS_PARM1);
+       int i;
+       PRVM_G_FLOAT(OFS_RETURN) = -1;
+       if (!model || !model->animscenes)
+               return;
+       for (i = 0;i < model->numframes;i++)
+       {
+               if (!strcasecmp(model->animscenes[i].name, name))
+               {
+                       PRVM_G_FLOAT(OFS_RETURN) = i;
+                       break;
+               }
+       }
+}
+
+// #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.
+static void VM_CL_frameduration(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       int framenum = (int)PRVM_G_FLOAT(OFS_PARM1);
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
+               return;
+       if (model->animscenes[framenum].framerate)
+               PRVM_G_FLOAT(OFS_RETURN) = model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
+}
+
 //============================================================================
 
+// To create a almost working builtin file from this replace:
+// "^NULL.*" with ""
+// "^{.*//.*}:Wh\(.*\)" with "\1"
+// "\:" with "//"
+// "^.*//:Wh{\#:d*}:Wh{.*}" with "\2 = \1;"
+// "\n\n+" with "\n\n"
+
 prvm_builtin_t vm_cl_builtins[] = {
 NULL,                                                  // #0 NULL function (not callable) (QUAKE)
 VM_CL_makevectors,                             // #1 void(vector ang) makevectors (QUAKE)
@@ -2673,7 +3971,7 @@ VM_vlen,                                          // #12 float(vector v) vlen (QUAKE)
 VM_vectoyaw,                                   // #13 float(vector v) vectoyaw (QUAKE)
 VM_CL_spawn,                                   // #14 entity() spawn (QUAKE)
 VM_remove,                                             // #15 void(entity e) remove (QUAKE)
-VM_CL_traceline,                               // #16 float(vector v1, vector v2, float tryents) traceline (QUAKE)
+VM_CL_traceline,                               // #16 void(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE)
 NULL,                                                  // #17 entity() checkclient (QUAKE)
 VM_find,                                               // #18 entity(entity start, .string fld, string match) find (QUAKE)
 VM_precache_sound,                             // #19 void(string s) precache_sound (QUAKE)
@@ -2689,7 +3987,7 @@ VM_coredump,                                      // #28 void() coredump (QUAKE)
 VM_traceon,                                            // #29 void() traceon (QUAKE)
 VM_traceoff,                                   // #30 void() traceoff (QUAKE)
 VM_eprint,                                             // #31 void(entity e) eprint (QUAKE)
-VM_CL_walkmove,                                        // #32 float(float yaw, float dist) walkmove (QUAKE)
+VM_CL_walkmove,                                        // #32 float(float yaw, float dist[, float settrace]) walkmove (QUAKE)
 NULL,                                                  // #33 (QUAKE)
 VM_CL_droptofloor,                             // #34 float() droptofloor (QUAKE)
 VM_CL_lightstyle,                              // #35 void(float style, string value) lightstyle (QUAKE)
@@ -2880,16 +4178,16 @@ NULL,                                                   // #217
 VM_bitshift,                                   // #218 float(float number, float quantity) bitshift (EXT_BITSHIFT)
 NULL,                                                  // #219
 NULL,                                                  // #220
-NULL,                                                  // #221
+VM_strstrofs,                                  // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
 VM_str2chr,                                            // #222 float(string str, float ofs) str2chr (FTE_STRINGS)
 VM_chr2str,                                            // #223 string(float c, ...) chr2str (FTE_STRINGS)
-NULL,                                                  // #224
-NULL,                                                  // #225
-NULL,                                                  // #226
-NULL,                                                  // #227
+VM_strconv,                                            // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
+VM_strpad,                                             // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
+VM_infoadd,                                            // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
+VM_infoget,                                            // #227 string(string info, string key) infoget (FTE_STRINGS)
 VM_strncmp,                                            // #228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
-NULL,                                                  // #229
-NULL,                                                  // #230
+VM_strncasecmp,                                        // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
+VM_strncasecmp,                                        // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
 NULL,                                                  // #231
 NULL,                                                  // #232 void(float index, float type, .void field) SV_AddStat (EXT_CSQC)
 NULL,                                                  // #233
@@ -2899,7 +4197,7 @@ NULL,                                                     // #236
 NULL,                                                  // #237
 NULL,                                                  // #238
 NULL,                                                  // #239
-NULL,                                                  // #240
+VM_CL_checkpvs,                                        // #240
 NULL,                                                  // #241
 NULL,                                                  // #242
 NULL,                                                  // #243
@@ -2922,21 +4220,21 @@ NULL,                                                   // #259
 NULL,                                                  // #260
 NULL,                                                  // #261
 NULL,                                                  // #262
-NULL,                                                  // #263
-NULL,                                                  // #264
-NULL,                                                  // #265
-NULL,                                                  // #266
-NULL,                                                  // #267
-NULL,                                                  // #268
-NULL,                                                  // #269
-NULL,                                                  // #270
-NULL,                                                  // #271
-NULL,                                                  // #272
-NULL,                                                  // #273
-NULL,                                                  // #274
-NULL,                                                  // #275
-NULL,                                                  // #276
-NULL,                                                  // #277
+VM_CL_skel_create,                             // #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure  (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex.
+VM_CL_skel_build,                              // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
+VM_CL_skel_get_numbones,               // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
+VM_CL_skel_get_bonename,               // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
+VM_CL_skel_get_boneparent,             // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, -1 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
+VM_CL_skel_find_bone,                  // #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
+VM_CL_skel_get_bonerel,                        // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
+VM_CL_skel_get_boneabs,                        // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
+VM_CL_skel_set_bone,                   // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+VM_CL_skel_mul_bone,                   // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+VM_CL_skel_mul_bones,                  // #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)
+VM_CL_skel_copybones,                  // #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
+VM_CL_skel_delete,                             // #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
+VM_CL_frameforname,                            // #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
+VM_CL_frameduration,                   // #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.
 NULL,                                                  // #278
 NULL,                                                  // #279
 NULL,                                                  // #280
@@ -2969,7 +4267,7 @@ VM_CL_R_AddDynamicLight,          // #305 void(vector org, float radius, vector lightcol
 VM_CL_R_PolygonBegin,                  // #306 void(string texturename, float flag[, float is2d, float lines]) R_BeginPolygon
 VM_CL_R_PolygonVertex,                 // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
 VM_CL_R_PolygonEnd,                            // #308 void() R_EndPolygon
-NULL,                                                  // #309
+NULL /* R_LoadWorldModel in menu VM, should stay unassigned in client*/, // #309
 VM_CL_unproject,                               // #310 vector (vector v) cs_unproject (EXT_CSQC)
 VM_CL_project,                                 // #311 vector (vector v) cs_project (EXT_CSQC)
 NULL,                                                  // #312
@@ -2986,10 +4284,10 @@ VM_drawpic,                                             // #322 float(vector position, string pic, vector size, vector
 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
-NULL,                                                  // #326
-NULL,                                                  // #327
-NULL,                                                  // #328
-NULL,                                                  // #329
+VM_drawcolorcodedstring,               // #326 float drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) (EXT_CSQC)
+VM_stringwidth,                 // #327 // FIXME is this okay?
+VM_drawsubpic,                                 // #328 // FIXME is this okay?
+VM_drawrotpic,                                 // #329 // FIXME is this okay?
 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)
@@ -3004,9 +4302,9 @@ 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_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_setsensitivityscale,             // #346 void(float sens) setsensitivityscale (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)
@@ -3028,7 +4326,7 @@ 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)
-NULL,                                                  // #368
+NULL,                                          // #368
 NULL,                                                  // #369
 NULL,                                                  // #370
 NULL,                                                  // #371
@@ -3105,10 +4403,10 @@ NULL,                                                   // #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIE
 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_search_begin,                               // #444 float(string pattern, float caseinsensitive, float quiet) search_begin (DP_QC_FS_SEARCH)
+VM_search_end,                                 // #445 void(float handle) search_end (DP_QC_FS_SEARCH)
+VM_search_getsize,                             // #446 float(float handle) search_getsize (DP_QC_FS_SEARCH)
+VM_search_getfilename,                 // #447 string(float handle, float num) search_getfilename (DP_QC_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)
@@ -3118,7 +4416,7 @@ NULL,                                                     // #453 void(entity clent) dropclient (DP_SV_DROPCLIENT)
 NULL,                                                  // #454 entity() spawnclient (DP_SV_BOTCLIENT)
 NULL,                                                  // #455 float(entity clent) clienttype (DP_SV_BOTCLIENT)
 NULL,                                                  // #456 void(float to, string s) WriteUnterminatedString (DP_SV_WRITEUNTERMINATEDSTRING)
-VM_CL_te_flamejet,                             // #457 void(vector org, vector vel, float howmany) te_flamejet = #457 (DP_TE_FLAMEJET)
+VM_CL_te_flamejet,                             // #457 void(vector org, vector vel, float howmany) te_flamejet (DP_TE_FLAMEJET)
 NULL,                                                  // #458
 VM_ftoe,                                               // #459 entity(float num) entitybyindex (DP_QC_EDICT_NUM)
 VM_buf_create,                                 // #460 float() buf_create (DP_QC_STRINGBUFFERS)
@@ -3140,47 +4438,183 @@ VM_tan,                                                        // #475 float(float a) VM_tan (DP_QC_ASINACOSATANATAN2TAN)
 VM_strlennocol,                                        // #476 float(string s) : DRESK - String Length (not counting color codes) (DP_QC_STRINGCOLORFUNCTIONS)
 VM_strdecolorize,                              // #477 string(string s) : DRESK - Decolorized String (DP_QC_STRINGCOLORFUNCTIONS)
 VM_strftime,                                   // #478 string(float uselocaltime, string format, ...) (DP_QC_STRFTIME)
-NULL,                                                  // #479
-NULL,                                                  // #480
-NULL,                                                  // #481
-NULL,                                                  // #482
-NULL,                                                  // #483
-NULL,                                                  // #484
-NULL,                                                  // #485
-NULL,                                                  // #486
-NULL,                                                  // #487
-NULL,                                                  // #488
-NULL,                                                  // #489
-NULL,                                                  // #490
-NULL,                                                  // #491
-NULL,                                                  // #492
-NULL,                                                  // #493
-NULL,                                                  // #494
-NULL,                                                  // #495
-NULL,                                                  // #496
-NULL,                                                  // #497
-NULL,                                                  // #498
-NULL,                                                  // #499
+VM_tokenizebyseparator,                        // #479 float(string s) tokenizebyseparator (DP_QC_TOKENIZEBYSEPARATOR)
+VM_strtolower,                                 // #480 string(string s) VM_strtolower (DP_QC_STRING_CASE_FUNCTIONS)
+VM_strtoupper,                                 // #481 string(string s) VM_strtoupper (DP_QC_STRING_CASE_FUNCTIONS)
+VM_cvar_defstring,                             // #482 string(string s) cvar_defstring (DP_QC_CVAR_DEFSTRING)
+VM_CL_pointsound,                              // #483 void(vector origin, string sample, float volume, float attenuation) pointsound (DP_SV_POINTSOUND)
+VM_strreplace,                                 // #484 string(string search, string replace, string subject) strreplace (DP_QC_STRREPLACE)
+VM_strireplace,                                        // #485 string(string search, string replace, string subject) strireplace (DP_QC_STRREPLACE)
+VM_CL_getsurfacepointattribute,// #486 vector(entity e, float s, float n, float a) getsurfacepointattribute
+VM_gecko_create,                                       // #487 float gecko_create( string name )
+VM_gecko_destroy,                                      // #488 void gecko_destroy( string name )
+VM_gecko_navigate,                             // #489 void gecko_navigate( string name, string URI )
+VM_gecko_keyevent,                             // #490 float gecko_keyevent( string name, float key, float eventtype )
+VM_gecko_movemouse,                            // #491 void gecko_mousemove( string name, float x, float y )
+VM_gecko_resize,                                       // #492 void gecko_resize( string name, float w, float h )
+VM_gecko_get_texture_extent,   // #493 vector gecko_get_texture_extent( string name )
+VM_crc16,                                              // #494 float(float caseinsensitive, string s, ...) crc16 = #494 (DP_QC_CRC16)
+VM_cvar_type,                                  // #495 float(string name) cvar_type = #495; (DP_QC_CVAR_TYPE)
+VM_numentityfields,                            // #496 float() numentityfields = #496; (QP_QC_ENTITYDATA)
+VM_entityfieldname,                            // #497 string(float fieldnum) entityfieldname = #497; (DP_QC_ENTITYDATA)
+VM_entityfieldtype,                            // #498 float(float fieldnum) entityfieldtype = #498; (DP_QC_ENTITYDATA)
+VM_getentityfieldstring,               // #499 string(float fieldnum, entity ent) getentityfieldstring = #499; (DP_QC_ENTITYDATA)
+VM_putentityfieldstring,               // #500 float(float fieldnum, entity ent, string s) putentityfieldstring = #500; (DP_QC_ENTITYDATA)
+VM_CL_ReadPicture,                             // #501 string() ReadPicture = #501;
+NULL,                                                  // #502
+VM_whichpack,                                  // #503 string(string) whichpack = #503;
+NULL,                                                  // #504
+NULL,                                                  // #505
+NULL,                                                  // #506
+NULL,                                                  // #507
+NULL,                                                  // #508
+NULL,                                                  // #509
+VM_uri_escape,                                 // #510 string(string in) uri_escape = #510;
+VM_uri_unescape,                               // #511 string(string in) uri_unescape = #511;
+VM_etof,                                       // #512 float(entity ent) num_for_edict = #512 (DP_QC_NUM_FOR_EDICT)
+VM_uri_get,                                            // #513 float(string uril, float id) uri_get = #512; (DP_QC_URI_GET)
+VM_tokenize_console,                                   // #514 float(string str) tokenize_console = #514; (DP_QC_TOKENIZE_CONSOLE)
+VM_argv_start_index,                                   // #515 float(float idx) argv_start_index = #515; (DP_QC_TOKENIZE_CONSOLE)
+VM_argv_end_index,                                             // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE)
+VM_buf_cvarlist,                                               // #517 void(float buf, string prefix, string antiprefix) buf_cvarlist = #517; (DP_QC_STRINGBUFFERS_CVARLIST)
+VM_cvar_description,                                   // #518 float(string name) cvar_description = #518; (DP_QC_CVAR_DESCRIPTION)
+VM_gettime,                                            // #519 float(float timer) gettime = #519; (DP_QC_GETTIME)
+VM_keynumtostring,                             // #520 string keynumtostring(float keynum)
+VM_findkeysforcommand,                 // #521 string findkeysforcommand(string command)
+VM_CL_InitParticleSpawner,             // #522 void(float max_themes) initparticlespawner (DP_CSQC_SPAWNPARTICLE)
+VM_CL_ResetParticle,                   // #523 void() resetparticle (DP_CSQC_SPAWNPARTICLE)
+VM_CL_ParticleTheme,                   // #524 void(float theme) particletheme (DP_CSQC_SPAWNPARTICLE)
+VM_CL_ParticleThemeSave,               // #525 void() particlethemesave, void(float theme) particlethemeupdate (DP_CSQC_SPAWNPARTICLE)
+VM_CL_ParticleThemeFree,               // #526 void() particlethemefree (DP_CSQC_SPAWNPARTICLE)
+VM_CL_SpawnParticle,                   // #527 float(vector org, vector vel, [float theme]) particle (DP_CSQC_SPAWNPARTICLE)
+VM_CL_SpawnParticleDelayed,            // #528 float(vector org, vector vel, float delay, float collisiondelay, [float theme]) delayedparticle (DP_CSQC_SPAWNPARTICLE)
+VM_loadfromdata,                               // #529
+VM_loadfromfile,                               // #530
+NULL,                                                  // #531
+VM_log,                                                        // #532
+NULL,                                                  // #533
+NULL,                                                  // #534
+NULL,                                                  // #535
+NULL,                                                  // #536
+NULL,                                                  // #537
+NULL,                                                  // #538
+NULL,                                                  // #539
+NULL,                                                  // #540
+NULL,                                                  // #541
+NULL,                                                  // #542
+NULL,                                                  // #543
+NULL,                                                  // #544
+NULL,                                                  // #545
+NULL,                                                  // #546
+NULL,                                                  // #547
+NULL,                                                  // #548
+NULL,                                                  // #549
+NULL,                                                  // #550
+NULL,                                                  // #551
+NULL,                                                  // #552
+NULL,                                                  // #553
+NULL,                                                  // #554
+NULL,                                                  // #555
+NULL,                                                  // #556
+NULL,                                                  // #557
+NULL,                                                  // #558
+NULL,                                                  // #559
+NULL,                                                  // #560
+NULL,                                                  // #561
+NULL,                                                  // #562
+NULL,                                                  // #563
+NULL,                                                  // #564
+NULL,                                                  // #565
+NULL,                                                  // #566
+NULL,                                                  // #567
+NULL,                                                  // #568
+NULL,                                                  // #569
+NULL,                                                  // #570
+NULL,                                                  // #571
+NULL,                                                  // #572
+NULL,                                                  // #573
+NULL,                                                  // #574
+NULL,                                                  // #575
+NULL,                                                  // #576
+NULL,                                                  // #577
+NULL,                                                  // #578
+NULL,                                                  // #579
+NULL,                                                  // #580
+NULL,                                                  // #581
+NULL,                                                  // #582
+NULL,                                                  // #583
+NULL,                                                  // #584
+NULL,                                                  // #585
+NULL,                                                  // #586
+NULL,                                                  // #587
+NULL,                                                  // #588
+NULL,                                                  // #589
+NULL,                                                  // #590
+NULL,                                                  // #591
+NULL,                                                  // #592
+NULL,                                                  // #593
+NULL,                                                  // #594
+NULL,                                                  // #595
+NULL,                                                  // #596
+NULL,                                                  // #597
+NULL,                                                  // #598
+NULL,                                                  // #599
+NULL,                                                  // #600
+NULL,                                                  // #601
+NULL,                                                  // #602
+NULL,                                                  // #603
+NULL,                                                  // #604
+VM_callfunction,                               // #605
+VM_writetofile,                                        // #606
+VM_isfunction,                                 // #607
+NULL,                                                  // #608
+NULL,                                                  // #609
+NULL,                                                  // #610
+NULL,                                                  // #611
+NULL,                                                  // #612
+VM_parseentitydata,                            // #613
+NULL,                                                  // #614
+NULL,                                                  // #615
+NULL,                                                  // #616
+NULL,                                                  // #617
+NULL,                                                  // #618
+NULL,                                                  // #619
+NULL,                                                  // #620
+NULL,                                                  // #621
+NULL,                                                  // #622
+NULL,                                                  // #623
+VM_CL_getextresponse,                  // #624 string getextresponse(void)
+NULL,                                                  // #625
+NULL,                                                  // #626
+VM_sprintf,                     // #627 string sprintf(string format, ...)
+NULL,                                                  // #628
 };
 
 const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
 
-void VM_CL_Cmd_Init(void)
+void VM_Polygons_Reset(void)
 {
+       vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
+
        // TODO: replace vm_polygons stuff with a more general debugging polygon system, and make vm_polygons functions use that system
-       if(vm_polygons_initialized)
+       if(polys->initialized)
        {
-               Mem_FreePool(&vm_polygons_pool);
-               vm_polygons_initialized = false;
+               Mem_FreePool(&polys->pool);
+               polys->initialized = false;
        }
 }
 
+void VM_CL_Cmd_Init(void)
+{
+       VM_Cmd_Init();
+       VM_Polygons_Reset();
+}
+
 void VM_CL_Cmd_Reset(void)
 {
-       if(vm_polygons_initialized)
-       {
-               Mem_FreePool(&vm_polygons_pool);
-               vm_polygons_initialized = false;
-       }
+       World_End(&cl.world);
+       VM_Cmd_Reset();
+       VM_Polygons_Reset();
 }
 
+