Fix engine not starting on Windows if linked against SDL > 2.0.5
[xonotic/darkplaces.git] / prvm_cmds.c
index e8a3a5e..81f6246 100644 (file)
@@ -17,6 +17,9 @@
 #include "mdfour.h"
 
 extern cvar_t prvm_backtraceforwarnings;
+#ifdef USEODE
+extern dllhandle_t ode_dll;
+#endif
 
 // LordHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value
 void VM_Warning(prvm_prog_t *prog, const char *fmt, ...)
@@ -29,13 +32,13 @@ void VM_Warning(prvm_prog_t *prog, const char *fmt, ...)
        dpvsnprintf(msg,sizeof(msg),fmt,argptr);
        va_end(argptr);
 
-       Con_DPrint(msg);
+       Con_Print(msg);
 
        // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black]
        if(prvm_backtraceforwarnings.integer && recursive != realtime) // NOTE: this compares to the time, just in case if PRVM_PrintState causes a Host_Error and keeps recursive set
        {
                recursive = realtime;
-               PRVM_PrintState(prog);
+               PRVM_PrintState(prog, 0);
                recursive = -1;
        }
 }
@@ -84,7 +87,7 @@ void VM_GenerateFrameGroupBlend(prvm_prog_t *prog, framegroupblend_t *framegroup
 // LordHavoc: quite tempting to break apart this function to reuse the
 //            duplicated code, but I suspect it is better for performance
 //            this way
-void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroupblend_t *framegroupblend, const dp_model_t *model)
+void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroupblend_t *framegroupblend, const dp_model_t *model, double curtime)
 {
        int sub2, numframes, f, i, k;
        int isfirstframegroup = true;
@@ -96,7 +99,10 @@ void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroup
 
        memset(blend, 0, MAX_FRAMEBLENDS * sizeof(*blend));
 
-       if (!model || !model->surfmesh.isanimated || model->numframes <= 1)
+       // rpolzer: Not testing isanimated here - a model might have
+       // "animations" that move no vertices (but only bones), thus rendering
+       // may assume it's not animated while processing can't.
+       if (!model)
        {
                blend[0].lerp = 1;
                return;
@@ -133,7 +139,7 @@ void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroup
                        if (scene->framecount > 1)
                        {
                                // this code path is only used on .zym models and torches
-                               sublerp = scene->framerate * (cl.time - g->start);
+                               sublerp = scene->framerate * (curtime - g->start);
                                f = (int) floor(sublerp);
                                sublerp -= f;
                                sub2 = f + 1;
@@ -274,15 +280,21 @@ static qboolean checkextension(prvm_prog_t *prog, const char *name)
                        e++;
                if ((e - start) == len && !strncasecmp(start, name, len))
                {
+#ifdef USEODE
                        // special sheck for ODE
                        if (!strncasecmp("DP_PHYSICS_ODE", name, 14))
                        {
-#ifdef USEODE
+#ifndef LINK_TO_LIBODE
                                return ode_dll ? true : false;
 #else
+#ifdef LINK_TO_LIBODE
+                               return true;
+#else
                                return false;
 #endif
+#endif
                        }
+#endif
 
                        // special sheck for d0_blind_id
                        if (!strcasecmp("DP_CRYPTO", name))
@@ -450,7 +462,7 @@ vector normalize(vector)
 */
 void VM_normalize(prvm_prog_t *prog)
 {
-       float   *value1;
+       prvm_vec_t      *value1;
        vec3_t  newvalue;
        double  f;
 
@@ -492,8 +504,8 @@ float vectoyaw(vector)
 */
 void VM_vectoyaw(prvm_prog_t *prog)
 {
-       float   *value1;
-       float   yaw;
+       prvm_vec_t      *value1;
+       prvm_vec_t      yaw;
 
        VM_SAFEPARMCOUNT(1,VM_vectoyaw);
 
@@ -521,9 +533,18 @@ vector vectoangles(vector[, vector])
 */
 void VM_vectoangles(prvm_prog_t *prog)
 {
+       vec3_t result, forward, up;
        VM_SAFEPARMCOUNTRANGE(1, 2,VM_vectoangles);
 
-       AnglesFromVectors(PRVM_G_VECTOR(OFS_RETURN), PRVM_G_VECTOR(OFS_PARM0), prog->argc >= 2 ? PRVM_G_VECTOR(OFS_PARM1) : NULL, true);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM0), forward);
+       if (prog->argc >= 2)
+       {
+               VectorCopy(PRVM_G_VECTOR(OFS_PARM1), up);
+               AnglesFromVectors(result, forward, up, true);
+       }
+       else
+               AnglesFromVectors(result, forward, NULL, true);
+       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
 }
 
 /*
@@ -763,15 +784,15 @@ string    ftos(float)
 
 void VM_ftos(prvm_prog_t *prog)
 {
-       float v;
+       prvm_vec_t v;
        char s[128];
 
        VM_SAFEPARMCOUNT(1, VM_ftos);
 
        v = PRVM_G_FLOAT(OFS_PARM0);
 
-       if ((float)((int)v) == v)
-               dpsnprintf(s, sizeof(s), "%i", (int)v);
+       if ((prvm_vec_t)((prvm_int_t)v) == v)
+               dpsnprintf(s, sizeof(s), "%.0f", v);
        else
                dpsnprintf(s, sizeof(s), "%f", v);
        PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
@@ -787,7 +808,7 @@ float       fabs(float)
 
 void VM_fabs(prvm_prog_t *prog)
 {
-       float   v;
+       prvm_vec_t v;
 
        VM_SAFEPARMCOUNT(1,VM_fabs);
 
@@ -850,7 +871,7 @@ void VM_stof(prvm_prog_t *prog)
 ========================
 VM_itof
 
-float itof(intt ent)
+float itof(int ent)
 ========================
 */
 void VM_itof(prvm_prog_t *prog)
@@ -868,10 +889,10 @@ entity ftoe(float num)
 */
 void VM_ftoe(prvm_prog_t *prog)
 {
-       int ent;
+       prvm_int_t ent;
        VM_SAFEPARMCOUNT(1, VM_ftoe);
 
-       ent = (int)PRVM_G_FLOAT(OFS_PARM0);
+       ent = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM0);
        if (ent < 0 || ent >= prog->max_edicts || PRVM_PROG_TO_EDICT(ent)->priv.required->free)
                ent = 0; // return world instead of a free or invalid entity
 
@@ -1178,9 +1199,9 @@ entity    findflags(entity start, .float field, float match)
 // LordHavoc: search for flags in float fields
 void VM_findflags(prvm_prog_t *prog)
 {
-       int             e;
-       int             f;
-       int             s;
+       prvm_int_t      e;
+       prvm_int_t      f;
+       prvm_int_t      s;
        prvm_edict_t    *ed;
 
        VM_SAFEPARMCOUNT(3, VM_findflags);
@@ -1188,7 +1209,7 @@ void VM_findflags(prvm_prog_t *prog)
 
        e = PRVM_G_EDICTNUM(OFS_PARM0);
        f = PRVM_G_INT(OFS_PARM1);
-       s = (int)PRVM_G_FLOAT(OFS_PARM2);
+       s = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM2);
 
        for (e++ ; e < prog->num_edicts ; e++)
        {
@@ -1198,7 +1219,7 @@ void VM_findflags(prvm_prog_t *prog)
                        continue;
                if (!PRVM_E_FLOAT(ed,f))
                        continue;
-               if ((int)PRVM_E_FLOAT(ed,f) & s)
+               if ((prvm_int_t)PRVM_E_FLOAT(ed,f) & s)
                {
                        VM_RETURN_EDICT(ed);
                        return;
@@ -1218,9 +1239,9 @@ entity    findchainflags(.float field, float match)
 // LordHavoc: chained search for flags in float fields
 void VM_findchainflags(prvm_prog_t *prog)
 {
-       int             i;
-       int             f;
-       int             s;
+       prvm_int_t              i;
+       prvm_int_t              f;
+       prvm_int_t              s;
        prvm_edict_t    *ent, *chain;
        int chainfield;
 
@@ -1236,7 +1257,7 @@ void VM_findchainflags(prvm_prog_t *prog)
        chain = (prvm_edict_t *)prog->edicts;
 
        f = PRVM_G_INT(OFS_PARM0);
-       s = (int)PRVM_G_FLOAT(OFS_PARM1);
+       s = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM1);
 
        ent = PRVM_NEXT_EDICT(prog->edicts);
        for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
@@ -1246,7 +1267,7 @@ void VM_findchainflags(prvm_prog_t *prog)
                        continue;
                if (!PRVM_E_FLOAT(ent,f))
                        continue;
-               if (!((int)PRVM_E_FLOAT(ent,f) & s))
+               if (!((prvm_int_t)PRVM_E_FLOAT(ent,f) & s))
                        continue;
 
                PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_EDICT_TO_PROG(chain);
@@ -1392,7 +1413,7 @@ float     rint(float)
 */
 void VM_rint(prvm_prog_t *prog)
 {
-       float f;
+       prvm_vec_t f;
        VM_SAFEPARMCOUNT(1,VM_rint);
 
        f = PRVM_G_FLOAT(OFS_PARM0);
@@ -2015,14 +2036,14 @@ void VM_entityfieldname(prvm_prog_t *prog)
 {
        ddef_t *d;
        int i = (int)PRVM_G_FLOAT(OFS_PARM0);
-       
+
        if (i < 0 || i >= prog->numfielddefs)
        {
-        VM_Warning(prog, "VM_entityfieldname: %s: field index out of bounds\n", prog->name);
-        PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
+               VM_Warning(prog, "VM_entityfieldname: %s: field index out of bounds\n", prog->name);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
                return;
        }
-       
+
        d = &prog->fielddefs[i];
        PRVM_G_INT(OFS_RETURN) = d->s_name; // presuming that s_name points to a string already
 }
@@ -2048,7 +2069,7 @@ void VM_entityfieldtype(prvm_prog_t *prog)
        }
        
        d = &prog->fielddefs[i];
-       PRVM_G_FLOAT(OFS_RETURN) = (float)d->type;
+       PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t)d->type;
 }
 
 // KrimZon - DP_QC_ENTITYDATA
@@ -2064,7 +2085,7 @@ void VM_getentityfieldstring(prvm_prog_t *prog)
        // put the data into a string
        ddef_t *d;
        int type, j;
-       int *v;
+       prvm_eval_t *val;
        prvm_edict_t * ent;
        int i = (int)PRVM_G_FLOAT(OFS_PARM0);
        char valuebuf[MAX_INPUTLINE];
@@ -2086,12 +2107,12 @@ void VM_getentityfieldstring(prvm_prog_t *prog)
                VM_Warning(prog, "VM_entityfielddata: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
                return;
        }
-       v = (int *)((char *)ent->fields.vp + d->ofs*4);
+       val = (prvm_eval_t *)(ent->fields.fp + d->ofs);
        
        // if it's 0 or blank, return an empty string
        type = d->type & ~DEF_SAVEGLOBAL;
        for (j=0 ; j<prvm_type_size[type] ; j++)
-               if (v[j])
+               if (val->ivector[j])
                        break;
        if (j == prvm_type_size[type])
        {
@@ -2099,7 +2120,7 @@ void VM_getentityfieldstring(prvm_prog_t *prog)
                return;
        }
                
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_UglyValueString(prog, (etype_t)d->type, (prvm_eval_t *)v, valuebuf, sizeof(valuebuf)));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
 }
 
 // KrimZon - DP_QC_ENTITYDATA
@@ -2194,8 +2215,8 @@ void VM_strlennocol(prvm_prog_t *prog)
 
        szString = PRVM_G_STRING(OFS_PARM0);
 
-       //nCnt = COM_StringLengthNoColors(szString, 0, NULL);
-       nCnt = u8_COM_StringLengthNoColors(szString, 0, NULL);
+       //nCnt = (int)COM_StringLengthNoColors(szString, 0, NULL);
+       nCnt = (int)u8_COM_StringLengthNoColors(szString, 0, NULL);
 
        PRVM_G_FLOAT(OFS_RETURN) = nCnt;
 }
@@ -2309,7 +2330,7 @@ void VM_substring(prvm_prog_t *prog)
 
        if (start < 0) // FTE_STRINGS feature
        {
-               u_slength = u8_strlen(s);
+               u_slength = (int)u8_strlen(s);
                start += u_slength;
                start = bound(0, start, u_slength);
        }
@@ -2317,7 +2338,7 @@ void VM_substring(prvm_prog_t *prog)
        if (length < 0) // FTE_STRINGS feature
        {
                if (!u_slength) // it's not calculated when it's not needed above
-                       u_slength = u8_strlen(s);
+                       u_slength = (int)u8_strlen(s);
                length += u_slength - start + 1;
        }
                
@@ -2668,7 +2689,7 @@ void VM_tokenizebyseparator (prvm_prog_t *prog)
                if (!s[0])
                        continue;
                separators[numseparators] = s;
-               separatorlen[numseparators] = strlen(s);
+               separatorlen[numseparators] = (int)strlen(s);
                numseparators++;
        }
 
@@ -2775,7 +2796,7 @@ void VM_isserver(prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0,VM_serverstate);
 
-       PRVM_G_FLOAT(OFS_RETURN) = sv.active && (svs.maxclients > 1 || cls.state == ca_dedicated);
+       PRVM_G_FLOAT(OFS_RETURN) = sv.active;
 }
 
 /*
@@ -2863,7 +2884,7 @@ void VM_gettime(prvm_prog_t *prog)
 
        if(prog->argc == 0)
        {
-               PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
+               PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) realtime;
        }
        else
        {
@@ -2920,7 +2941,7 @@ void VM_getsoundtime (prvm_prog_t *prog)
        entchannel = CHAN_USER2ENGINE(entchannel);
        if (!IS_CHAN(entchannel))
                VM_Warning(prog, "VM_getsoundtime: %s: bad channel %i\n", prog->name, entchannel);
-       PRVM_G_FLOAT(OFS_RETURN) = (float)S_GetEntChannelPosition(entnum, entchannel);
+       PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t)S_GetEntChannelPosition(entnum, entchannel);
 }
 
 /*
@@ -3025,13 +3046,13 @@ float   mod(float val, float m)
 */
 void VM_modulo(prvm_prog_t *prog)
 {
-       int val, m;
+       prvm_int_t val, m;
        VM_SAFEPARMCOUNT(2,VM_module);
 
-       val = (int) PRVM_G_FLOAT(OFS_PARM0);
-       m       = (int) PRVM_G_FLOAT(OFS_PARM1);
+       val = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM0);
+       m       = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM1);
 
-       PRVM_G_FLOAT(OFS_RETURN) = (float) (val % m);
+       PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) (val % m);
 }
 
 static void VM_Search_Init(prvm_prog_t *prog)
@@ -3241,18 +3262,33 @@ VM_precache_pic
 string precache_pic(string pic)
 =========
 */
+#define PRECACHE_PIC_FROMWAD 1 /* FTEQW, not supported here */
+#define PRECACHE_PIC_NOTPERSISTENT 2
+//#define PRECACHE_PIC_NOCLAMP 4
+#define PRECACHE_PIC_MIPMAP 8
 void VM_precache_pic(prvm_prog_t *prog)
 {
        const char      *s;
+       int flags = CACHEPICFLAG_FAILONMISSING;
 
-       VM_SAFEPARMCOUNT(1, VM_precache_pic);
+       VM_SAFEPARMCOUNTRANGE(1, 2, VM_precache_pic);
 
        s = PRVM_G_STRING(OFS_PARM0);
        PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
        VM_CheckEmptyString(prog, s);
 
-       // AK Draw_CachePic is supposed to always return a valid pointer
-       if( Draw_CachePic_Flags(s, 0)->tex == r_texture_notexture )
+       if(prog->argc >= 2)
+       {
+               int f = PRVM_G_FLOAT(OFS_PARM1);
+               if(f & PRECACHE_PIC_NOTPERSISTENT)
+                       flags |= CACHEPICFLAG_NOTPERSISTENT;
+               //if(f & PRECACHE_PIC_NOCLAMP)
+               //      flags |= CACHEPICFLAG_NOCLAMP;
+               if(f & PRECACHE_PIC_MIPMAP)
+                       flags |= CACHEPICFLAG_MIPMAP;
+       }
+
+       if( !Draw_IsPicLoaded(Draw_CachePic_Flags(s, flags)) )
                PRVM_G_INT(OFS_RETURN) = OFS_NULL;
 }
 
@@ -3304,12 +3340,15 @@ float   drawcharacter(vector position, float character, vector scale, vector rgb,
 */
 void VM_drawcharacter(prvm_prog_t *prog)
 {
-       float *pos,*scale,*rgb;
+       prvm_vec_t *pos,*scale,*rgb;
        char   character;
        int flag;
        float sx, sy;
        VM_SAFEPARMCOUNT(6,VM_drawcharacter);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        character = (char) PRVM_G_FLOAT(OFS_PARM1);
        if(character == 0)
        {
@@ -3331,7 +3370,7 @@ void VM_drawcharacter(prvm_prog_t *prog)
        }
 
        if(pos[2] || scale[2])
-               Con_Printf("VM_drawcharacter: z value%c from %s discarded\n",(pos[2] && scale[2]) ? 's' : 0,((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
+               VM_Warning(prog, "VM_drawcharacter: z value%c from %s discarded\n",(pos[2] && scale[2]) ? 's' : 0,((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
 
        if(!scale[0] || !scale[1])
        {
@@ -3354,12 +3393,15 @@ float   drawstring(vector position, string text, vector scale, vector rgb, float a
 */
 void VM_drawstring(prvm_prog_t *prog)
 {
-       float *pos,*scale,*rgb;
+       prvm_vec_t *pos,*scale,*rgb;
        const char  *string;
        int flag = 0;
        float sx, sy;
        VM_SAFEPARMCOUNTRANGE(5,6,VM_drawstring);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        string = PRVM_G_STRING(OFS_PARM1);
        pos = PRVM_G_VECTOR(OFS_PARM0);
        scale = PRVM_G_VECTOR(OFS_PARM2);
@@ -3382,7 +3424,7 @@ void VM_drawstring(prvm_prog_t *prog)
        }
 
        if(pos[2] || scale[2])
-               Con_Printf("VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
+               VM_Warning(prog, "VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
 
        getdrawfontscale(prog, &sx, &sy);
        DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont(prog));
@@ -3401,7 +3443,7 @@ float     drawcolorcodedstring(vector position, string text, vector scale, vector rg
 */
 void VM_drawcolorcodedstring(prvm_prog_t *prog)
 {
-       float *pos, *scale;
+       prvm_vec_t *pos, *scale;
        const char  *string;
        int flag;
        vec3_t rgb;
@@ -3409,6 +3451,9 @@ void VM_drawcolorcodedstring(prvm_prog_t *prog)
 
        VM_SAFEPARMCOUNTRANGE(5,6,VM_drawcolorcodedstring);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        if (prog->argc == 6) // full 6 parms, like normal drawstring
        {
                pos = PRVM_G_VECTOR(OFS_PARM0);
@@ -3445,7 +3490,7 @@ void VM_drawcolorcodedstring(prvm_prog_t *prog)
        }
 
        if(pos[2] || scale[2])
-               Con_Printf("VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
+               VM_Warning(prog, "VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
 
        getdrawfontscale(prog, &sx, &sy);
        DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], alpha, flag, NULL, false, getdrawfont(prog));
@@ -3464,7 +3509,7 @@ float     stringwidth(string text, float allowColorCodes, float size)
 void VM_stringwidth(prvm_prog_t *prog)
 {
        const char  *string;
-       float *szv;
+       vec2_t szv;
        float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
        int colors;
        float sx, sy;
@@ -3474,14 +3519,13 @@ void VM_stringwidth(prvm_prog_t *prog)
        getdrawfontscale(prog, &sx, &sy);
        if(prog->argc == 3)
        {
-               szv = PRVM_G_VECTOR(OFS_PARM2);
+               Vector2Copy(PRVM_G_VECTOR(OFS_PARM2), szv);
                mult = 1;
        }
        else
        {
                // we want the width for 8x8 font size, divided by 8
-               static float defsize[] = {8, 8};
-               szv = defsize;
+               Vector2Set(szv, 8, 8);
                mult = 0.125;
                // to make sure snapping is turned off, ALWAYS use a nontrivial scale in this case
                if(sx >= 0.9 && sx <= 1.1)
@@ -3690,11 +3734,14 @@ float   drawpic(vector position, string pic, vector size, vector rgb, float alpha,
 void VM_drawpic(prvm_prog_t *prog)
 {
        const char *picname;
-       float *size, *pos, *rgb;
+       prvm_vec_t *size, *pos, *rgb;
        int flag = 0;
 
        VM_SAFEPARMCOUNTRANGE(5,6,VM_drawpic);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        picname = PRVM_G_STRING(OFS_PARM1);
        VM_CheckEmptyString(prog, picname);
 
@@ -3720,7 +3767,7 @@ void VM_drawpic(prvm_prog_t *prog)
        }
 
        if(pos[2] || size[2])
-               Con_Printf("VM_drawpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
+               VM_Warning(prog, "VM_drawpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
 
        DrawQ_Pic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
        PRVM_G_FLOAT(OFS_RETURN) = 1;
@@ -3735,11 +3782,14 @@ float   drawrotpic(vector position, string pic, vector size, vector org, float ang
 void VM_drawrotpic(prvm_prog_t *prog)
 {
        const char *picname;
-       float *size, *pos, *org, *rgb;
+       prvm_vec_t *size, *pos, *org, *rgb;
        int flag;
 
        VM_SAFEPARMCOUNT(8,VM_drawrotpic);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        picname = PRVM_G_STRING(OFS_PARM1);
        VM_CheckEmptyString(prog, picname);
 
@@ -3765,7 +3815,7 @@ void VM_drawrotpic(prvm_prog_t *prog)
        }
 
        if(pos[2] || size[2] || org[2])
-               Con_Printf("VM_drawrotpic: z value from pos/size/org discarded\n");
+               VM_Warning(prog, "VM_drawrotpic: z value from pos/size/org discarded\n");
 
        DrawQ_RotPic(pos[0], pos[1], Draw_CachePic_Flags(picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], org[0], org[1], PRVM_G_FLOAT(OFS_PARM4), rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM6), flag);
        PRVM_G_FLOAT(OFS_RETURN) = 1;
@@ -3781,11 +3831,14 @@ float   drawsubpic(vector position, vector size, string pic, vector srcPos, vector
 void VM_drawsubpic(prvm_prog_t *prog)
 {
        const char *picname;
-       float *size, *pos, *rgb, *srcPos, *srcSize, alpha;
+       prvm_vec_t *size, *pos, *rgb, *srcPos, *srcSize, alpha;
        int flag;
 
        VM_SAFEPARMCOUNT(8,VM_drawsubpic);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        picname = PRVM_G_STRING(OFS_PARM2);
        VM_CheckEmptyString(prog, picname);
 
@@ -3813,7 +3866,7 @@ void VM_drawsubpic(prvm_prog_t *prog)
        }
 
        if(pos[2] || size[2])
-               Con_Printf("VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
+               VM_Warning(prog, "VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
 
        DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT),
                size[0], size[1],
@@ -3834,11 +3887,13 @@ float drawfill(vector position, vector size, vector rgb, float alpha, float flag
 */
 void VM_drawfill(prvm_prog_t *prog)
 {
-       float *size, *pos, *rgb;
+       prvm_vec_t *size, *pos, *rgb;
        int flag;
 
        VM_SAFEPARMCOUNT(5,VM_drawfill);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
 
        pos = PRVM_G_VECTOR(OFS_PARM0);
        size = PRVM_G_VECTOR(OFS_PARM1);
@@ -3853,7 +3908,7 @@ void VM_drawfill(prvm_prog_t *prog)
        }
 
        if(pos[2] || size[2])
-               Con_Printf("VM_drawfill: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
+               VM_Warning(prog, "VM_drawfill: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
 
        DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
        PRVM_G_FLOAT(OFS_RETURN) = 1;
@@ -3871,6 +3926,9 @@ void VM_drawsetcliparea(prvm_prog_t *prog)
        float x,y,w,h;
        VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
        y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
        w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
@@ -3890,6 +3948,9 @@ void VM_drawresetcliparea(prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
 
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        DrawQ_ResetClipArea();
 }
 
@@ -3911,9 +3972,16 @@ void VM_getimagesize(prvm_prog_t *prog)
        VM_CheckEmptyString(prog, p);
 
        pic = Draw_CachePic_Flags (p, CACHEPICFLAG_NOTPERSISTENT);
-
-       PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
-       PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
+       if (!Draw_IsPicLoaded(pic))
+       {
+               PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
+               PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
+       }
+       else
+       {
+               PRVM_G_VECTOR(OFS_RETURN)[0] = Draw_GetPicWidth(pic);
+               PRVM_G_VECTOR(OFS_RETURN)[1] = Draw_GetPicHeight(pic);
+       }
        PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
 }
 
@@ -4270,8 +4338,13 @@ void makevectors(vector angle)
 */
 void VM_makevectors (prvm_prog_t *prog)
 {
+       vec3_t angles, forward, right, up;
        VM_SAFEPARMCOUNT(1, VM_makevectors);
-       AngleVectors(PRVM_G_VECTOR(OFS_PARM0), PRVM_gameglobalvector(v_forward), PRVM_gameglobalvector(v_right), PRVM_gameglobalvector(v_up));
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM0), angles);
+       AngleVectors(angles, forward, right, up);
+       VectorCopy(forward, PRVM_gameglobalvector(v_forward));
+       VectorCopy(right, PRVM_gameglobalvector(v_right));
+       VectorCopy(up, PRVM_gameglobalvector(v_up));
 }
 
 /*
@@ -4284,9 +4357,13 @@ vectorvectors(vector)
 */
 void VM_vectorvectors (prvm_prog_t *prog)
 {
+       vec3_t forward, right, up;
        VM_SAFEPARMCOUNT(1, VM_vectorvectors);
-       VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), PRVM_gameglobalvector(v_forward));
-       VectorVectors(PRVM_gameglobalvector(v_forward), PRVM_gameglobalvector(v_right), PRVM_gameglobalvector(v_up));
+       VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), forward);
+       VectorVectors(forward, right, up);
+       VectorCopy(forward, PRVM_gameglobalvector(v_forward));
+       VectorCopy(right, PRVM_gameglobalvector(v_right));
+       VectorCopy(up, PRVM_gameglobalvector(v_up));
 }
 
 /*
@@ -4298,11 +4375,15 @@ void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, fl
 */
 void VM_drawline (prvm_prog_t *prog)
 {
-       float   *c1, *c2, *rgb;
+       prvm_vec_t      *c1, *c2, *rgb;
        float   alpha, width;
        unsigned char   flags;
 
        VM_SAFEPARMCOUNT(6, VM_drawline);
+
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = true;
+
        width   = PRVM_G_FLOAT(OFS_PARM0);
        c1              = PRVM_G_VECTOR(OFS_PARM1);
        c2              = PRVM_G_VECTOR(OFS_PARM2);
@@ -4315,11 +4396,11 @@ void VM_drawline (prvm_prog_t *prog)
 // float(float number, float quantity) bitshift (EXT_BITSHIFT)
 void VM_bitshift (prvm_prog_t *prog)
 {
-       int n1, n2;
+       prvm_int_t n1, n2;
        VM_SAFEPARMCOUNT(2, VM_bitshift);
 
-       n1 = (int)fabs((float)((int)PRVM_G_FLOAT(OFS_PARM0)));
-       n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
+       n1 = (prvm_int_t)fabs((prvm_vec_t)((prvm_int_t)PRVM_G_FLOAT(OFS_PARM0)));
+       n2 = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM1);
        if(!n1)
                PRVM_G_FLOAT(OFS_RETURN) = n1;
        else
@@ -4360,7 +4441,7 @@ void VM_altstr_count(prvm_prog_t *prog)
                }
        }
 
-       PRVM_G_FLOAT( OFS_RETURN ) = (float) (count / 2);
+       PRVM_G_FLOAT( OFS_RETURN ) = (prvm_vec_t) (count / 2);
 }
 
 /*
@@ -4372,23 +4453,25 @@ string altstr_prepare(string)
 */
 void VM_altstr_prepare(prvm_prog_t *prog)
 {
-       char *out;
        const char *instr, *in;
-       int size;
        char outstr[VM_STRINGTEMP_LENGTH];
+       size_t outpos;
 
        VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
 
        instr = PRVM_G_STRING( OFS_PARM0 );
 
-       for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )
-               if( *in == '\'' ) {
-                       *out++ = '\\';
-                       *out = '\'';
-                       size--;
-               } else
-                       *out = *in;
-       *out = 0;
+       for (in = instr, outpos = 0; *in && outpos < sizeof(outstr) - 1; ++in)
+       {
+               if (*in == '\'' && outpos < sizeof(outstr) - 2)
+               {
+                       outstr[outpos++] = '\\';
+                       outstr[outpos++] = '\'';
+               }
+               else
+                       outstr[outpos++] = *in;
+       }
+       outstr[outpos] = 0;
 
        PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
 }
@@ -4584,6 +4667,94 @@ static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
        return strncmp(b, a, stringbuffers_sortlength);
 }
 
+prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, int flags, const char *format)
+{
+       prvm_stringbuffer_t *stringbuffer;
+       int i;
+
+       if (bufindex < 0)
+               return NULL;
+
+       // find buffer with wanted index
+       if (bufindex < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray))
+       {
+               if ( (stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, bufindex)) )
+               {
+                       if (stringbuffer->flags & STRINGBUFFER_TEMP)
+                               stringbuffer->flags = flags; // created but has not been used yet
+                       return stringbuffer;
+               }
+               return NULL;
+       }
+
+       // allocate new buffer with wanted index
+       while(1)
+       {
+               stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
+               stringbuffer->flags = STRINGBUFFER_TEMP;
+               for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
+               if (i == bufindex)
+               {
+                       stringbuffer->flags = flags; // mark as used
+                       break;
+               }
+       }
+       return stringbuffer;
+}
+
+void BufStr_Set(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex, const char *str)
+{
+       size_t  alloclen;
+
+       if (!stringbuffer || strindex < 0)
+               return;
+
+       BufStr_Expand(prog, stringbuffer, strindex);
+       stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
+       if (stringbuffer->strings[strindex])
+               Mem_Free(stringbuffer->strings[strindex]);
+       stringbuffer->strings[strindex] = NULL;
+
+       if (str)
+       {
+               // not the NULL string!
+               alloclen = strlen(str) + 1;
+               stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+               memcpy(stringbuffer->strings[strindex], str, alloclen);
+       }
+
+       BufStr_Shrink(prog, stringbuffer);
+}
+
+void BufStr_Del(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer)
+{
+       int i;
+       
+       if (!stringbuffer)
+               return;
+
+       for (i = 0;i < stringbuffer->num_strings;i++)
+               if (stringbuffer->strings[i])
+                       Mem_Free(stringbuffer->strings[i]);
+       if (stringbuffer->strings)
+               Mem_Free(stringbuffer->strings);
+       if(stringbuffer->origin)
+               PRVM_Free((char *)stringbuffer->origin);
+       Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
+}
+
+void BufStr_Flush(prvm_prog_t *prog)
+{
+       prvm_stringbuffer_t *stringbuffer;
+       int i, numbuffers;
+
+       numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
+       for (i = 0; i < numbuffers; i++)
+               if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
+                       BufStr_Del(prog, stringbuffer);
+       Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
+}
+
 /*
 ========================
 VM_buf_create
@@ -4599,7 +4770,7 @@ void VM_buf_create (prvm_prog_t *prog)
        int i;
        
        VM_SAFEPARMCOUNTRANGE(0, 2, VM_buf_create);
-
+       
        // VorteX: optional parm1 (buffer format) is unfinished, to keep intact with future databuffers extension must be set to "string"
        if(prog->argc >= 1 && strcmp(PRVM_G_STRING(OFS_PARM0), "string"))
        {
@@ -4611,7 +4782,7 @@ void VM_buf_create (prvm_prog_t *prog)
        stringbuffer->origin = PRVM_AllocationOrigin(prog);
        // optional flags parm
        if (prog->argc >= 2)
-               stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & 0xFF;
+               stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & STRINGBUFFER_QCFLAGS;
        PRVM_G_FLOAT(OFS_RETURN) = i;
 }
 
@@ -4630,17 +4801,7 @@ void VM_buf_del (prvm_prog_t *prog)
        VM_SAFEPARMCOUNT(1, VM_buf_del);
        stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
        if (stringbuffer)
-       {
-               int i;
-               for (i = 0;i < stringbuffer->num_strings;i++)
-                       if (stringbuffer->strings[i])
-                               Mem_Free(stringbuffer->strings[i]);
-               if (stringbuffer->strings)
-                       Mem_Free(stringbuffer->strings);
-               if(stringbuffer->origin)
-                       PRVM_Free((char *)stringbuffer->origin);
-               Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
-       }
+               BufStr_Del(prog, stringbuffer);
        else
        {
                VM_Warning(prog, "VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
@@ -4840,7 +5001,6 @@ void bufstr_set(float bufhandle, float string_index, string str) = #466;
 */
 void VM_bufstr_set (prvm_prog_t *prog)
 {
-       size_t alloclen;
        int                             strindex;
        prvm_stringbuffer_t *stringbuffer;
        const char              *news;
@@ -4860,23 +5020,8 @@ void VM_bufstr_set (prvm_prog_t *prog)
                return;
        }
 
-       BufStr_Expand(prog, stringbuffer, strindex);
-       stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
-
-       if(stringbuffer->strings[strindex])
-               Mem_Free(stringbuffer->strings[strindex]);
-       stringbuffer->strings[strindex] = NULL;
-
-       if(PRVM_G_INT(OFS_PARM2))
-       {
-               // not the NULL string!
-               news = PRVM_G_STRING(OFS_PARM2);
-               alloclen = strlen(news) + 1;
-               stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
-               memcpy(stringbuffer->strings[strindex], news, alloclen);
-       }
-
-       BufStr_Shrink(prog, stringbuffer);
+       news = PRVM_G_STRING(OFS_PARM2);
+       BufStr_Set(prog, stringbuffer, strindex, news);
 }
 
 /*
@@ -4963,11 +5108,353 @@ void VM_bufstr_free (prvm_prog_t *prog)
        BufStr_Shrink(prog, stringbuffer);
 }
 
+/*
+========================
+VM_buf_loadfile
+load a file into string buffer, return 0 or 1
+float buf_loadfile(string filename, float bufhandle) = #535;
+========================
+*/
+void VM_buf_loadfile(prvm_prog_t *prog)
+{
+       size_t alloclen;
+       prvm_stringbuffer_t *stringbuffer;
+       char string[VM_STRINGTEMP_LENGTH];
+       int strindex, c, end;
+       const char *filename;
+       char vabuf[1024];
+       qfile_t *file;
 
+       VM_SAFEPARMCOUNT(2, VM_buf_loadfile);
 
+       // get file
+       filename = PRVM_G_STRING(OFS_PARM0);
+       file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", filename), false);
+       if (file == NULL)
+               file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", filename), false);
+       if (file == NULL)
+       {
+               if (developer_extra.integer)
+                       VM_Warning(prog, "VM_buf_loadfile: failed to open file %s in %s\n", filename, prog->name);
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
 
+       // get string buffer
+       stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
+       if(!stringbuffer)
+       {
+               VM_Warning(prog, "VM_buf_loadfile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
 
+       // read file (append to the end of buffer)
+       strindex = stringbuffer->num_strings;
+       while(1)
+       {
+               // read line
+               end = 0;
+               for (;;)
+               {
+                       c = FS_Getc(file);
+                       if (c == '\r' || c == '\n' || c < 0)
+                               break;
+                       if (end < VM_STRINGTEMP_LENGTH - 1)
+                               string[end++] = c;
+               }
+               string[end] = 0;
+               // remove \n following \r
+               if (c == '\r')
+               {
+                       c = FS_Getc(file);
+                       if (c != '\n')
+                               FS_UnGetc(file, (unsigned char)c);
+               }
+               // add and continue
+               if (c >= 0 || end)
+               {
+                       BufStr_Expand(prog, stringbuffer, strindex);
+                       stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
+                       alloclen = strlen(string) + 1;
+                       stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+                       memcpy(stringbuffer->strings[strindex], string, alloclen);
+                       strindex = stringbuffer->num_strings;
+               }
+               else
+                       break;
+       }
+
+       // close file
+       FS_Close(file);
+       PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+
+/*
+========================
+VM_buf_writefile
+writes stringbuffer to a file, returns 0 or 1
+float buf_writefile(float filehandle, float bufhandle, [, float startpos, float numstrings]) = #468;
+========================
+*/
+
+void VM_buf_writefile(prvm_prog_t *prog)
+{
+       int filenum, strindex, strnum, strlength;
+       prvm_stringbuffer_t *stringbuffer;
+
+       VM_SAFEPARMCOUNTRANGE(2, 4, VM_buf_writefile);
+
+       // get file
+       filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
+       if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
+       {
+               VM_Warning(prog, "VM_buf_writefile: invalid file handle %i used in %s\n", filenum, prog->name);
+               return;
+       }
+       if (prog->openfiles[filenum] == NULL)
+       {
+               VM_Warning(prog, "VM_buf_writefile: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
+               return;
+       }
+       
+       // get string buffer
+       stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
+       if(!stringbuffer)
+       {
+               VM_Warning(prog, "VM_buf_writefile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+
+       // get start and end parms
+       if (prog->argc > 3)
+       {
+               strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
+               strnum = (int)PRVM_G_FLOAT(OFS_PARM3);
+       }
+       else if (prog->argc > 2)
+       {
+               strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
+               strnum = stringbuffer->num_strings - strindex;
+       }
+       else
+       {
+               strindex = 0;
+               strnum = stringbuffer->num_strings;
+       }
+       if (strindex < 0 || strindex >= stringbuffer->num_strings)
+       {
+               VM_Warning(prog, "VM_buf_writefile: wrong start string index %i used in %s\n", strindex, prog->name);
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+       if (strnum < 0)
+       {
+               VM_Warning(prog, "VM_buf_writefile: wrong strings count %i used in %s\n", strnum, prog->name);
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+
+       // write
+       while(strindex < stringbuffer->num_strings && strnum)
+       {
+               if (stringbuffer->strings[strindex])
+               {
+                       if ((strlength = (int)strlen(stringbuffer->strings[strindex])))
+                               FS_Write(prog->openfiles[filenum], stringbuffer->strings[strindex], strlength);
+                       FS_Write(prog->openfiles[filenum], "\n", 1);
+               }
+               strindex++;
+               strnum--;
+       }
+
+       PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+
+#define MATCH_AUTO     0
+#define MATCH_WHOLE    1
+#define MATCH_LEFT     2
+#define MATCH_RIGHT    3
+#define MATCH_MIDDLE   4
+#define MATCH_PATTERN  5
+
+static const char *detect_match_rule(char *pattern, int *matchrule)
+{
+       char *ppos, *qpos;
+       int patternlength;
+
+       patternlength = (int)strlen(pattern);
+       ppos = strchr(pattern, '*');
+       qpos = strchr(pattern, '?');
+       // has ? - pattern
+       if (qpos) 
+       {
+               *matchrule = MATCH_PATTERN;
+               return pattern;
+       }
+       // has * - left, mid, right or pattern
+       if (ppos)
+       {
+               // starts with * - may be right/mid or pattern
+               if ((ppos - pattern) == 0)
+               {
+                       ppos = strchr(pattern+1, '*');
+                       // *something 
+                       if (!ppos) 
+                       {
+                               *matchrule = MATCH_RIGHT;
+                               return pattern+1;
+                       }
+                       // *something*
+                       if ((ppos - pattern) == patternlength)
+                       {
+                               *matchrule = MATCH_MIDDLE;
+                               *ppos = 0;
+                               return pattern+1;
+                       }
+                       // *som*thing
+                       *matchrule = MATCH_PATTERN;
+                       return pattern;
+               }
+               // end with * - left
+               if ((ppos - pattern) == patternlength)
+               {
+                       *matchrule = MATCH_LEFT;
+                       *ppos = 0;
+                       return pattern;
+               }
+               // som*thing
+               *matchrule = MATCH_PATTERN;
+               return pattern;
+       }
+       // have no wildcards - whole string
+       *matchrule = MATCH_WHOLE;
+       return pattern;
+}
+
+// todo: support UTF8
+static qboolean match_rule(const char *string, int max_string, const char *pattern, int patternlength, int rule)
+{
+       const char *mid;
+
+       if (rule == 1)
+               return !strncmp(string, pattern, max_string) ? true : false;
+       if (rule == 2)
+               return !strncmp(string, pattern, patternlength) ? true : false;
+       if (rule == 3)
+       {
+               mid = strstr(string, pattern);
+               return mid && !*(mid+patternlength);
+       }
+       if (rule == 4)
+               return strstr(string, pattern) ? true : false;
+       // pattern
+       return matchpattern_with_separator(string, pattern, false, "", false) ? true : false;
+}
+
+/*
+========================
+VM_bufstr_find
+find an index of bufstring matching rule
+float bufstr_find(float bufhandle, string match, float matchrule, float startpos, float step) = #468;
+========================
+*/
+
+void VM_bufstr_find(prvm_prog_t *prog)
+{
+       prvm_stringbuffer_t *stringbuffer;
+       char string[VM_STRINGTEMP_LENGTH];
+       int matchrule, matchlen, i, step;
+       const char *match;
+       
+       VM_SAFEPARMCOUNTRANGE(3, 5, VM_bufstr_find);
+
+       PRVM_G_FLOAT(OFS_RETURN) = -1;
+
+       // get string buffer
+       stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+       if(!stringbuffer)
+       {
+               VM_Warning(prog, "VM_bufstr_find: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
+               return;
+       }
+
+       // get pattern/rule
+       matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
+       if (matchrule < 0 && matchrule > 5)
+       {
+               VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
+               return;
+       }
+       if (matchrule)
+               match = PRVM_G_STRING(OFS_PARM1);
+       else
+       {
+               strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
+               match = detect_match_rule(string, &matchrule);
+       }
+       matchlen = (int)strlen(match);
+
+       // find
+       i = (prog->argc > 3) ? (int)PRVM_G_FLOAT(OFS_PARM3) : 0;
+       step = (prog->argc > 4) ? (int)PRVM_G_FLOAT(OFS_PARM4) : 1;
+       while(i < stringbuffer->num_strings)
+       {
+               if (stringbuffer->strings[i] && match_rule(stringbuffer->strings[i], VM_STRINGTEMP_LENGTH, match, matchlen, matchrule))
+               {
+                       PRVM_G_FLOAT(OFS_RETURN) = i;
+                       break;
+               }
+               i += step;
+       }
+}
+
+/*
+========================
+VM_matchpattern
+float matchpattern(string s, string pattern, float matchrule, float startpos) = #468;
+========================
+*/
+void VM_matchpattern(prvm_prog_t *prog)
+{
+       const char *s, *match;
+       char string[VM_STRINGTEMP_LENGTH];
+       int matchrule, l;
 
+       VM_SAFEPARMCOUNTRANGE(2, 4, VM_matchpattern);
+
+       s = PRVM_G_STRING(OFS_PARM0);
+
+       // get pattern/rule
+       matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
+       if (matchrule < 0 && matchrule > 5)
+       {
+               VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
+               return;
+       }
+       if (matchrule)
+               match = PRVM_G_STRING(OFS_PARM1);
+       else
+       {
+               strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
+               match = detect_match_rule(string, &matchrule);
+       }
+
+       // offset
+       l = (int)strlen(match);
+       if (prog->argc > 3)
+               s += max(0, min((unsigned int)PRVM_G_FLOAT(OFS_PARM3), strlen(s)-1));
+
+       // match
+       PRVM_G_FLOAT(OFS_RETURN) = match_rule(s, VM_STRINGTEMP_LENGTH, match, l, matchrule);
+}
+
+/*
+========================
+VM_buf_cvarlist
+========================
+*/
 
 void VM_buf_cvarlist(prvm_prog_t *prog)
 {
@@ -5190,7 +5677,7 @@ void VM_strstrofs (prvm_prog_t *prog)
        instr = PRVM_G_STRING(OFS_PARM0);
        match = PRVM_G_STRING(OFS_PARM1);
        firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
-       firstofs = u8_bytelen(instr, firstofs);
+       firstofs = (int)u8_bytelen(instr, firstofs);
 
        if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
        {
@@ -5213,7 +5700,7 @@ void VM_str2chr (prvm_prog_t *prog)
        int index;
        VM_SAFEPARMCOUNT(2, VM_str2chr);
        s = PRVM_G_STRING(OFS_PARM0);
-       index = u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
+       index = (int)u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
 
        if((unsigned)index < strlen(s))
        {
@@ -5343,7 +5830,7 @@ void VM_strconv (prvm_prog_t *prog)
        redalpha = (int) PRVM_G_FLOAT(OFS_PARM1);       //0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate
        rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
        VM_VarString(prog, 3, (char *) resbuf, sizeof(resbuf));
-       len = strlen((char *) resbuf);
+       len = (int)strlen((char *) resbuf);
 
        for (i = 0; i < len; i++, result++)     //should this be done backwards?
        {
@@ -5495,7 +5982,7 @@ void VM_digest_hex(prvm_prog_t *prog)
        if(!digest)
                digest = "";
        VM_VarString(prog, 1, s, sizeof(s));
-       len = strlen(s);
+       len = (int)strlen(s);
 
        outlen = 0;
 
@@ -5577,11 +6064,14 @@ void VM_Cmd_Init(prvm_prog_t *prog)
        VM_Search_Init(prog);
 }
 
+static void animatemodel_reset(prvm_prog_t *prog);
+
 void VM_Cmd_Reset(prvm_prog_t *prog)
 {
        CL_PurgeOwner( MENUOWNER );
        VM_Search_Reset(prog);
        VM_Files_CloseAll(prog);
+       animatemodel_reset(prog);
 }
 
 // #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
@@ -5674,7 +6164,7 @@ void VM_whichpack (prvm_prog_t *prog)
        fn = PRVM_G_STRING(OFS_PARM0);
        pack = FS_WhichPack(fn);
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, pack ? pack : "");
+       PRVM_G_INT(OFS_RETURN) = pack ? PRVM_SetTempString(prog, pack) : 0;
 }
 
 typedef struct
@@ -5683,6 +6173,7 @@ typedef struct
        double starttime;
        float id;
        char buffer[MAX_INPUTLINE];
+       char posttype[128];
        unsigned char *postdata; // free when uri_to_prog_t is freed
        size_t postlen;
        char *sigdata; // free when uri_to_prog_t is freed
@@ -5842,7 +6333,8 @@ void VM_uri_get (prvm_prog_t *prog)
                        handle->sigdata[handle->siglen] = 0;
                }
 out1:
-               ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+               strlcpy(handle->posttype, posttype, sizeof(handle->posttype));
+               ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, handle->posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
        }
        else
        {
@@ -5873,7 +6365,7 @@ out1:
 out2:
                handle->postdata = NULL;
                handle->postlen = 0;
-               ret = Curl_Begin_ToMemory(url, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+               ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, NULL, NULL, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
        }
        if(ret)
        {
@@ -6041,13 +6533,14 @@ void VM_sprintf(prvm_prog_t *prog)
        const char *s, *s0;
        char outbuf[MAX_INPUTLINE];
        char *o = outbuf, *end = outbuf + sizeof(outbuf), *err;
+       const char *p;
        int argpos = 1;
        int width, precision, thisarg, flags;
        char formatbuf[16];
        char *f;
        int isfloat;
-       static int dummyivec[3] = {0, 0, 0};
-       static float dummyvec[3] = {0, 0, 0};
+       static prvm_int_t dummyivec[3] = {0, 0, 0};
+       static prvm_vec_t dummyvec[3] = {0, 0, 0};
        char vabuf[1024];
 
 #define PRINTF_ALTERNATE 1
@@ -6063,7 +6556,7 @@ void VM_sprintf(prvm_prog_t *prog)
 #define GETARG_FLOAT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
 #define GETARG_VECTOR(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)
 #define GETARG_INT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0)
-#define GETARG_INTVECTOR(a) (((a)>=1 && (a)<prog->argc) ? ((int*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)
+#define GETARG_INTVECTOR(a) (((a)>=1 && (a)<prog->argc) ? ((prvm_int_t*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)
 #define GETARG_STRING(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_STRING(OFS_PARM0 + 3 * (a))) : "")
 
        for(;;)
@@ -6253,6 +6746,12 @@ nolength:
                                                *f++ = '.';
                                                *f++ = '*';
                                        }
+                                       if(*s == 'd' || *s == 'i' || *s == 'o' || *s == 'u' || *s == 'x' || *s == 'X')
+                                       {
+                                               // make it use a good integer type
+                                               for(p = INT_LOSSLESS_FORMAT_SIZE; *p; )
+                                                       *f++ = *p++;
+                                       }
                                        *f++ = *s;
                                        *f++ = 0;
 
@@ -6263,15 +6762,15 @@ nolength:
                                        {
                                                case 'd': case 'i':
                                                        if(precision < 0) // not set
-                                                               o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));
+                                                               o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_INT(thisarg))));
                                                        else
-                                                               o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));
+                                                               o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_INT(thisarg))));
                                                        break;
                                                case 'o': case 'u': case 'x': case 'X':
                                                        if(precision < 0) // not set
-                                                               o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+                                                               o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_INT(thisarg))));
                                                        else
-                                                               o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+                                                               o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_INT(thisarg))));
                                                        break;
                                                case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
                                                        if(precision < 0) // not set
@@ -6326,7 +6825,10 @@ nolength:
                                                        {
                                                                if(precision < 0) // not set
                                                                        precision = end - o - 1;
-                                                               o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
+                                                               if(flags & PRINTF_SIGNPOSITIVE)
+                                                                       o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
+                                                               else
+                                                                       o += u8_strpad_colorcodes(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
                                                        }
                                                        break;
                                                default:
@@ -6339,7 +6841,8 @@ nolength:
                        default:
 verbatim:
                                if(o < end - 1)
-                                       *o++ = *s++;
+                                       *o++ = *s;
+                               ++s;
                                break;
                }
        }
@@ -6361,9 +6864,8 @@ static dp_model_t *getmodel(prvm_prog_t *prog, prvm_edict_t *ed)
                return NULL;
 }
 
-typedef struct
+struct animatemodel_cache
 {
-       unsigned int progid;
        dp_model_t *model;
        frameblend_t frameblend[MAX_FRAMEBLENDS];
        skeleton_t *skeleton_p;
@@ -6377,61 +6879,74 @@ typedef struct
        float *buf_svector3f;
        float *buf_tvector3f;
        float *buf_normal3f;
+};
+
+static void animatemodel_reset(prvm_prog_t *prog)
+{
+       if (!prog->animatemodel_cache)
+               return;
+       if(prog->animatemodel_cache->buf_vertex3f) Mem_Free(prog->animatemodel_cache->buf_vertex3f);
+       if(prog->animatemodel_cache->buf_svector3f) Mem_Free(prog->animatemodel_cache->buf_svector3f);
+       if(prog->animatemodel_cache->buf_tvector3f) Mem_Free(prog->animatemodel_cache->buf_tvector3f);
+       if(prog->animatemodel_cache->buf_normal3f) Mem_Free(prog->animatemodel_cache->buf_normal3f);
+       Mem_Free(prog->animatemodel_cache);
 }
-animatemodel_cache_t;
-static animatemodel_cache_t animatemodel_cache;
 
 static void animatemodel(prvm_prog_t *prog, dp_model_t *model, prvm_edict_t *ed)
 {
        skeleton_t *skeleton;
        int skeletonindex = -1;
        qboolean need = false;
+       struct animatemodel_cache *animatemodel_cache;
+       if (!prog->animatemodel_cache)
+       {
+               prog->animatemodel_cache = (struct animatemodel_cache *)Mem_Alloc(prog->progs_mempool, sizeof(struct animatemodel_cache));
+               memset(prog->animatemodel_cache, 0, sizeof(struct animatemodel_cache));
+       }
+       animatemodel_cache = prog->animatemodel_cache;
        if(!(model->surfmesh.isanimated && model->AnimateVertices))
        {
-               animatemodel_cache.data_vertex3f = model->surfmesh.data_vertex3f;
-               animatemodel_cache.data_svector3f = model->surfmesh.data_svector3f;
-               animatemodel_cache.data_tvector3f = model->surfmesh.data_tvector3f;
-               animatemodel_cache.data_normal3f = model->surfmesh.data_normal3f;
+               animatemodel_cache->data_vertex3f = model->surfmesh.data_vertex3f;
+               animatemodel_cache->data_svector3f = model->surfmesh.data_svector3f;
+               animatemodel_cache->data_tvector3f = model->surfmesh.data_tvector3f;
+               animatemodel_cache->data_normal3f = model->surfmesh.data_normal3f;
                return;
        }
-       if(animatemodel_cache.progid != prog->id)
-               memset(&animatemodel_cache, 0, sizeof(animatemodel_cache));
-       need |= (animatemodel_cache.model != model);
+       need |= (animatemodel_cache->model != model);
        VM_GenerateFrameGroupBlend(prog, ed->priv.server->framegroupblend, ed);
-       VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model);
-       need |= (memcmp(&animatemodel_cache.frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend))) != 0;
+       VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model, PRVM_serverglobalfloat(time));
+       need |= (memcmp(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend))) != 0;
        skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 1;
        if (!(skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones))
                skeleton = NULL;
-       need |= (animatemodel_cache.skeleton_p != skeleton);
+       need |= (animatemodel_cache->skeleton_p != skeleton);
        if(skeleton)
-               need |= (memcmp(&animatemodel_cache.skeleton, skeleton, sizeof(ed->priv.server->skeleton))) != 0;
+               need |= (memcmp(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton))) != 0;
        if(!need)
                return;
-       if(model->surfmesh.num_vertices > animatemodel_cache.max_vertices)
-       {
-               animatemodel_cache.max_vertices = model->surfmesh.num_vertices * 2;
-               if(animatemodel_cache.buf_vertex3f) Mem_Free(animatemodel_cache.buf_vertex3f);
-               if(animatemodel_cache.buf_svector3f) Mem_Free(animatemodel_cache.buf_svector3f);
-               if(animatemodel_cache.buf_tvector3f) Mem_Free(animatemodel_cache.buf_tvector3f);
-               if(animatemodel_cache.buf_normal3f) Mem_Free(animatemodel_cache.buf_normal3f);
-               animatemodel_cache.buf_vertex3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
-               animatemodel_cache.buf_svector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
-               animatemodel_cache.buf_tvector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
-               animatemodel_cache.buf_normal3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
-       }
-       animatemodel_cache.data_vertex3f = animatemodel_cache.buf_vertex3f;
-       animatemodel_cache.data_svector3f = animatemodel_cache.buf_svector3f;
-       animatemodel_cache.data_tvector3f = animatemodel_cache.buf_tvector3f;
-       animatemodel_cache.data_normal3f = animatemodel_cache.buf_normal3f;
+       if(model->surfmesh.num_vertices > animatemodel_cache->max_vertices)
+       {
+               animatemodel_cache->max_vertices = model->surfmesh.num_vertices * 2;
+               if(animatemodel_cache->buf_vertex3f) Mem_Free(animatemodel_cache->buf_vertex3f);
+               if(animatemodel_cache->buf_svector3f) Mem_Free(animatemodel_cache->buf_svector3f);
+               if(animatemodel_cache->buf_tvector3f) Mem_Free(animatemodel_cache->buf_tvector3f);
+               if(animatemodel_cache->buf_normal3f) Mem_Free(animatemodel_cache->buf_normal3f);
+               animatemodel_cache->buf_vertex3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
+               animatemodel_cache->buf_svector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
+               animatemodel_cache->buf_tvector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
+               animatemodel_cache->buf_normal3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
+       }
+       animatemodel_cache->data_vertex3f = animatemodel_cache->buf_vertex3f;
+       animatemodel_cache->data_svector3f = animatemodel_cache->buf_svector3f;
+       animatemodel_cache->data_tvector3f = animatemodel_cache->buf_tvector3f;
+       animatemodel_cache->data_normal3f = animatemodel_cache->buf_normal3f;
        VM_UpdateEdictSkeleton(prog, ed, model, ed->priv.server->frameblend);
-       model->AnimateVertices(model, ed->priv.server->frameblend, &ed->priv.server->skeleton, animatemodel_cache.data_vertex3f, animatemodel_cache.data_normal3f, animatemodel_cache.data_svector3f, animatemodel_cache.data_tvector3f);
-       animatemodel_cache.progid = prog->id;
-       animatemodel_cache.model = model;
-       memcpy(&animatemodel_cache.frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
-       animatemodel_cache.skeleton_p = skeleton;
+       model->AnimateVertices(model, ed->priv.server->frameblend, &ed->priv.server->skeleton, animatemodel_cache->data_vertex3f, animatemodel_cache->data_normal3f, animatemodel_cache->data_svector3f, animatemodel_cache->data_tvector3f);
+       animatemodel_cache->model = model;
+       memcpy(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
+       animatemodel_cache->skeleton_p = skeleton;
        if(skeleton)
-               memcpy(&animatemodel_cache.skeleton, skeleton, sizeof(ed->priv.server->skeleton));
+               memcpy(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton));
 }
 
 static void getmatrix(prvm_prog_t *prog, prvm_edict_t *ed, matrix4x4_t *out)
@@ -6487,9 +7002,9 @@ static void clippointtosurface(prvm_prog_t *prog, prvm_edict_t *ed, dp_model_t *
        {
                // clip original point to each triangle of the surface and find the
                // triangle that is closest
-               v[0] = animatemodel_cache.data_vertex3f + e[0] * 3;
-               v[1] = animatemodel_cache.data_vertex3f + e[1] * 3;
-               v[2] = animatemodel_cache.data_vertex3f + e[2] * 3;
+               v[0] = prog->animatemodel_cache->data_vertex3f + e[0] * 3;
+               v[1] = prog->animatemodel_cache->data_vertex3f + e[1] * 3;
+               v[2] = prog->animatemodel_cache->data_vertex3f + e[2] * 3;
                TriangleNormal(v[0], v[1], v[2], facenormal);
                VectorNormalize(facenormal);
                offsetdist = DotProduct(v[0], facenormal) - DotProduct(p, facenormal);
@@ -6543,6 +7058,7 @@ void VM_getsurfacepoint(prvm_prog_t *prog)
        dp_model_t *model;
        msurface_t *surface;
        int pointnum;
+       vec3_t result;
        VM_SAFEPARMCOUNT(3, VM_getsurfacepoint);
        VectorClear(PRVM_G_VECTOR(OFS_RETURN));
        ed = PRVM_G_EDICT(OFS_PARM0);
@@ -6553,7 +7069,8 @@ void VM_getsurfacepoint(prvm_prog_t *prog)
        if (pointnum < 0 || pointnum >= surface->num_vertices)
                return;
        animatemodel(prog, model, ed);
-       applytransform_forward(prog, &(animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+       applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
+       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
 }
 //PF_getsurfacepointattribute,     // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
 // float SPA_POSITION = 0;
@@ -6570,6 +7087,7 @@ void VM_getsurfacepointattribute(prvm_prog_t *prog)
        msurface_t *surface;
        int pointnum;
        int attributetype;
+       vec3_t result;
 
        VM_SAFEPARMCOUNT(4, VM_getsurfacepoint);
        VectorClear(PRVM_G_VECTOR(OFS_RETURN));
@@ -6586,36 +7104,40 @@ void VM_getsurfacepointattribute(prvm_prog_t *prog)
        switch( attributetype ) {
                // float SPA_POSITION = 0;
                case 0:
-                       applytransform_forward(prog, &(animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+                       applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
+                       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
                        break;
                // float SPA_S_AXIS = 1;
                case 1:
-                       applytransform_forward_direction(prog, &(animatemodel_cache.data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+                       applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
+                       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
                        break;
                // float SPA_T_AXIS = 2;
                case 2:
-                       applytransform_forward_direction(prog, &(animatemodel_cache.data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+                       applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
+                       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
                        break;
                // float SPA_R_AXIS = 3; // same as SPA_NORMAL
                case 3:
-                       applytransform_forward_direction(prog, &(animatemodel_cache.data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+                       applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
+                       VectorCopy(result, 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;
+                       result[0] = texcoord[0];
+                       result[1] = texcoord[1];
+                       result[2] = 0.0f;
+                       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
                        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;
+                       result[0] = texcoord[0];
+                       result[1] = texcoord[1];
+                       result[2] = 0.0f;
+                       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
                        break;
                }
                // float SPA_LIGHTMAP0_COLOR = 6;
@@ -6634,6 +7156,7 @@ void VM_getsurfacenormal(prvm_prog_t *prog)
        dp_model_t *model;
        msurface_t *surface;
        vec3_t normal;
+       vec3_t result;
        VM_SAFEPARMCOUNT(2, VM_getsurfacenormal);
        VectorClear(PRVM_G_VECTOR(OFS_RETURN));
        if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
@@ -6641,9 +7164,10 @@ void VM_getsurfacenormal(prvm_prog_t *prog)
        // note: this only returns the first triangle, so it doesn't work very
        // well for curved surfaces or arbitrary meshes
        animatemodel(prog, model, PRVM_G_EDICT(OFS_PARM0));
-       TriangleNormal((animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex), (animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex) + 3, (animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex) + 6, normal);
-       applytransform_forward_normal(prog, normal, PRVM_G_EDICT(OFS_PARM0), PRVM_G_VECTOR(OFS_RETURN));
-       VectorNormalize(PRVM_G_VECTOR(OFS_RETURN));
+       TriangleNormal((prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex), (prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex) + 3, (prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex) + 6, normal);
+       applytransform_forward_normal(prog, normal, PRVM_G_EDICT(OFS_PARM0), result);
+       VectorNormalize(result);
+       VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
 }
 //PF_getsurfacetexture,   // #437 string(entity e, float s) getsurfacetexture = #437;
 void VM_getsurfacetexture(prvm_prog_t *prog)
@@ -6665,11 +7189,11 @@ void VM_getsurfacenearpoint(prvm_prog_t *prog)
        prvm_edict_t *ed;
        dp_model_t *model;
        msurface_t *surface;
-       vec_t *point;
+       vec3_t point;
        VM_SAFEPARMCOUNT(2, VM_getsurfacenearpoint);
        PRVM_G_FLOAT(OFS_RETURN) = -1;
        ed = PRVM_G_EDICT(OFS_PARM0);
-       point = PRVM_G_VECTOR(OFS_PARM1);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM1), point);
 
        if (!ed || ed->priv.server->free)
                return;
@@ -6712,14 +7236,15 @@ void VM_getsurfaceclippedpoint(prvm_prog_t *prog)
        prvm_edict_t *ed;
        dp_model_t *model;
        msurface_t *surface;
-       vec3_t p, out;
+       vec3_t p, out, inp;
        VM_SAFEPARMCOUNT(3, VM_te_getsurfaceclippedpoint);
        VectorClear(PRVM_G_VECTOR(OFS_RETURN));
        ed = PRVM_G_EDICT(OFS_PARM0);
        if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
                return;
        animatemodel(prog, model, ed);
-       applytransform_inverted(prog, PRVM_G_VECTOR(OFS_PARM2), ed, p);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), inp);
+       applytransform_inverted(prog, inp, ed, p);
        clippointtosurface(prog, ed, model, surface, p, out);
        VectorAdd(out, PRVM_serveredictvector(ed, origin), PRVM_G_VECTOR(OFS_RETURN));
 }
@@ -6826,9 +7351,9 @@ void VM_physics_addforce(prvm_prog_t *prog)
                VM_Warning(prog, "VM_physics_addforce: entity is not MOVETYPE_PHYSICS!\n");
                return;
        }
-       f.type = ODEFUNC_RELFORCEATPOS;
+       f.type = ODEFUNC_FORCE;
        VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
-       VectorSubtract(PRVM_serveredictvector(ed, origin), PRVM_G_VECTOR(OFS_PARM2), f.v2);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), f.v2);
        VM_physics_ApplyCmd(ed, &f);
 }
 
@@ -6852,7 +7377,15 @@ void VM_physics_addtorque(prvm_prog_t *prog)
                VM_Warning(prog, "VM_physics_addtorque: entity is not MOVETYPE_PHYSICS!\n");
                return;
        }
-       f.type = ODEFUNC_RELTORQUE;
+       f.type = ODEFUNC_TORQUE;
        VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
        VM_physics_ApplyCmd(ed, &f);
 }
+
+extern cvar_t prvm_coverage;
+void VM_coverage(prvm_prog_t *prog)
+{
+       VM_SAFEPARMCOUNT(0, VM_coverage);
+       if (prog->explicit_profile[prog->xstatement]++ == 0 && (prvm_coverage.integer & 2))
+               PRVM_ExplicitCoverageEvent(prog, prog->xfunction, prog->xstatement);
+}