]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - pr_cmds.c
implemented scissor rect clipping of lights in realtime lighting mode
[xonotic/darkplaces.git] / pr_cmds.c
index ac18f05b8539a2aa94798f501f7c6c5f619b7397..7cf39ca5786d8f7aef93eca4d8bcb4989f292223 100644 (file)
--- a/pr_cmds.c
+++ b/pr_cmds.c
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -20,6 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
+cvar_t sv_aim = {CVAR_SAVE, "sv_aim", "2"}; //"0.93"}; // LordHavoc: disabled autoaim by default
+
 #define        RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
 
 
@@ -31,75 +33,86 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ===============================================================================
 */
 
+
 char *PF_VarString (int        first)
 {
        int             i;
-       static char out[256];
-       
+       static char out[4096]; // FIXME: buffer overflow potential
+
        out[0] = 0;
-       for (i=first ; i<pr_argc ; i++)
-       {
+       for (i = first;i < pr_argc;i++)
                strcat (out, G_STRING((OFS_PARM0+i*3)));
-       }
        return out;
 }
 
-char *QSG_EXTENSIONS = "\
-DP_BLOOD \
-DP_BLOODSHOWER \
-DP_CORPSE \
-DP_DRAWONLYTOCLIENT \
-DP_EXPLOSION3 \
-DP_PARTICLECUBE \
-DP_PARTICLERAIN \
-DP_PARTICLESNOW \
-DP_GETLIGHT \
-DP_NODRAWTOCLIENT \
-DP_RANDOMVEC \
-DP_REGISTERCVAR \
-DP_SPARK \
-DP_SPRITE32 \
-DP_MODEL32 \
-DP_TRACEBOX \
-DP_MINMAXBOUND \
-DP_FINDFLOAT \
-NEH_PLAY2 \
-QSG_ALPHA \
-QSG_BUTTONS \
-QSG_CHANGEPITCH \
-QSG_COLORMOD \
-QSG_DELTA \
-QSG_ETOS \
-QSG_FOG \
-QSG_FOLLOW \
-QSG_GLOW \
-QSG_MATH \
-QSG_MONSTERWALK \
-QSG_QUAKE2MODEL \
-QSG_SCALE \
-QSG_SKYBOX \
-QSG_TRACETOSS \
-QSG_VIEWMODEL \
-QSG_COPYENT \
-";
+char *ENGINE_EXTENSIONS =
+"DP_ENT_ALPHA "
+"DP_ENT_CUSTOMCOLORMAP "
+"DP_ENT_EXTERIORMODELTOCLIENT "
+"DP_ENT_LOWPRECISION "
+"DP_ENT_GLOW "
+"DP_ENT_SCALE "
+"DP_ENT_VIEWMODEL "
+"DP_GFX_FOG "
+"DP_HALFLIFE_MAP "
+"DP_INPUTBUTTONS "
+"DP_MONSTERWALK "
+"DP_MOVETYPEFOLLOW "
+"DP_QC_CHANGEPITCH "
+"DP_QC_COPYENTITY "
+"DP_QC_ETOS "
+"DP_QC_FINDCHAIN "
+"DP_QC_FINDCHAINFLOAT "
+"DP_QC_FINDFLOAT "
+"DP_QC_GETLIGHT "
+"DP_QC_MINMAXBOUND "
+"DP_QC_RANDOMVEC "
+"DP_QC_SINCOSSQRTPOW "
+"DP_QC_TRACEBOX "
+"DP_QC_TRACETOSS "
+"DP_QC_VECTORVECTORS "
+"DP_QUAKE2_MODEL "
+"DP_REGISTERCVAR "
+"DP_SOLIDCORPSE "
+"DP_SPRITE32 "
+"DP_SV_DRAWONLYTOCLIENT "
+"DP_SV_EFFECT "
+"DP_SV_EXTERIORMODELTOCLIENT "
+"DP_SV_NODRAWTOCLIENT "
+"DP_SV_PLAYERPHYSICS "
+"DP_SV_SETCOLOR "
+"DP_SV_SLOWMO "
+"DP_TE_BLOOD "
+"DP_TE_BLOODSHOWER "
+"DP_TE_EXPLOSIONRGB "
+"DP_TE_PARTICLECUBE "
+"DP_TE_PARTICLERAIN "
+"DP_TE_PARTICLESNOW "
+"DP_TE_SPARK "
+"NEH_CMD_PLAY2 "
+"NEH_RESTOREGAME "
+"TW_SV_STEPCONTROL "
+;
 
 qboolean checkextension(char *name)
 {
        int len;
-       char *e;
+       char *e, *start;
        len = strlen(name);
-       for (e = QSG_EXTENSIONS;*e;e++)
+       for (e = ENGINE_EXTENSIONS;*e;e++)
        {
                while (*e == ' ')
                        e++;
                if (!*e)
                        break;
-               if (!strncasecmp(e, name, len))
-                       return TRUE;
+               start = e;
                while (*e && *e != ' ')
                        e++;
+               if (e - start == len)
+                       if (!strncasecmp(start, name, len))
+                               return true;
        }
-       return FALSE;
+       return false;
 }
 
 /*
@@ -130,10 +143,9 @@ void PF_error (void)
 {
        char    *s;
        edict_t *ed;
-       
+
        s = PF_VarString(0);
-       Con_Printf ("======SERVER ERROR in %s:\n%s\n"
-       ,pr_strings + pr_xfunction->s_name,s);
+       Con_Printf ("======SERVER ERROR in %s:\n%s\n", pr_strings + pr_xfunction->s_name, s);
        ed = PROG_TO_EDICT(pr_global_struct->self);
        ED_Print (ed);
 
@@ -154,19 +166,15 @@ void PF_objerror (void)
 {
        char    *s;
        edict_t *ed;
-       
+
        s = PF_VarString(0);
-       Con_Printf ("======OBJECT ERROR in %s:\n%s\n"
-       ,pr_strings + pr_xfunction->s_name,s);
+       Con_Printf ("======OBJECT ERROR in %s:\n%s\n", pr_strings + pr_xfunction->s_name, s);
        ed = PROG_TO_EDICT(pr_global_struct->self);
        ED_Print (ed);
        ED_Free (ed);
-       
-       Host_Error ("Program error");
 }
 
 
-
 /*
 ==============
 PF_makevectors
@@ -180,6 +188,20 @@ void PF_makevectors (void)
        AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
 }
 
+/*
+==============
+PF_vectorvectors
+
+Writes new values for v_forward, v_up, and v_right based on the given forward vector
+vectorvectors(vector, vector)
+==============
+*/
+void PF_vectorvectors (void)
+{
+       VectorNormalize2(G_VECTOR(OFS_PARM0), pr_global_struct->v_forward);
+       VectorVectors(pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
+}
+
 /*
 =================
 PF_setorigin
@@ -203,75 +225,17 @@ void PF_setorigin (void)
 
 void SetMinMaxSize (edict_t *e, float *min, float *max, qboolean rotate)
 {
-       float   *angles;
-       vec3_t  rmin, rmax;
-       float   bounds[2][3];
-       float   xvector[2], yvector[2];
-       float   a;
-       vec3_t  base, transformed;
-       int             i, j, k, l;
+       int             i;
        
        for (i=0 ; i<3 ; i++)
                if (min[i] > max[i])
-                       PR_RunError ("backwards mins/maxs");
-
-       rotate = false;         // FIXME: implement rotation properly again
+                       Host_Error ("backwards mins/maxs");
 
-       if (!rotate)
-       {
-               VectorCopy (min, rmin);
-               VectorCopy (max, rmax);
-       }
-       else
-       {
-       // find min / max for rotations
-               angles = e->v.angles;
-               
-               a = angles[1]/180 * M_PI;
-               
-               xvector[0] = cos(a);
-               xvector[1] = sin(a);
-               yvector[0] = -sin(a);
-               yvector[1] = cos(a);
-               
-               VectorCopy (min, bounds[0]);
-               VectorCopy (max, bounds[1]);
-               
-               rmin[0] = rmin[1] = rmin[2] = 9999;
-               rmax[0] = rmax[1] = rmax[2] = -9999;
-               
-               for (i=0 ; i<= 1 ; i++)
-               {
-                       base[0] = bounds[i][0];
-                       for (j=0 ; j<= 1 ; j++)
-                       {
-                               base[1] = bounds[j][1];
-                               for (k=0 ; k<= 1 ; k++)
-                               {
-                                       base[2] = bounds[k][2];
-                                       
-                               // transform the point
-                                       transformed[0] = xvector[0]*base[0] + yvector[0]*base[1];
-                                       transformed[1] = xvector[1]*base[0] + yvector[1]*base[1];
-                                       transformed[2] = base[2];
-                                       
-                                       for (l=0 ; l<3 ; l++)
-                                       {
-                                               if (transformed[l] < rmin[l])
-                                                       rmin[l] = transformed[l];
-                                               if (transformed[l] > rmax[l])
-                                                       rmax[l] = transformed[l];
-                                       }
-                               }
-                       }
-               }
-       }
-       
 // set derived values
-       VectorCopy (rmin, e->v.mins);
-       VectorCopy (rmax, e->v.maxs);
+       VectorCopy (min, e->v.mins);
+       VectorCopy (max, e->v.maxs);
        VectorSubtract (max, min, e->v.size);
-       
+
        SV_LinkEdict (e, false);
 }
 
@@ -280,6 +244,7 @@ void SetMinMaxSize (edict_t *e, float *min, float *max, qboolean rotate)
 PF_setsize
 
 the size box is rotated by the current angle
+LordHavoc: no it isn't...
 
 setsize (entity, minvector, maxvector)
 =================
@@ -317,31 +282,18 @@ void PF_setmodel (void)
        for (i=0, check = sv.model_precache ; *check ; i++, check++)
                if (!strcmp(*check, m))
                        break;
-                       
+
        if (!*check)
-               PR_RunError ("no precache: %s\n", m);
-               
+               Host_Error ("no precache: %s\n", m);
+
 
        e->v.model = m - pr_strings;
-       e->v.modelindex = i; //SV_ModelIndex (m);
+       e->v.modelindex = i;
+
+       mod = sv.models[ (int)e->v.modelindex];
 
-       mod = sv.models[ (int)e->v.modelindex];  // Mod_ForName (m, true);
-       
        if (mod)
-       /*
-       { // LordHavoc: corrected model bounding box, but for compatibility that means I have to break it here
-               vec3_t min, max;
-               if (mod->type == ALIASTYPE_MDL)
-               {
-                       min[0] = min[1] = min[2] = -16;
-                       max[0] = max[1] = max[2] = 16;
-                       SetMinMaxSize (e, min, max, true);
-               }
-               else
-                       SetMinMaxSize (e, mod->mins, mod->maxs, true);
-       }
-       */
-               SetMinMaxSize (e, mod->mins, mod->maxs, true);
+               SetMinMaxSize (e, mod->normalmins, mod->normalmaxs, true);
        else
                SetMinMaxSize (e, vec3_origin, vec3_origin, true);
 }
@@ -467,7 +419,7 @@ void PF_vlen (void)
 {
        float   *value1;
        float   new;
-       
+
        value1 = G_VECTOR(OFS_PARM0);
 
        new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
@@ -528,11 +480,19 @@ void PF_vectoangles (void)
        }
        else
        {
-               yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
-               if (yaw < 0)
-                       yaw += 360;
+               // LordHavoc: optimized a bit
+               if (value1[0])
+               {
+                       yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
+                       if (yaw < 0)
+                               yaw += 360;
+               }
+               else if (value1[1] > 0)
+                       yaw = 90;
+               else
+                       yaw = 270;
 
-               forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
+               forward = sqrt(value1[0]*value1[0] + value1[1]*value1[1]);
                pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
                if (pitch < 0)
                        pitch += 360;
@@ -594,7 +554,7 @@ void PF_ambientsound (void)
        char            *samp;
        float           *pos;
        float           vol, attenuation;
-       int                     i, soundnum;
+       int                     i, soundnum, large;
 
        pos = G_VECTOR (OFS_PARM0);                     
        samp = G_STRING(OFS_PARM1);
@@ -605,20 +565,31 @@ void PF_ambientsound (void)
        for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++)
                if (!strcmp(*check,samp))
                        break;
-                       
+
        if (!*check)
        {
                Con_Printf ("no precache: %s\n", samp);
                return;
        }
 
-// add an svc_spawnambient command to the level signon packet
+       large = false;
+       if (soundnum >= 256)
+               large = true;
+
+       // add an svc_spawnambient command to the level signon packet
+
+       if (large)
+               MSG_WriteByte (&sv.signon, svc_spawnstaticsound2);
+       else
+               MSG_WriteByte (&sv.signon, svc_spawnstaticsound);
 
-       MSG_WriteByte (&sv.signon,svc_spawnstaticsound);
        for (i=0 ; i<3 ; i++)
-               MSG_WriteFloatCoord(&sv.signon, pos[i]);
+               MSG_WriteDPCoord(&sv.signon, pos[i]);
 
-       MSG_WriteByte (&sv.signon, soundnum);
+       if (large)
+               MSG_WriteShort (&sv.signon, soundnum);
+       else
+               MSG_WriteByte (&sv.signon, soundnum);
 
        MSG_WriteByte (&sv.signon, vol*255);
        MSG_WriteByte (&sv.signon, attenuation*64);
@@ -633,7 +604,7 @@ Each entity can have eight independant sound sources, like voice,
 weapon, feet, etc.
 
 Channel 0 is an auto-allocate channel, the others override anything
-allready running on that entity/channel pair.
+already running on that entity/channel pair.
 
 An attenuation of 0 will play full volume everywhere in the level.
 Larger attenuations will drop off.
@@ -675,9 +646,7 @@ break()
 */
 void PF_break (void)
 {
-Con_Printf ("break statement\n");
-*(int *)-4 = 0;        // dump to debugger
-//     PR_RunError ("break statement");
+       Host_Error ("break statement");
 }
 
 /*
@@ -703,7 +672,7 @@ void PF_traceline (void)
        nomonsters = G_FLOAT(OFS_PARM2);
        ent = G_EDICT(OFS_PARM3);
 
-       trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent);
+       trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters ? MOVE_NOMONSTERS : MOVE_NORMAL, ent);
 
        pr_global_struct->trace_allsolid = trace.allsolid;
        pr_global_struct->trace_startsolid = trace.startsolid;
@@ -712,11 +681,12 @@ void PF_traceline (void)
        pr_global_struct->trace_inopen = trace.inopen;
        VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
        VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
-       pr_global_struct->trace_plane_dist =  trace.plane.dist; 
+       pr_global_struct->trace_plane_dist =  trace.plane.dist;
        if (trace.ent)
                pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
        else
                pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+       // FIXME: add trace_endcontents
 }
 
 
@@ -746,7 +716,7 @@ void PF_tracebox (void)
        nomonsters = G_FLOAT(OFS_PARM4);
        ent = G_EDICT(OFS_PARM5);
 
-       trace = SV_Move (v1, m1, m2, v2, nomonsters, ent);
+       trace = SV_Move (v1, m1, m2, v2, nomonsters ? MOVE_NOMONSTERS : MOVE_NORMAL, ent);
 
        pr_global_struct->trace_allsolid = trace.allsolid;
        pr_global_struct->trace_startsolid = trace.startsolid;
@@ -805,12 +775,12 @@ void PF_checkpos (void)
 
 //============================================================================
 
-byte   checkpvs[MAX_MAP_LEAFS/8];
+qbyte checkpvs[MAX_MAP_LEAFS/8];
 
 int PF_newcheckclient (int check)
 {
        int             i;
-       byte    *pvs;
+       qbyte   *pvs;
        edict_t *ent;
        mleaf_t *leaf;
        vec3_t  org;
@@ -864,7 +834,7 @@ PF_checkclient
 Returns a client (or object that has a client enemy) that would be a
 valid target.
 
-If there are more than one valid options, they are cycled each frame
+If there is more than one valid option, they are cycled each frame
 
 If (self.origin + self.viewofs) is not in the PVS of the current target,
 it is not returned at all.
@@ -872,7 +842,6 @@ it is not returned at all.
 name checkclient ()
 =================
 */
-#define        MAX_CHECK       16
 int c_invis, c_notvis;
 void PF_checkclient (void)
 {
@@ -880,15 +849,15 @@ void PF_checkclient (void)
        mleaf_t *leaf;
        int             l;
        vec3_t  view;
-       
-// find a new check if on a new frame
+
+       // find a new check if on a new frame
        if (sv.time - sv.lastchecktime >= 0.1)
        {
                sv.lastcheck = PF_newcheckclient (sv.lastcheck);
                sv.lastchecktime = sv.time;
        }
 
-// return check if it might be visible 
+       // return check if it might be visible
        ent = EDICT_NUM(sv.lastcheck);
        if (ent->free || ent->v.health <= 0)
        {
@@ -896,20 +865,23 @@ void PF_checkclient (void)
                return;
        }
 
-// if current entity can't possibly see the check entity, return 0
+       // if current entity can't possibly see the check entity, return 0
        self = PROG_TO_EDICT(pr_global_struct->self);
        VectorAdd (self->v.origin, self->v.view_ofs, view);
        leaf = Mod_PointInLeaf (view, sv.worldmodel);
-       l = (leaf - sv.worldmodel->leafs) - 1;
-       if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
+       if (leaf)
        {
-c_notvis++;
-               RETURN_EDICT(sv.edicts);
-               return;
+               l = (leaf - sv.worldmodel->leafs) - 1;
+               if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
+               {
+                       c_notvis++;
+                       RETURN_EDICT(sv.edicts);
+                       return;
+               }
        }
 
-// might be able to see it
-c_invis++;
+       // might be able to see it
+       c_invis++;
        RETURN_EDICT(ent);
 }
 
@@ -933,9 +905,9 @@ void PF_stuffcmd (void)
        
        entnum = G_EDICTNUM(OFS_PARM0);
        if (entnum < 1 || entnum > svs.maxclients)
-               PR_RunError ("Parm 0 not a client");
+               Host_Error ("Parm 0 not a client");
        str = G_STRING(OFS_PARM1);      
-       
+
        old = host_client;
        host_client = &svs.clients[entnum-1];
        Host_ClientCommands ("%s", str);
@@ -954,7 +926,7 @@ localcmd (string)
 void PF_localcmd (void)
 {
        char    *str;
-       
+
        str = G_STRING(OFS_PARM0);      
        Cbuf_AddText (str);
 }
@@ -971,7 +943,7 @@ void PF_cvar (void)
        char    *str;
        
        str = G_STRING(OFS_PARM0);
-       
+
        G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str);
 }
 
@@ -1003,16 +975,18 @@ findradius (origin, radius)
 */
 void PF_findradius (void)
 {
-       edict_t *ent, *chain;
-       float   rad;
-       float   *org;
-       vec3_t  eorg;
-       int             i, j;
+       edict_t *ent, *chain;
+       float radius;
+       float radius2;
+       float *org;
+       float eorg[3];
+       int i;
 
        chain = (edict_t *)sv.edicts;
        
        org = G_VECTOR(OFS_PARM0);
-       rad = G_FLOAT(OFS_PARM1);
+       radius = G_FLOAT(OFS_PARM1);
+       radius2 = radius * radius;
 
        ent = NEXT_EDICT(sv.edicts);
        for (i=1 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
@@ -1021,9 +995,13 @@ void PF_findradius (void)
                        continue;
                if (ent->v.solid == SOLID_NOT)
                        continue;
-               for (j=0 ; j<3 ; j++)
-                       eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5);                  
-               if (Length(eorg) > rad)
+
+               // LordHavoc: compare against bounding box rather than center,
+               // and use DotProduct instead of Length, major speedup
+               eorg[0] = (org[0] - ent->v.origin[0]) - bound(ent->v.mins[0], (org[0] - ent->v.origin[0]), ent->v.maxs[0]);
+               eorg[1] = (org[1] - ent->v.origin[1]) - bound(ent->v.mins[1], (org[1] - ent->v.origin[1]), ent->v.maxs[1]);
+               eorg[2] = (org[2] - ent->v.origin[2]) - bound(ent->v.mins[2], (org[2] - ent->v.origin[2]), ent->v.maxs[2]);
+               if (DotProduct(eorg, eorg) > radius2)
                        continue;
                        
                ent->v.chain = EDICT_TO_PROG(chain);
@@ -1044,22 +1022,30 @@ void PF_dprint (void)
        Con_DPrintf ("%s",PF_VarString(0));
 }
 
-char   pr_string_temp[128];
+// LordHavoc: added this to semi-fix the problem of using many ftos calls in a print
+#define STRINGTEMP_BUFFERS 16
+#define STRINGTEMP_LENGTH 128
+static char pr_string_temp[STRINGTEMP_BUFFERS][STRINGTEMP_LENGTH];
+static int pr_string_tempindex = 0;
+
+static char *PR_GetTempString(void)
+{
+       char *s;
+       s = pr_string_temp[pr_string_tempindex];
+       pr_string_tempindex = (pr_string_tempindex + 1) % STRINGTEMP_BUFFERS;
+       return s;
+}
 
 void PF_ftos (void)
 {
-       float   v;
+       float v;
+       char *s;
        v = G_FLOAT(OFS_PARM0);
 
+       s = PR_GetTempString();
        // LordHavoc: ftos improvement
-       sprintf (pr_string_temp, "%g", v);
-       /*
-       if (v == (int)v)
-               sprintf (pr_string_temp, "%d",(int)v);
-       else
-               sprintf (pr_string_temp, "%5.1f",v);
-       */
-       G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+       sprintf (s, "%g", v);
+       G_INT(OFS_RETURN) = s - pr_strings;
 }
 
 void PF_fabs (void)
@@ -1071,14 +1057,18 @@ void PF_fabs (void)
 
 void PF_vtos (void)
 {
-       sprintf (pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
-       G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+       char *s;
+       s = PR_GetTempString();
+       sprintf (s, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
+       G_INT(OFS_RETURN) = s - pr_strings;
 }
 
 void PF_etos (void)
 {
-       sprintf (pr_string_temp, "entity %i", G_EDICTNUM(OFS_PARM0));
-       G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+       char *s;
+       s = PR_GetTempString();
+       sprintf (s, "entity %i", G_EDICTNUM(OFS_PARM0));
+       G_INT(OFS_RETURN) = s - pr_strings;
 }
 
 void PF_Spawn (void)
@@ -1091,8 +1081,12 @@ void PF_Spawn (void)
 void PF_Remove (void)
 {
        edict_t *ed;
-       
+
        ed = G_EDICT(OFS_PARM0);
+       if (ed == sv.edicts)
+               Host_Error("remove: tried to remove world\n");
+       if (NUM_FOR_EDICT(ed) <= svs.maxclients)
+               Host_Error("remove: tried to remove a client\n");
        ED_Free (ed);
 }
 
@@ -1100,7 +1094,7 @@ void PF_Remove (void)
 // entity (entity start, .string field, string match) find = #5;
 void PF_Find (void)
 {
-       int             e;      
+       int             e;
        int             f;
        char    *s, *t;
        edict_t *ed;
@@ -1108,9 +1102,12 @@ void PF_Find (void)
        e = G_EDICTNUM(OFS_PARM0);
        f = G_INT(OFS_PARM1);
        s = G_STRING(OFS_PARM2);
-       if (!s)
-               PR_RunError ("PF_Find: bad search string");
-               
+       if (!s || !s[0])
+       {
+               RETURN_EDICT(sv.edicts);
+               return;
+       }
+
        for (e++ ; e < sv.num_edicts ; e++)
        {
                ed = EDICT_NUM(e);
@@ -1156,10 +1153,76 @@ void PF_FindFloat (void)
        RETURN_EDICT(sv.edicts);
 }
 
+// chained search for strings in entity fields
+// entity(.string field, string match) findchain = #402;
+void PF_findchain (void)
+{
+       int             i;      
+       int             f;
+       char    *s, *t;
+       edict_t *ent, *chain;
+
+       chain = (edict_t *)sv.edicts;
+
+       f = G_INT(OFS_PARM0);
+       s = G_STRING(OFS_PARM1);
+       if (!s || !s[0])
+       {
+               RETURN_EDICT(sv.edicts);
+               return;
+       }
+               
+       ent = NEXT_EDICT(sv.edicts);
+       for (i = 1;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent))
+       {
+               if (ent->free)
+                       continue;
+               t = E_STRING(ent,f);
+               if (!t)
+                       continue;
+               if (strcmp(t,s))
+                       continue;
+
+               ent->v.chain = EDICT_TO_PROG(chain);
+               chain = ent;
+       }
+
+       RETURN_EDICT(chain);
+}
+
+// LordHavoc: chained search for float, int, and entity reference fields
+// entity(.string field, float match) findchainfloat = #403;
+void PF_findchainfloat (void)
+{
+       int             i;      
+       int             f;
+       float   s;
+       edict_t *ent, *chain;
+
+       chain = (edict_t *)sv.edicts;
+
+       f = G_INT(OFS_PARM0);
+       s = G_FLOAT(OFS_PARM1);
+               
+       ent = NEXT_EDICT(sv.edicts);
+       for (i = 1;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent))
+       {
+               if (ent->free)
+                       continue;
+               if (E_FLOAT(ent,f) != s)
+                       continue;
+
+               ent->v.chain = EDICT_TO_PROG(chain);
+               chain = ent;
+       }
+
+       RETURN_EDICT(chain);
+}
+
 void PR_CheckEmptyString (char *s)
 {
        if (s[0] <= ' ')
-               PR_RunError ("Bad string");
+               Host_Error ("Bad string");
 }
 
 void PF_precache_file (void)
@@ -1171,10 +1234,10 @@ void PF_precache_sound (void)
 {
        char    *s;
        int             i;
-       
+
        if (sv.state != ss_loading)
-               PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
-               
+               Host_Error ("PF_Precache_*: Precache can only be done in spawn functions");
+
        s = G_STRING(OFS_PARM0);
        G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
        PR_CheckEmptyString (s);
@@ -1189,19 +1252,20 @@ void PF_precache_sound (void)
                if (!strcmp(sv.sound_precache[i], s))
                        return;
        }
-       PR_RunError ("PF_precache_sound: overflow");
+       Host_Error ("PF_precache_sound: overflow");
 }
 
-int blahblah = 0;
 void PF_precache_model (void)
 {
        char    *s;
        int             i;
        
        if (sv.state != ss_loading)
-               PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
-               
+               Host_Error ("PF_Precache_*: Precache can only be done in spawn functions");
+
        s = G_STRING(OFS_PARM0);
+       if (sv.worldmodel->ishlbsp && ((!s) || (!s[0])))
+               return;
        G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
        PR_CheckEmptyString (s);
 
@@ -1210,17 +1274,13 @@ void PF_precache_model (void)
                if (!sv.model_precache[i])
                {
                        sv.model_precache[i] = s;
-                       if (sv.active < 0)
-                               blahblah++;
-                       sv.models[i] = Mod_ForName (s, true);
-                       if (sv.active < 0)
-                               blahblah++;
+                       sv.models[i] = Mod_ForName (s, true, false, false);
                        return;
                }
                if (!strcmp(sv.model_precache[i], s))
                        return;
        }
-       PR_RunError ("PF_precache_model: overflow");
+       Host_Error ("PF_precache_model: overflow");
 }
 
 
@@ -1299,13 +1359,13 @@ void PF_droptofloor (void)
        edict_t         *ent;
        vec3_t          end;
        trace_t         trace;
-       
+
        ent = PROG_TO_EDICT(pr_global_struct->self);
 
        VectorCopy (ent->v.origin, end);
        end[2] -= 256;
-       
-       trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
+
+       trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);
 
        if (trace.fraction == 1 || trace.allsolid)
                G_FLOAT(OFS_RETURN) = 0;
@@ -1316,6 +1376,8 @@ void PF_droptofloor (void)
                ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
                ent->v.groundentity = EDICT_TO_PROG(trace.ent);
                G_FLOAT(OFS_RETURN) = 1;
+               // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+               ent->suspendedinairflag = true;
        }
 }
 
@@ -1332,17 +1394,17 @@ void PF_lightstyle (void)
        char    *val;
        client_t        *client;
        int                     j;
-       
+
        style = G_FLOAT(OFS_PARM0);
        val = G_STRING(OFS_PARM1);
 
 // change the string in sv
        sv.lightstyles[style] = val;
-       
+
 // send message to all clients on this server
        if (sv.state != ss_active)
                return;
-       
+
        for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
                if (client->active || client->spawned)
                {
@@ -1388,7 +1450,7 @@ PF_pointcontents
 */
 void PF_pointcontents (void)
 {
-       G_FLOAT(OFS_RETURN) = SV_PointContents (G_VECTOR(OFS_PARM0));
+       G_FLOAT(OFS_RETURN) = Mod_PointContents(G_VECTOR(OFS_PARM0), sv.worldmodel);
 }
 
 /*
@@ -1402,7 +1464,7 @@ void PF_nextent (void)
 {
        int             i;
        edict_t *ent;
-       
+
        i = G_EDICTNUM(OFS_PARM0);
        while (1)
        {
@@ -1429,7 +1491,6 @@ Pick a vector for the player to shoot along
 vector aim(entity, missilespeed)
 =============
 */
-cvar_t sv_aim = {"sv_aim", "0.93"};
 void PF_aim (void)
 {
        edict_t *ent, *check, *bestent;
@@ -1438,7 +1499,7 @@ void PF_aim (void)
        trace_t tr;
        float   dist, bestdist;
        float   speed;
-       
+
        ent = G_EDICT(OFS_PARM0);
        speed = G_FLOAT(OFS_PARM1);
 
@@ -1448,9 +1509,9 @@ void PF_aim (void)
 // try sending a trace straight
        VectorCopy (pr_global_struct->v_forward, dir);
        VectorMA (start, 2048, dir, end);
-       tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
-       if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM
-       && (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) )
+       tr = SV_Move (start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent);
+       if (tr.ent && ((edict_t *)tr.ent)->v.takedamage == DAMAGE_AIM
+       && (!teamplay.integer || ent->v.team <=0 || ent->v.team != ((edict_t *)tr.ent)->v.team) )
        {
                VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN));
                return;
@@ -1461,7 +1522,7 @@ void PF_aim (void)
        VectorCopy (dir, bestdir);
        bestdist = sv_aim.value;
        bestent = NULL;
-       
+
        check = NEXT_EDICT(sv.edicts);
        for (i=1 ; i<sv.num_edicts ; i++, check = NEXT_EDICT(check) )
        {
@@ -1469,7 +1530,7 @@ void PF_aim (void)
                        continue;
                if (check == ent)
                        continue;
-               if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team)
+               if (teamplay.integer && ent->v.team > 0 && ent->v.team == check->v.team)
                        continue;       // don't aim at teammate
                for (j=0 ; j<3 ; j++)
                        end[j] = check->v.origin[j]
@@ -1479,14 +1540,14 @@ void PF_aim (void)
                dist = DotProduct (dir, pr_global_struct->v_forward);
                if (dist < bestdist)
                        continue;       // to far to turn
-               tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
+               tr = SV_Move (start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent);
                if (tr.ent == check)
                {       // can shoot at this one
                        bestdist = dist;
                        bestent = check;
                }
        }
-       
+
        if (bestent)
        {
                VectorSubtract (bestent->v.origin, ent->v.origin, dir);
@@ -1494,7 +1555,7 @@ void PF_aim (void)
                VectorScale (pr_global_struct->v_forward, dist, end);
                end[2] = dir[2];
                VectorNormalize (end);
-               VectorCopy (end, G_VECTOR(OFS_RETURN)); 
+               VectorCopy (end, G_VECTOR(OFS_RETURN));
        }
        else
        {
@@ -1513,12 +1574,12 @@ void PF_changeyaw (void)
 {
        edict_t         *ent;
        float           ideal, current, move, speed;
-       
+
        ent = PROG_TO_EDICT(pr_global_struct->self);
-       current = anglemod( ent->v.angles[1] );
+       current = ANGLEMOD(ent->v.angles[1]);
        ideal = ent->v.ideal_yaw;
        speed = ent->v.yaw_speed;
-       
+
        if (current == ideal)
                return;
        move = ideal - current;
@@ -1542,8 +1603,8 @@ void PF_changeyaw (void)
                if (move < -speed)
                        move = -speed;
        }
-       
-       ent->v.angles[1] = anglemod (current + move);
+
+       ent->v.angles[1] = ANGLEMOD (current + move);
 }
 
 /*
@@ -1556,24 +1617,24 @@ void PF_changepitch (void)
        edict_t         *ent;
        float           ideal, current, move, speed;
        eval_t          *val;
-       
+
        ent = G_EDICT(OFS_PARM0);
-       current = anglemod( ent->v.angles[0] );
+       current = ANGLEMOD( ent->v.angles[0] );
        if ((val = GETEDICTFIELDVALUE(ent, eval_idealpitch)))
                ideal = val->_float;
        else
        {
-               PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
+               Host_Error ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
                return;
        }
        if ((val = GETEDICTFIELDVALUE(ent, eval_pitch_speed)))
                speed = val->_float;
        else
        {
-               PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
+               Host_Error ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
                return;
        }
-       
+
        if (current == ideal)
                return;
        move = ideal - current;
@@ -1597,8 +1658,8 @@ void PF_changepitch (void)
                if (move < -speed)
                        move = -speed;
        }
-       
-       ent->v.angles[0] = anglemod (current + move);
+
+       ent->v.angles[0] = ANGLEMOD (current + move);
 }
 
 /*
@@ -1625,25 +1686,25 @@ sizebuf_t *WriteDest (void)
        {
        case MSG_BROADCAST:
                return &sv.datagram;
-       
+
        case MSG_ONE:
                ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
                entnum = NUM_FOR_EDICT(ent);
                if (entnum < 1 || entnum > svs.maxclients)
-                       PR_RunError ("WriteDest: not a client");
+                       Host_Error ("WriteDest: not a client");
                return &svs.clients[entnum-1].message;
-               
+
        case MSG_ALL:
                return &sv.reliable_datagram;
-       
+
        case MSG_INIT:
                return &sv.signon;
 
        default:
-               PR_RunError ("WriteDest: bad destination");
+               Host_Error ("WriteDest: bad destination");
                break;
        }
-       
+
        return NULL;
 }
 
@@ -1674,7 +1735,7 @@ void PF_WriteAngle (void)
 
 void PF_WriteCoord (void)
 {
-       MSG_WriteFloatCoord (WriteDest(), G_FLOAT(OFS_PARM1));
+       MSG_WriteDPCoord (WriteDest(), G_FLOAT(OFS_PARM1));
 }
 
 void PF_WriteString (void)
@@ -1690,25 +1751,35 @@ void PF_WriteEntity (void)
 
 //=============================================================================
 
-int SV_ModelIndex (char *name);
-
 void PF_makestatic (void)
 {
-       edict_t *ent;
-       int             i;
-       
+       edict_t *ent;
+       int i, large;
+
        ent = G_EDICT(OFS_PARM0);
 
-       MSG_WriteByte (&sv.signon,svc_spawnstatic);
+       large = false;
+       if (ent->v.modelindex >= 256 || ent->v.frame >= 256)
+               large = true;
 
-       MSG_WriteByte (&sv.signon, SV_ModelIndex(pr_strings + ent->v.model));
+       if (large)
+       {
+               MSG_WriteByte (&sv.signon,svc_spawnstatic2);
+               MSG_WriteShort (&sv.signon, ent->v.modelindex);
+               MSG_WriteShort (&sv.signon, ent->v.frame);
+       }
+       else
+       {
+               MSG_WriteByte (&sv.signon,svc_spawnstatic);
+               MSG_WriteByte (&sv.signon, ent->v.modelindex);
+               MSG_WriteByte (&sv.signon, ent->v.frame);
+       }
 
-       MSG_WriteByte (&sv.signon, ent->v.frame);
        MSG_WriteByte (&sv.signon, ent->v.colormap);
        MSG_WriteByte (&sv.signon, ent->v.skin);
        for (i=0 ; i<3 ; i++)
        {
-               MSG_WriteFloatCoord(&sv.signon, ent->v.origin[i]);
+               MSG_WriteDPCoord(&sv.signon, ent->v.origin[i]);
                MSG_WriteAngle(&sv.signon, ent->v.angles[i]);
        }
 
@@ -1732,7 +1803,7 @@ void PF_setspawnparms (void)
        ent = G_EDICT(OFS_PARM0);
        i = NUM_FOR_EDICT(ent);
        if (i < 1 || i > svs.maxclients)
-               PR_RunError ("Entity is not a client");
+               Host_Error ("Entity is not a client");
 
        // copy spawn parms out of the client_t
        client = svs.clients + (i-1);
@@ -1754,7 +1825,7 @@ void PF_changelevel (void)
        if (svs.changelevel_issued)
                return;
        svs.changelevel_issued = true;
-       
+
        s = G_STRING(OFS_PARM0);
        Cbuf_AddText (va("changelevel %s\n",s));
 }
@@ -1793,7 +1864,7 @@ void PF_randomvec (void)
                temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
        }
        while (DotProduct(temp, temp) >= 1);
-       VectorCopy (temp, G_VECTOR(OFS_RETURN));        
+       VectorCopy (temp, G_VECTOR(OFS_RETURN));
 }
 
 void SV_LightPoint (vec3_t color, vec3_t p);
@@ -1815,7 +1886,7 @@ void PF_GetLight (void)
        vec_t*          p;
        p = G_VECTOR(OFS_PARM0);
        SV_LightPoint (color, p);
-       VectorCopy (color, G_VECTOR(OFS_RETURN));       
+       VectorCopy (color, G_VECTOR(OFS_RETURN));
 }
 
 #define MAX_QC_CVARS 128
@@ -1824,15 +1895,15 @@ int currentqc_cvar;
 
 void PF_registercvar (void)
 {
-       char    *name, *value;
-       cvar_t  *variable;
-       name = G_STRING(OFS_PARM1);
-       value = G_STRING(OFS_PARM2);
+       char *name, *value;
+       cvar_t *variable;
+       name = G_STRING(OFS_PARM0);
+       value = G_STRING(OFS_PARM1);
        G_FLOAT(OFS_RETURN) = 0;
-// first check to see if it has allready been defined
+// first check to see if it has already been defined
        if (Cvar_FindVar (name))
                return;
-       
+
 // check for overlap with a command
        if (Cmd_Exists (name))
        {
@@ -1841,19 +1912,17 @@ void PF_registercvar (void)
        }
 
        if (currentqc_cvar >= MAX_QC_CVARS)
-               PR_RunError ("PF_registercvar: ran out of cvar slots (%i)\n", MAX_QC_CVARS);
+               Host_Error ("PF_registercvar: ran out of cvar slots (%i)\n", MAX_QC_CVARS);
 
 // copy the name and value
        variable = &qc_cvar[currentqc_cvar++];
-       variable->name = Z_Malloc (strlen(name)+1);     
+       variable->name = Z_Malloc (strlen(name)+1);
        strcpy (variable->name, name);
-       variable->string = Z_Malloc (strlen(value)+1);  
+       variable->string = Z_Malloc (strlen(value)+1);
        strcpy (variable->string, value);
        variable->value = atof (value);
-       
-// link the variable in
-       variable->next = cvar_vars;
-       cvar_vars = variable;
+
+       Cvar_RegisterVariable(variable);
        G_FLOAT(OFS_RETURN) = 1; // success
 }
 
@@ -1881,7 +1950,7 @@ void PF_min (void)
                G_FLOAT(OFS_RETURN) = f;
        }
        else
-               PR_RunError("min: must supply at least 2 floats\n");
+               Host_Error("min: must supply at least 2 floats\n");
 }
 
 /*
@@ -1908,7 +1977,7 @@ void PF_max (void)
                G_FLOAT(OFS_RETURN) = f;
        }
        else
-               PR_RunError("max: must supply at least 2 floats\n");
+               Host_Error("max: must supply at least 2 floats\n");
 }
 
 /*
@@ -1969,28 +2038,597 @@ void PF_setcolor (void)
 {
        client_t        *client;
        int                     entnum, i;
-       
+
        entnum = G_EDICTNUM(OFS_PARM0);
        i = G_FLOAT(OFS_PARM1);
-       
+
        if (entnum < 1 || entnum > svs.maxclients)
        {
                Con_Printf ("tried to setcolor a non-client\n");
                return;
        }
-               
+
        client = &svs.clients[entnum-1];
        client->colors = i;
        client->edict->v.team = (i & 15) + 1;
-               
+
        MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
        MSG_WriteByte (&sv.reliable_datagram, entnum - 1);
        MSG_WriteByte (&sv.reliable_datagram, i);
 }
 
+/*
+=================
+PF_effect
+
+effect(origin, modelname, startframe, framecount, framerate)
+=================
+*/
+void PF_effect (void)
+{
+       char *s;
+       s = G_STRING(OFS_PARM1);
+       if (!s || !s[0])
+               Host_Error("effect: no model specified\n");
+
+       SV_StartEffect(G_VECTOR(OFS_PARM0), SV_ModelIndex(s), G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), G_FLOAT(OFS_PARM4));
+}
+
+void PF_te_blood (void)
+{
+       if (G_FLOAT(OFS_PARM2) < 1)
+               return;
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_BLOOD);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // velocity
+       MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[0], 127));
+       MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[1], 127));
+       MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[2], 127));
+       // count
+       MSG_WriteByte(&sv.datagram, bound(0, (int) G_FLOAT(OFS_PARM2), 255));
+}
+
+void PF_te_bloodshower (void)
+{
+       if (G_FLOAT(OFS_PARM3) < 1)
+               return;
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_BLOODSHOWER);
+       // min
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // max
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // speed
+       MSG_WriteDPCoord(&sv.datagram, G_FLOAT(OFS_PARM2));
+       // count
+       MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
+}
+
+void PF_te_explosionrgb (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_EXPLOSIONRGB);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // color
+       MSG_WriteByte(&sv.datagram, bound(0, (int) (G_VECTOR(OFS_PARM1)[0] * 255), 255));
+       MSG_WriteByte(&sv.datagram, bound(0, (int) (G_VECTOR(OFS_PARM1)[1] * 255), 255));
+       MSG_WriteByte(&sv.datagram, bound(0, (int) (G_VECTOR(OFS_PARM1)[2] * 255), 255));
+}
+
+void PF_te_particlecube (void)
+{
+       if (G_FLOAT(OFS_PARM3) < 1)
+               return;
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_PARTICLECUBE);
+       // min
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // max
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // velocity
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
+       // count
+       MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
+       // color
+       MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM4));
+       // gravity true/false
+       MSG_WriteByte(&sv.datagram, ((int) G_FLOAT(OFS_PARM5)) != 0);
+       // randomvel
+       MSG_WriteDPCoord(&sv.datagram, G_FLOAT(OFS_PARM6));
+}
+
+void PF_te_particlerain (void)
+{
+       if (G_FLOAT(OFS_PARM3) < 1)
+               return;
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_PARTICLERAIN);
+       // min
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // max
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // velocity
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
+       // count
+       MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
+       // color
+       MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM4));
+}
+
+void PF_te_particlesnow (void)
+{
+       if (G_FLOAT(OFS_PARM3) < 1)
+               return;
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_PARTICLESNOW);
+       // min
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // max
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // velocity
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
+       // count
+       MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
+       // color
+       MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM4));
+}
+
+void PF_te_spark (void)
+{
+       if (G_FLOAT(OFS_PARM2) < 1)
+               return;
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_SPARK);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // velocity
+       MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[0], 127));
+       MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[1], 127));
+       MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[2], 127));
+       // count
+       MSG_WriteByte(&sv.datagram, bound(0, (int) G_FLOAT(OFS_PARM2), 255));
+}
+
+void PF_te_gunshotquad (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_GUNSHOTQUAD);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_spikequad (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_SPIKEQUAD);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_superspikequad (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_SUPERSPIKEQUAD);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_explosionquad (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_EXPLOSIONQUAD);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_smallflash (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_SMALLFLASH);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_customflash (void)
+{
+       if (G_FLOAT(OFS_PARM1) < 8 || G_FLOAT(OFS_PARM2) < (1.0 / 256.0))
+               return;
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_CUSTOMFLASH);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // radius
+       MSG_WriteByte(&sv.datagram, bound(0, G_FLOAT(OFS_PARM1) / 8 - 1, 255));
+       // lifetime
+       MSG_WriteByte(&sv.datagram, bound(0, G_FLOAT(OFS_PARM2) / 256 - 1, 255));
+       // color
+       MSG_WriteByte(&sv.datagram, bound(0, G_VECTOR(OFS_PARM3)[0] * 255, 255));
+       MSG_WriteByte(&sv.datagram, bound(0, G_VECTOR(OFS_PARM3)[1] * 255, 255));
+       MSG_WriteByte(&sv.datagram, bound(0, G_VECTOR(OFS_PARM3)[2] * 255, 255));
+}
+
+void PF_te_gunshot (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_GUNSHOT);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_spike (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_SPIKE);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_superspike (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_SUPERSPIKE);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_explosion (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_EXPLOSION);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_tarexplosion (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_TAREXPLOSION);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_wizspike (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_WIZSPIKE);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_knightspike (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_KNIGHTSPIKE);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_lavasplash (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_LAVASPLASH);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_teleport (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_TELEPORT);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+void PF_te_explosion2 (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_EXPLOSION2);
+       // origin
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+       // color
+       MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM1));
+}
+
+void PF_te_lightning1 (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_LIGHTNING1);
+       // owner entity
+       MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
+       // start
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // end
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
+}
+
+void PF_te_lightning2 (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_LIGHTNING2);
+       // owner entity
+       MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
+       // start
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // end
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
+}
+
+void PF_te_lightning3 (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_LIGHTNING3);
+       // owner entity
+       MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
+       // start
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // end
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
+}
+
+void PF_te_beam (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_BEAM);
+       // owner entity
+       MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
+       // start
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
+       // end
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
+}
+
+void PF_te_plasmaburn (void)
+{
+       MSG_WriteByte(&sv.datagram, svc_temp_entity);
+       MSG_WriteByte(&sv.datagram, TE_PLASMABURN);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
+       MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
+}
+
+static void clippointtosurface(msurface_t *surf, vec3_t p, vec3_t out)
+{
+       int i, j;
+       vec3_t v1, clipplanenormal, normal;
+       vec_t clipplanedist, clipdist;
+       VectorCopy(p, out);
+       if (surf->flags & SURF_PLANEBACK)
+               VectorNegate(surf->plane->normal, normal);
+       else
+               VectorCopy(surf->plane->normal, normal);
+       for (i = 0, j = surf->poly_numverts - 1;i < surf->poly_numverts;j = i, i++)
+       {
+               VectorSubtract(&surf->poly_verts[j * 3], &surf->poly_verts[i * 3], v1);
+               VectorNormalizeFast(v1);
+               CrossProduct(v1, normal, clipplanenormal);
+               clipplanedist = DotProduct(&surf->poly_verts[i * 3], clipplanenormal);
+               clipdist = DotProduct(out, clipplanenormal) - clipplanedist;
+               if (clipdist > 0)
+               {
+                       clipdist = -clipdist;
+                       VectorMA(out, clipdist, clipplanenormal, out);
+               }
+       }
+}
+
+static msurface_t *getsurface(edict_t *ed, int surfnum)
+{
+       int modelindex;
+       model_t *model;
+       if (!ed || ed->free)
+               return NULL;
+       modelindex = ed->v.modelindex;
+       if (modelindex < 1 || modelindex >= MAX_MODELS)
+               return NULL;
+       model = sv.models[modelindex];
+       if (model->type != mod_brush)
+               return NULL;
+       if (surfnum < 0 || surfnum >= model->nummodelsurfaces)
+               return NULL;
+       return model->surfaces + surfnum + model->firstmodelsurface;
+}
+
+
+//PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
+void PF_getsurfacenumpoints(void)
+{
+       msurface_t *surf;
+       // return 0 if no such surface
+       if (!(surf = getsurface(G_EDICT(OFS_PARM0), G_FLOAT(OFS_PARM1))))
+       {
+               G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+
+       G_FLOAT(OFS_RETURN) = surf->poly_numverts;
+}
+//PF_getsurfacepoint,     // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
+void PF_getsurfacepoint(void)
+{
+       edict_t *ed;
+       msurface_t *surf;
+       int pointnum;
+       VectorClear(G_VECTOR(OFS_RETURN));
+       ed = G_EDICT(OFS_PARM0);
+       if (!ed || ed->free)
+               return;
+       if (!(surf = getsurface(ed, G_FLOAT(OFS_PARM1))))
+               return;
+       pointnum = G_FLOAT(OFS_PARM2);
+       if (pointnum < 0 || pointnum >= surf->poly_numverts)
+               return;
+       // FIXME: implement rotation/scaling
+       VectorAdd(&surf->poly_verts[pointnum * 3], ed->v.origin, G_VECTOR(OFS_RETURN));
+}
+//PF_getsurfacenormal,    // #436 vector(entity e, float s) getsurfacenormal = #436;
+void PF_getsurfacenormal(void)
+{
+       msurface_t *surf;
+       VectorClear(G_VECTOR(OFS_RETURN));
+       if (!(surf = getsurface(G_EDICT(OFS_PARM0), G_FLOAT(OFS_PARM1))))
+               return;
+       // FIXME: implement rotation/scaling
+       if (surf->flags & SURF_PLANEBACK)
+               VectorNegate(surf->plane->normal, G_VECTOR(OFS_RETURN));
+       else
+               VectorCopy(surf->plane->normal, G_VECTOR(OFS_RETURN));
+}
+//PF_getsurfacetexture,   // #437 string(entity e, float s) getsurfacetexture = #437;
+void PF_getsurfacetexture(void)
+{
+       msurface_t *surf;
+       G_INT(OFS_RETURN) = 0;
+       if (!(surf = getsurface(G_EDICT(OFS_PARM0), G_FLOAT(OFS_PARM1))))
+               return;
+       G_INT(OFS_RETURN) = surf->texinfo->texture->name - pr_strings;
+}
+//PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
+void PF_getsurfacenearpoint(void)
+{
+       int surfnum, best, modelindex;
+       vec3_t clipped, p;
+       vec_t dist, bestdist;
+       edict_t *ed;
+       model_t *model;
+       msurface_t *surf;
+       vec_t *point;
+       G_FLOAT(OFS_RETURN) = -1;
+       ed = G_EDICT(OFS_PARM0);
+       point = G_VECTOR(OFS_PARM1);
+
+       if (!ed || ed->free)
+               return;
+       modelindex = ed->v.modelindex;
+       if (modelindex < 1 || modelindex >= MAX_MODELS)
+               return;
+       model = sv.models[modelindex];
+       if (model->type != mod_brush)
+               return;
+
+       // FIXME: implement rotation/scaling
+       VectorSubtract(point, ed->v.origin, p);
+       best = -1;
+       bestdist = 1000000000;
+       for (surfnum = 0;surfnum < model->nummodelsurfaces;surfnum++)
+       {
+               surf = model->surfaces + surfnum + model->firstmodelsurface;
+               dist = PlaneDiff(p, surf->plane);
+               dist = dist * dist;
+               if (dist < bestdist)
+               {
+                       clippointtosurface(surf, p, clipped);
+                       VectorSubtract(clipped, p, clipped);
+                       dist += DotProduct(clipped, clipped);
+                       if (dist < bestdist)
+                       {
+                               best = surfnum;
+                               bestdist = dist;
+                       }
+               }
+       }
+       G_FLOAT(OFS_RETURN) = best;
+}
+//PF_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
+void PF_getsurfaceclippedpoint(void)
+{
+       edict_t *ed;
+       msurface_t *surf;
+       vec3_t p, out;
+       VectorClear(G_VECTOR(OFS_RETURN));
+       ed = G_EDICT(OFS_PARM0);
+       if (!ed || ed->free)
+               return;
+       if (!(surf = getsurface(ed, G_FLOAT(OFS_PARM1))))
+               return;
+       // FIXME: implement rotation/scaling
+       VectorSubtract(G_VECTOR(OFS_PARM2), ed->v.origin, p);
+       clippointtosurface(surf, p, out);
+       // FIXME: implement rotation/scaling
+       VectorAdd(out, ed->v.origin, G_VECTOR(OFS_RETURN));
+}
+
 void PF_Fixme (void)
 {
-       PR_RunError ("unimplemented builtin"); // LordHavoc: was misspelled (bulitin)
+       Host_Error ("unimplemented QC builtin"); // LordHavoc: was misspelled (bulitin)
 }
 
 
@@ -2085,35 +2723,73 @@ PF_precache_file,
 
 PF_setspawnparms,
 
-PF_Fixme,                      // #79 LordHavoc: dunno who owns 79-89, so these are just padding
-PF_Fixme,                      // #80 
-PF_Fixme,                      // #81
-PF_Fixme,                      // #82
-PF_Fixme,                      // #83
-PF_Fixme,                      // #84
-PF_Fixme,                      // #85
-PF_Fixme,                      // #86
-PF_Fixme,                      // #87
-PF_Fixme,                      // #88
-PF_Fixme,                      // #89
-
-PF_tracebox,           // #90 LordHavoc builtin range (9x)
-PF_randomvec,          // #91
-PF_GetLight,           // #92
-PF_registercvar,       // #93
-PF_min,                                // #94
-PF_max,                                // #95
-PF_bound,                      // #96
-PF_pow,                                // #97
-PF_FindFloat,          // #98
-PF_checkextension,     // #99
+PF_Fixme,                              // #79 LordHavoc: dunno who owns 79-89, so these are just padding
+PF_Fixme,                              // #80
+PF_Fixme,                              // #81
+PF_Fixme,                              // #82
+PF_Fixme,                              // #83
+PF_Fixme,                              // #84
+PF_Fixme,                              // #85
+PF_Fixme,                              // #86
+PF_Fixme,                              // #87
+PF_Fixme,                              // #88
+PF_Fixme,                              // #89
+
+PF_tracebox,                   // #90 LordHavoc builtin range (9x)
+PF_randomvec,                  // #91
+PF_GetLight,                   // #92
+PF_registercvar,               // #93
+PF_min,                                        // #94
+PF_max,                                        // #95
+PF_bound,                              // #96
+PF_pow,                                        // #97
+PF_FindFloat,                  // #98
+PF_checkextension,             // #99
 #define a PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme,
 #define aa a a a a a a a a a a
 aa // #200
 aa // #300
 aa // #400
-PF_copyentity,         // #400 LordHavoc: builtin range (4xx)
-PF_setcolor,           // #401
+PF_copyentity,                 // #400 LordHavoc: builtin range (4xx)
+PF_setcolor,                   // #401
+PF_findchain,                  // #402
+PF_findchainfloat,             // #403
+PF_effect,                             // #404
+PF_te_blood,                   // #405
+PF_te_bloodshower,             // #406
+PF_te_explosionrgb,            // #407
+PF_te_particlecube,            // #408
+PF_te_particlerain,            // #409
+PF_te_particlesnow,            // #410
+PF_te_spark,                   // #411
+PF_te_gunshotquad,             // #412
+PF_te_spikequad,               // #413
+PF_te_superspikequad,  // #414
+PF_te_explosionquad,   // #415
+PF_te_smallflash,              // #416
+PF_te_customflash,             // #417
+PF_te_gunshot,                 // #418
+PF_te_spike,                   // #419
+PF_te_superspike,              // #420
+PF_te_explosion,               // #421
+PF_te_tarexplosion,            // #422
+PF_te_wizspike,                        // #423
+PF_te_knightspike,             // #424
+PF_te_lavasplash,              // #425
+PF_te_teleport,                        // #426
+PF_te_explosion2,              // #427
+PF_te_lightning1,              // #428
+PF_te_lightning2,              // #429
+PF_te_lightning3,              // #430
+PF_te_beam,                            // #431
+PF_vectorvectors,              // #432
+PF_te_plasmaburn,              // #433
+PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
+PF_getsurfacepoint,     // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
+PF_getsurfacenormal,    // #436 vector(entity e, float s) getsurfacenormal = #436;
+PF_getsurfacetexture,   // #437 string(entity e, float s) getsurfacetexture = #437;
+PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
+PF_getsurfaceclippedpoint,// #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
 };
 
 builtin_t *pr_builtins = pr_builtin;